전체적인 리팩토링

This commit is contained in:
2026-03-12 09:26:27 +09:00
parent 406af7408a
commit e51d767878
97 changed files with 13651 additions and 363 deletions

View File

@@ -0,0 +1,82 @@
# 자동매매 모델 카탈로그 운영 런북 (Codex/Gemini)
이 문서는 **새 모델이 나왔을 때** 자동매매 모델 선택 UI/서버 설정을 안전하게 갱신하기 위한 운영 절차입니다.
## 1) 목적
1. Codex/Gemini 신모델을 빠르게 목록에 반영한다.
2. 잘못된 모델 ID로 인해 자동매매가 fallback으로 떨어지는 문제를 줄인다.
3. 운영자가 "어디를 고치고 어떻게 검증하는지"를 한 번에 확인할 수 있게 한다.
## 2) 적용 범위
1. 자동매매 설정창 모델 드롭다운
2. 서버 모델 선택 우선순위(env + UI)
3. 전략/신호 응답에서 `providerVendor`, `providerModel` 추적
## 3) 빠른 절차 (입력 -> 처리 -> 결과)
1. 입력: 공식 문서에서 신규 모델 ID 확인
2. 처리: 모델 옵션 상수 + 안내 문구 + 기본 env 값 점검
3. 결과: UI 선택 가능 + 로그/응답에서 실제 모델 확인 가능
## 4) 공식 소스(항상 여기 먼저 확인)
1. OpenAI Codex CLI: <https://developers.openai.com/codex/cli>
2. OpenAI Models: <https://platform.openai.com/docs/models>
3. Gemini CLI model command: <https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/model.md>
4. Gemini CLI model routing: <https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/model-routing.md>
5. Gemini API models: <https://ai.google.dev/gemini-api/docs/models>
## 5) 코드 반영 위치
1. 모델 드롭다운 목록
- `features/autotrade/types/autotrade.types.ts`
- `AUTOTRADE_SUBSCRIPTION_CLI_MODEL_OPTIONS.codex`
- `AUTOTRADE_SUBSCRIPTION_CLI_MODEL_OPTIONS.gemini`
2. 기본값/우선순위 점검
- `lib/autotrade/strategy.ts` (`resolveDefaultSubscriptionCliModel`)
- `lib/autotrade/cli-provider.ts` (`resolveSubscriptionCliModel`)
3. 사용자 안내 문구(필요 시)
- `features/autotrade/components/AutotradeControlPanel.tsx`
4. 샘플 환경변수 문서화
- `.env.example`
## 6) 모델 추가 규칙
1. 모델 ID는 **공식 문서 표기 그대로** 입력한다.
2. preview 모델은 라벨에 `(프리뷰)`를 명시한다.
3. 종료 예정 모델은 라벨/설명에 종료 예정일을 남긴다.
4. 기존 안정형 모델 1개 이상은 항상 남겨둔다.
5. 목록에 없는 모델도 쓸 수 있도록 `직접 입력` 경로는 유지한다.
## 7) 검증 체크리스트
- [ ] 드롭다운에 신규 모델이 보인다.
- [ ] 신규 모델 선택 후 compile/signal 요청 payload에 `subscriptionCliModel`이 들어간다.
- [ ] 응답에 `providerVendor`, `providerModel`이 기대값으로 온다.
- [ ] 자동매매 로그에 `subscription_cli:vendor:model`이 표시된다.
- [ ] `npm run -s lint` 통과
## 8) 수동 검증 포인트(화면 기준)
1. 자동매매 설정 -> 구독형 CLI 엔진 선택(codex 또는 gemini)
2. 신규 모델 선택 후 자동매매 시작
3. 로그에서 아래 3개 필드 확인
- `subscriptionCliVendor`
- `subscriptionCliModel`
- `providerModel`
## 9) 장애 대응
1. 모델 호출 실패 시 우선 `직접 입력`으로 동일 ID 재시도
2. 계속 실패하면 직전 안정 모델로 즉시 롤백
3. `AUTOTRADE_SUBSCRIPTION_CLI_DEBUG=1`로 서버 로그에서 CLI stderr 확인
## 10) 변경 이력 템플릿
```md
- YYYY-MM-DD: [vendor] modelA, modelB 추가
- YYYY-MM-DD: [vendor] modelX 종료 예정 표기
- YYYY-MM-DD: 기본 추천 모델 변경 (old -> new)
```

