# 자동매매 사용/검증/보안 가이드 (3계층 구조) 이 문서는 자동매매를 아래 3개 영역으로 나눠서 설명합니다. 1. 사용자 브라우저 2. Next.js 서버(API) 3. 워커(Node) 프롬프트 입력값이 실제로 어디 함수/어디 API로 흘러가는지 추적하려면 아래 문서를 같이 보세요. - `common-docs/features/autotrade-prompt-flow-guide.md` --- ## 1) 한눈에 구조 ```text ┌───────────────────────────── 사용자 브라우저 ─────────────────────────────┐ │ /trade 자동매매 UI │ │ - 설정 입력(전략/투자금/손실한도/임계치) │ │ - start/stop/heartbeat/signals 호출 │ └───────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────── Next.js 서버 (API) ──────────────────────────┐ │ /api/autotrade/strategies/* │ │ /api/autotrade/sessions/* │ │ /api/autotrade/signals/generate │ │ /api/autotrade/worker/tick │ └───────────────────────────────────────────────────────────────────────────┘ ▲ │ x-autotrade-worker-token │ ┌────────────────────────────── Worker (Node) ─────────────────────────────┐ │ scripts/autotrade-worker.mjs │ │ - 주기적으로 /api/autotrade/worker/tick 호출 │ │ - heartbeat 만료 세션 정리 │ └───────────────────────────────────────────────────────────────────────────┘ ``` --- ## 1-1) 개발 실행 시 (내 PC 기준) 1. 브라우저: React 화면 사용 2. Next 개발 서버(`npm run dev`): 화면 + API를 함께 처리 3. 워커(`node scripts/autotrade-worker.mjs`): tick 호출 담당 즉, 개발에서는 보통 `Next 1개 + Worker 1개` 프로세스를 실행합니다. ## 1-2) 운영 배포 시 운영은 보통 아래 2가지 중 하나입니다. 1. 같은 Linux 서버에 Next + Worker 같이 운영 2. Next는 배포 플랫폼, Worker는 별도 Linux 서버에서 운영 공통 원칙: 1. 브라우저는 Next API를 호출 2. 워커도 Next API(`/api/autotrade/worker/tick`)를 호출 3. 워커 인증은 `x-autotrade-worker-token`으로 처리 --- ## 2) 레이어별 역할 ## 2-1) 사용자 브라우저 하는 일: 1. 자동매매 설정 입력 2. 전략 컴파일/검증 요청 3. 세션 시작 후 10초마다 heartbeat 전송 4. 신호 요청 후 주문 가능 여부 판단 5. 브라우저 종료/외부 이동 시 중지 처리 핵심 소스: 1. UI: [`AutotradeControlPanel`](../../features/autotrade/components/AutotradeControlPanel.tsx#L25) 2. 엔진: [`useAutotradeEngine`](../../features/autotrade/hooks/useAutotradeEngine.ts#L118) 3. heartbeat 루프: [`useAutotradeEngine`](../../features/autotrade/hooks/useAutotradeEngine.ts#L336) 4. 주문 직전 게이트+주문 호출: [`useAutotradeEngine`](../../features/autotrade/hooks/useAutotradeEngine.ts#L426) ## 2-2) Next.js 서버(API) 하는 일: 1. 사용자 인증 검사 2. 전략 compile/validate 처리 3. 세션 start/heartbeat/stop/active 관리 4. AI 호출 실패 시 폴백 전략/신호로 대응 5. 워커 토큰 인증 후 만료 세션 정리 핵심 소스: 1. 공통 유틸: [`_shared.ts`](../../app/api/autotrade/_shared.ts) 2. compile: [`POST /strategies/compile`](../../app/api/autotrade/strategies/compile/route.ts#L22) 3. validate: [`POST /strategies/validate`](../../app/api/autotrade/strategies/validate/route.ts#L19) 4. sessions: [`/sessions/start`](../../app/api/autotrade/sessions/start/route.ts#L21), [`/sessions/heartbeat`](../../app/api/autotrade/sessions/heartbeat/route.ts#L18), [`/sessions/stop`](../../app/api/autotrade/sessions/stop/route.ts#L27), [`/sessions/active`](../../app/api/autotrade/sessions/active/route.ts#L9) 5. 신호 생성: [`POST /signals/generate`](../../app/api/autotrade/signals/generate/route.ts#L41) ## 2-3) 워커(Node) 하는 일: 1. 주기적으로 Next API `/api/autotrade/worker/tick` 호출 2. heartbeat 끊긴 세션을 timeout 종료 3. 정리 결과 로그 출력 핵심 소스: 1. 워커 스크립트: [`autotrade-worker.mjs`](../../scripts/autotrade-worker.mjs) 2. 워커 API: [`POST /worker/tick`](../../app/api/autotrade/worker/tick/route.ts#L12) 3. 만료 정리 함수: [`sweepExpiredAutotradeSessions()`](../../app/api/autotrade/_shared.ts#L147) --- ## 3) 가장 헷갈리는 개념 3개 ## 3-1) 폴백 전략(fallback) 뜻: 1. AI를 못 쓰는 상황에서 쓰는 대체 규칙 2. 자동매매를 완전 중지하지 않고 보수적으로 유지 3. 애매하면 `hold`를 더 자주 반환 관련 소스: 1. 전략 폴백: [`createFallbackCompiledStrategy()`](../../lib/autotrade/strategy.ts#L16) 2. 신호 폴백: [`createFallbackSignalCandidate()`](../../lib/autotrade/strategy.ts#L36) 3. AI 호출: [`callOpenAiJson()`](../../lib/autotrade/openai.ts#L187) ## 3-2) heartbeat 뜻: 1. 브라우저가 Next 서버로 보내는 "세션 살아있음" 신호 2. 워커가 보내는 신호가 아님 ## 3-3) worker tick 뜻: 1. 워커가 Next 서버로 보내는 "만료 세션 정리 요청" 2. heartbeat가 끊긴 세션을 timeout 종료 --- ## 3-4) 구독형 CLI 자동판단(신규) 뜻: 1. OpenAI API 키 대신 서버에 설치된 `gemini` 또는 `codex` CLI를 호출해 자동판단 2. 자동판단 결과(JSON)를 파싱해 전략/신호에 반영 3. CLI 호출 실패 또는 파싱 실패 시 규칙 기반으로 자동 폴백 UI에서 선택: 1. 자동매매 설정창에서 `구독형 CLI 엔진`을 `auto/codex/gemini` 중 선택 2. `codex` 또는 `gemini` 선택 시 공식 문서 기반 추천 모델 목록을 드롭다운으로 선택 3. 목록에 없는 최신 모델은 `직접 입력`으로 설정 모델 우선순위: 1. UI에서 선택한 모델(있을 때) 2. `AUTOTRADE_CODEX_MODEL` / `AUTOTRADE_GEMINI_MODEL` 3. `AUTOTRADE_SUBSCRIPTION_CLI_MODEL` 4. 각 CLI 기본 모델 환경변수: ```env AUTOTRADE_AI_MODE=subscription_cli AUTOTRADE_SUBSCRIPTION_CLI=auto AUTOTRADE_SUBSCRIPTION_CLI_MODEL= AUTOTRADE_CODEX_MODEL= AUTOTRADE_GEMINI_MODEL= AUTOTRADE_SUBSCRIPTION_CLI_TIMEOUT_MS=60000 AUTOTRADE_SUBSCRIPTION_CLI_DEBUG=0 AUTOTRADE_CODEX_COMMAND= AUTOTRADE_GEMINI_COMMAND= ``` 동작 우선순위: 1. `AUTOTRADE_SUBSCRIPTION_CLI=auto`면 codex -> gemini 순서로 시도 2. 모델 선택 우선순위는 `vendor 전용 모델` -> `AUTOTRADE_SUBSCRIPTION_CLI_MODEL` -> `CLI 기본 모델` 3. 둘 다 실패하면 fallback 규칙 신호 사용 4. 로그에 `attempts=codex:default:timeout`가 나오면 CLI 타임아웃이므로 `AUTOTRADE_SUBSCRIPTION_CLI_TIMEOUT_MS`를 더 크게 설정 5. 로그에 `attempts=codex:gpt-5-codex:error(...)`처럼 괄호가 붙으면 실제 실패 원인(stderr/spawn 에러)입니다. 어떤 CLI를 썼는지 확인: 1. 자동매매 로그에서 `신호 수신 [subscription_cli:codex:gpt-5-codex]` 또는 `신호 수신 [subscription_cli:gemini:flash]` 확인 2. Network 응답에서 `providerVendor` 확인 - `/api/autotrade/strategies/compile` 응답: `compiledStrategy.providerVendor` - `/api/autotrade/signals/generate` 응답: `signal.providerVendor` 3. Network 응답에서 `providerModel` 확인 - `/api/autotrade/strategies/compile` 응답: `compiledStrategy.providerModel` - `/api/autotrade/signals/generate` 응답: `signal.providerModel` 4. 파싱 실패 시 reason/summary에 `selected=vendor:model; attempts=...` 형태로 시도 결과 포함 `selected=none:default; attempts=codex:gpt-5-codex:error(...)`가 보이면: 1. `AUTOTRADE_SUBSCRIPTION_CLI_DEBUG=1`로 켜고 `npm run dev`를 재시작합니다. 2. Next 서버 콘솔에서 `[autotrade-cli]` 로그를 확인합니다. 3. `spawn:ENOENT`가 보이면 `AUTOTRADE_CODEX_COMMAND` 또는 `AUTOTRADE_GEMINI_COMMAND`에 CLI 절대경로를 넣습니다. 4. 예: `AUTOTRADE_CODEX_COMMAND=C:\\Users\\<계정>\\AppData\\Roaming\\npm\\codex.cmd` 모델 지정 예시: ```env # Codex만 쓸 때 AUTOTRADE_SUBSCRIPTION_CLI=codex AUTOTRADE_CODEX_MODEL=gpt-5-codex # Gemini만 쓸 때 AUTOTRADE_SUBSCRIPTION_CLI=gemini AUTOTRADE_GEMINI_MODEL=flash # auto 모드에서 공통 모델 fallback만 쓸 때 AUTOTRADE_SUBSCRIPTION_CLI=auto AUTOTRADE_SUBSCRIPTION_CLI_MODEL=auto ``` 공식 문서: 1. Codex CLI 옵션(`--model`): 2. OpenAI 모델 목록(`gpt-5-codex` 포함): 3. Gemini CLI 모델 선택: 4. Gemini CLI 모델 우선순위(`--model` > `GEMINI_MODEL`): 모델 갱신 운영 런북: 1. 새 모델 출시 대응 절차: [`autotrade-model-catalog-runbook.md`](./autotrade-model-catalog-runbook.md) 관련 소스: 1. CLI 공급자: [`lib/autotrade/cli-provider.ts`](../../lib/autotrade/cli-provider.ts) 2. 전략 compile 라우트: [`/strategies/compile`](../../app/api/autotrade/strategies/compile/route.ts) 3. 신호 generate 라우트: [`/signals/generate`](../../app/api/autotrade/signals/generate/route.ts) --- ## 4) 환경변수: 어디에 넣는지 ## 4-1) 앱(Next.js 서버) 위치: 1. 로컬: `.env.local` 2. 운영: 배포 환경변수 필수: ```env AUTOTRADE_WORKER_TOKEN=<랜덤시크릿> OPENAI_API_KEY=<옵션, 없으면 폴백 동작> ``` ## 4-2) 워커(Node/PM2) 위치: 1. PM2 실행 셸 환경변수 2. 서버 시스템 환경변수 필수: ```env AUTOTRADE_WORKER_TOKEN=<앱과동일값> AUTOTRADE_APP_URL= AUTOTRADE_WORKER_POLL_MS=5000 ``` 중요: 1. `AUTOTRADE_WORKER_TOKEN`은 사용자별이 아니라 서비스별 시크릿 2. 앱과 워커가 같은 값을 써야 인증 통과 --- ## 5) 실행 순서 (앱/워커 분리) ## 5-1) 로컬 개발 터미널 A: ```bash npm run dev ``` 터미널 B: ```bash AUTOTRADE_WORKER_TOKEN="<앱과같은값>" \ AUTOTRADE_APP_URL="http://127.0.0.1:3001" \ node scripts/autotrade-worker.mjs ``` Windows PowerShell: ```powershell npm run dev # 새 터미널 $env:AUTOTRADE_WORKER_TOKEN="<앱과같은값>" $env:AUTOTRADE_APP_URL="http://127.0.0.1:3001" $env:AUTOTRADE_WORKER_POLL_MS="5000" npm run worker:autotrade ``` 또는 `.env.local` 기반: ```powershell npm run worker:autotrade:dev ``` ## 5-2) 운영(PM2) ```bash export AUTOTRADE_WORKER_TOKEN="<앱과같은값>" export AUTOTRADE_APP_URL="https://your-domain.com" export AUTOTRADE_WORKER_POLL_MS="5000" pm2 start scripts/pm2.autotrade-worker.config.cjs pm2 logs autotrade-worker ``` --- ## 6) end-to-end 흐름 (브라우저 -> 서버 -> 워커) 1. 브라우저: 설정 입력 2. 서버: `/strategies/compile` (AI 또는 폴백) 3. 서버: `/strategies/validate` (리스크 계산) 4. 서버: `/sessions/start` 5. 브라우저: 10초마다 `/sessions/heartbeat` 6. 브라우저: 주기적으로 `/signals/generate` 7. 브라우저: 리스크 게이트 통과 시 주문 API 호출 8. 브라우저: 중지 이벤트 시 `/sessions/stop` 9. 워커: `/worker/tick`로 heartbeat 만료 세션 정리 --- ## 6-1) AI가 실제로 받는 판단 데이터 자동매매는 "자연어 프롬프트만" 보내는 구조가 아닙니다. 실행 중에는 아래 구조화된 데이터가 같이 전달됩니다. 1. 전략(compile 결과) - `selectedTechniques` - `confidenceThreshold` - `maxDailyOrders` - `cooldownSec` - `maxOrderAmountRatio` 2. 시세 스냅샷(signal 요청 시) - `symbol` - `currentPrice` - `changeRate` - `open/high/low` - `tradeVolume` - `accumulatedVolume` - `recentPrices`(최근 체결가 배열) 3. 서버 리스크 검증 결과 - AI 신호가 `buy/sell`이어도 리스크 게이트 미통과 시 주문 차단 즉, AI는 "현재 종목 + 현재가 + 가격 흐름 + 전략 제약"을 같이 받아 판단하고, 최종 주문은 리스크 게이트를 통과해야 실행됩니다. 관련 소스: 1. 스냅샷 구성: [`useAutotradeEngine.ts`](../../features/autotrade/hooks/useAutotradeEngine.ts) 2. 신호 route 검증: [`signals/generate/route.ts`](../../app/api/autotrade/signals/generate/route.ts) 3. 리스크 게이트: [`risk.ts`](../../lib/autotrade/risk.ts) --- ## 7) 보안: 레이어별 핵심 ## 7-1) 브라우저 1. KIS 민감정보는 세션 저장소(sessionStorage) 사용 2. 브라우저 종료 시 세션 저장소 제거 ## 7-2) Next 서버 1. 자동매매 API는 사용자 인증 필요 2. 워커 API는 `x-autotrade-worker-token` 인증 필요 3. 민감정보 문자열 마스킹 처리 ## 7-3) 워커 1. 토큰이 틀리면 401 2. 토큰은 코드 하드코딩 금지 --- ## 8) 역할별로 어디 보면 되는지 1. 기획/대표: 1, 2, 6, 7장 2. QA: 5, 6, 7장 + worker 문서 6, 7장 3. 개발: 2장 소스링크 + worker 문서 전체 --- ## 9) 추가 문서 1. 워커 상세 운영: [`autotrade-worker-pm2.md`](./autotrade-worker-pm2.md)