전체적인 리팩토링
This commit is contained in:
82
common-docs/features/autotrade-model-catalog-runbook.md
Normal file
82
common-docs/features/autotrade-model-catalog-runbook.md
Normal 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)
|
||||
```
|
||||
144
common-docs/features/autotrade-prompt-flow-guide.md
Normal file
144
common-docs/features/autotrade-prompt-flow-guide.md
Normal 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 실행 인자에 우선 적용됩니다.
|
||||
407
common-docs/features/autotrade-usage-security-guide.md
Normal file
407
common-docs/features/autotrade-usage-security-guide.md
Normal 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)
|
||||
269
common-docs/features/autotrade-worker-pm2.md
Normal file
269
common-docs/features/autotrade-worker-pm2.md
Normal 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)
|
||||
Reference in New Issue
Block a user