모바일
구현 완료 — 두 기능 모두 최신 APK에 포함 ✅
APK: pro-trading-web/android/app/build/outputs/apk/debug/app-debug.apk (14:57 빌드 — 블로그 재생 + 음성매매 모두 포함, dex에 플러그인 탑재 확인)
- 🎧 블로그 백그라운드 재생
블로그에서 영상 보다가 🐯로 매매화면 복귀 → 재생 지속. 우하단에 🐯⏸ 정지 칩, 독 🐯 버튼에 🔊 배지.
- 🎤 음성 즉시 매매 (즉시실행 · 코인 MEXC)
| 말하기 | 동작 |
|---|---|
| "매수" / "롱" | 현재 심볼 시장가 LONG (기존 SHORT면 자동 리버스, 동방향이면 물타기 추가) |
| "매도" / "숏" | 시장가 SHORT (동일 로직) |
| "청산" / "전량" / "다 팔아" | 전량 FLASH 청산 |
| "취소" | 미체결 주문 취소 |
사용법: 매매화면 우하단 🎤 버튼 탭 → 첫 회 마이크 권한 승인 → 빨간 버튼으로 변하며 연속 청취 ("듣는 중…" + 인식 문구 표시) → 말하면 즉시 주문 + 결과 TTS 발화. 다시 탭하면 중지.
설계 특이점:
- WebView는 음성인식(Web Speech API)이 아예 안 되어서 네이티브 SpeechPlugin.java 직접 구현 (기존 __mrSetPip 브리지와 동일 패턴, ko-KR, 부분 결과 스트리밍으로 "매수" 끝말 즉시 촉발)
- 집행은 기존 /api/manual-trade — 프랍 필터 완전 오버라이드 시장가 진입 + 진입 후 TP/SL·트레일링은 엔진이 자동 관리 (반자동 철학 유지)
- 오주문 방지 3중장치: 같은 명령 5초 중복 무시 (TTS가 "매수 완료" 말하는 에코 재인식 차단) / 명령 간 최소 1.5초 / 🎤 켜는 순간 블로그 영상 자동 정지 (영상 음성이 "매수"를 발화하는 사고 방지)
- 코인(USDT 심볼)에서만 동작 — KR 주식/옵션 화면에선 음성으로 거부 안내
- 앱 백그라운드 전환 시 청취 자동 종료
참고:
- 음성인식엔 구글 음성 서비스 필요 (대부분 한국 폰 기본 탑재)
- 블로그 서버와 모바일 양쪽 다 커밋하지 않은 상태입니다 — 확인 후 커밋하시면 됩니다
- 후보 과제: KIS 주식 음성, "절반 청산" 같은 비율 명령
지정가 진입 → 자동 추적 연결 완료 ✅
동작 흐름 (말씀하신 방식 그대로):
"지정가 매수" 🎤
→ 현재가 − step_offset% 지정가 주문(메이커) + follow 플래그 탑재
→ 가격 도달해 체결되는 순간
→ 🎯 추적 자동 실행 = 익절가를 "현재가" 메이커 지정가로 고정
· 롱 포지션 → 현재가 위에 지정가 매도 (수수료 0)
· 숏 포지션 → 현재가 아래에 지정가 매수
구현 (백엔드 엔진 — 앱을 꺼도 작동):
- /api/manual-step에 follow 파라미터 추가, 음성 지정가 주문은 항상 follow: true
- DEMO: 가드 루프의 지정가 체결 지점(신규 진입 추적 발동
- LIVE: 3초 싱크 루프가 거래소 체결을 감지하는 기존 블록([LIVE 지정가 체결 감지])에 훅 — 체결 감지 즉시 메이커 청산
지정가 안착
- follow 플래그는 대기주문 기록 안에만 살아있어서, 주문 취소하거나 드래그로 가격 옮기면 자동 추적도 자동 해제됩니다
(오발동 없음)
- 시스템 로그에 🎯 [자동 추적·…청결] 로그 남음
상태:
- 백엔드 재시작 완료 — OpenAPI에 follow 필드
- APK 21:23 빌드 → 재설치: pro-trading-web/android/app/build/outputs/apk/debug/app-debug.apk
체결 후 차트에서 TP 라인이 현재가 바로 위(아래)에 붙는 것으로 확인하실 수 있습니다. 이후 가격이 더 유리해지면 📊추적 버튼을 다시 눌러 라인을 끌어올리는 기존 패턴
비중 음성 명령 추가 완료 ✅ (APK 22:42 빌드)
사용법 — 비중을 명령 앞에:
| 말하기 | 동작 |
|---|---|
| "50% 지정가 매수" | 비중 50% 지정가 |
| "10% 매수" | 비중 10% 시장가 매수 |
| "70% 지정가 매도" | 비중 70% 지정가 |
인식 범위:
- 숫자 형태 전부 지원: "50%", "50퍼센트", "50프로", 그리고 한글 숫자 "오십 프로", "이십오 퍼센트"도 인식합니다 (예약된 1/5/10/25/50/70 포함 1~100 전부)
- 비중 없이 그냥 "지정가 매수"하면 현재 설정된 비중 그대로
작 방식:
- 주문패드의 비중 셀렉터(1/5/10/25/50/70)와 동일한 경로(order_size_pct 패치)로 적용 — 주문 직전에 반영 완료를 기다린 뒤 주문합니다 (레이스 방지)
- 적용된 비중은 패드 셀렉트에도 즉시 표시되고 그 비중 — 패드에서 바꾸면 그걸 따름)
- 결과 발화: "비중 50퍼센트 지정가 매수 완료…"
TP/SL + 추적 음성 명령 추가 완료 ✅ (APK 23:27 빌드)
전체 음성 명령 체계 (최종):
| 말하기 | 동작 |
|---|---|
| "TP 40%" / "티피 40" / "익절 40" | TP 40% 설정 — 열린 포지션 즉시 라이브 반영(차트 라인 이동) + 다음 진입 설정 |
| "SL 15%" / "에스엘 15" / "손절 15" | SL 15% 설정 (동일) |
| "추적" / "트레일" | 📊추적 버튼과 동일 — 지정가 대기 중이면 현재가 메이커 진입으로 전환, 포지션 보유 중이면 익절가 현재가 재고정 |
| 매수/매도 · 지정가 매수/매도 · N% 접두 · 청산 · 취소 | 기존 그대로 |
설계 포인트:
- 파서 최우선 판정: "TP 40%"의 숫자 40이 주문비중으로 오인되지 않게 TP/SL 매칭을 비중 파싱보다 먼저 소비합니다 (실수 방지 핵심)
- TP/SL은 주문패드 ±버튼과 완전 동일한 경로(handleEditTpsl) — 열린 포지션엔 /api/edit-tpsl 즉시 반영, 언급 안 쪽 값은 현재 설정 유지 (예: "TP 40"만 말해도 SL은 그대로)
- TP/SL·추적 후에도 마이크 유지 — "티피 40" → 바로 "손절 15" 연속 입력 가능
- 발화 예시: "티피 40퍼센트 설정 완료" / "추적 완료. 추적: 현재가 0.079 메이커 LONG 신규 진입"
사용 시나리오 (말씀하신 흐름):
"지정가 매수" → 현재가 메이커 진입
"TP 40" → 익절 40%
"SL 15" → 손절 15%
(필요 시) "추적" → 익절가를 현재가로 끌어올리기
데스크탑(macOS·에어팟) 음성 매매 구현 완료 ✅
아키텍처:
에어팟(맥 기본 입력)
→ Swift 헬퍼 (macOS SFSpeechRecognizer·ko-KR·★온디바이스 우선 — 네트워크 왕복 없음)
→ Electron main (spawn·IPC 중계)
→ 데스크탑 화면 🎤 버튼 (우하단) — 기존 주문 API·설정·TTS 전부 재사용
모바일과 완전 동일한 명령 체계:
매수/매도 · 지정가 매수/매도(추적 자동 전환) · N% 비중("50% 지정가 매수") · TP/SL("tp 40"/"손절 15") · 추적 · 청산 · 취소 — 진입 집행 성공 시 마이크 자동 종료, TTS 결과 발화까지 동일 규칙.
필요한 작업 (지금부터):
- 데스크탑 앱 재시작 — main 프로세스(preload 포함) 변경은 HMR이 안 됩니다:
cd /Users/moonrise/Documents/IdeaProjects/pro- dev:electron
- 첫 🎤 클릭 시 macOS 권한 대화상자 2개 (마이크 + 음성인식) → 모두 허용
- 우하단 🎤 탭 → 빨간색으로 변하면 에어팟으로
주의 (기록해둔 함정):
- Electron node_modules 재설치 시 Info.plist 권한 문구 패치가 사라집니다 — 그때 말씀 주시면 재패치합니다 (기존 bg-mode 패치와 동일한 취급)온디바이스 한국어 모델이 맥에 없으면 자동으 (설정 > 키보드 > 받아쓰기에서 온디바이스다운로드 가능)
현재 상태: 데스크탑 빌드 통과 · Swift 헬퍼 컴파일+자가점검 OK · 미커밋 (테스트 후 커밋)
테스트해보시고 괜찮으면 이번 세션 전체 작업(음성 지연·백엔드 종료 수정 + TP/SL·추적 + 데스크탑) 한 번에 커밋하겠습니다.
완료했습니다. ultracode 워크플로(8에이전트 조사+적대적 검증, 전원 코드 레벨 CONFIRMED)로 원인을 확정하고 수정했습니다.
고스트봉 원인 5가지 → 수정
| 원인 | 현상 | 수정 |
|---|---|---|
| 스테일 fetch 잔상 (주범) | 종목 A→B 전환 시 A의 늦은 fetch가 B 차트에 그려짐 | loadSeqRef 에포크 토큰 — loadHistory 매 호출마다 번호를 올리고, await 복귀 시 최신 호출만 setData 권한 |
| 전환 창구 틱 혼입 | 분봉 전환 직후 새 분봉 버킷 틱이 구분봉 데이터에 봉을 append | loadedIvRef — 시리즈에 실제 로드된 분봉 기록 → applyTick·10s 폴(진입+await 후 양쪽)에서 불일치 시 차단 |
| 백필 클로저 오염 | 틱 점프 백필의 .then이 전환 후 이전 분봉을 시리즈에 주입 | 백필 시작 에포크 캡처 후 복귀 시 검사 + seed:false |
| 얕은 캐시 시드 | K-시그널(1000봉)·WhatIf(300)·백필(60)이 빈 캐시를 시드 → 왼쪽이 뚫린 짧은 차트 | getCandles(sym, iv, minLen) — 깊이 부족하면 캐시히트 불가(완전 fetch) |
| fetch 실패 방치 | 빈 응답 시 이전 데이터 잔상 유지 | 클리어 + 1.5s×2회 제한 재시도 |
속도·최적화
- KR 10s 폴 스킵 — raw time이 압축 축에 update 불가해 전량 throw되는 완전 무응답이었음 (KR_TICK이 실시간 담당)
- KR_TICK → 200ms 스로틀 경유 — 기존엔 무스로틀 applyTick 직결이라 발열·과다 redraw 유발
- 프리웜 절감 — 깊은 캐시가 이미 있으면 재전환 시 5분봉×2000봉 재페치 생략 + 타이머 클린업
- KSignal limit 1000 → chartKlineLimit — 차트와 inflight 1회 합침(같은 키 이중 fetch 제거)
- klineCache inflight 크로스-해제 방지 — 다른 limit 페치가 교체된 inflight를 옛 finally가 지우는 버그 (양측 동기화)
변경 파일
- 모바일 App.jsx (가드 5종), lib/klineCache.js, components/KSignal.jsx
- 데스크탑 lib/klineCache.js (양측 동일 로직 규칙대로 미러)
- 빌드 검증 통과 (120 모듈, 194ms) — 앱에서 웹뷰 리로드하면 적용됩니다
잔여 최적화(선택 과제, 이번엔 미실시): React.memo 무력화 해소(flowRowChip useMemo 등), drawMarkers/loadManualTrends 시그니처 메모 — 필요하면 말씀 주세요.