408 lines
14 KiB
Markdown
408 lines
14 KiB
Markdown
|
|
# 자동매매 사용/검증/보안 가이드 (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)
|