docs: update architecture and README for improved clarity and structure

- Revised the architecture document to enhance clarity on system overview, trading decision process, and technical stack.
- Updated the README to emphasize the bot's operational guidelines and risk management features.
- Added new sections in the architecture document detailing the trading decision gates and data pipeline flow.
- Improved the table of contents for better navigation and understanding of the bot's architecture.
This commit is contained in:
21in7
2026-03-07 02:12:48 +09:00
parent 2a767c35d4
commit c577019793
8 changed files with 578 additions and 401 deletions

View File

@@ -1,25 +1,37 @@
# CoinTrader — 아키텍처 문서 # CoinTrader — 아키텍처 문서
> 이 문서는 CoinTrader 코드베이스를 처음 접하는 개발자와 트레이딩 배경 독자 모두를 위해 작성되었습니다. > 이 문서는 CoinTrader의 내부 구조를 설명합니다.
> 기술 스택, 레이어별 역할, MLOps 파이프라인, 핵심 동작 시나리오를 순서대로 설명합니다. > **봇 사용법**은 [README.md](README.md)를 참고하세요.
--- ---
## 목차 ## 목차
1. [시스템 오버뷰](#1-시스템-오버뷰) 1. [시스템 개요](#1-시스템-개요) — 봇이 무엇을 하는지, 어떤 구조인지
2. [코어 레이어 아키텍처](#2-코어-레이어-아키텍처) 2. [매매 판단 과정](#2-매매-판단-과정) — 15분마다 어떤 과정을 거쳐 매매하는지
3. [MLOps 파이프라인 — 자가 진화 시스템](#3-mlops-파이프라인--자가-진화-시스템) 3. [5개 레이어 상세](#3-5개-레이어-상세) — 각 레이어의 역할과 동작 원리
4. [핵심 동작 시나리오](#4-핵심-동작-시나리오) 4. [MLOps 파이프라인](#4-mlops-파이프라인) — ML 모델의 학습·배포·모니터링 전체 흐름
5. [테스트 커버리지](#5-테스트-커버리지) 5. [핵심 동작 시나리오](#5-핵심-동작-시나리오) — 실제 상황별 봇의 동작 흐름도
6. [테스트 커버리지](#6-테스트-커버리지) — 무엇을 어떻게 테스트하는지
7. [파일 구조](#7-파일-구조) — 전체 파일 역할 요약
--- ---
## 1. 시스템 오버뷰 ## 1. 시스템 개요
CoinTrader는 **Binance Futures 자동매매 봇**입니다. 기술 지표 신호를 1차 필터로, LightGBM(또는 MLX 신경망) 모델을 2차 필터로 사용하여 다중 심볼(XRP, TRX, DOGE 등) 선물 포지션을 동시에 자동 진입·청산합니다. CoinTrader는 **Binance Futures 자동매매 봇**입니다.
### 멀티심볼 아키텍처 **한 줄 요약**: 15분마다 기술 지표로 매매 신호를 생성하고, ML 모델로 한 번 더 검증한 뒤, 조건을 충족하면 자동으로 주문을 넣습니다.
### 1.1 전체 흐름 (간략)
```
15분봉 마감 → 기술 지표 계산 → 매매 신호 생성 → ML 필터 검증 → 리스크 체크 → 주문 실행 → Discord 알림
```
### 1.2 멀티심볼 아키텍처
여러 심볼을 동시에 거래합니다. 각 심볼은 독립된 봇 인스턴스로 실행되며, 리스크 관리만 공유합니다.
``` ```
main.py main.py
@@ -32,9 +44,27 @@ main.py
) )
``` ```
각 봇은 독립적인 `Exchange`, `MLFilter`, `DataStream`을 소유합니다. `RiskManager`만 공유 싱글턴으로 글로벌 리스크(일일 손실 한도, 동일 방향 제한, 최대 포지션 수)를 관리합니다. - **독립**: 각 봇은 자체 `Exchange`, `MLFilter`, `DataStream`을 소유
- **공유**: `RiskManager`만 싱글턴으로 글로벌 리스크(일일 손실 한도, 동일 방향 제한) 관리
- **병렬**: `asyncio.gather()`로 동시 실행, 서로 간섭 없음
### 전체 데이터 파이프라인 흐름도 ### 1.3 기술 스택
| 분류 | 기술 |
|------|------|
| 언어 | Python 3.11+ |
| 비동기 런타임 | `asyncio` + `python-binance` WebSocket |
| 기술 지표 | `pandas-ta` (RSI, MACD, BB, EMA, StochRSI, ATR, ADX) |
| ML 프레임워크 | `LightGBM` (CPU) / `MLX` (Apple Silicon GPU) |
| 모델 서빙 | `onnxruntime` (ONNX 우선) / `joblib` (LightGBM 폴백) |
| 하이퍼파라미터 탐색 | `Optuna` (TPE Sampler + MedianPruner) |
| 데이터 저장 | `Parquet` (pyarrow) |
| 로깅 | `Loguru` |
| 알림 | Discord Webhook (`httpx`) |
| 컨테이너화 | Docker + Docker Compose |
| CI/CD | Jenkins + Gitea Container Registry |
### 1.4 데이터 파이프라인 전체 흐름도
```mermaid ```mermaid
flowchart TD flowchart TD
@@ -48,20 +78,20 @@ flowchart TD
DS["data_stream.py<br/>MultiSymbolStream (심볼별)<br/>캔들 버퍼 (deque 200개)"] DS["data_stream.py<br/>MultiSymbolStream (심볼별)<br/>캔들 버퍼 (deque 200개)"]
IND["indicators.py<br/>기술 지표 계산<br/>RSI·MACD·BB·EMA·StochRSI·ATR·ADX"] IND["indicators.py<br/>기술 지표 계산<br/>RSI·MACD·BB·EMA·StochRSI·ATR·ADX"]
MF["ml_features.py<br/>26개 피처 추출<br/>(XRP 13 + BTC/ETH 8 + OI/FR 2 + OI파생 2 + ADX 1)"] MF["ml_features.py<br/>26개 피처 추출<br/>(XRP 13 + BTC/ETH 8 + OI/FR 2 + OI파생 2 + ADX 1)"]
ML["ml_filter.py<br/>MLFilter<br/>ONNX 우선 / LightGBM 폴백<br/>확률 ≥ 0.60 시 진입 허용"] ML["ml_filter.py<br/>MLFilter<br/>ONNX 우선 / LightGBM 폴백<br/>확률 ≥ 0.55 시 진입 허용"]
RM["risk_manager.py<br/>RiskManager (공유 싱글턴)<br/>일일 손실 5% 한도<br/>동적 증거금 비율<br/>동일 방향 제한"] RM["risk_manager.py<br/>RiskManager (공유 싱글턴)<br/>일일 손실 5% 한도<br/>동적 증거금 비율<br/>동일 방향 제한"]
EX["exchange.py<br/>BinanceFuturesClient<br/>주문·레버리지·잔고 API"] EX["exchange.py<br/>BinanceFuturesClient<br/>주문·레버리지·잔고 API"]
UDS["user_data_stream.py<br/>UserDataStream<br/>TP/SL 즉시 감지"] UDS["user_data_stream.py<br/>UserDataStream<br/>TP/SL 즉시 감지"]
NT["notifier.py<br/>DiscordNotifier<br/>진입·청산·오류 알림"] NT["notifier.py<br/>DiscordNotifier<br/>진입·청산·오류 알림"]
end end
subgraph mlops["MLOps 파이프라인 (맥미니 — 수동/크론)"] subgraph mlops["MLOps 파이프라인 (수동/크론)"]
FH["fetch_history.py<br/>과거 캔들 + OI/펀딩비<br/>Parquet Upsert"] FH["fetch_history.py<br/>과거 캔들 + OI/펀딩비<br/>Parquet Upsert"]
DB["dataset_builder.py<br/>벡터화 데이터셋 생성<br/>레이블: ATR SL/TP 6시간 룩어헤드"] DB["dataset_builder.py<br/>벡터화 데이터셋 생성<br/>레이블: ATR SL/TP 6시간 룩어헤드"]
TM["train_model.py<br/>LightGBM 학습<br/>Walk-Forward 5폴드 검증"] TM["train_model.py<br/>LightGBM 학습<br/>Walk-Forward 5폴드 검증"]
TN["tune_hyperparams.py<br/>Optuna 50 trials<br/>TPE + MedianPruner"] TN["tune_hyperparams.py<br/>Optuna 50 trials<br/>TPE + MedianPruner"]
AP["active_lgbm_params.json<br/>Active Config 패턴<br/>승인된 파라미터 저장"] AP["active_lgbm_params.json<br/>Active Config 패턴<br/>승인된 파라미터 저장"]
DM["deploy_model.sh<br/>rsync → LXC 서버<br/>봇 핫리로드 트리거"] DM["deploy_model.sh<br/>rsync → 운영 서버<br/>봇 핫리로드 트리거"]
end end
WS1 -->|캔들 마감 이벤트| DS WS1 -->|캔들 마감 이벤트| DS
@@ -84,26 +114,55 @@ flowchart TD
DM -->|모델 파일 전송| ML DM -->|모델 파일 전송| ML
``` ```
### 기술 스택 요약 ---
| 분류 | 기술 | ## 2. 매매 판단 과정
|------|------|
| 언어 | Python 3.11+ | 봇이 매매를 결정하는 과정을 단계별로 설명합니다. 코드를 읽기 전에 이 섹션을 먼저 이해하면 전체 구조가 명확해집니다.
| 비동기 런타임 | `asyncio` + `python-binance` WebSocket |
| 기술 지표 | `pandas-ta` (RSI, MACD, BB, EMA, StochRSI, ATR) | ### 2.1 진입 판단 (5단계 게이트)
| ML 프레임워크 | `LightGBM` (CPU) / `MLX` (Apple Silicon GPU) |
| 모델 서빙 | `onnxruntime` (ONNX 우선) / `joblib` (LightGBM 폴백) | ```
| 하이퍼파라미터 탐색 | `Optuna` (TPE Sampler + MedianPruner) | Gate 1: 추세 존재 확인
| 데이터 저장 | `Parquet` (pyarrow) | └─ ADX ≥ 25 인가? → 미만이면 HOLD (횡보장 진입 차단)
| 로깅 | `Loguru` |
| 알림 | Discord Webhook (`httpx`) | Gate 2: 기술 지표 신호 생성
| 컨테이너화 | Docker + Docker Compose | └─ RSI, MACD, 볼린저, EMA, StochRSI 점수 합산
| CI/CD | Jenkins + Gitea Container Registry | └─ 합계 ≥ SIGNAL_THRESHOLD(기본 3)인가?
| 운영 서버 | LXC 컨테이너 (`10.1.10.24`) |
Gate 3: 거래량 확인
└─ 거래량 ≥ 20MA × VOL_MULTIPLIER(기본 2.5)인가?
└─ 또는 신호 점수가 SIGNAL_THRESHOLD + 1 이상인가?
Gate 4: ML 필터 (활성화 시)
└─ 26개 피처로 성공 확률 예측
└─ 확률 ≥ ML_THRESHOLD(기본 0.55)인가?
Gate 5: 리스크 관리
└─ 일일 손실 한도 미초과?
└─ 동일 방향 포지션 2개 미만?
└─ 같은 심볼 기존 포지션 없음?
→ 5개 게이트 모두 통과 → 주문 실행
```
### 2.2 청산 메커니즘
| 청산 방식 | 설명 |
|-----------|------|
| **TP (익절)** | 진입가 ± ATR × ATR_TP_MULT 도달 시 자동 청산 |
| **SL (손절)** | 진입가 ∓ ATR × ATR_SL_MULT 도달 시 자동 청산 |
| **반대 시그널** | 보유 중 반대 방향 신호 → 즉시 청산 후 반대 방향 재진입 |
### 2.3 현재 ML 필터 상태
**현재 비활성화** (`NO_ML_FILTER=true`)
Walk-Forward 검증 결과 각 폴드 학습 세트에 유효 신호가 약 27건으로, LightGBM이 의미 있는 패턴을 학습하기엔 표본이 부족합니다. 전략 파라미터 스윕에서 ADX 필터 + 거래량 배수 조합만으로 PF 1.57~2.39를 달성하여, 충분한 트레이드 데이터가 축적될 때까지 ML 없이 운영합니다.
--- ---
## 2. 코어 레이어 아키텍처 ## 3. 5개 레이어 상세
봇은 5개의 레이어로 구성됩니다. 각 레이어는 단일 책임을 가지며, 위에서 아래로 데이터가 흐릅니다. 봇은 5개의 레이어로 구성됩니다. 각 레이어는 단일 책임을 가지며, 위에서 아래로 데이터가 흐릅니다.
@@ -134,7 +193,7 @@ flowchart TD
**파일:** `src/data_stream.py` **파일:** `src/data_stream.py`
각 봇 인스턴스가 시작되면 가장 먼저 실행되는 레이어입니다. Binance Combined WebSocket 단일 연결로 주 거래 심볼 + 상관관계 심볼(BTC/ETH)의 15분봉 캔들을 동시에 수신합니다. Binance Combined WebSocket 단일 연결로 주 거래 심볼 + 상관관계 심볼(BTC/ETH)의 15분봉 캔들을 동시에 수신합니다.
**핵심 동작:** **핵심 동작:**
@@ -170,7 +229,7 @@ flowchart TD
| ADX | length=14 | 추세 강도 측정 → 횡보장 필터 (ADX < 25 시 진입 차단) | | ADX | length=14 | 추세 강도 측정 → 횡보장 필터 (ADX < 25 시 진입 차단) |
| Volume MA | length=20 | 거래량 급증 감지 | | Volume MA | length=20 | 거래량 급증 감지 |
**신호 생성 로직 (ADX 필터 + 가중치 합산):** **신호 생성 로직:**
``` ```
[1단계] ADX 횡보장 필터: [1단계] ADX 횡보장 필터:
@@ -183,9 +242,12 @@ flowchart TD
EMA 정배열 (9 > 21 > 50) → +1 EMA 정배열 (9 > 21 > 50) → +1
StochRSI K < 20 and K > D → +1 StochRSI K < 20 and K > D → +1
진입 조건: 점수 ≥ SIGNAL_THRESHOLD(기본 3) AND (거래량 ≥ 20MA × VOL_MULTIPLIER(기본 2.5) OR 점수 ≥ SIGNAL_THRESHOLD + 1) 진입 조건: 점수 ≥ SIGNAL_THRESHOLD(기본 3)
AND (거래량 ≥ 20MA × VOL_MULTIPLIER(기본 2.5) OR 점수 ≥ SIGNAL_THRESHOLD + 1)
SL = 진입가 - ATR × ATR_SL_MULT (기본 2.0) SL = 진입가 - ATR × ATR_SL_MULT (기본 2.0)
TP = 진입가 + ATR × ATR_TP_MULT (기본 2.0) TP = 진입가 + ATR × ATR_TP_MULT (기본 2.0)
※ SL/TP/신호임계값/ADX/거래량배수 모두 환경변수로 설정 가능 ※ SL/TP/신호임계값/ADX/거래량배수 모두 환경변수로 설정 가능
``` ```
@@ -197,7 +259,7 @@ TP = 진입가 + ATR × ATR_TP_MULT (기본 2.0)
**파일:** `src/ml_filter.py`, `src/ml_features.py` **파일:** `src/ml_filter.py`, `src/ml_features.py`
기술 지표 신호가 발생해도 ML 모델이 "이 타점은 실패 확률이 높다"고 판단하면 진입을 차단합니다. 오진입(억까 타점)을 줄이는 2차 게이트키퍼입니다. 기술 지표 신호가 발생해도 ML 모델이 "이 타점은 실패 확률이 높다"고 판단하면 진입을 차단합니다. 오진입을 줄이는 2차 게이트키퍼입니다.
**모델 우선순위:** **모델 우선순위:**
@@ -238,13 +300,9 @@ OI 파생 피처 (2개):
```python ```python
proba = model.predict_proba(features)[0][1] # 성공 확률 proba = model.predict_proba(features)[0][1] # 성공 확률
return proba >= 0.55 # 임계값 55% (ML_THRESHOLD 환경변수) return proba >= 0.55 # 임계값 (ML_THRESHOLD 환경변수로 조절)
``` ```
**ML 필터 현황 — 현재 비활성화 상태:**
프로덕션에서 `NO_ML_FILTER=true`로 ML 필터를 비활성화하고 있습니다. Walk-Forward 검증 결과 각 폴드 학습 세트에 유효 신호가 약 27건으로, LightGBM이 의미 있는 패턴을 학습하기엔 표본이 절대적으로 부족합니다. 모든 입력에 동일한 확률(~0.55)을 출력하여 필터링 효과가 없었습니다. 전략 파라미터 스윕에서 ADX 필터(≥25) + 거래량 배수(2.5) 조합만으로 PF 1.57~2.39를 달성하여, 충분한 트레이드 데이터가 축적될 때까지 ML 없이 운영합니다.
--- ---
### Layer 4: Execution & Risk Layer ### Layer 4: Execution & Risk Layer
@@ -272,8 +330,6 @@ ML 필터를 통과한 신호를 실제 주문으로 변환하고, 리스크 한
4. place_order(TAKE_PROFIT_MARKET) ← TP 설정 4. place_order(TAKE_PROFIT_MARKET) ← TP 설정
``` ```
SL/TP 주문은 `/fapi/v1/algoOrder` 엔드포인트로 전송됩니다 (일반 계정의 `-4120` 오류 대응).
**리스크 제어:** **리스크 제어:**
| 제어 항목 | 기준 | | 제어 항목 | 기준 |
@@ -319,7 +375,7 @@ Binance `ORDER_TRADE_UPDATE` 웹소켓 이벤트를 구독하여 TP/SL 체결을
net_pnl = realized_pnl - commission net_pnl = realized_pnl - commission
``` ```
**Discord 알림 포맷:** **Discord 알림 예시:**
진입 시: 진입 시:
``` ```
@@ -331,7 +387,7 @@ RSI: 32.50 | MACD Hist: -0.000123 | ATR: 0.023400
청산 시: 청산 시:
``` ```
[XRPUSDT] LONG TP 청산 [XRPUSDT] LONG TP 청산
청산가: 2.4150 청산가: 2.4150
예상 수익: +7.0000 USDT 예상 수익: +7.0000 USDT
실제 순수익: +6.7800 USDT 실제 순수익: +6.7800 USDT
@@ -340,20 +396,20 @@ RSI: 32.50 | MACD Hist: -0.000123 | ATR: 0.023400
--- ---
## 3. MLOps 파이프라인 — 자가 진화 시스템 ## 4. MLOps 파이프라인
봇의 ML 모델은 고정된 것이 아니라 주기적으로 재학습·개선됩니다. 전체 라이프사이클은 다음과 같습니다. 봇의 ML 모델은 고정된 것이 아니라 주기적으로 재학습·개선됩니다.
### 3.1 전체 라이프사이클 ### 4.1 전체 라이프사이클
```mermaid ```mermaid
flowchart LR flowchart LR
A["주말 수동 트리거<br/>tune_hyperparams.py<br/>(Optuna 50 trials, ~30분)"] A["주말 수동 트리거<br/>tune_hyperparams.py<br/>(Optuna 50 trials)"]
B["결과 검토<br/>tune_results_YYYYMMDD.json<br/>Best AUC vs Baseline 비교"] B["결과 검토<br/>tune_results_YYYYMMDD.json<br/>Best AUC vs Baseline 비교"]
C{"개선폭 충분?<br/>(AUC +0.01 이상<br/>폴드 분산 낮음)"} C{"개선폭 충분?<br/>(AUC +0.01 이상<br/>폴드 분산 낮음)"}
D["active_lgbm_params.json<br/>업데이트<br/>(Active Config 패턴)"] D["active_lgbm_params.json<br/>업데이트<br/>(Active Config 패턴)"]
E["새벽 2시 크론탭<br/>train_and_deploy.sh<br/>(데이터 수집 → 학습 → 배포)"] E["크론탭 또는 수동 실행<br/>train_and_deploy.sh<br/>(데이터 수집 → 학습 → 배포)"]
F["LXC 서버<br/>lgbm_filter.pkl 교체"] F["운영 서버<br/>lgbm_filter.pkl 교체"]
G["봇 핫리로드<br/>다음 캔들 mtime 감지<br/>→ 자동 리로드"] G["봇 핫리로드<br/>다음 캔들 mtime 감지<br/>→ 자동 리로드"]
A --> B A --> B
@@ -366,7 +422,7 @@ flowchart LR
G --> A G --> A
``` ```
### 3.2 단계별 상세 설명 ### 4.2 단계별 상세
#### Step 1: Optuna 하이퍼파라미터 탐색 #### Step 1: Optuna 하이퍼파라미터 탐색
@@ -416,7 +472,7 @@ Optuna가 찾은 파라미터는 **자동으로 적용되지 않습니다.** 사
> **주의**: Optuna 결과는 과적합 위험이 있습니다. 폴드별 AUC 분산이 크거나 (std > 0.05), 개선폭이 미미하면 (< 0.01) 적용하지 않는 것을 권장합니다. > **주의**: Optuna 결과는 과적합 위험이 있습니다. 폴드별 AUC 분산이 크거나 (std > 0.05), 개선폭이 미미하면 (< 0.01) 적용하지 않는 것을 권장합니다.
#### Step 3: 자동 학습 및 배포 (크론탭) #### Step 3: 자동 학습 및 배포
`scripts/train_and_deploy.sh`는 3단계를 자동으로 실행합니다: `scripts/train_and_deploy.sh`는 3단계를 자동으로 실행합니다:
@@ -433,8 +489,8 @@ Optuna가 찾은 파라미터는 **자동으로 적용되지 않습니다.** 사
- Walk-Forward 5폴드 검증 후 최종 모델 저장 - Walk-Forward 5폴드 검증 후 최종 모델 저장
- 학습 로그: models/{symbol}/training_log.json - 학습 로그: models/{symbol}/training_log.json
[3/3] LXC 배포 (deploy_model.sh --symbol {SYM}) [3/3] 운영 서버 배포 (deploy_model.sh --symbol {SYM})
- rsync로 models/{symbol}/lgbm_filter.pkl → LXC 서버 전송 - rsync로 models/{symbol}/lgbm_filter.pkl → 운영 서버 전송
- 기존 모델 자동 백업 (lgbm_filter_prev.pkl) - 기존 모델 자동 백업 (lgbm_filter_prev.pkl)
- ONNX 파일 충돌 방지 (우선순위 보장) - ONNX 파일 충돌 방지 (우선순위 보장)
``` ```
@@ -456,18 +512,18 @@ if onnx_changed or lgbm_changed:
매 캔들 마감(15분)마다 모델 파일의 `mtime`을 확인합니다. 변경이 감지되면 즉시 리로드합니다. 매 캔들 마감(15분)마다 모델 파일의 `mtime`을 확인합니다. 변경이 감지되면 즉시 리로드합니다.
### 3.3 주간 전략 모니터링 ### 4.3 주간 전략 모니터링
`scripts/weekly_report.py`가 매주 자동으로 전략 성능을 측정하고 Discord로 리포트를 전송합니다. `scripts/weekly_report.py`가 매주 자동으로 전략 성능을 측정하고 Discord로 리포트를 전송합니다.
``` ```
[매주 일요일 크론탭] [매주 일요일 크론탭]
[1/6] 데이터 수집 (fetch_history.py × 3심볼, 최근 35일 Upsert) [1/6] 데이터 수집 (fetch_history.py × 심볼, 최근 35일 Upsert)
[2/6] Walk-Forward 백테스트 (심볼별 → 합산 PF/승률/MDD) [2/6] Walk-Forward 백테스트 (심볼별 → 합산 PF/승률/MDD)
[3/6] 실전 봇 로그 파싱 (이번 주 진입/청산 기록) [3/6] 운영 대시보드 API 조회 (GET /api/trades + GET /api/stats → 실전 거래 통계)
[4/6] 추이 분석 (이전 results/weekly/*.json에서 PF/승률/MDD 추이) [4/6] 추이 분석 (이전 리포트에서 PF/승률/MDD 추이 로드)
[5/6] ML 재도전 체크 (누적 트레이드 ≥ 150, PF < 1.0, PF 3주 하락 → 2/3 충족 시 권장) [5/6] ML 재학습 체크 (누적 트레이드 ≥ 150, PF < 1.0, PF 3주 하락 → 2/3 충족 시 권장)
[6/6] PF < 1.0이면 파라미터 스윕 실행 → 상위 3개 대안 제시 [6/6] PF < 1.0이면 파라미터 스윕 실행 → 상위 3개 대안 제시
→ Discord 알림 + results/weekly/report_YYYY-MM-DD.json 저장 → Discord 알림 + results/weekly/report_YYYY-MM-DD.json 저장
@@ -475,7 +531,7 @@ if onnx_changed or lgbm_changed:
**전략 파라미터 스윕**: 성능 저하 감지 시 324개 파라미터 조합(SL/TP/ADX/신호임계값/거래량배수)을 자동 탐색하여 현재보다 높은 PF의 대안을 제시합니다. 자동 적용되지 않으며, 사람이 검토 후 승인해야 합니다. **전략 파라미터 스윕**: 성능 저하 감지 시 324개 파라미터 조합(SL/TP/ADX/신호임계값/거래량배수)을 자동 탐색하여 현재보다 높은 PF의 대안을 제시합니다. 자동 적용되지 않으며, 사람이 검토 후 승인해야 합니다.
### 3.4 레이블 생성 방식 ### 4.4 레이블 생성 방식
학습 데이터의 레이블은 **미래 6시간(24캔들) 룩어헤드**로 생성됩니다. 학습 데이터의 레이블은 **미래 6시간(24캔들) 룩어헤드**로 생성됩니다.
@@ -494,11 +550,11 @@ if onnx_changed or lgbm_changed:
--- ---
## 4. 핵심 동작 시나리오 ## 5. 핵심 동작 시나리오
### 시나리오 1: 15분 캔들 마감 시 봇의 동작 흐름 ### 시나리오 1: 15분 캔들 마감 → 진입 판단
> "XRP 15분봉이 마감되면 봇은 무엇을 하는가?" > "15분봉이 마감되면 봇은 무엇을 하는가?"
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
@@ -529,7 +585,7 @@ sequenceDiagram
BOT->>MF: build_features(df, signal, btc_df, eth_df, oi_change, funding_rate) BOT->>MF: build_features(df, signal, btc_df, eth_df, oi_change, funding_rate)
MF-->>BOT: features (26개 피처 Series) MF-->>BOT: features (26개 피처 Series)
BOT->>ML: should_enter(features) BOT->>ML: should_enter(features)
ML-->>BOT: proba=0.73 ≥ 0.60 → True ML-->>BOT: proba=0.73 ≥ 0.55 → True
BOT->>EX: get_balance() BOT->>EX: get_balance()
BOT->>RM: get_dynamic_margin_ratio(balance) BOT->>RM: get_dynamic_margin_ratio(balance)
@@ -551,7 +607,7 @@ sequenceDiagram
--- ---
### 시나리오 2: TP/SL 체결 시 봇의 동작 흐름 ### 시나리오 2: TP/SL 체결 → 포지션 종료
> "거래소에서 TP가 작동하면 봇은 어떻게 반응하는가?" > "거래소에서 TP가 작동하면 봇은 어떻게 반응하는가?"
@@ -589,118 +645,102 @@ sequenceDiagram
**핵심 포인트:** **핵심 포인트:**
- User Data Stream은 `asyncio.gather()`로 캔들 스트림과 **병렬** 실행 - User Data Stream은 `asyncio.gather()`로 캔들 스트림과 **병렬** 실행
- 체결 즉시 감지 (최대 15분 지연이었던 폴링 방식 대비 실시간) - 체결 즉시 감지 (폴링 방식의 최대 15분 지연 해소)
- `realized_pnl - commission` = 정확한 순수익 (슬리피지·수수료 포함) - `realized_pnl - commission` = 정확한 순수익 (슬리피지·수수료 포함)
- `_is_reentering` 플래그: 반대 시그널 재진입 중에는 콜백이 신규 포지션 상태를 초기화하지 않음 - `_is_reentering` 플래그: 반대 시그널 재진입 중에는 콜백이 신규 포지션 상태를 초기화하지 않음
--- ---
## 5. 테스트 커버리지 ## 6. 테스트 커버리지
### 5.1 테스트 파일 구성 ### 6.1 테스트 실행
`tests/` 폴더에 15개 테스트 파일, 총 **135개의 테스트 케이스**가 작성되어 있습니다.
```bash ```bash
pytest tests/ -v # 전체 실행 pytest tests/ -v # 전체 실행
bash scripts/run_tests.sh # 래퍼 스크립트 실행 bash scripts/run_tests.sh # 래퍼 스크립트 실행
``` ```
### 5.2 모듈별 테스트 현황 `tests/` 폴더에 15개 테스트 파일, 총 **136개의 테스트 케이스**가 작성되어 있습니다.
| 테스트 파일 | 대상 모듈 | 테스트 케이스 | 주요 검증 항목 | ### 6.2 모듈 테스트 현황
|------------|----------|:------------:|--------------|
| `test_bot.py` | `src/bot.py` | 11 | 반대 시그널 재진입 흐름, ML 차단 시 재진입 스킵, OI/펀딩비 피처 전달, OI 변화율 계산 | | 테스트 파일 | 대상 모듈 | 케이스 | 주요 검증 항목 |
| `test_indicators.py` | `src/indicators.py` | 7 | RSI 범위(0~100), MACD 컬럼 존재, 볼린저 밴드 상하단 대소관계, 신호 반환값 유효성, ADX 컬럼 존재, ADX<25 횡보장 차단, ADX NaN 폴스루 | |------------|----------|:------:|--------------|
| `test_ml_features.py` | `src/ml_features.py` | 11 | 26개 피처 수, BTC/ETH 포함 시 피처 수, RS 분모 0 처리, NaN 없음, side 인코딩, OI/펀딩비 파라미터 반영 | | `test_bot.py` | `src/bot.py` | 11 | 반대 시그널 재진입, ML 차단 시 스킵, OI/펀딩비 피처 전달 |
| `test_ml_filter.py` | `src/ml_filter.py` | 5 | 모델 없을 때 폴백 허용, 임계값 이상/미만 판단, 핫리로드 후 상태 변화 | | `test_indicators.py` | `src/indicators.py` | 7 | RSI 범위, MACD 컬럼, 볼린저 대소관계, ADX 횡보장 차단 |
| `test_risk_manager.py` | `src/risk_manager.py` | 13 | 일일 손실 한도 초과 차단, 최대 포지션 수 제한, 동일 방향 제한, 심볼 중복 차단, 비동기 포지션 등록/해제, 동적 증거금 비율 상한/하한 클램핑 | | `test_ml_features.py` | `src/ml_features.py` | 11 | 26개 피처 수, RS 분모 0 처리, NaN 없음 |
| `test_exchange.py` | `src/exchange.py` | 8 | 수량 계산(기본/최소명목금액/잔고0), OI·펀딩비 조회 정상/오류 시 반환값 | | `test_ml_filter.py` | `src/ml_filter.py` | 5 | 모델 없을 때 폴백, 임계값 판단, 핫리로드 |
| `test_data_stream.py` | `src/data_stream.py` | 6 | 3심볼 버퍼 존재, 빈 버퍼 None 반환, 캔들 파싱, 마감 캔들 콜백 호출, 프리로드 200개 | | `test_risk_manager.py` | `src/risk_manager.py` | 13 | 일일 손실 한도, 동일 방향 제한, 동적 증거금 비율 |
| `test_label_builder.py` | `src/label_builder.py` | 4 | LONG TP 도달 → 1, LONG SL 도달 → 0, 미결 → None, SHORT TP 도달 → 1 | | `test_exchange.py` | `src/exchange.py` | 8 | 수량 계산, OI·펀딩비 조회 정상/오류 |
| `test_dataset_builder.py` | `src/dataset_builder.py` | 9 | DataFrame 반환, 필수 컬럼 존재, 레이블 이진값, BTC/ETH 포함 시 23개 피처, inf/NaN 없음, OI nan 마스킹, RS 분모 0 처리 | | `test_data_stream.py` | `src/data_stream.py` | 6 | 3심볼 버퍼, 캔들 파싱, 프리로드 200개 |
| `test_mlx_filter.py` | `src/mlx_filter.py` | 5 | GPU 디바이스 확인, 학습 전 예측 형태, 학습 후 유효 확률, NaN 피처 처리, 저장/로드 후 동일 예측 | | `test_label_builder.py` | `src/label_builder.py` | 4 | TP/SL 도달 레이블, 미결 → None |
| `test_fetch_history.py` | `scripts/fetch_history.py` | 5 | OI=0 구간 Upsert, 신규 행 추가, 기존 비0값 보존, 파일 없을 때 신규 반환, 타임스탬프 오름차순 정렬 | | `test_dataset_builder.py` | `src/dataset_builder.py` | 9 | DataFrame 반환, 필수 컬럼, inf/NaN 없음 |
| `test_config.py` | `src/config.py` | 6 | 환경변수 로드, 동적 증거금 파라미터 로드, `symbols` 리스트, `correlation_symbols`, `max_same_direction`, SYMBOL→symbols 폴백 | | `test_mlx_filter.py` | `src/mlx_filter.py` | 5 | GPU 학습, 저장/로드 동일 예측 (Apple Silicon 전용) |
| `test_weekly_report.py` | `scripts/weekly_report.py` | 14 | 데이터 수집 subprocess 호출, WF 백테스트 실행, 로그 파싱(진입/청산), 추이 로드(PF 하락 감지), ML 트리거 체크, 성능 저하 스윕, Discord 포맷/전송, JSON 저장 | | `test_fetch_history.py` | `scripts/fetch_history.py` | 5 | OI=0 Upsert, 중복 방지, 타임스탬프 정렬 |
| `test_config.py` | `src/config.py` | 6 | 환경변수 로드, symbols 리스트 파싱 |
| `test_weekly_report.py` | `scripts/weekly_report.py` | 15 | 백테스트, 대시보드 API, 추이 분석, ML 트리거, 스윕 |
> `test_mlx_filter.py`는 Apple Silicon(`mlx` 패키지)이 없는 환경에서 자동 스킵됩니다. > `test_mlx_filter.py`는 Apple Silicon(`mlx` 패키지)이 없는 환경에서 자동 스킵됩니다.
### 5.3 커버리지 매트릭스 ### 6.3 커버리지 매트릭스
아래는 핵심 비즈니스 로직의 테스트 커버 여부입니다. | 기능 | 단위 | 통합 | 비고 |
|------|:----:|:----:|------|
| 기능 | 단위 테스트 | 통합 수준 테스트 | 비고 | | 기술 지표 계산 | ✅ | ✅ | `test_indicators` + `test_ml_features` + `test_dataset_builder` |
|------|:----------:|:--------------:|------|
| 기술 지표 계산 (RSI/MACD/BB/EMA/StochRSI/ADX) | ✅ | ✅ | `test_indicators` + `test_ml_features` + `test_dataset_builder` |
| 신호 생성 (가중치 합산) | ✅ | ✅ | `test_indicators` + `test_dataset_builder` | | 신호 생성 (가중치 합산) | ✅ | ✅ | `test_indicators` + `test_dataset_builder` |
| ADX 횡보장 필터 (ADX < 25 차단) | ✅ | ✅ | `test_indicators` + `test_dataset_builder` (`_calc_signals` 실제 호출) | | ADX 횡보장 필터 | ✅ | ✅ | `test_indicators` |
| ML 피처 추출 (26개) | ✅ | ✅ | `test_ml_features` + `test_dataset_builder` (`_calc_features_vectorized` 실제 호출) | | ML 피처 추출 (26개) | ✅ | ✅ | `test_ml_features` + `test_dataset_builder` |
| ML 필터 추론 (임계값 판단) | ✅ | — | `test_ml_filter` | | ML 필터 추론 | ✅ | — | `test_ml_filter` |
| MLX 신경망 학습/저장/로드 | ✅ | — | `test_mlx_filter` (Apple Silicon 전용) | | MLX 신경망 학습/저장/로드 | ✅ | — | `test_mlx_filter` (Apple Silicon 전용) |
| 레이블 생성 (SL/TP 룩어헤드) | ✅ | ✅ | `test_label_builder` + `test_dataset_builder` (전체 파이프라인 실제 호출) | | 레이블 생성 (SL/TP 룩어헤드) | ✅ | ✅ | `test_label_builder` + `test_dataset_builder` |
| 벡터화 데이터셋 빌더 | ✅ | ✅ | `test_dataset_builder` | | 벡터화 데이터셋 빌더 | ✅ | ✅ | `test_dataset_builder` |
| 동적 증거금 비율 계산 | ✅ | — | `test_risk_manager` | | 동적 증거금 비율 | ✅ | — | `test_risk_manager` |
| 동일 방향 포지션 제한 | ✅ | — | `test_risk_manager` | | 동일 방향 포지션 제한 | ✅ | — | `test_risk_manager` |
| 심볼 중복 진입 차단 | ✅ | — | `test_risk_manager` | | 일일 손실 한도 | ✅ | — | `test_risk_manager` |
| 일일 손실 한도 제어 | ✅ | — | `test_risk_manager` |
| 포지션 수량 계산 | ✅ | — | `test_exchange` | | 포지션 수량 계산 | ✅ | — | `test_exchange` |
| OI/펀딩비 API 조회 (정상/오류) | ✅ | ✅ | `test_exchange` + `test_bot` (`process_candle` → OI/펀딩비 → `build_features` 전달) | | OI/펀딩비 API 조회 | ✅ | ✅ | `test_exchange` + `test_bot` |
| 반대 시그널 재진입 흐름 | ✅ | ✅ | `test_bot` | | 반대 시그널 재진입 | ✅ | ✅ | `test_bot` |
| ML 차단 시 재진입 스킵 | ✅ | ✅ | `test_bot` (`_close_and_reenter` → ML 판단 → 스킵 전체 흐름) | | OI 변화율 계산 | ✅ | ✅ | `test_bot` |
| OI 변화율 계산 (API 실패 폴백) | ✅ | | `test_bot` (`process_candle` → OI 조회 → `_calc_oi_change` 흐름) | | Parquet Upsert | ✅ | | `test_fetch_history` |
| 캔들 버퍼 관리 및 프리로드 | ✅ | | `test_data_stream` | | 주간 리포트 | ✅ | | `test_weekly_report` |
| Parquet Upsert (OI=0 보충) | | — | `test_fetch_history` | | User Data Stream TP/SL | | — | 미작성 (WebSocket 의존) |
| 주간 리포트 (백테스트+로그+추이+스윕) | ✅ | ✅ | `test_weekly_report` (14개 테스트: 데이터 수집, 백테스트, 로그 파싱, 추이, ML 트리거, 스윕, 포맷, 전송, JSON 저장) |
| User Data Stream TP/SL 감지 | ❌ | — | 미작성 (실제 WebSocket 의존) |
| Discord 알림 전송 | ❌ | — | 미작성 (외부 웹훅 의존) | | Discord 알림 전송 | ❌ | — | 미작성 (외부 웹훅 의존) |
| CI/CD 파이프라인 | ❌ | — | Jenkins 환경 의존 |
### 5.4 테스트 전략 ### 6.4 테스트 전략
**Mock 활용 원칙:** - **Mock 원칙**: Binance API 호출은 모두 `unittest.mock.AsyncMock`으로 대체. 외부 의존성(Discord, WebSocket)은 테스트 대상에서 제외.
- Binance API 호출(`BinanceFuturesClient`, `AsyncClient`)은 모두 `unittest.mock.AsyncMock`으로 대체합니다. - **비동기 테스트**: `pytest-asyncio` + `@pytest.mark.asyncio`
- 외부 의존성(Discord Webhook, Binance WebSocket)은 테스트 대상에서 제외합니다. - **경계값 중심**: 분모 0 처리, API 실패 폴백, 최소 주문 금액 미달, OI=0 구간 Upsert
- `tmp_path` pytest fixture로 Parquet 파일 I/O를 격리합니다.
**비동기 테스트:**
- `pytest-asyncio`를 사용하며, `@pytest.mark.asyncio` 데코레이터로 `async def` 테스트를 실행합니다.
**경계값 및 엣지 케이스 중심:**
- 분모 0 (RS 계산, bb_range, vol_ma20)
- API 실패 시 `None` 반환 및 `0.0` 폴백
- 최소 명목금액 미달 시 주문 스킵
- OI=0 구간 Parquet Upsert 보존/덮어쓰기 조건
--- ---
## 부록: 파일별 역할 요약 ## 7. 파일 구조
| 파일 | 레이어 | 역할 | | 파일 | 레이어 | 역할 |
|------|--------|------| |------|--------|------|
| `main.py` | — | 진입점. 심볼별 `TradingBot` 생성 + 공유 `RiskManager` + `asyncio.gather()` | | `main.py` | — | 진입점. 심볼별 `TradingBot` 생성 + 공유 `RiskManager` + `asyncio.gather()` |
| `src/bot.py` | 오케스트레이터 | 심볼별 독립 트레이딩 루프 (symbol, risk 주입) | | `src/bot.py` | 오케스트레이터 | 심볼별 독립 트레이딩 루프 |
| `src/config.py` | — | 환경변수 기반 설정 (`symbols` 리스트, `correlation_symbols`) | | `src/config.py` | — | 환경변수 기반 설정 (`symbols` 리스트, `correlation_symbols`) |
| `src/data_stream.py` | Data | Combined WebSocket 캔들 수신·버퍼 관리 | | `src/data_stream.py` | Data | Combined WebSocket 캔들 수신·버퍼 관리 |
| `src/indicators.py` | Signal | 기술 지표 계산 및 복합 신호 생성 | | `src/indicators.py` | Signal | 기술 지표 계산 및 복합 신호 생성 |
| `src/ml_features.py` | ML Filter | 26개 ML 피처 추출 | | `src/ml_features.py` | ML Filter | 26개 ML 피처 추출 |
| `src/ml_filter.py` | ML Filter | ONNX/LightGBM 모델 로드·추론·핫리로드 | | `src/ml_filter.py` | ML Filter | ONNX/LightGBM 모델 로드·추론·핫리로드 |
| `src/mlx_filter.py` | ML Filter | Apple Silicon GPU 학습 + ONNX export | | `src/mlx_filter.py` | ML Filter | Apple Silicon GPU 학습 + ONNX export |
| `src/exchange.py` | Execution | Binance Futures REST API 클라이언트 (심볼별 독립) | | `src/exchange.py` | Execution | Binance Futures REST API 클라이언트 |
| `src/risk_manager.py` | Risk | 공유 싱글턴 — 일일 손실 한도·동일 방향 제한·동적 증거금 비율 | | `src/risk_manager.py` | Risk | 공유 싱글턴 — 일일 손실 한도·동일 방향 제한·동적 증거금 비율 |
| `src/user_data_stream.py` | Event | User Data Stream TP/SL 즉시 감지 | | `src/user_data_stream.py` | Event | User Data Stream TP/SL 즉시 감지 |
| `src/notifier.py` | Alert | Discord 웹훅 알림 | | `src/notifier.py` | Alert | Discord 웹훅 알림 |
| `src/label_builder.py` | MLOps | 학습 레이블 생성 (ATR SL/TP 룩어헤드) | | `src/label_builder.py` | MLOps | 학습 레이블 생성 (ATR SL/TP 룩어헤드) |
| `src/dataset_builder.py` | MLOps | 벡터화 데이터셋 빌더 (학습용) | | `src/dataset_builder.py` | MLOps | 벡터화 데이터셋 빌더 (학습용) |
| `src/logger_setup.py` | — | Loguru 로거 설정 |
| `scripts/fetch_history.py` | MLOps | 과거 캔들 + OI/펀딩비 수집 (`--symbol` 지원) |
| `scripts/train_model.py` | MLOps | LightGBM 모델 학습 (`--symbol` 지원) |
| `scripts/train_mlx_model.py` | MLOps | MLX 신경망 학습 (Apple Silicon GPU) |
| `scripts/tune_hyperparams.py` | MLOps | Optuna 하이퍼파라미터 탐색 (`--symbol` 지원) |
| `scripts/train_and_deploy.sh` | MLOps | 전체 파이프라인 (`--symbol` / `--all` 지원) |
| `scripts/deploy_model.sh` | MLOps | 모델 파일 LXC 서버 전송 (`--symbol` 지원) |
| `scripts/strategy_sweep.py` | MLOps | 전략 파라미터 그리드 스윕 (324개 조합) |
| `scripts/weekly_report.py` | MLOps | 주간 전략 리포트 (백테스트+로그+추이+스윕+Discord) |
| `scripts/run_backtest.py` | MLOps | 단일 백테스트 CLI |
| `src/backtester.py` | MLOps | 백테스트 엔진 (단일 + Walk-Forward) | | `src/backtester.py` | MLOps | 백테스트 엔진 (단일 + Walk-Forward) |
| `models/{symbol}/active_lgbm_params.json` | MLOps | 심볼별 승인된 LightGBM 파라미터 (Active Config) | | `src/logger_setup.py` | — | Loguru 로거 설정 |
| `scripts/fetch_history.py` | MLOps | 과거 캔들 + OI/펀딩비 수집 |
| `scripts/train_model.py` | MLOps | LightGBM 모델 학습 |
| `scripts/train_mlx_model.py` | MLOps | MLX 신경망 학습 (Apple Silicon GPU) |
| `scripts/tune_hyperparams.py` | MLOps | Optuna 하이퍼파라미터 탐색 |
| `scripts/train_and_deploy.sh` | MLOps | 전체 파이프라인 (수집 → 학습 → 배포) |
| `scripts/deploy_model.sh` | MLOps | 모델 파일 운영 서버 전송 |
| `scripts/strategy_sweep.py` | MLOps | 전략 파라미터 그리드 스윕 (324개 조합) |
| `scripts/weekly_report.py` | MLOps | 주간 전략 리포트 (백테스트+대시보드API+추이+스윕+Discord) |
| `scripts/run_backtest.py` | MLOps | 단일 백테스트 CLI |
| `models/{symbol}/active_lgbm_params.json` | MLOps | 심볼별 승인된 LightGBM 파라미터 |

View File

@@ -138,3 +138,4 @@ All design documents and implementation plans are stored in `docs/plans/` with t
| 2026-03-06 | `multi-symbol-dashboard` (design + plan) | Completed | | 2026-03-06 | `multi-symbol-dashboard` (design + plan) | Completed |
| 2026-03-06 | `strategy-parameter-sweep` (plan) | Completed | | 2026-03-06 | `strategy-parameter-sweep` (plan) | Completed |
| 2026-03-07 | `weekly-report` (plan) | Completed | | 2026-03-07 | `weekly-report` (plan) | Completed |
| 2026-03-07 | `code-review-improvements` | Pending |

510
README.md
View File

@@ -2,38 +2,210 @@
Binance Futures 자동매매 봇. 복합 기술 지표와 ML 필터(LightGBM / MLX 신경망)를 결합하여 다중 심볼(XRP, TRX, DOGE 등) 선물 포지션을 동시에 자동 진입·청산하며, Discord로 실시간 알림을 전송합니다. Binance Futures 자동매매 봇. 복합 기술 지표와 ML 필터(LightGBM / MLX 신경망)를 결합하여 다중 심볼(XRP, TRX, DOGE 등) 선물 포지션을 동시에 자동 진입·청산하며, Discord로 실시간 알림을 전송합니다.
> **아키텍처 문서**: 코드 구조, 레이어별 역할, MLOps 파이프라인, 동작 시나리오를 상세히 설명한 [ARCHITECTURE.md](./ARCHITECTURE.md)를 참고하세요. > **이 봇은 실제 자산을 거래합니다.** 운영 전 반드시 Binance Testnet에서 충분히 검증하세요.
> 과거 수익이 미래 수익을 보장하지 않습니다. 투자 손실에 대한 책임은 사용자 본인에게 있습니다.
--- ---
## 주요 기능 ## 주요 기능
- **복합 기술 지표 신호**: RSI, MACD 크로스, 볼린저 밴드, EMA 정/역배열, Stochastic RSI, 거래량 급증 — 가중치 합계 ≥ 3 시 진입 - **멀티심볼 동시 거래**: 심볼별 독립 봇 인스턴스를 병렬 실행, 공유 RiskManager로 글로벌 리스크 관리
- **ML 필터 (ONNX 우선 / LightGBM 폴백)**: 기술 지표 신호를 한 번 더 검증하여 오진입 차단. 우선순위: ONNX > LightGBM > 폴백(항상 허용) - **복합 기술 지표 신호**: RSI, MACD, 볼린저 밴드, EMA, Stochastic RSI, ADX, 거래량 급증 — 가중치 합산 시스템
- **모델 핫리로드**: 캔들마다 모델 파일 mtime을 감지해 변경 시 자동 리로드 (봇 재시작 불필요) - **ML 필터 (선택)**: LightGBM / ONNX 모델로 오진입 차단 (비활성화 가능)
- **멀티심볼 스트림**: XRP/BTC/ETH 3개 심볼을 단일 Combined WebSocket으로 수신, BTC·ETH 상관관계 피처 활용 - **ATR 기반 손절/익절**: 변동성에 따라 동적으로 SL/TP 계산, 환경변수로 배수 조절
- **26개 ML 피처**: XRP 기술 지표 13개 + BTC/ETH 수익률·상대강도 8개 + OI 변화율·펀딩비 2개 + OI 파생 피처 2개(oi_change_ma5, oi_price_spread) + ADX 1개 (캔들 마감 시 실시간 조회, 실패 시 0으로 폴백) - **반대 시그널 재진입**: 보유 포지션과 반대 신호 발생 시 즉시 청산 후 재진입
- **점진적 OI 데이터 축적 (Upsert)**: 바이낸스 OI 히스토리 API는 최근 30일치만 제공. `fetch_history.py` 실행 시 기존 parquet의 `oi_change/funding_rate=0` 구간을 신규 값으로 채워 학습 데이터 품질을 점진적으로 개선 - **리스크 관리**: 동일 방향 포지션 제한, 일일 손실 한도(5%), 동적 증거금 비율
- **실시간 OI/펀딩비 조회**: 캔들 마감마다 `get_open_interest()` / `get_funding_rate()`를 비동기 병렬 조회하여 ML 피처에 전달. 이전 캔들 대비 OI 변화율로 변환하여 train-serve skew 해소 - **실시간 TP/SL 감지**: Binance User Data Stream으로 즉시 감지
- **ATR 기반 손절/익절**: 변동성에 따라 동적으로 SL/TP 계산 (기본 2.0× / 2.0× ATR, 환경변수로 설정 가능)
- **전략 파라미터 스윕**: 324개 파라미터 조합(SL/TP/ADX/신호임계값/거래량배수)을 Walk-Forward 백테스트로 체계적 탐색, 수익 구간 자동 발견
- **주간 전략 리포트**: 매주 자동으로 백테스트 성능 측정, 실전 로그 파싱, 추이 추적, ML 재학습 시점 판단, 성능 저하 시 대안 파라미터 스윕, Discord 알림
- **ML 필터 비활성화 모드**: `NO_ML_FILTER=true` 설정 시 ML 모델 로드 없이 기술 지표 신호만으로 운영 (현재 프로덕션 기본값 — 아래 "ML 필터 현황" 참고)
- **Algo Order API 지원**: 계정 설정에 따라 STOP_MARKET/TAKE_PROFIT_MARKET 주문을 `/fapi/v1/algoOrder` 엔드포인트로 자동 전송 (오류 코드 -4120 대응)
- **동적 증거금 비율**: 잔고 증가에 따라 선형 감소 (최대 50% → 최소 20%)
- **반대 시그널 재진입**: 보유 포지션과 반대 신호 발생 시 즉시 청산 후 ML 필터 통과 시 반대 방향 재진입
- **멀티심볼 동시 거래**: 심볼별 독립 봇 인스턴스를 `asyncio.gather()`로 병렬 실행. 공유 RiskManager로 글로벌 리스크 관리
- **리스크 관리**: 트레이드당 리스크 비율, 최대 포지션 수, 동일 방향 포지션 제한(기본 2개), 일일 손실 한도(5%) 제어
- **포지션 복구**: 봇 재시작 시 기존 포지션 자동 감지 및 상태 복원
- **실시간 TP/SL 감지**: Binance User Data Stream으로 TP/SL 작동을 즉시 감지 (캔들 마감 대기 없음)
- **순수익(Net PnL) 기록**: 바이낸스 `realizedProfit - commission`으로 정확한 순수익 계산
- **Discord 상세 청산 알림**: 예상 수익 vs 실제 순수익 + 슬리피지/수수료 차이 표시
- **listenKey 자동 갱신**: 30분 keepalive + 네트워크 단절 시 자동 재연결. `stream.recv()` 기반으로 수신하며, 라이브러리 내부 에러 페이로드(`{"e":"error"}`) 감지 시 즉시 재연결하여 좀비 커넥션 방지
- **Discord 알림**: 진입·청산·오류 이벤트 실시간 웹훅 알림 - **Discord 알림**: 진입·청산·오류 이벤트 실시간 웹훅 알림
- **CI/CD**: Jenkins + Gitea Container Registry 기반 Docker 이미지 자동 빌드·배포 (LXC 운영 서버 자동 적용) - **모니터링 대시보드**: 거래 내역, 수익 통계, 차트를 웹에서 조회
- **주간 전략 리포트**: 자동 성능 측정, 추이 추적, ML 재학습 시점 판단
--- ---
# 봇 사용 가이드
봇을 설치하고 운영하려는 사용자를 위한 섹션입니다.
## 요구사항
- Python 3.11+ (또는 Docker)
- Binance Futures 계정 + API 키
- (선택) Discord 웹훅 URL
## 빠른 시작
### 1. 환경변수 설정
```bash
git clone <repository-url>
cd cointrader
cp .env.example .env
```
`.env` 파일을 열어 아래 필수 값을 채웁니다.
```env
# 필수
BINANCE_API_KEY=your_api_key
BINANCE_API_SECRET=your_api_secret
SYMBOLS=XRPUSDT # 거래할 심볼 (쉼표 구분, 예: XRPUSDT,TRXUSDT,DOGEUSDT)
# 권장
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
LEVERAGE=10
```
> 처음 사용 시 Binance Testnet에서 먼저 테스트하는 것을 권장합니다. `BINANCE_TESTNET_API_KEY`와 `BINANCE_TESTNET_API_SECRET`을 설정하세요.
### 2-A. Docker로 실행 (권장)
```bash
docker compose up -d
```
로그 확인:
```bash
docker compose logs -f cointrader
```
### 2-B. 로컬 실행
```bash
pip install -r requirements.txt
python main.py
```
### 3. 정상 동작 확인
봇이 정상 실행되면 다음과 같은 로그가 출력됩니다:
```
INFO | 봇 시작: XRPUSDT (레버리지 10x)
INFO | 과거 캔들 200개 프리로드 완료
INFO | WebSocket 연결 완료
```
Discord 웹훅을 설정했다면 진입/청산 시 실시간 알림을 받게 됩니다.
---
## 매매 전략
### 기술 지표 신호 (15분봉)
| 지표 | 롱 조건 | 숏 조건 | 가중치 |
|------|---------|---------|--------|
| RSI (14) | < 35 | > 65 | 1 |
| MACD 크로스 | 골든크로스 | 데드크로스 | 2 |
| 볼린저 밴드 | 하단 이탈 | 상단 돌파 | 1 |
| EMA 정배열 (9/21/50) | 정배열 | 역배열 | 1 |
| Stochastic RSI | < 20 + K>D | > 80 + K<D | 1 |
| 거래량 | 20MA × `VOL_MULTIPLIER` 이상 시 신호 강화 | — | 보조 |
**진입 조건**: 가중치 합계 ≥ `SIGNAL_THRESHOLD` + (거래량 급증 또는 가중치 합계 ≥ `SIGNAL_THRESHOLD` + 1)
**ADX 필터**: ADX < `ADX_THRESHOLD` 시 횡보장으로 판단, 진입 차단
**손절/익절**: ATR × `ATR_SL_MULT` / ATR × `ATR_TP_MULT`
### 전략 파라미터 조절
환경변수로 전략 파라미터를 조절할 수 있습니다. 기본값은 Walk-Forward 백테스트 스윕 결과에서 선정된 값입니다.
| 환경변수 | 기본값 | 설명 |
|---------|--------|------|
| `ATR_SL_MULT` | `2.0` | 손절 ATR 배수 |
| `ATR_TP_MULT` | `2.0` | 익절 ATR 배수 |
| `SIGNAL_THRESHOLD` | `3` | 진입을 위한 최소 가중치 점수 |
| `ADX_THRESHOLD` | `25` | ADX 횡보장 필터 (0=비활성) |
| `VOL_MULTIPLIER` | `2.5` | 거래량 급증 감지 배수 |
### ML 필터
ML 필터는 기술 지표 신호를 한 번 더 검증하여 오진입을 차단합니다. 기본적으로 **비활성화** 상태입니다.
- `NO_ML_FILTER=true` (기본값) — ML 없이 기술 지표만으로 운영
- `NO_ML_FILTER=false` — ML 필터 활성화 (모델 파일 필요)
> 현재 기본값이 비활성화인 이유: 학습 데이터가 충분히 축적되기 전까지 ML 모델의 예측력이 낮습니다. ADX 필터와 거래량 배수 조합만으로 PF 1.5 이상을 달성하고 있어, 충분한 거래 데이터(150건 이상)가 쌓일 때까지 ML 없이 운영합니다.
---
## 리스크 관리
| 설정 | 기본값 | 설명 |
|------|--------|------|
| `LEVERAGE` | `10` | 레버리지 배수 |
| `MAX_SAME_DIRECTION` | `2` | 동일 방향 최대 포지션 수 |
| `MARGIN_MAX_RATIO` | `0.50` | 최대 증거금 비율 (잔고 대비) |
| `MARGIN_MIN_RATIO` | `0.20` | 최소 증거금 비율 (잔고 대비) |
| `MARGIN_DECAY_RATE` | `0.0006` | 잔고 증가 시 증거금 비율 감소 속도 |
- **일일 손실 한도**: 기준 잔고의 5% 초과 시 당일 거래 중단
- **동적 증거금**: 잔고가 늘어날수록 비율을 선형으로 줄여 과노출 방지
- **포지션 복구**: 봇 재시작 시 기존 포지션 자동 감지 및 상태 복원
---
## 대시보드
봇 로그를 실시간으로 파싱하여 거래 내역, 수익 통계, 차트를 웹에서 조회할 수 있습니다.
```bash
docker compose up -d
# 접속: http://<서버IP>:8080
```
| 탭 | 내용 |
|----|------|
| **Overview** | 총 수익, 승률, 거래 수, 최대 수익/손실 KPI + 일별 PnL 차트 + 누적 수익 곡선 |
| **Trades** | 전체 거래 내역 — 진입/청산가, 방향, 레버리지, 기술 지표, SL/TP, 순익 상세 |
| **Chart** | 15분봉 가격 차트 + RSI 지표 + ADX 추세 강도 |
### API 엔드포인트
| 엔드포인트 | 설명 |
|-----------|------|
| `GET /api/position` | 현재 포지션 + 봇 상태 |
| `GET /api/trades` | 청산 거래 내역 (페이지네이션) |
| `GET /api/daily` | 일별 PnL 집계 |
| `GET /api/stats` | 전체 통계 (총 거래, 승률, 수수료 등) |
| `GET /api/candles` | 최근 캔들 + 기술 지표 |
| `GET /api/health` | 헬스 체크 |
---
## 환경변수 전체 레퍼런스
| 변수 | 기본값 | 필수 | 설명 |
|------|--------|:----:|------|
| `BINANCE_API_KEY` | — | ✅ | Binance API 키 |
| `BINANCE_API_SECRET` | — | ✅ | Binance API 시크릿 |
| `SYMBOLS` | `XRPUSDT` | | 거래 심볼 목록 (쉼표 구분) |
| `CORRELATION_SYMBOLS` | `BTCUSDT,ETHUSDT` | | 상관관계 심볼 (BTC/ETH 피처용) |
| `LEVERAGE` | `10` | | 레버리지 배수 |
| `MAX_SAME_DIRECTION` | `2` | | 동일 방향 최대 포지션 수 |
| `DISCORD_WEBHOOK_URL` | — | | Discord 웹훅 URL |
| `MARGIN_MAX_RATIO` | `0.50` | | 최대 증거금 비율 |
| `MARGIN_MIN_RATIO` | `0.20` | | 최소 증거금 비율 |
| `MARGIN_DECAY_RATE` | `0.0006` | | 잔고 증가 시 감소 속도 |
| `NO_ML_FILTER` | `true` | | ML 필터 비활성화 |
| `ML_THRESHOLD` | `0.55` | | ML 예측 확률 임계값 |
| `ATR_SL_MULT` | `2.0` | | 손절 ATR 배수 |
| `ATR_TP_MULT` | `2.0` | | 익절 ATR 배수 |
| `SIGNAL_THRESHOLD` | `3` | | 최소 가중치 점수 |
| `ADX_THRESHOLD` | `25` | | ADX 횡보장 필터 (0=비활성) |
| `VOL_MULTIPLIER` | `2.5` | | 거래량 급증 배수 |
| `DASHBOARD_API_URL` | `http://10.1.10.24:8000` | | 대시보드 API 주소 (주간 리포트용) |
| `BINANCE_TESTNET_API_KEY` | — | | Testnet API 키 |
| `BINANCE_TESTNET_API_SECRET` | — | | Testnet API 시크릿 |
---
# 개발 가이드
코드를 수정하거나 기능을 추가하려는 개발자를 위한 섹션입니다.
> **아키텍처 문서**: 5-레이어 구조, 데이터 흐름, MLOps 파이프라인, 동작 시나리오를 상세히 설명한 [ARCHITECTURE.md](./ARCHITECTURE.md)를 참고하세요.
## 프로젝트 구조 ## 프로젝트 구조
``` ```
@@ -50,6 +222,7 @@ cointrader/
│ ├── mlx_filter.py # MLX 신경망 필터 (Apple Silicon GPU 학습 + ONNX export) │ ├── mlx_filter.py # MLX 신경망 필터 (Apple Silicon GPU 학습 + ONNX export)
│ ├── label_builder.py # 학습 레이블 생성 │ ├── label_builder.py # 학습 레이블 생성
│ ├── dataset_builder.py # 벡터화 데이터셋 빌더 (학습용) │ ├── dataset_builder.py # 벡터화 데이터셋 빌더 (학습용)
│ ├── backtester.py # 백테스트 엔진 (단일 + Walk-Forward)
│ ├── risk_manager.py # 공유 리스크 관리 (asyncio.Lock, 동일 방향 제한) │ ├── risk_manager.py # 공유 리스크 관리 (asyncio.Lock, 동일 방향 제한)
│ ├── notifier.py # Discord 웹훅 알림 │ ├── notifier.py # Discord 웹훅 알림
│ └── logger_setup.py # Loguru 로거 설정 │ └── logger_setup.py # Loguru 로거 설정
@@ -60,7 +233,7 @@ cointrader/
│ ├── train_and_deploy.sh # 전체 파이프라인 (--symbol / --all 지원) │ ├── train_and_deploy.sh # 전체 파이프라인 (--symbol / --all 지원)
│ ├── tune_hyperparams.py # Optuna 하이퍼파라미터 자동 탐색 (--symbol 지원) │ ├── tune_hyperparams.py # Optuna 하이퍼파라미터 자동 탐색 (--symbol 지원)
│ ├── strategy_sweep.py # 전략 파라미터 그리드 스윕 (324개 조합) │ ├── strategy_sweep.py # 전략 파라미터 그리드 스윕 (324개 조합)
│ ├── weekly_report.py # 주간 전략 리포트 (백테스트+로그+추이+Discord) │ ├── weekly_report.py # 주간 전략 리포트 (백테스트+대시보드API+추이+Discord)
│ ├── run_backtest.py # 단일 백테스트 CLI │ ├── run_backtest.py # 단일 백테스트 CLI
│ ├── deploy_model.sh # 모델 파일 LXC 서버 전송 (--symbol 지원) │ ├── deploy_model.sh # 모델 파일 LXC 서버 전송 (--symbol 지원)
│ └── run_tests.sh # 전체 테스트 실행 │ └── run_tests.sh # 전체 테스트 실행
@@ -68,128 +241,85 @@ cointrader/
│ ├── api/ # FastAPI 백엔드 (로그 파서 + REST API) │ ├── api/ # FastAPI 백엔드 (로그 파서 + REST API)
│ └── ui/ # React 프론트엔드 (Vite + Recharts) │ └── ui/ # React 프론트엔드 (Vite + Recharts)
├── models/ # 학습된 모델 저장 (심볼별 하위 디렉토리) ├── models/ # 학습된 모델 저장 (심볼별 하위 디렉토리)
│ ├── xrpusdt/ # models/xrpusdt/lgbm_filter.pkl
│ ├── trxusdt/ # models/trxusdt/lgbm_filter.pkl
│ └── dogeusdt/ # models/dogeusdt/lgbm_filter.pkl
├── data/ # 과거 데이터 캐시 (심볼별 하위 디렉토리) ├── data/ # 과거 데이터 캐시 (심볼별 하위 디렉토리)
│ ├── xrpusdt/ # data/xrpusdt/combined_15m.parquet
│ ├── trxusdt/ # data/trxusdt/combined_15m.parquet
│ └── dogeusdt/ # data/dogeusdt/combined_15m.parquet
├── results/ ├── results/
│ └── weekly/ # 주간 리포트 JSON 저장 │ └── weekly/ # 주간 리포트 JSON 저장
├── logs/ # 로그 파일 ├── logs/ # 로그 파일
├── docs/plans/ # 설계 문서 및 구현 플랜 ├── docs/plans/ # 설계 문서 및 구현 플랜
├── tests/ # 테스트 코드 ├── tests/ # 테스트 코드 (15파일, 136개 케이스)
├── Dockerfile ├── Dockerfile
├── docker-compose.yml ├── docker-compose.yml
├── Jenkinsfile ├── Jenkinsfile
└── requirements.txt └── requirements.txt
``` ```
--- ## 개발 환경 설정
## 빠른 시작
### 1. 환경변수 설정
```bash ```bash
# 가상환경 생성 및 활성화
python -m venv .venv
source .venv/bin/activate
# 의존성 설치
pip install -r requirements.txt
# 환경변수 설정
cp .env.example .env cp .env.example .env
``` ```
`.env` 파일을 열어 아래 값을 채웁니다. ## 테스트
```env
BINANCE_API_KEY=your_api_key
BINANCE_API_SECRET=your_api_secret
SYMBOLS=XRPUSDT,TRXUSDT,DOGEUSDT
CORRELATION_SYMBOLS=BTCUSDT,ETHUSDT
LEVERAGE=10
MAX_SAME_DIRECTION=2
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
```
### 2. 로컬 실행
```bash ```bash
pip install -r requirements.txt # 전체 테스트 (136개)
python main.py bash scripts/run_tests.sh
# 특정 키워드 필터
bash scripts/run_tests.sh -k bot
# pytest 직접 실행
pytest tests/ -v
``` ```
### 3. Docker Compose로 실행 모든 외부 API(Binance, Discord)는 `unittest.mock.AsyncMock`으로 대체되며, 비동기 테스트는 `@pytest.mark.asyncio`를 사용합니다.
```bash
docker compose up -d
```
로그 확인:
```bash
docker compose logs -f cointrader
```
---
## ML 모델 학습 ## ML 모델 학습
봇은 모델 파일이 없으면 ML 필터 없이 동작합니다. 최초 실행 전 또는 수동 재학습 시 아래 순서로 진행합니다. 봇은 모델 파일이 없으면 ML 필터 없이 동작합니다. 모델을 학습하려면:
### 전체 파이프라인 (권장) ### 전체 파이프라인 (권장)
맥미니에서 데이터 수집 → 학습 → LXC 배포까지 한 번에 실행합니다.
> **자동 분기**: `data/{symbol}/combined_15m.parquet`가 없으면 1년치(365일) 전체 수집, 있으면 35일치 Upsert로 자동 전환합니다. 서버 이전이나 데이터 유실 시에도 사람의 개입 없이 자동 복구됩니다.
```bash ```bash
# 전체 심볼 학습 + 배포 (SYMBOLS 환경변수의 모든 심볼) # 전체 심볼 학습 + 배포
bash scripts/train_and_deploy.sh bash scripts/train_and_deploy.sh
# 단일 심볼만 학습 + 배포 # 단일 심볼만 학습 + 배포
bash scripts/train_and_deploy.sh --symbol TRXUSDT bash scripts/train_and_deploy.sh --symbol TRXUSDT
# MLX GPU 학습 (단일 심볼) # MLX GPU 학습 (Apple Silicon, 단일 심볼)
bash scripts/train_and_deploy.sh mlx --symbol XRPUSDT bash scripts/train_and_deploy.sh mlx --symbol XRPUSDT
# LightGBM + Walk-Forward 3폴드
bash scripts/train_and_deploy.sh lgbm 3
# 학습만 (배포 없이) # 학습만 (배포 없이)
bash scripts/train_and_deploy.sh lgbm 0 bash scripts/train_and_deploy.sh lgbm 0
``` ```
> **자동 분기**: `data/{symbol}/combined_15m.parquet`가 없으면 1년치 전체 수집, 있으면 35일치 Upsert로 자동 전환.
### 단계별 수동 실행 ### 단계별 수동 실행
```bash ```bash
# 1. 과거 데이터 수집 (단일 심볼 — 상관관계 심볼 자동 추가) # 1. 과거 데이터 수집
python scripts/fetch_history.py --symbol TRXUSDT --interval 15m --days 365 python scripts/fetch_history.py --symbol TRXUSDT --interval 15m --days 365
# → data/trxusdt/combined_15m.parquet 에 저장
# 1-alt. 명시적 심볼 지정 (기존 방식도 지원) # 2. LightGBM 모델 학습
python scripts/fetch_history.py \
--symbols XRPUSDT BTCUSDT ETHUSDT \
--interval 15m \
--days 365 \
--output data/combined_15m.parquet
# 2-A. LightGBM 모델 학습 (심볼별)
python scripts/train_model.py --symbol TRXUSDT python scripts/train_model.py --symbol TRXUSDT
# → models/trxusdt/lgbm_filter.pkl 에 저장
# 2-B. MLX 신경망 학습 (Apple Silicon GPU) # 3. 서버에 모델 배포
python scripts/train_mlx_model.py --data data/xrpusdt/combined_15m.parquet bash scripts/deploy_model.sh --symbol TRXUSDT
# 3. LXC 서버에 모델 배포
bash scripts/deploy_model.sh --symbol XRPUSDT
bash scripts/deploy_model.sh mlx --symbol XRPUSDT
``` ```
학습된 모델은 `models/{symbol}/lgbm_filter.pkl` (LightGBM) 또는 `models/{symbol}/mlx_filter.weights.onnx` (MLX) 에 저장됩니다. 심볼별 디렉토리가 없으면 `models/` 루트로 폴백합니다. > **모델 핫리로드**: 봇 실행 중 모델 파일을 교체하면, 다음 캔들 마감 시 자동으로 감지해 리로드합니다.
> **모델 핫리로드**: 봇이 실행 중일 때 모델 파일을 교체하면, 다음 캔들 마감 시 자동으로 감지해 리로드합니다. 봇 재시작이 필요 없습니다. ### 하이퍼파라미터 튜닝 (Optuna)
### 하이퍼파라미터 자동 튜닝 (Optuna)
봇 성능이 저하되거나 데이터가 충분히 축적되었을 때 Optuna로 최적 LightGBM 파라미터를 탐색합니다.
결과를 확인하고 직접 승인한 후 재학습에 반영하는 **수동 트리거** 방식입니다.
```bash ```bash
# 심볼별 튜닝 (50 trials, 5폴드 Walk-Forward, ~30분) # 심볼별 튜닝 (50 trials, 5폴드 Walk-Forward, ~30분)
@@ -197,73 +327,19 @@ python scripts/tune_hyperparams.py --symbol XRPUSDT
# 빠른 테스트 (10 trials, 3폴드, ~5분) # 빠른 테스트 (10 trials, 3폴드, ~5분)
python scripts/tune_hyperparams.py --symbol TRXUSDT --trials 10 --folds 3 python scripts/tune_hyperparams.py --symbol TRXUSDT --trials 10 --folds 3
# 베이스라인 측정 없이 탐색만
python scripts/tune_hyperparams.py --symbol XRPUSDT --no-baseline
``` ```
결과는 `models/{symbol}/tune_results_YYYYMMDD_HHMMSS.json`에 저장됩니다. 결과는 `models/{symbol}/tune_results_YYYYMMDD_HHMMSS.json`에 저장됩니다. Optuna가 찾은 파라미터는 과적합 위험이 있으므로 폴드별 AUC 분산과 개선폭을 반드시 검토하세요.
콘솔에 Best Params, 베이스라인 대비 개선폭, 폴드별 AUC를 출력하므로 직접 확인 후 판단하세요.
> **주의**: Optuna가 찾은 파라미터는 과적합 위험이 있습니다. Best Params를 `train_model.py`에 반영하기 전에 반드시 폴드별 AUC 분산과 개선폭을 검토하세요. ### Apple Silicon GPU 가속 (M1/M2/M3/M4)
### Apple Silicon GPU 가속 학습 (M1/M2/M3/M4) ```bash
pip install mlx # Apple Silicon 전용, requirements.txt에 미포함
M 시리즈 맥에서는 MLX를 사용해 통합 GPU(Metal)로 학습할 수 있습니다. bash scripts/train_and_deploy.sh mlx --symbol XRPUSDT
```
> **설치**: `mlx`는 Apple Silicon 전용이며 `requirements.txt`에 포함되지 않습니다.
> 맥미니에서 별도 설치: `pip install mlx`
MLX로 학습한 모델은 ONNX 포맷으로 export되어 Linux 서버에서 `onnxruntime`으로 추론합니다. MLX로 학습한 모델은 ONNX 포맷으로 export되어 Linux 서버에서 `onnxruntime`으로 추론합니다.
> **참고**: LightGBM은 Apple Silicon GPU를 공식 지원하지 않습니다. MLX는 Apple이 만든 ML 프레임워크로 통합 GPU를 자동으로 활용합니다.
---
## 매매 전략
### 기술 지표 신호 (15분봉)
| 지표 | 롱 조건 | 숏 조건 | 가중치 |
|------|---------|---------|--------|
| RSI (14) | < 35 | > 65 | 1 |
| MACD 크로스 | 골든크로스 | 데드크로스 | 2 |
| 볼린저 밴드 | 하단 이탈 | 상단 돌파 | 1 |
| EMA 정배열 (9/21/50) | 정배열 | 역배열 | 1 |
| Stochastic RSI | < 20 + K>D | > 80 + K<D | 1 |
| 거래량 | 20MA × 1.5 이상 시 신호 강화 | — | 보조 |
**진입 조건**: 가중치 합계 ≥ `SIGNAL_THRESHOLD`(기본 3) + (거래량 ≥ 20MA × `VOL_MULTIPLIER`(기본 2.5) 또는 가중치 합계 ≥ `SIGNAL_THRESHOLD` + 1)
**ADX 필터**: ADX < `ADX_THRESHOLD`(기본 25) 시 횡보장으로 판단, 진입 차단
**손절/익절**: ATR × `ATR_SL_MULT`(기본 2.0) / ATR × `ATR_TP_MULT`(기본 2.0) — 환경변수로 설정 가능
**ML 필터**: 예측 확률 ≥ 0.55 이어야 최종 진입 (현재 `NO_ML_FILTER=true`로 비활성화 — 아래 참고)
### 반대 시그널 재진입
보유 포지션과 반대 방향 신호가 발생하면:
1. 기존 포지션 즉시 청산 (미체결 SL/TP 주문 취소 포함)
2. ML 필터 통과 시 반대 방향으로 즉시 재진입
### ML 필터 현황 — 왜 현재 ML을 사용하지 않는가
현재 프로덕션 봇은 `NO_ML_FILTER=true`로 ML 필터를 **비활성화**한 상태로 운영 중입니다.
**비활성화 사유:**
1. **학습 데이터 부족**: Walk-Forward 검증(학습 3개월, 테스트 1개월) 시 각 폴드의 학습 세트에서 유효 신호가 약 27건에 불과. LightGBM이 의미 있는 패턴을 학습하기엔 표본 수가 절대적으로 부족.
2. **예측 무차별**: 학습된 모델이 모든 입력에 대해 거의 동일한 확률(~0.55)을 출력하여 필터링 효과가 사실상 없음. 모든 신호를 차단하거나 모든 신호를 통과시키는 극단적 동작.
3. **전략 파라미터 스윕 결과**: ADX 필터(≥25)와 거래량 배수(2.5)를 적용한 기본 기술 지표 전략만으로 PF 1.57~2.39를 달성. ML 없이도 수익성 확보 가능.
**ML 재활성화 조건 (주간 리포트에서 자동 체크):**
- 누적 트레이드 ≥ 150건 (충분한 학습 데이터 확보)
- 현재 PF < 1.0 (기술 지표만으로 수익성 저하)
- PF 3주 연속 하락 추세
3개 조건 중 2개 이상 충족 시 `scripts/weekly_report.py`가 Discord로 ML 재학습 권장 알림을 전송합니다.
---
## 전략 파라미터 스윕 ## 전략 파라미터 스윕
기술 지표 전략의 최적 파라미터를 Walk-Forward 백테스트로 탐색합니다. 기술 지표 전략의 최적 파라미터를 Walk-Forward 백테스트로 탐색합니다.
@@ -271,9 +347,6 @@ MLX로 학습한 모델은 ONNX 포맷으로 export되어 Linux 서버에서 `on
```bash ```bash
# 전체 스윕 (324개 조합, ~30분) # 전체 스윕 (324개 조합, ~30분)
python scripts/strategy_sweep.py --symbols XRPUSDT --train-months 3 --test-months 1 python scripts/strategy_sweep.py --symbols XRPUSDT --train-months 3 --test-months 1
# 결과 확인
cat results/sweep_*.json | python -m json.tool | head -50
``` ```
5개 파라미터 × 3~4개 값 = 324개 조합을 순차 테스트: 5개 파라미터 × 3~4개 값 = 324개 조합을 순차 테스트:
@@ -283,12 +356,10 @@ cat results/sweep_*.json | python -m json.tool | head -50
| `ATR_SL_MULT` | 1.0, 1.5, 2.0 | 손절 ATR 배수 | | `ATR_SL_MULT` | 1.0, 1.5, 2.0 | 손절 ATR 배수 |
| `ATR_TP_MULT` | 2.0, 3.0, 4.0 | 익절 ATR 배수 | | `ATR_TP_MULT` | 2.0, 3.0, 4.0 | 익절 ATR 배수 |
| `SIGNAL_THRESHOLD` | 3, 4, 5 | 최소 가중치 점수 | | `SIGNAL_THRESHOLD` | 3, 4, 5 | 최소 가중치 점수 |
| `ADX_THRESHOLD` | 0, 20, 25, 30 | ADX 필터 (0=비활성) | | `ADX_THRESHOLD` | 0, 20, 25, 30 | ADX 필터 |
| `VOL_MULTIPLIER` | 1.5, 2.0, 2.5 | 거래량 급증 배수 | | `VOL_MULTIPLIER` | 1.5, 2.0, 2.5 | 거래량 급증 배수 |
> **핵심 발견**: ADX ≥ 25 필터가 가장 영향력 있는 단일 파라미터. 상위 10개 결과 모두 ADX ≥ 25를 사용하며, 횡보장 노이즈 신호를 효과적으로 필터링. > **핵심 발견**: ADX ≥ 25 필터가 가장 영향력 있는 파라미터. 횡보장 노이즈 신호를 효과적으로 필터링.
---
## 주간 전략 리포트 ## 주간 전략 리포트
@@ -307,117 +378,36 @@ python scripts/weekly_report.py --date 2026-03-07
**리포트 내용:** **리포트 내용:**
- Walk-Forward 백테스트 성능 (심볼별 PF/승률/MDD) - Walk-Forward 백테스트 성능 (심볼별 PF/승률/MDD)
- 실전 트레이드 로그 파싱 (이번 주 거래 수/순수익/승률) - 운영 대시보드 API에서 실전 트레이드 통계 조회 (거래 수/순수익/승률)
- 성능 추이 (최근 4주 PF/승률/MDD 변화) - 성능 추이 (최근 4주 PF/승률/MDD 변화)
- ML 재도전 체크리스트 (3개 조건 자동 판단) - ML 재도전 체크리스트 (3개 조건 자동 판단)
- PF < 1.0 시 파라미터 스윕 대안 제시 - PF < 1.0 시 파라미터 스윕 대안 제시
**크론탭 설정 (프로덕션 서버):** > 실전 데이터는 운영 대시보드 API(`GET /api/trades`, `GET /api/stats`)에서 조회합니다. `DASHBOARD_API_URL` 환경변수로 주소를 설정하세요.
**크론탭 설정:**
```bash ```bash
# 매주 일요일 새벽 3시 KST # 매주 일요일 새벽 3시 KST
0 18 * * 6 cd /app && python scripts/weekly_report.py >> logs/cron.log 2>&1 0 18 * * 6 cd /app && python scripts/weekly_report.py >> logs/cron.log 2>&1
``` ```
리포트 결과는 `results/weekly/report_YYYY-MM-DD.json`에 저장됩니다.
---
## CI/CD ## CI/CD
`main` 브랜치에 푸시하면 Jenkins 파이프라인이 자동으로 실행됩니다. `main` 브랜치에 푸시하면 Jenkins 파이프라인이 자동 실행됩니다.
1. **Notify Build Start** — Discord 빌드 시작 알림 1. **Notify Build Start** — Discord 빌드 시작 알림
2. **Git Clone from Gitea** — 소스 체크아웃 2. **Git Clone from Gitea** — 소스 체크아웃
3. **Build Docker Image** — Docker 이미지 빌드 (`:{BUILD_NUMBER}` + `:latest` 태그) 3. **Build Docker Image** — Docker 이미지 빌드 (`:{BUILD_NUMBER}` + `:latest`)
4. **Push to Gitea Registry** Gitea Container Registry(`10.1.10.28:3000`)에 푸시 4. **Push to Gitea Registry** — Container Registry에 푸시
5. **Deploy to Prod LXC** — 운영 LXC 서버(`10.1.10.24`)에 자동 배포 (`docker compose pull && up -d`) 5. **Deploy to Prod** — 운영 서버에 자동 배포 (`docker compose pull && up -d`)
6. **Cleanup** 빌드 서버 로컬 이미지 정리 6. **Cleanup** — 로컬 이미지 정리
빌드 성공/실패 결과는 Discord로 자동 알림됩니다. 빌드 성공/실패 결과는 Discord로 자동 알림됩니다.
--- ## 설계 문서
## 대시보드 모든 설계 문서와 구현 계획은 `docs/plans/`에 저장됩니다.
봇 로그를 실시간으로 파싱하여 거래 내역, 수익 통계, 차트를 웹에서 조회할 수 있는 모니터링 대시보드입니다. - `YYYY-MM-DD-feature-name-design.md` — 설계 결정 문서
- `YYYY-MM-DD-feature-name-plan.md` — 단계별 구현 계획
### 기술 스택 - [ARCHITECTURE.md](./ARCHITECTURE.md) — 전체 아키텍처 (5-레이어, MLOps 파이프라인, 동작 시나리오, 테스트 커버리지)
- **프론트엔드**: React 18 + Vite + Recharts, Nginx 정적 서빙
- **백엔드**: FastAPI + SQLite, 로그 파서(5초 주기 폴링)
- **배포**: Docker Compose 3컨테이너 (`dashboard-ui`, `dashboard-api`, `cointrader`)
### 주요 화면
| 탭 | 내용 |
|----|------|
| **Overview** | 총 수익, 승률, 거래 수, 최대 수익/손실 KPI + 일별 PnL 차트 + 누적 수익 곡선 |
| **Trades** | 전체 거래 내역 — 진입/청산가, 방향, 레버리지, 기술 지표(RSI, MACD, ATR), SL/TP, 순익 상세 |
| **Chart** | XRP/USDT 15분봉 가격 차트 + RSI 지표 + ADX 추세 강도 |
### API 엔드포인트
| 엔드포인트 | 설명 |
|-----------|------|
| `GET /api/position` | 현재 포지션 + 봇 상태 |
| `GET /api/trades` | 청산 거래 내역 (페이지네이션) |
| `GET /api/daily` | 일별 PnL 집계 |
| `GET /api/stats` | 전체 통계 (총 거래, 승률, 수수료 등) |
| `GET /api/candles` | 최근 캔들 + 기술 지표 |
| `GET /api/health` | 헬스 체크 |
| `POST /api/reset` | DB 초기화 + 로그 파서 재시작 |
### 실행
```bash
docker compose up -d
```
대시보드는 `http://<서버IP>:8080`에서 접속할 수 있습니다. 봇 로그를 읽기 전용으로 마운트하여 봇 코드를 수정하지 않는 디커플드 설계입니다.
---
## 테스트
```bash
# 전체 테스트
bash scripts/run_tests.sh
# 특정 키워드 필터
bash scripts/run_tests.sh -k bot
# pytest 직접 실행
pytest tests/ -v
```
---
## 환경변수 레퍼런스
| 변수 | 기본값 | 설명 |
|------|--------|------|
| `BINANCE_API_KEY` | — | Binance API 키 |
| `BINANCE_API_SECRET` | — | Binance API 시크릿 |
| `SYMBOLS` | `XRPUSDT` | 거래 심볼 목록 (쉼표 구분, 예: `XRPUSDT,TRXUSDT,DOGEUSDT`) |
| `CORRELATION_SYMBOLS` | `BTCUSDT,ETHUSDT` | 상관관계 심볼 (BTC/ETH 수익률·상대강도 피처용) |
| `LEVERAGE` | `10` | 레버리지 배수 |
| `MAX_SAME_DIRECTION` | `2` | 동일 방향 최대 포지션 수 (LONG 2개면 3번째 LONG 차단) |
| `DISCORD_WEBHOOK_URL` | — | Discord 웹훅 URL |
| `MARGIN_MAX_RATIO` | `0.50` | 최대 증거금 비율 (잔고 대비 50%) |
| `MARGIN_MIN_RATIO` | `0.20` | 최소 증거금 비율 (잔고 대비 20%) |
| `MARGIN_DECAY_RATE` | `0.0006` | 잔고 증가 시 증거금 비율 감소 속도 |
| `NO_ML_FILTER` | `true` | `true`/`1`/`yes` 설정 시 ML 필터 완전 비활성화 — 모델 로드 없이 모든 신호 허용 (현재 프로덕션 기본값) |
| `ML_THRESHOLD` | `0.55` | ML 필터 예측 확률 임계값 — 이 값 이상이어야 진입 허용 |
| `ATR_SL_MULT` | `2.0` | 손절 ATR 배수 (진입가 ± ATR × 이 값) |
| `ATR_TP_MULT` | `2.0` | 익절 ATR 배수 (진입가 ± ATR × 이 값) |
| `SIGNAL_THRESHOLD` | `3` | 진입을 위한 최소 가중치 지표 점수 |
| `ADX_THRESHOLD` | `25` | ADX 횡보장 필터 (이 값 미만이면 진입 차단, 0=비활성) |
| `VOL_MULTIPLIER` | `2.5` | 거래량 급증 감지 배수 (20MA × 이 값 이상 시 급증 판정) |
---
## 주의사항
> **이 봇은 실제 자산을 거래합니다.** 운영 전 반드시 Binance Testnet에서 충분히 검증하세요.
> 과거 수익이 미래 수익을 보장하지 않습니다. 투자 손실에 대한 책임은 사용자 본인에게 있습니다.
> 성투기원합니다.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,146 @@
# 코드 리뷰 개선 사항
**날짜**: 2026-03-07
**상태**: 대기
## 목표
전체 코드베이스 리뷰에서 발견된 버그, 엣지 케이스, 로직 오류를 우선순위별로 정리하고 수정한다.
---
## Critical (즉시 수정 필요)
### 1. OI 변화율 계산 시 Division by Zero
**파일**: `src/bot.py:120`
`_prev_oi`가 0.0일 때 `(current_oi - self._prev_oi) / self._prev_oi`에서 ZeroDivisionError 발생. `get_open_interest()` 실패 시 0.0을 반환하므로 실제로 발생 가능.
**수정**: `_prev_oi == 0.0`이면 `oi_change = 0.0`으로 처리.
### 2. 누적 트레이드 수 계산 로직 오류
**파일**: `scripts/weekly_report.py:415-423`
```python
# 현재 (잘못됨) — max()로 비교하여 누적이 아닌 최대값만 가져옴
cumulative = live_count
for rpath in sorted(rdir.glob("report_*.json")):
cumulative = max(cumulative, prev.get("live_trades", {}).get("count", 0))
```
ML 재학습 트리거 조건(`≥ 150건`)이 제대로 작동하지 않음.
**수정**: 이전 리포트의 `live_trades.count`를 합산하도록 변경.
---
## Important (이번 주 수정 권장)
### 3. Training-Serving Skew (OI/펀딩비 피처)
**파일**: `src/dataset_builder.py` vs `src/ml_features.py`
- 학습 시: OI=0 구간을 NaN으로 마스킹 후 z-score
- 서빙 시: OI 값을 그대로 NaN으로 설정
ML 활성화 시 학습/서빙 간 피처 분포 불일치 발생. 현재 ML OFF이므로 당장은 영향 없지만, ML 재활성화 전 반드시 수정 필요.
### 4. `fetch_history.py` — API 실패/Rate Limit 미처리
**파일**: `scripts/fetch_history.py:46-61`
`futures_klines()` 호출에 retry 로직이 없음. Rate limit(429) 발생 시 예외로 크래시. `weekly_report.py`의 subprocess가 무한 대기할 수 있음.
**수정**: `tenacity` 또는 수동 retry 로직 추가 (최대 3회, exponential backoff).
### 5. Parquet Upsert 시 중복 타임스탬프 미제거
**파일**: `scripts/fetch_history.py:314`
`sort_index()`만 하고 `drop_duplicates()`를 하지 않음. API 응답에 중복 타임스탬프가 있으면 지표 계산이 이중 계산됨.
**수정**: `sort_index()` 앞에 `df[~df.index.duplicated(keep='last')]` 추가.
### 6. `record_pnl()`에 asyncio.Lock 미사용
**파일**: `src/risk_manager.py:55`
`record_pnl()``self.daily_pnl`을 수정하지만 `async with self._lock`을 사용하지 않음. 멀티심볼 환경에서 동시 호출 시 일일 손실 한도 체크가 부정확할 수 있음.
**수정**: `record_pnl()`을 async로 변경하고 `async with self._lock:` 추가.
### 7. 백테스터 Equity Curve 미구현
**파일**: `src/backtester.py:509-510`
`_record_equity()``pass`로 비어 있음. MDD 계산이 실현 PnL 기준이지 포트폴리오 가치(미실현 PnL 포함) 기준이 아님. MDD가 과소평가될 수 있음.
**수정**: 미실현 PnL을 포함한 equity 계산 구현.
### 8. User Data Stream — exit_price 기본값 0.0
**파일**: `src/user_data_stream.py:95`
`order.get("ap", "0")`에서 필드 누락 시 exit_price=0.0으로 설정되어 PnL이 완전히 잘못 계산됨.
**수정**: `exit_price == 0.0`이면 청산 처리를 스킵하고 WARNING 로그 출력.
---
## Minor (다음 스프린트)
### 9. 거래량 급증 진입 조건 의도 불일치
**파일**: `src/indicators.py:115-118`
`(vol_surge or long_signals >= signal_threshold + 1)` — 거래량 급증만으로도 진입 허용됨. "강한 신호 + 거래량 급증"이 의도라면 AND 조건이어야 하는데, 현재 OR로 구현됨. 현재 전략 파라미터 스윕 결과(ADX=25, Vol=2.5)에서는 큰 문제 없으나, 의도를 확인하고 정리 필요.
### 10. ML 모델 피처 불일치 시 Silent Failure
**파일**: `src/ml_filter.py:152`
ONNX 모델과 현재 FEATURE_COLS가 다르면 예외를 잡고 `False`를 반환(모든 신호 차단). 사용자에게 원인이 보이지 않아 디버깅이 어려움.
**수정**: 피처 수 불일치는 WARNING이 아닌 ERROR로 로깅하고, 최초 발생 시 Discord 알림 전송.
### 11. `train_model.py` — 빈 데이터셋 미처리
**파일**: `scripts/train_model.py:196`
`generate_dataset_vectorized()`가 빈 DataFrame을 반환하면 Walk-Forward 검증에서 step=0이 되어 무한 루프 가능.
**수정**: 빈 데이터셋 시 `ValueError("No samples generated")` raise.
### 12. `data_stream.py` — AsyncClient 생성 실패 시 전체 크래시
**파일**: `src/data_stream.py:79-82`
네트워크 단절 상태에서 봇 시작 시 `AsyncClient.create()` 실패로 모든 심볼이 함께 크래시.
**수정**: retry with exponential backoff (최대 5회) 추가.
### 13. `fetch_history.py` — Parquet 타임존 처리 불일치
**파일**: `scripts/fetch_history.py:286-289`
`tz_localize("UTC")` 호출 시 기존 데이터가 실제로 UTC인지 검증하지 않음. 타임존이 다른 데이터가 섞이면 OI/펀딩비 병합이 시간축으로 어긋남.
**수정**: `tz_localize(tz='UTC', ambiguous='raise', nonexistent='raise')` 사용.
---
## 수정 우선순위
| 우선순위 | 이슈 | 난이도 | 영향도 |
|---------|------|--------|--------|
| 즉시 | #1 OI division by zero | 5분 | 봇 크래시 |
| 즉시 | #2 누적 트레이드 계산 | 5분 | ML 트리거 오작동 |
| 이번주 | #4 fetch_history retry | 30분 | 데이터 수집 행 |
| 이번주 | #5 Parquet 중복 제거 | 5분 | 지표 이중 계산 |
| 이번주 | #6 record_pnl Lock | 5분 | 리스크 한도 부정확 |
| 이번주 | #8 exit_price=0 방어 | 10분 | PnL 오계산 |
| ML 재활성화 전 | #3 Training-Serving skew | 30분 | 예측 품질 저하 |
| 다음 스프린트 | #7 Equity curve 구현 | 1시간 | MDD 과소평가 |
| 다음 스프린트 | #9-13 기타 | 각 10-30분 | 안정성 개선 |

View File

@@ -62,7 +62,7 @@
}, },
"live_trades": { "live_trades": {
"count": 0, "count": 0,
"net_pnl": 0, "net_pnl": 0.0,
"win_rate": 0 "win_rate": 0
}, },
"trend": { "trend": {
@@ -85,7 +85,7 @@
}, },
"met_count": 0, "met_count": 0,
"recommend": false, "recommend": false,
"cumulative_trades": 88, "cumulative_trades": 0,
"threshold": 150 "threshold": 150
}, },
"sweep": null "sweep": null