View File

@@ -0,0 +1,144 @@
# 자동매매 프롬프트 흐름 추적 가이드 (UI -> 함수 -> AI -> 주문)
이 문서는 "전략 프롬프트를 입력하면 실제로 어디 함수로 흘러가고, 어디서 AI가 호출되는지"를 코드 라인 기준으로 설명합니다.
## 1) 한 줄 요약
사용자가 UI에 프롬프트를 입력하면, 시작/검증 시점에 `compile` API로 전달되어 전략 JSON으로 바뀌고, 실행 중에는 그 전략 JSON + 실시간 시세로 신호를 생성해 주문 여부를 결정합니다.
## 2) 구조 그림
```text
[브라우저 UI]
AutotradeControlPanel.tsx
└─ 프롬프트 입력 + 시작/검증 클릭
[브라우저 엔진 훅]
useAutotradeEngine.ts
└─ prepareStrategy()에서 compile/validate 실행
[브라우저 API 클라이언트]
autotrade.api.ts
└─ /api/autotrade/strategies/compile 호출
[Next 서버 route]
strategies/compile/route.ts
└─ OpenAI / subscription_cli / fallback 분기
[AI Provider]
openai.ts 또는 cli-provider.ts
└─ 전략 JSON 반환
[브라우저 엔진 훅]
useAutotradeEngine.ts
└─ compiledStrategy 저장 후 실행 루프 시작
[신호 루프]
/api/autotrade/signals/generate -> 리스크 게이트 -> 주문 API
```
## 3) 프롬프트 입력 -> 전략 컴파일 (상세 추적)
1. 프롬프트 입력 UI
- 컴포넌트: [`AutotradeControlPanel.tsx#L335`](../../features/autotrade/components/AutotradeControlPanel.tsx#L335)
- 입력 이벤트: [`handlePromptChange`](../../features/autotrade/components/AutotradeControlPanel.tsx#L123)
- store 반영: [`patchSetupForm({ prompt })`](../../features/autotrade/components/AutotradeControlPanel.tsx#L126)
- 같은 화면에서 구독형 CLI vendor/model도 선택 가능: `subscriptionCliVendor`, `subscriptionCliModel`
2. 시작/검증 버튼 클릭
- 시작 버튼 핸들러: [`handleStartAutotrade`](../../features/autotrade/components/AutotradeControlPanel.tsx#L102)
- 검증 버튼 핸들러: [`handlePreviewValidation`](../../features/autotrade/components/AutotradeControlPanel.tsx#L113)
3. 엔진 훅에서 전략 준비
- 함수: [`prepareStrategy()`](../../features/autotrade/hooks/useAutotradeEngine.ts#L138)
- compile 호출: [`compileAutotradeStrategy(...)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L153)
4. 브라우저 API 클라이언트
- 함수: [`compileAutotradeStrategy`](../../features/autotrade/apis/autotrade.api.ts#L30)
- HTTP 호출: [`POST /api/autotrade/strategies/compile`](../../features/autotrade/apis/autotrade.api.ts#L36)
- 전달 필드: `aiMode`, `subscriptionCliVendor`, `subscriptionCliModel`, `prompt`, `selectedTechniques`, `confidenceThreshold`
5. Next API route에서 provider 분기
- 엔드포인트: [`strategies/compile/route.ts#L44`](../../app/api/autotrade/strategies/compile/route.ts#L44)
- fallback 전략 준비: [`createFallbackCompiledStrategy`](../../app/api/autotrade/strategies/compile/route.ts#L67)
- OpenAI 분기: [`compileStrategyWithOpenAi`](../../app/api/autotrade/strategies/compile/route.ts#L87)
- 구독형 CLI 분기: [`compileStrategyWithSubscriptionCliDetailed`](../../app/api/autotrade/strategies/compile/route.ts#L119)
6. OpenAI 실제 호출 지점
- OpenAI 전략 함수: [`compileStrategyWithOpenAi`](../../lib/autotrade/openai.ts#L51)
- 공통 호출기: [`callOpenAiJson`](../../lib/autotrade/openai.ts#L203)
- 외부 API: [`https://api.openai.com/v1/chat/completions`](../../lib/autotrade/openai.ts#L19)
7. 컴파일 결과 반영
- compiledStrategy 저장: [`setCompiledStrategy(...)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L160)
- validate 저장: [`setValidation(...)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L173)
## 4) 실행 중 "자동 프롬프트"가 도는 방식
중요: 실행 중 매 틱마다 자연어 프롬프트를 다시 보내지 않습니다.
1. 시작 시점에만 프롬프트를 전략 JSON으로 컴파일합니다.
2. 실행 루프에서는 "컴파일된 전략 JSON + 현재 시세 스냅샷"으로 신호를 만듭니다.
관련 코드:
1. 신호 요청 주기(12초): [`SIGNAL_REQUEST_INTERVAL_MS`](../../features/autotrade/hooks/useAutotradeEngine.ts#L51)
2. 신호 API 호출: [`generateAutotradeSignal(...)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L495)
3. 서버 신호 route: [`signals/generate/route.ts#L74`](../../app/api/autotrade/signals/generate/route.ts#L74)
4. 신호 생성 OpenAI 함수: [`generateSignalWithOpenAi`](../../lib/autotrade/openai.ts#L116)
신호 요청 시 스냅샷 실제 필드:
1. `symbol`
2. `currentPrice`
3. `changeRate`
4. `open`
5. `high`
6. `low`
7. `tradeVolume`
8. `accumulatedVolume`
9. `recentPrices`
## 5) 신호 -> 주문 판단 (자동 실행 핵심)
1. 신호 생성 결과 수신: [`runtime.setLastSignal(signal)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L504)
2. 리스크 게이트 검사: [`evaluateSignalBlockers(...)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L516)
3. 통과 시 주문 API 호출: [`fetchOrderCash(...)`](../../features/autotrade/hooks/useAutotradeEngine.ts#L556)
즉, AI가 `buy/sell`을 주더라도 리스크 게이트를 통과하지 못하면 주문은 실행되지 않습니다.
## 6) AI를 못 쓰는 경우
1. 전략 폴백: [`createFallbackCompiledStrategy`](../../lib/autotrade/strategy.ts#L26)
2. 신호 폴백: [`createFallbackSignalCandidate`](../../lib/autotrade/strategy.ts#L48)
AI(OpenAI/CLI) 응답 실패 시에도 시스템이 멈추지 않고 보수적으로 동작하도록 설계되어 있습니다.
## 7) Codex CLI인지 Gemini CLI인지 확인하는 법
1. 자동매매 로그에서 확인
- `신호 수신 [subscription_cli:codex:gpt-5-codex]` 또는 `신호 수신 [subscription_cli:gemini:flash]`
- 로그 코드: [`useAutotradeEngine.ts`](../../features/autotrade/hooks/useAutotradeEngine.ts#L564)
2. Network 응답에서 확인
- 전략 컴파일 응답: `compiledStrategy.providerVendor`
- 신호 생성 응답: `signal.providerVendor`
3. 실패 시 어떤 순서로 시도했는지 확인
- 파싱 실패 문구에 `selected=vendor:model; attempts=vendor:model:status` 포함
- `status=timeout`이면 CLI 실행시간 초과입니다. `AUTOTRADE_SUBSCRIPTION_CLI_TIMEOUT_MS`를 늘리세요(권장: 60000).
- 생성 코드: [`summarizeSubscriptionCliExecution`](../../lib/autotrade/cli-provider.ts#L112)
4. 모델 선택 환경변수
- `AUTOTRADE_CODEX_MODEL` (예: `gpt-5-codex`)
- `AUTOTRADE_GEMINI_MODEL` (예: `auto`, `pro`, `flash`, `flash-lite`)
- `AUTOTRADE_SUBSCRIPTION_CLI_MODEL` (vendor 전용 값이 없을 때 공통 fallback)
5. 모델 선택 UI (환경변수보다 우선)
- 자동매매 설정창에서 `subscriptionCliVendor`, `subscriptionCliModel` 선택 시 해당 값이 API payload로 전달되어 CLI 실행 인자에 우선 적용됩니다.

View File

@@ -0,0 +1,407 @@
# 자동매매 사용/검증/보안 가이드 (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`): <https://developers.openai.com/codex/cli>
2. OpenAI 모델 목록(`gpt-5-codex` 포함): <https://platform.openai.com/docs/models>
3. Gemini CLI 모델 선택: <https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/model.md>
4. Gemini CLI 모델 우선순위(`--model` > `GEMINI_MODEL`): <https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/model-routing.md>
모델 갱신 운영 런북:
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=<Next서버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)

View File

@@ -0,0 +1,269 @@
# 자동매매 워커 운영 가이드 (실행/배포 구조 이해용)
이 문서는 "앱을 실행하면 뭐가 어디서 도는지"를 먼저 설명하고, 그다음 실행 방법을 설명합니다.
## 0) 먼저 용어 정리
1. React 앱: 브라우저에서 보이는 UI (`/trade` 화면)
2. Next.js 서버: React 화면 제공 + API(`/api/*`) 처리
3. 워커(Node): 백그라운드에서 `/api/autotrade/worker/tick` 호출하는 별도 프로세스
중요:
1. React와 API는 보통 같은 Next 프로세스에서 동작합니다.
2. 워커는 Next와 별도 프로세스입니다.
---
## 1) 개발(local)에서 실제로 어디서 도는가
```text
내 PC
┌──────────────────────────────────────────────────────────────────────────┐
│ 브라우저(Chrome) │
│ - /trade 화면 렌더링 │
│ - heartbeat 전송 (/sessions/heartbeat) │
└──────────────────────────────────────────────────────────────────────────┘
│ http://127.0.0.1:3001
┌──────────────────────────────────────────────────────────────────────────┐
│ 터미널 A: Next 개발 서버 (`npm run dev`) │
│ - React 페이지 제공 │
│ - /api/autotrade/* API 처리 │
└──────────────────────────────────────────────────────────────────────────┘
│ x-autotrade-worker-token
┌──────────────────────────────────────────────────────────────────────────┐
│ 터미널 B: 워커 (`node scripts/autotrade-worker.mjs`) │
│ - /api/autotrade/worker/tick 주기 호출 │
└──────────────────────────────────────────────────────────────────────────┘
외부 클라우드 서비스
- Supabase(Auth/DB)
- KIS API
- OpenAI API(선택)
```
핵심:
1. 개발에서는 보통 프로세스 2개를 띄웁니다.
2. Next 1개 + Worker 1개
---
## 2) 운영(prod)에서 실제로 어디서 도는가
## 2-1) 패턴 A: 같은 Linux 서버에 Next + Worker
```text
사용자 브라우저
│ HTTPS
[Linux 서버]
- Next 앱 프로세스 (웹 + API)
- Worker 프로세스 (PM2)
└─ 내부에서 /api/autotrade/worker/tick 호출
```
장점:
1. 구성 단순
2. 네트워크 경로 짧음
## 2-2) 패턴 B: Next는 플랫폼(Vercel 등), Worker는 별도 Linux
```text
사용자 브라우저 ──HTTPS──> Next 배포 플랫폼(웹+API)
│ HTTPS + x-autotrade-worker-token
Linux Worker 서버(PM2)
```
장점:
1. 앱/워커 분리 운영 가능
2. 워커 자원 독립 관리 가능
주의:
1. 워커 서버에서 Next 도메인으로 접근 가능해야 함
2. 토큰/URL 설정을 양쪽에 정확히 맞춰야 함
---
## 3) 서버에서 "무엇이 돌아가는지" 체크표
| 구성요소 | 실제 실행 위치 | 프로세스 | 시작 명령 예시 | 역할 |
|---|---|---|---|---|
| React UI | 사용자 브라우저 | Browser Tab | URL 접속 | 화면 렌더링, 사용자 입력 |
| Next 서버 | Linux/플랫폼 | Node(Next) | `npm run dev` 또는 `npm run start` | 웹 + `/api/autotrade/*` 처리 |
| Worker | Linux/Worker 서버 | Node Script(PM2) | `pm2 start scripts/pm2.autotrade-worker.config.cjs` | 만료 세션 정리 |
---
## 4) heartbeat와 worker/tick 차이
1. heartbeat
브라우저 -> Next 서버
세션 살아있음 알림
2. worker/tick
워커 -> Next 서버
heartbeat 끊긴 세션 정리 요청
즉:
1. heartbeat는 "상태 보고"
2. tick은 "청소 작업"
---
## 5) 토큰/URL: 뭘 어떻게 넣어야 하나
## 5-1) `AUTOTRADE_WORKER_TOKEN`
뜻:
1. 사용자용 토큰 아님
2. 앱 서버와 워커 간 내부 인증 시크릿
3. 환경별(dev/staging/prod)로 1개 사용
생성 예시:
```bash
openssl rand -hex 32
```
## 5-2) `AUTOTRADE_APP_URL`
뜻:
1. 워커가 호출할 Next 서버 주소
예시:
1. 로컬: `http://127.0.0.1:3001`
2. 운영: `https://your-domain.com`
---
## 6) 어디 파일/어디 시스템에 넣나
## 6-1) 앱(Next 서버)
위치:
1. 로컬: `.env.local`
2. 운영: 배포 환경변수
필수:
```env
AUTOTRADE_WORKER_TOKEN=<랜덤시크릿>
```
## 6-2) 워커(Node/PM2)
위치:
1. PM2 실행 셸 환경변수
2. 서버 시스템 환경변수
필수:
```env
AUTOTRADE_WORKER_TOKEN=<앱과동일값>
AUTOTRADE_APP_URL=<Next서버URL>
AUTOTRADE_WORKER_POLL_MS=5000
```
중요:
1. 앱/워커 토큰 값은 완전히 같아야 합니다.
2. 다르면 `/worker/tick`가 401로 실패합니다.
---
## 7) 실행 방법
## 7-1) 로컬 개발
터미널 A (Next):
```bash
npm run dev
```
터미널 B (Worker):
```bash
AUTOTRADE_WORKER_TOKEN="<앱과같은값>" \
AUTOTRADE_APP_URL="http://127.0.0.1:3001" \
AUTOTRADE_WORKER_POLL_MS="5000" \
node scripts/autotrade-worker.mjs
```
## 7-1-a) 로컬 개발 (Windows PowerShell)
터미널 A (Next):
```powershell
npm run dev
```
터미널 B (Worker):
```powershell
$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
```
## 7-2) 운영 서버 (PM2)
```bash
npm i -g pm2
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 status
pm2 logs autotrade-worker
pm2 save
pm2 startup
```
---
## 8) 장애 시 빠른 점검
1. 워커 401
원인: 앱/워커 토큰 불일치
조치: `AUTOTRADE_WORKER_TOKEN` 동일화
2. fetch failed
원인: `AUTOTRADE_APP_URL` 오타, Next 미기동
조치: URL/앱 프로세스 확인
3. 세션이 안 정리됨
원인: heartbeat 정상 수신 중일 수 있음
조치: 브라우저 종료 후 TTL 경과 뒤 확인
---
## 9) 관련 소스
1. 워커: [`scripts/autotrade-worker.mjs`](../../scripts/autotrade-worker.mjs)
2. PM2 설정: [`scripts/pm2.autotrade-worker.config.cjs`](../../scripts/pm2.autotrade-worker.config.cjs)
3. 워커 API: [`app/api/autotrade/worker/tick/route.ts`](../../app/api/autotrade/worker/tick/route.ts)
4. heartbeat API: [`app/api/autotrade/sessions/heartbeat/route.ts`](../../app/api/autotrade/sessions/heartbeat/route.ts)
5. 세션 만료 정리: [`app/api/autotrade/_shared.ts`](../../app/api/autotrade/_shared.ts#L147)