Files
upbit-trader/STRATEGY.md
joungmin 6580cda017 docs: update STRATEGY.md with F&G filter and project structure changes
- Add F&G filter section: FNG_MIN_ENTRY=41, 1-year backtest proof
  (no filter: +95K KRW → F&G>=41: +1.72M KRW over 1 year)
- Add F&G backtest result (section D) with adaptive param findings
- Update section E: rename velocity backtest (was D)
- Split file table into production core vs tests/ scripts
- Update simulation commands to use tests/ path prefix
- Update env config: add FNG_MIN_ENTRY=41
- Add changelog entries for F&G filter, project structure, ecosystem.config.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 16:14:50 +09:00

358 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Volume Lead 전략 가이드
## 전략 개요
**거래량 선행(Volume Lead) 매집 전략** — 가격이 횡보하는 중 거래량 급증이 발생하면
매집 신호로 기록하고, 이후 일정 수준 이상 상승 시 진입하는 선진입 전략.
> 핵심 아이디어: 대형 매수자는 가격을 올리지 않고 조용히 매집한다.
> 거래량이 먼저 급증하고, 가격 상승은 그 뒤에 따라온다.
**캔들 단위: 40분봉** (Upbit `minute10` API로 수신 후 인메모리 40분 리샘플링)
---
## 진입 조건 (2단계)
### 1단계: 매집 신호 감지
다음 두 조건 동시 충족 시 `signal_price` + `vol_ratio` 기록:
| 조건 | 파라미터 | 기본값 |
|------|----------|--------|
| 2h 가격 변동 < N% (횡보) | `PRICE_QUIET_PCT` | 2.0% |
| 직전 40분봉 거래량 ≥ 로컬 5h(7봉) 평균 × M배 | `VOLUME_MULTIPLIER` | 2.0x |
- 신호 발생 시 텔레그램 알림 발송
- `SIGNAL_TIMEOUT_H` 내 진입 조건 미달 시 신호 초기화 (기본: 8h)
- 신호가 이하 하락 시 즉시 초기화 (매집 실패 판단)
> **2h 횡보 체크**: Oracle DB에 저장된 실시간 가격 기록을 조회 (`get_price_n_hours_ago`)
> **거래량 체크**: `minute10` → 40분봉 resample → 직전 완성봉 vs 이전 7봉 평균
### 2단계: 추세 확인 후 진입 (거리 기반 OR 속도 기반 — 먼저 충족되는 조건으로 진입)
**A. 거리 기반**: `signal_price` 대비 +임계값% 이상 상승 시 매수 (vol_ratio에 따라 동적 조정)
| vol_ratio | 진입 임계값 | 설명 |
|-----------|------------|------|
| ≥ 5.0x | +1.0% | 매우 강한 신호 |
| ≥ 3.5x | +2.0% | 강한 신호 |
| ≥ 2.5x | +3.0% | 중간 신호 |
| < 2.5x | +`TREND_AFTER_VOL`% | 기본 임계값 |
**B. 속도 기반 (조기 진입)**: 신호 후 가격 상승 속도가 `VELOCITY_THRESHOLD` 이상이면 즉시 진입
| 파라미터 | 기본값 | 설명 |
|----------|--------|------|
| `TREND_AFTER_VOL` | 4.8% | 거리 기반 기본 임계값 |
| `VELOCITY_THRESHOLD` | 0.10%/분 | 속도 기준 (6%/h — 가파른 상승 감지) |
| `VELOCITY_MIN_MOVE` | 0.5% | 속도 체크 전 최소 이동 % |
| `VELOCITY_MIN_AGE_M` | 5분 | 속도 체크 전 최소 경과 시간 |
> **예시 (BTC 23:20 신호)**: 23:30에 +1.26%/6분 = 0.21%/분 → 속도 기준 충족 → 조기 진입
> 실제 진입(00:34, 100,840,000) 대비 약 1시간 빠른 23:30(97,286,000) 진입 가능
### 신호 감시 스레드 (Fast Poll)
신호 감지 후 전체 스캔 60초를 기다리지 않고 해당 종목만 빠르게 폴링.
| 파라미터 | 기본값 | 설명 |
|----------|--------|------|
| `SCAN_INTERVAL` | 60초 | 전체 시장 스캔 주기 |
| `SIGNAL_POLL_INTERVAL` | 15초 | 신호 종목 집중 감시 주기 |
- 신호 발생 시 별도 스레드(`signal-fast-poll`)가 15초마다 해당 종목만 체크
- 목표 임계값(거리 or 속도) 도달 즉시 매수 → 60초 지연 제거
---
## 청산 조건
### 트레일링 스탑 (ATR 기반)
- `minute10` → 40분봉 resample 후 최근 7봉(≈5h)의 평균 진폭 계산
- ATR = 평균진폭 × 1.5 계수 → 동적 손절폭 산출
- 최소 1.0% / **최대 2.0%** 범위 내 자동 조정
- 최고가 대비 손절폭 이하 하락 시 즉시 청산
- ATR 캐시: 40분마다 갱신 (캐시 TTL=2400초)
### 타임 스탑
- 보유 `TIME_STOP_HOURS`h 경과 후 수익률 < `TIME_STOP_MIN_GAIN_PCT`% 이면 청산
- 기본값: 8시간 경과 / 수익률 3% 미만
---
## 진입 사전 필터
### F&G (공포탐욕지수) 필터 — `core/fng.py`
**alternative.me API** 기반 일일 공포탐욕지수(0~100) 조회. 극공포·공포 구간에서는
신규 매수를 전면 차단한다.
| F&G 구간 | 레이블 | 진입 여부 |
|----------|--------|-----------|
| 76 ~ 100 | 극탐욕 | ✅ 허용 |
| 56 ~ 75 | 탐욕 | ✅ 허용 |
| 46 ~ 55 | 중립 | ✅ 허용 |
| 41 ~ 45 | 약공포 | ✅ 허용 |
| 26 ~ 40 | 공포 | ❌ 차단 |
| 0 ~ 25 | 극공포 | ❌ 차단 |
- 환경변수 `FNG_MIN_ENTRY=41` (기본값) — 이 값 미만이면 스캔 전체 스킵
- API는 하루 1회 KST 09:00 업데이트 → 캐시 TTL 24시간
- API 실패 시 폴백: 50 (중립) — 안전하게 진입 허용
- BEAR 레짐 차단과 독립적으로 동작 (둘 다 통과해야 스캔 진행)
**1년 백테스트 검증 결과** (2025-03-03 ~ 2026-03-03, 18종목 1h봉):
| 구성 | 거래수 | 승률 | 평균PnL | 누적PnL(KRW) |
|------|--------|------|---------|------------|
| 필터 없음 | 820건 | 32.7% | +0.012% | +95,743원 |
| **F&G≥41** | **372건** | **39.5%** | **+0.462%** | **+1,719,047원** |
- 차단 거래 452건 평균 PnL: **-0.372%** → 손실 거래 제거 효과
- 극공포 구간(0~25): 23.5% 승률, -0.540%/건 → 파라미터 조정으로 개선 불가 (검증됨)
---
## 리스크 관리
### Walk-Forward (WF) 필터
| 파라미터 | 기본값 | 설명 |
|----------|--------|------|
| `WF_WINDOW` | 4 | 이력 윈도우 (직전 N건) — 4연패 시 차단 |
| `WF_MIN_WIN_RATE` | 0.01 | 최소 승률 임계값 (1%) |
| `WF_SHADOW_WINS` | 2 | 차단 해제 조건 (가상 N연승) |
- 직전 4건 승률 < 1% → 해당 종목 진입 차단
- 차단 후 가상 추적으로 2연승 달성 시 자동 복귀
- **WF 차단 상태는 Oracle DB(`wf_state` 테이블)에 영속 저장** → 재시작 후에도 복원
### 예산 관리 (복리)
- 수익 발생 시: `운용예산 = 초기예산 + 누적수익` (복리 증가)
- 손실 발생 시: `운용예산 = 초기예산 + 누적수익` (차감)
- 하한선: 초기예산의 30% (기본: 4,500,000원)
- 포지션당 크기: `운용예산 / MAX_POSITIONS`
---
## 시장 레짐 적응
BTC·ETH·SOL·XRP 가중평균 **2h 추세 score**로 레짐 결정.
| 종목 | 가중치 |
|------|--------|
| KRW-BTC | 40% |
| KRW-ETH | 30% |
| KRW-SOL | 15% |
| KRW-XRP | 15% |
| 레짐 | score 기준 | vol_mult | 신규 진입 |
|------|-----------|----------|---------|
| BULL | ≥ +1.5% | 1.5x | 허용 |
| NEUTRAL | -0.5% ~ +1.5% | 2.0x | 허용 |
| BEAR | < -0.5% | 3.5x | **전면 차단** |
- BEAR 레짐 감지 시 신규 매수 전면 차단 (기존 포지션 청산은 정상 진행)
- 레짐 캐시 TTL: 10분 (API 호출 최소화)
- 현재가는 매 레짐 계산 시 Oracle DB(`price_history`)에 저장 → 2h 전 가격 조회에 재활용
> **2026-03-03 조정**: BEAR 기준 -1.0% → **-0.5%**로 강화
> 완만한 하락장(score ≈ -0.4%)에서 NEUTRAL로 오판하던 문제 수정
---
## 운용 설정 (.env)
```env
# 핵심 전략
PRICE_QUIET_PCT=2.0 # 2h 횡보 기준 (%)
TREND_AFTER_VOL=4.8 # 진입 임계값 (신호가 대비 %)
SIGNAL_TIMEOUT_H=8.0 # 신호 유효 시간 (h)
VOLUME_MULTIPLIER=2.0 # 거래량 배수 기준
# 속도 기반 조기 진입
VELOCITY_THRESHOLD=0.10 # %/분 (0.10 = 6%/h)
VELOCITY_MIN_MOVE=0.5 # 최소 이동 % (잡음 제거)
VELOCITY_MIN_AGE_M=5.0 # 최소 경과 시간 (분)
# 청산
TIME_STOP_HOURS=8 # 타임스탑 보유 시간
TIME_STOP_MIN_GAIN_PCT=3 # 타임스탑 최소 수익률
# 포트폴리오
MAX_BUDGET=15000000 # 초기 운용 예산
MAX_POSITIONS=3 # 최대 동시 보유 종목
# 감시 주기
SIGNAL_POLL_INTERVAL=15 # 신호 종목 빠른 감시 (초)
# WF 필터
WF_WINDOW=4
WF_MIN_WIN_RATE=0.01
WF_SHADOW_WINS=2
# F&G 필터
FNG_MIN_ENTRY=41 # 이 값 미만(공포/극공포) 시 신규 매수 전면 차단
```
---
## 백테스트 결과 요약
### A. 365일 — 1h봉, WF 적용 (`sim_365.py`)
> 기간: 2025-03-02 ~ 2026-03-02 / 데이터: Oracle DB 1h OHLCV / 20종목
| 항목 | 값 |
|------|-----|
| 초기 예산 | 15,000,000원 |
| 최종 자산 | 29,996,109원 |
| 수익률 | **+100%** |
| 최대 낙폭 | -3.81% (-57만원) |
| 거래수 | 190건 (WF 183건 차단) |
| 승률 | 46% |
| 월평균 수익 | 약 115만원 |
### B. 45일 — 40분봉, WF + 복리 적용 (`sim_45m40.py`)
> 기간: 2026-01-20 ~ 2026-03-02 / 데이터: Upbit minute10 캐시 40분 리샘플링 / 20종목
| 항목 | 값 |
|------|-----|
| 초기 예산 | 15,000,000원 |
| 최종 자산 | 21,684,574원 |
| 수익률 | **+44.56%** |
| 최대 낙폭 | -3.90% (-585,102원) |
| 거래수 | 87건 (WF 3건 차단 / MAX_POS 1건 스킵) |
| 승률 | 47.1% |
| 월평균 수익 | 약 2,228,000원 |
| 월 | 거래 | 승률 | 월수익 | 누적수익 |
|----|------|------|--------|---------|
| 2026-01 | 17건 | 29% | -160,000원 | -160,000원 |
| 2026-02 | 61건 | 49% | +6,217,000원 | +6,057,000원 |
| 2026-03 | 9건 | 67% | +627,000원 | +6,685,000원 |
> **참고 — 봉 단위별 단순 PnL 합산 비교** (WF 미적용, `interval_sweep.py`)
>
> | 봉 단위 | 거래수 | 승률 | 누적PnL | 최대낙폭 |
> |---------|--------|------|---------|---------|
> | 10분 | 180 | 33.9% | +15.8% | -32.6% |
> | 20분 | 120 | 36.7% | +31.0% | -16.7% |
> | 30분 | 91 | 48.4% | +81.7% | -12.9% |
> | **40분** | **91** | **48.4%** | **+119.4%** | **-11.2%** ← 채택 |
> | 50분 | 83 | 50.6% | +94.7% | -17.1% |
> | 60분 | 65 | 50.8% | +88.3% | -11.9% |
### C. ATR_MAX_STOP 스윕 — 1h봉 기준 (`atr_sweep.py`)
> 데이터: Oracle DB 1h OHLCV / 20종목
| ATR_MAX | 승률 | 누적PnL | 최대낙폭 | 평균스탑 |
|---------|------|---------|---------|---------|
| 1.5% | 52.3% | +442% | -3.2% | 1.49% |
| **2.0%** | **50.8%** | **+299%** | **-4.1%** | **1.49%** ← 현재 채택 |
| 2.5% | 50.8% | +256% | -5.3% | 1.77% |
| 4.0% | 45.9% | -52% | -29.1% | 3.11% |
### D. F&G 필터 효과 비교 — 1h봉 (`tests/fng_sim_comparison.py`)
> 기간: 2025-03-03 ~ 2026-03-03 / 데이터: Oracle DB 1h OHLCV / 18종목
| 구성 | 거래수 | 승률 | 평균PnL | 누적(KRW) |
|------|--------|------|---------|----------|
| 필터 없음 | 820건 | 32.7% | +0.012% | +95,743원 |
| **F&G≥41** | **372건** | **39.5%** | **+0.462%** | **+1,719,047원** |
- 차단 452건 평균 PnL -0.372% → 약 168만원 손실 방지
- 극공포 적응 파라미터 테스트(`tests/fng_adaptive_backtest.py`): 어떤 파라미터도 극공포 성과 개선 불가 → 진입 차단이 유일한 해결책
### E. 속도 진입 효과 비교 — 10분봉 (`tests/velocity_backtest.py`)
> 기간: 2026-01-19 ~ 2026-03-02 / 10분봉 캐시 / 20종목
| 설정 | 속도진입 | 승률 | 수익률 | 최대낙폭 |
|------|---------|------|--------|---------|
| A: 거리 기반만 | 0건 | 34.7% | +8.83% | -8.35% |
| B: +속도(0.10) | 89건 | 33.6% | +13.36% | **-4.50%** |
| B: +속도(0.15) | 39건 | 35.7% | +17.19% | -7.84% |
- **0.10 채택**: 낙폭 -8.35% → -4.50% 개선, 수익률 +4.5%p
- 속도진입 BTC 예시: 0.21%/분 → 0.10 기준 충족 (0.20도 아슬하게 충족)
---
## 주요 파일
**프로덕션 코어**
| 파일 | 역할 |
|------|------|
| `core/strategy.py` | 진입 신호 로직 (40분봉 vol-lead + 속도 기반 조기 진입) |
| `core/fng.py` | F&G 필터 (alternative.me API, 24h 캐시) |
| `core/monitor.py` | ATR 트레일링 스탑 + 타임스탑 (40분봉 ATR) |
| `core/trader.py` | 주문 실행 + 복리 예산 관리 |
| `core/market_regime.py` | 시장 레짐 감지 (BTC/ETH/SOL/XRP 가중 2h 추세) |
| `core/price_db.py` | 가격 DB + WF 상태 영속화 |
| `core/notify.py` | 텔레그램 알림 (매수/매도/신호/상태) |
| `daemon/runner.py` | 전체 스캔 루프 + 신호 종목 fast-poll 스레드 |
| `main.py` | 진입점 (스레드 시작 + pm2) |
| `ecosystem.config.js` | pm2 설정 (로그 경로 `logs/`) |
**백테스트 / 분석 (`tests/`)**
| 파일 | 역할 |
|------|------|
| `tests/fng_sim_comparison.py` | F&G 필터 적용 전후 수익 비교 (1년) |
| `tests/fng_1y_backtest.py` | F&G 구간별 성과 분석 (7개 구간, 1년) |
| `tests/fng_adaptive_backtest.py` | F&G 구간별 적응 파라미터 테스트 |
| `tests/sim_365.py` | 365일 복리 시뮬레이션 (1h봉, DB) |
| `tests/sim_45m40.py` | 45일 복리 시뮬레이션 (40분봉, 캐시) |
| `tests/velocity_backtest.py` | 속도 진입 효과 비교 백테스트 |
| `tests/atr_sweep.py` | ATR_MAX_STOP 파라미터 스윕 |
| `tests/interval_sweep.py` | 봉 단위별 성과 비교 |
| `tests/backtest_db.py` | 백테스트 결과 Oracle DB 저장 |
| `tests/ohlcv_db.py` | OHLCV 시계열 DB 캐시 관리 |
---
## 시뮬레이션 실행
```bash
# F&G 필터 효과 비교 (필터 없음 vs F&G≥41)
python tests/fng_sim_comparison.py
# F&G 구간별 성과 분석 (7개 구간, 1년)
python tests/fng_1y_backtest.py
# 45일 복리 시뮬 — 40분봉 (현재 전략 기준)
python tests/sim_45m40.py
# 365일 복리 시뮬 — 1h봉 (DB에서 로드)
python tests/sim_365.py
# 속도 진입 효과 비교
python tests/velocity_backtest.py
# 봉 단위별 비교 (10m 캐시 필요)
python tests/interval_sweep.py
# ATR_MAX_STOP 스윕 (DB에서 로드)
python tests/atr_sweep.py
# OHLCV DB 상태 확인 / 업데이트
python tests/ohlcv_db.py status
python tests/ohlcv_db.py update
```
---
## 변경 이력
| 날짜 | 변경 내용 |
|------|---------|
| 2026-03-03 | **F&G 필터 추가** (`FNG_MIN_ENTRY=41`) — 1년 백테스트로 검증, 수익 +95K→+1.72M원 |
| 2026-03-03 | F&G 정보 텔레그램 알림 통합 (매수/신호/상태 리포트) |
| 2026-03-03 | F&G API 캐시 TTL 24시간 (하루 1회 KST 09:00 업데이트) |
| 2026-03-03 | 프로젝트 구조 정리: `tests/` `data/` `logs/` 폴더 분리 |
| 2026-03-03 | `ecosystem.config.js` 추가 — pm2 로그 경로 `logs/` 통일 |
| 2026-03-03 | BEAR_THRESHOLD -1.0% → **-0.5%** 강화 (완만한 하락장 오판 수정) |
| 2026-03-03 | 속도 기반 조기 진입 추가 (`VELOCITY_THRESHOLD=0.10%/분`) |
| 2026-03-03 | 신호 종목 fast-poll 스레드 추가 (`SIGNAL_POLL_INTERVAL=15s`) |
| 2026-03-03 | vol_ratio 강도별 진입 임계값 티어 추가 (5x→1%, 3.5x→2%, 2.5x→3%) |