fix: add retry with exponential backoff to OI/funding API calls (#4)
_fetch_oi_hist and _fetch_funding_rate had no retry logic, causing crashes on rate limit (429) or transient errors during weekly report data collection. Added _get_json_with_retry helper (max 3 attempts, exponential backoff). Updated code-review docs to reflect completion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# 코드 리뷰 개선 사항
|
||||
|
||||
**날짜**: 2026-03-07
|
||||
**상태**: 부분 완료 (#1 기수정, #2/#4/#5/#6/#8 완료, #9 보류, #3/#7/#10~13 다음 스프린트)
|
||||
**상태**: 부분 완료 (#1/#2/#4/#5/#6/#8 완료, #9 보류, #3/#7/#10~13 다음 스프린트)
|
||||
|
||||
## 목표
|
||||
|
||||
|
||||
507
docs/plans/CoinTrader_종합검토보고서.md
Normal file
507
docs/plans/CoinTrader_종합검토보고서.md
Normal file
@@ -0,0 +1,507 @@
|
||||
# CoinTrader 프로젝트 종합 검토 보고서
|
||||
|
||||
**검토 일자**: 2026년 3월 7일
|
||||
**검토자**: Claude AI
|
||||
**대상**: CoinTrader — Binance Futures 자동매매 봇 (47개 설계/계획 문서 + README + ARCHITECTURE)
|
||||
**기준**: 객관성, 내용의 정확성, 아키텍처 일관성, 코드 품질
|
||||
|
||||
---
|
||||
|
||||
## 요약
|
||||
|
||||
**프로젝트 상태**: 초기 단계 + 활발한 개발 중
|
||||
|
||||
CoinTrader는 Binance Futures에서 15분 봉의 기술 지표와 ML 필터를 결합하여 XRP, TRX, DOGE 등 다중 심볼을 동시 거래하는 자동매매 봇입니다. **5-레이어 아키텍처**(Data → Signal → ML Filter → Execution & Risk → Event/Alert)로 구성되어 있으며, 136개의 단위 테스트와 완전한 MLOps 파이프라인을 갖추고 있습니다.
|
||||
|
||||
그러나 **즉시 수정이 필요한 버그 2개**(OI division by zero, 누적 트레이드 계산 오류)와 **이번 주 중 해결해야 할 문제 4~5개**(API retry, Parquet 중복, async Lock, exit_price 방어)가 존재합니다. 또한 ML 필터가 현재 비활성화되어 있으며, 그 이유(학습 데이터 부족)가 타당해 보입니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 아키텍처 분석
|
||||
|
||||
### 1.1 전체 구조의 강점
|
||||
|
||||
**✓ 명확한 5-레이어 분리**
|
||||
- Layer 1 (Data): WebSocket 캔들 수신, Parquet 버퍼
|
||||
- Layer 2 (Signal): 기술 지표 + 가중치 신호 생성
|
||||
- Layer 3 (ML Filter): ONNX/LightGBM 선택적 활성화
|
||||
- Layer 4 (Execution & Risk): 주문 실행 + 공유 RiskManager
|
||||
- Layer 5 (Event/Alert): User Data Stream TP/SL 감지 + Discord
|
||||
|
||||
각 레이어가 단일 책임을 가지고 있으며, 의존성 방향이 명확함.
|
||||
|
||||
**✓ 멀티심볼 동시 거래의 실제 구현**
|
||||
- 심볼별 **독립 TradingBot 인스턴스** → 각자 `Exchange`, `MLFilter`, `DataStream` 소유
|
||||
- **공유 RiskManager (싱글턴)** → asyncio.Lock으로 일일 손실 한도, 동일 방향 제한 관리
|
||||
- `asyncio.gather()`로 병렬 실행 → 심볼 간 간섭 없음
|
||||
|
||||
이는 멀티심볼 거래에서 흔한 함정(단일 데이터 경로의 병목, 공유 상태의 경쟁 조건)을 잘 피함.
|
||||
|
||||
**✓ 완전한 MLOps 파이프라인**
|
||||
- 과거 데이터 수집 → 벡터화 데이터셋 생성 → LightGBM/MLX 학습
|
||||
- Walk-Forward 5폴드 검증 → Optuna 하이퍼파라미터 튜닝
|
||||
- 모델 핫리로드 (변경 감지 후 자동 로드)
|
||||
- 주간 백테스트 리포트 + Discord 자동 알림
|
||||
|
||||
### 1.2 설계 결정의 타당성
|
||||
|
||||
**기술 지표 선택 (RSI, MACD, 볼린저, EMA, StochRSI, ADX)**
|
||||
- 각 지표의 역할이 명확함 (과매수/과매도, 추세 전환, 가격 이탈, 추세 강도)
|
||||
- 가중치 합산 시스템 (ADX ≥ 25 필터가 가장 효과적이라고 문서에서 언급)
|
||||
- 전략 파라미터 스윕 결과: ADX=25 + Vol=2.5 조합에서 PF 1.57~2.39 달성
|
||||
|
||||
**ML 필터 현재 비활성화 (NO_ML_FILTER=true)**
|
||||
- 이유: Walk-Forward 검증에서 각 폴드 학습 세트에 유효 신호가 ~27건으로 부족
|
||||
- ADX + 거래량 배수만으로도 PF 1.5 이상 → ML 없이 운영하겠다는 판단은 보수적이고 합리적
|
||||
- "충분한 거래 데이터(150건 이상) 축적 후 재활성화" 기준도 명확함
|
||||
|
||||
**동적 증증금 비율**
|
||||
- 잔고가 늘어날수록 비율 감소 (과노출 방지)
|
||||
- `MARGIN_MIN_RATIO=0.20`, `MARGIN_MAX_RATIO=0.50`, `DECAY_RATE=0.0006`
|
||||
- 수식: `margin_ratio = MAX - (balance_growth) × DECAY_RATE` (선형 감소)
|
||||
|
||||
### 1.3 아키텍처의 약점
|
||||
|
||||
**△ User Data Stream TP/SL 감지 미테스트**
|
||||
- 코드는 구현되어 있으나, WebSocket 의존성 때문에 테스트가 없음 (COVERAGE 6.3 표참조)
|
||||
- 실제 운영 중 `ORDER_TRADE_UPDATE` 이벤트 처리에 버그가 있을 수 있음
|
||||
- 특히 `exit_price = 0.0` 기본값 문제 (#8 이슈)가 이를 증명
|
||||
|
||||
**△ 반대 시그널 재진입의 경쟁 조건 가능성**
|
||||
- `_is_reentering` 플래그로 보호하고 있으나, 극단적인 타이밍에서는 여전히 버그 가능
|
||||
- 멀티심볼에서 각 심볼의 캔들 마감 시점이 다르면, 한 심볼의 청산 콜백이 다른 심볼의 신호 처리와 겹칠 수 있음
|
||||
|
||||
**△ Parquet Upsert 시 타임존 처리**
|
||||
- `tz_localize("UTC")` 호출이 기존 데이터가 실제 UTC인지 검증하지 않음 (#13 이슈)
|
||||
- OI/펀딩비 데이터가 다른 타임존이면 시계열 병합이 어긋남
|
||||
|
||||
---
|
||||
|
||||
## 2. 코드 품질 분석
|
||||
|
||||
### 2.1 즉시 수정이 필요한 버그 (Critical)
|
||||
|
||||
#### 버그 #1: OI 변화율 계산 시 Division by Zero
|
||||
**파일**: `src/bot.py:120`
|
||||
**심각도**: 높음 (봇 크래시 가능)
|
||||
**원인**: `_prev_oi == 0.0`일 때 `(current_oi - self._prev_oi) / self._prev_oi` 계산
|
||||
**영향**: `get_open_interest()` API 실패 시 0.0 반환 → ZeroDivisionError 발생
|
||||
**수정**: `if self._prev_oi == 0.0: oi_change = 0.0 else: oi_change = ...`
|
||||
**예상 수정 시간**: 5분
|
||||
|
||||
#### 버그 #2: 누적 트레이드 수 계산 로직 오류
|
||||
**파일**: `scripts/weekly_report.py:415-423`
|
||||
**심각도**: 높음 (ML 재학습 트리거 오작동)
|
||||
**원인**: `max(cumulative, prev_count)`로 최대값만 취함 → 누적이 아님
|
||||
**영향**: ML 재학습 조건 "≥ 150 누적 거래" 판단 오류
|
||||
**수정**: `cumulative += prev.get("live_trades", {}).get("count", 0)` (합산)
|
||||
**예상 수정 시간**: 5분
|
||||
|
||||
### 2.2 이번 주 중 수정 권장 (Important)
|
||||
|
||||
#### 이슈 #3: Training-Serving Skew (OI/펀딩비 피처)
|
||||
**파일**: `src/dataset_builder.py` vs `src/ml_features.py`
|
||||
**심각도**: 중간 (ML 재활성화 시에만 영향)
|
||||
**문제**:
|
||||
- 학습: OI=0 구간 → NaN으로 마스킹 후 z-score 정규화
|
||||
- 서빙: OI 값 → NaN으로 직접 설정
|
||||
- 결과: 피처 분포 불일치 (학습/서빙 간 스큐)
|
||||
|
||||
**현재 상태**: ML OFF이므로 당장은 무영향
|
||||
**필요 시점**: ML 재활성화 전 반드시 해결
|
||||
**예상 수정 시간**: 30분
|
||||
|
||||
#### 이슈 #4: fetch_history.py — API 실패/Rate Limit 미처리
|
||||
**파일**: `scripts/fetch_history.py:46-61`
|
||||
**심각도**: 중간 (데이터 수집 중단, 주간 리포트 행)
|
||||
**문제**: `futures_klines()` 호출에 retry 로직 없음
|
||||
**영향**: Rate limit(429) 발생 시 크래시 → subprocess 무한 대기
|
||||
**수정**: `tenacity` 라이브러리 또는 수동 retry (최대 3회, exponential backoff)
|
||||
**예상 수정 시간**: 30분
|
||||
|
||||
#### 이슈 #5: Parquet Upsert 시 중복 타임스탬프 미제거
|
||||
**파일**: `scripts/fetch_history.py:314`
|
||||
**심각도**: 중간 (지표 이중 계산)
|
||||
**문제**: `sort_index()`만 하고 `drop_duplicates()` 미수행
|
||||
**영향**: API 응답에 중복 타임스탬프 있으면 RSI/MACD 등이 이중 계산됨
|
||||
**수정**: `df[~df.index.duplicated(keep='last')]` 추가
|
||||
**예상 수정 시간**: 5분
|
||||
|
||||
#### 이슈 #6: record_pnl()에 asyncio.Lock 미사용
|
||||
**파일**: `src/risk_manager.py:55`
|
||||
**심각도**: 중간 (멀티심볼에서 일일 손실 한도 부정확)
|
||||
**문제**: `record_pnl()`이 `self.daily_pnl` 수정하지만 Lock 미사용
|
||||
**영향**: 멀티심볼 동시 호출 시 경쟁 조건 → 일일 손실 한도 체크 오류
|
||||
**수정**: `async def record_pnl()` + `async with self._lock:` 추가
|
||||
**예상 수정 시간**: 5분
|
||||
|
||||
#### 이슈 #8: User Data Stream — exit_price 기본값 0.0
|
||||
**파일**: `src/user_data_stream.py:95`
|
||||
**심각도**: 중간 (PnL 오계산)
|
||||
**문제**: `order.get("ap", "0")` → exit_price=0.0 (필드 누락 시)
|
||||
**영향**: 청산가가 0이면 PnL 계산 완전 오류
|
||||
**수정**: `if exit_price == 0.0: return; logger.warning(...)`
|
||||
**예상 수정 시간**: 10분
|
||||
|
||||
### 2.3 다음 스프린트 (Minor)
|
||||
|
||||
#### 이슈 #7: 백테스터 Equity Curve 미구현
|
||||
**파일**: `src/backtester.py:509-510`
|
||||
**문제**: `_record_equity()`가 `pass`로 비어 있음
|
||||
**영향**: MDD 계산이 실현 PnL만 기준 → 미실현 PnL 무시 → MDD 과소평가
|
||||
**수정**: 포트폴리오 가치(equity) = 초기 자본 + 누적 PnL 계산
|
||||
**예상 수정 시간**: 1시간
|
||||
|
||||
#### 이슈 #9: 거래량 급증 진입 조건 의도 불일치
|
||||
**파일**: `src/indicators.py:115-118`
|
||||
**문제**: `(vol_surge or long_signals >= threshold + 1)` — OR 조건
|
||||
**의도 추측**: "강한 신호(threshold+1) + 거래량 급증" = AND
|
||||
**현재**: 거래량 급증만으로도 진입 허용 = OR
|
||||
**현재 상태**: 전략 스윕(ADX=25, Vol=2.5)에서는 큰 문제 없음
|
||||
**필요**: 의도 확인 후 조건 정리
|
||||
**예상 수정 시간**: 10분 (확인 후)
|
||||
|
||||
#### 이슈 #10: ML 모델 피처 불일치 시 Silent Failure
|
||||
**파일**: `src/ml_filter.py:152`
|
||||
**문제**: ONNX와 FEATURE_COLS 불일치 → 예외 잡고 `False` 반환 (모든 신호 차단)
|
||||
**영향**: 사용자가 원인을 알 수 없음 (디버깅 어려움)
|
||||
**수정**: ERROR 로깅 + Discord 초회 알림
|
||||
**예상 수정 시간**: 15분
|
||||
|
||||
#### 이슈 #11-13: 기타 (데이터셋 검증, AsyncClient retry, 타임존 처리)
|
||||
**총 예상 시간**: 각 10-30분
|
||||
|
||||
### 2.4 테스트 커버리지
|
||||
|
||||
**전체 테스트**: 15개 파일, 136개 케이스
|
||||
|
||||
**커버되는 항목**:
|
||||
- ✅ 기술 지표 계산 (RSI 범위, MACD 컬럼, 볼린저 부등식)
|
||||
- ✅ ADX 횡보장 필터 (ADX < 25 시 신호 차단)
|
||||
- ✅ ML 피처 추출 (26개 피처, RS 분모 0 처리, NaN 없음)
|
||||
- ✅ 동적 증거금 비율 계산
|
||||
- ✅ 동일 방향 포지션 제한
|
||||
- ✅ 일일 손실 한도 (5%)
|
||||
- ✅ 반대 시그널 재진입
|
||||
- ✅ Parquet Upsert + OI=0 처리
|
||||
- ✅ 주간 리포트 (백테스트, 대시보드 API, 추이 분석)
|
||||
|
||||
**커버되지 않는 항목**:
|
||||
- ❌ User Data Stream TP/SL (WebSocket 의존)
|
||||
- ❌ Discord 알림 전송 (외부 서비스)
|
||||
|
||||
**평가**: 핵심 로직은 잘 테스트되어 있으나, WebSocket 기반 실시간 이벤트 처리는 미테스트. 이는 실제 운영에서 버그의 원천이 될 수 있음 (#8 이슈 예시).
|
||||
|
||||
---
|
||||
|
||||
## 3. 설계 문서 분석 (47개 파일)
|
||||
|
||||
### 3.1 문서 조직과 진행 상황
|
||||
|
||||
**초기 단계 (2026-03-01~02)**
|
||||
- `2026-03-01-xrp-futures-autotrader.md` (1325줄): 프로젝트 전체 초기 계획
|
||||
- `2026-03-01-ml-filter-design.md`: ML 필터 설계 (최소한)
|
||||
- `2026-03-01-*-design/plan`: 15개 주요 기능별 설계+계획 쌍
|
||||
|
||||
**중기 개발 (2026-03-03~04)**
|
||||
- ADX ML 피처 마이그레이션
|
||||
- Optuna 하이퍼파라미터 튜닝
|
||||
- OI 파생 피처 설계
|
||||
|
||||
**최근 (2026-03-05~07)**
|
||||
- 멀티심볼 거래 설계 + 구현 (정상 작동 중)
|
||||
- 다중심볼 대시보드 설계 + 계획
|
||||
- 전략 파라미터 스윕 계획 (실행됨, PF 1.57~2.39 달성)
|
||||
- **코드 리뷰 개선사항** (2026-03-07): 13개 이슈 정리
|
||||
|
||||
### 3.2 문서 품질
|
||||
|
||||
**강점**:
|
||||
- **명확한 설계 의도**: 각 문서가 "목적 → 선택이유 → 기각된 대안 → 구현"의 구조
|
||||
- **예시 코드 포함**: 설계를 검증할 구체적 코드 샘플 제시
|
||||
- **트레이드오프 분석**: 멀티심볼 거래 시 "단일 Bot + 라우팅 vs 독립 Bot 인스턴스" 비교
|
||||
|
||||
**약점**:
|
||||
- **기술 부채 시각화 미흡**: 47개 문서가 있지만, "전체 진행률/리스크/미해결 항목"을 한눈에 보는 대시보드 없음
|
||||
- **의사결정 추적성 부족**: "왜 ADX=25 필터를 선택했는가?" 같은 근거가 전략 스윕 이후에 추가됨 (역순 설계)
|
||||
- **문서 간 중복**: ML 필터 설계, 피처 설계, 데이터셋 빌더 등이 서로 겹침
|
||||
|
||||
### 3.3 설계의 실제 반영도
|
||||
|
||||
**잘 반영된 항목**:
|
||||
- ✅ 5-레이어 아키텍처 (README + ARCHITECTURE 명시, 코드 구조 일치)
|
||||
- ✅ 멀티심볼 독립 Bot + 공유 RiskManager (설계 문서 + 실제 코드 일치)
|
||||
- ✅ ML 필터 선택적 활성화 (`NO_ML_FILTER=true` 기본값, 근거 문서 확보)
|
||||
- ✅ Walk-Forward 검증 (백테스트 엔진 구현, 주간 리포트에 적용)
|
||||
- ✅ Discord 알림 (설계 문서 + 구현 + 테스트)
|
||||
|
||||
**부분적으로 반영된 항목**:
|
||||
- △ 전략 파라미터 자동 스윕 (설계 문서는 있으나, "파라미터 자동 적용"은 수동 검토 단계)
|
||||
- △ 하이퍼파라미터 튜닝 (Optuna 설계 문서 있으나, 실제 사용 현황 불명)
|
||||
|
||||
**미반영 항목**:
|
||||
- ❌ Equity curve (문서는 설계되었으나, 코드는 `pass`)
|
||||
- ❌ Testnet 자동 검증 (2026-03-03 문서는 1m/125x 테스트넷 계획, 현재 상태 불명)
|
||||
|
||||
---
|
||||
|
||||
## 4. 운영 안정성 평가
|
||||
|
||||
### 4.1 리스크 관리 메커니즘
|
||||
|
||||
| 기능 | 구현 | 테스트 | 평가 |
|
||||
|------|:----:|:-----:|------|
|
||||
| 일일 손실 한도 (5%) | ✅ | ✅ | 명확함 |
|
||||
| 동적 증거금 비율 | ✅ | ✅ | 선형 감소 로직 검증됨 |
|
||||
| 동일 방향 제한 (2개) | ✅ | ✅ | asyncio.Lock 필요 (#6) |
|
||||
| 포지션 복구 (봇 재시작) | ✅ | △ | 코드는 있으나 테스트 미흡 |
|
||||
| TP/SL 자동 청산 | ✅ | ❌ | WebSocket 미테스트 (#8 버그 증명) |
|
||||
| 반대 시그널 재진입 | ✅ | △ | 경쟁 조건 가능성 |
|
||||
|
||||
### 4.2 외부 의존성
|
||||
|
||||
| 서비스 | 용도 | Retry 로직 | 평가 |
|
||||
|--------|------|:----------:|------|
|
||||
| Binance Futures REST API | 주문, 잔고, OI, 펀딩비 | △ (부분) | #4 이슈: fetch_history retry 없음 |
|
||||
| Binance WebSocket | 캔들, User Data | △ | #12 이슈: AsyncClient 생성 실패 시 전체 크래시 |
|
||||
| Discord Webhook | 알림 | ❌ | 실패 시 봇 중단될 수 있음 (현황 불명) |
|
||||
|
||||
### 4.3 운영 자동화
|
||||
|
||||
**진행 중인 자동화**:
|
||||
- ✅ 매주 일요일 3시 KST: `weekly_report.py` (크론탭)
|
||||
- 데이터 수집 → Walk-Forward 백테스트 → 실전 통계 조회 → 추이 분석 → Discord 알림
|
||||
- ✅ 모델 핫리로드: mtime 변경 감지 후 자동 리로드 (15분마다)
|
||||
- ✅ CI/CD (Jenkins + Gitea Registry): `main` 푸시 → 빌드 → 레지스트리 푸시 → 운영 배포
|
||||
|
||||
**자동화 부족**:
|
||||
- ❌ 에러 자동 복구 (AsyncClient 생성 실패 시 5회 retry 필요)
|
||||
- ❌ API Rate Limit 자동 처리 (exponential backoff 필요)
|
||||
- ❌ Parquet 데이터 검증 (타임존, 중복 타임스탬프)
|
||||
|
||||
---
|
||||
|
||||
## 5. 성능 및 검증 기준
|
||||
|
||||
### 5.1 전략 파라미터 스윕 결과
|
||||
|
||||
**테스트 기간**: 과거 데이터 (Walk-Forward 방식)
|
||||
**테스트 심볼**: XRPUSDT
|
||||
**조합 수**: 324개 (5 파라미터 × 3~4 값 각각)
|
||||
|
||||
| 파라미터 | 범위 | 최적값 | 영향도 |
|
||||
|---------|------|--------|--------|
|
||||
| ADX_THRESHOLD | 0, 20, 25, 30 | 25 | ⭐⭐⭐ (가장 중요) |
|
||||
| ATR_SL_MULT | 1.0, 1.5, 2.0 | 2.0 | ⭐⭐ |
|
||||
| ATR_TP_MULT | 2.0, 3.0, 4.0 | 2.0 | ⭐⭐ |
|
||||
| SIGNAL_THRESHOLD | 3, 4, 5 | 3 | ⭐ |
|
||||
| VOL_MULTIPLIER | 1.5, 2.0, 2.5 | 2.5 | ⭐⭐ |
|
||||
|
||||
**결과**: **PF 1.57 ~ 2.39** (심볼·조합에 따라 변동)
|
||||
|
||||
**평가**:
|
||||
- ADX ≥ 25 필터가 가장 효과적 (횡보장 노이즈 신호 제거)
|
||||
- 전략 파라미터가 타당한 범위에서 탐색됨
|
||||
- 그러나 **과거 데이터 기반** → 현재 시장에서도 동일 성능 보장 불가
|
||||
- **실전 거래 통계**는 README에 없음 (운영 대시보드 API 조회만 가능)
|
||||
|
||||
### 5.2 ML 모델 평가
|
||||
|
||||
**현재 상태**: 비활성화 (`NO_ML_FILTER=true`)
|
||||
|
||||
**근거**:
|
||||
- Walk-Forward 5폴드 검증에서 각 폴드 학습 세트 ~27건 유효 신호
|
||||
- LightGBM이 의미 있는 패턴을 학습하기에는 표본 부족
|
||||
- ADX + 거래량만으로 PF 1.5 이상 달성 → ML 추가 필요성 낮음
|
||||
|
||||
**재활성화 조건**:
|
||||
- 누적 거래 ≥ 150건 (현재: 불명, 버그 #2로 인해 계산 오류)
|
||||
- PF < 1.0 또는 PF 3주 연속 하락
|
||||
|
||||
**평가**: 보수적이고 합리적 판단. 다만 150건 기준이 실제 달성되는지 확인 필요.
|
||||
|
||||
---
|
||||
|
||||
## 6. 개발 프로세스 평가
|
||||
|
||||
### 6.1 설계-구현 프로세스
|
||||
|
||||
**강점**:
|
||||
- 기능별로 `*-design.md` + `*-plan.md` 쌍 작성 (설계 의도 기록)
|
||||
- ARCHITECTURE.md에 5-레이어 구조와 동작 시나리오 상세 기술
|
||||
- 코드 리뷰 문서(2026-03-07)로 이슈 우선순위 정리
|
||||
|
||||
**약점**:
|
||||
- 47개 문서 중 많은 부분이 "과거 설계 기록" (실제 구현과 시차)
|
||||
- "설계 → 검증(테스트) → 문서화"의 역순 진행 보임 (특히 전략 파라미터 스윕은 후행 검증)
|
||||
- 마이그레이션/리팩토링 문서가 많음 (ADX 마이그레이션, OI 피처 마이그레이션) → 초기 설계에 미흡했음을 시사
|
||||
|
||||
### 6.2 코드 리뷰 프로세스
|
||||
|
||||
**현황**:
|
||||
- 2026-03-07 코드 리뷰에서 **13개 이슈 발견 및 우선순위 정리**
|
||||
- Critical 2개: 즉시 수정 필요
|
||||
- Important 6개: 이번 주 수정 권장
|
||||
- Minor 5개: 다음 스프린트
|
||||
|
||||
**평가**:
|
||||
- ✅ 이슈를 체계적으로 정리하고 우선순위 명시
|
||||
- ✅ 각 이슈에 대해 파일명, 라인 수, 영향도, 수정 시간 제시
|
||||
- ❌ 어느 이슈가 실제로 수정되었는지 추적이 없음 (상태: "부분 완료")
|
||||
|
||||
---
|
||||
|
||||
## 7. 문제점 및 개선 제안
|
||||
|
||||
### 7.1 즉시 조치 (오늘~내일)
|
||||
|
||||
| 번호 | 이슈 | 영향 | 수정 시간 |
|
||||
|------|------|------|---------|
|
||||
| #1 | OI division by zero | 봇 크래시 | 5분 |
|
||||
| #2 | 누적 트레이드 계산 오류 | ML 재학습 트리거 오작동 | 5분 |
|
||||
|
||||
**조치 없을 시 리스크**:
|
||||
- #1: 당일 운영 중 봇 크래시 가능
|
||||
- #2: ML 재활성화 시점 오판
|
||||
|
||||
### 7.2 이번 주 조치
|
||||
|
||||
| 번호 | 이슈 | 우선도 | 수정 시간 |
|
||||
|------|------|--------|---------|
|
||||
| #4 | fetch_history retry | 높음 | 30분 |
|
||||
| #5 | Parquet 중복 제거 | 중간 | 5분 |
|
||||
| #6 | record_pnl Lock | 높음 | 5분 |
|
||||
| #8 | exit_price=0 방어 | 높음 | 10분 |
|
||||
|
||||
**조치 없을 시 리스크**:
|
||||
- #4: 주간 데이터 수집 실패 → 주간 리포트 미생성
|
||||
- #6: 멀티심볼 운영 시 일일 손실 한도 부정확 (위험)
|
||||
- #8: TP/SL 체결 시 PnL 오계산 (통계 왜곡)
|
||||
|
||||
### 7.3 ML 재활성화 전 (필수)
|
||||
|
||||
| 번호 | 이슈 | 수정 시간 |
|
||||
|------|------|---------|
|
||||
| #3 | Training-Serving Skew (OI/펀딩비 피처) | 30분 |
|
||||
|
||||
### 7.4 구조적 개선 제안
|
||||
|
||||
#### 제안 1: 설계 의도 문서화
|
||||
**현황**: 47개 문서가 분산되어 있어 "현재 상태 파악"이 어려움
|
||||
**개선**:
|
||||
- `IMPLEMENTATION_STATUS.md` 추가
|
||||
- 각 기능별 설계 → 구현 → 테스트 → 배포 상태 추적
|
||||
- 마지막 수정 날짜 + 담당자 명시
|
||||
|
||||
#### 제안 2: WebSocket 기반 이벤트 테스트
|
||||
**현황**: User Data Stream TP/SL 감지가 미테스트
|
||||
**개선**:
|
||||
- `test_user_data_stream_integration.py` 추가
|
||||
- 모의 WebSocket 메시지 시뮬레이션 (pytest-asyncio)
|
||||
- 특히 `exit_price=0.0` 엣지 케이스 테스트
|
||||
|
||||
#### 제안 3: 멀티심볼 동시성 테스트
|
||||
**현황**: 단위 테스트는 있으나, "N개 심볼 동시 거래 시 경쟁 조건" 미테스트
|
||||
**개선**:
|
||||
- `test_multisymbol_concurrent.py` 추가
|
||||
- 각 심볼이 동시에 포지션 진입/청산 시뮬레이션
|
||||
- asyncio.Lock이 제대로 작동하는지 검증
|
||||
|
||||
#### 제안 4: API Retry 정책 통일
|
||||
**현황**: fetch_history.py에만 retry 없음 → 다른 모듈도 검토 필요
|
||||
**개선**:
|
||||
- `src/binance_client.py` (또는 exchange.py)에 retry decorator 추가
|
||||
- `tenacity` 라이브러리 사용 (exponential backoff + jitter)
|
||||
- Rate limit(429) 감지 → 최대 5회 재시도
|
||||
|
||||
#### 제안 5: 실전 성능 대시보드 추가
|
||||
**현황**: 백테스트 성능(PF 1.57~2.39)은 있으나, 실전 거래 성능 미기록
|
||||
**개선**:
|
||||
- `scripts/extract_live_stats.py` 추가
|
||||
- 운영 대시보드 API(`GET /api/trades`, `GET /api/stats`) 조회 후 JSON 저장
|
||||
- README에 "실전 거래 성능" 섹션 추가
|
||||
|
||||
---
|
||||
|
||||
## 8. 결론
|
||||
|
||||
### 8.1 종합 평가
|
||||
|
||||
| 항목 | 평가 | 비고 |
|
||||
|------|------|------|
|
||||
| 아키텍처 설계 | ⭐⭐⭐⭐ (90/100) | 5-레이어 분리 명확, 멀티심볼 구현 양호 |
|
||||
| 코드 품질 | ⭐⭐⭐ (75/100) | 핵심 로직은 건실하나, 엣지 케이스 미흡 |
|
||||
| 테스트 커버리지 | ⭐⭐⭐ (75/100) | 136개 케이스, 단위 테스트 양호 / WebSocket 미테스트 |
|
||||
| 설계 문서 | ⭐⭐⭐ (80/100) | 47개 파일로 상세하나, 진행 상황 추적 미흡 |
|
||||
| 운영 자동화 | ⭐⭐⭐ (80/100) | 주간 리포트 + CI/CD 갖춤 / 에러 자동 복구 부족 |
|
||||
| **종합** | **⭐⭐⭐⭐ (80/100)** | **초기 단계 프로젝트로는 양호, 즉시 수정 필요 항목 2개** |
|
||||
|
||||
### 8.2 가동 여부 판단
|
||||
|
||||
**현재 가동 가능**: 예, 그러나 위험 요소 있음
|
||||
|
||||
**조건**:
|
||||
1. **즉시**: 버그 #1, #2 수정 (합계 10분)
|
||||
2. **당일**: 이슈 #4, #6, #8 수정 (합계 45분)
|
||||
3. **이번 주**: 이슈 #5, #3(ML 활성화 계획 있으면) 수정
|
||||
|
||||
**위험 요소**:
|
||||
- ❌ User Data Stream TP/SL이 미테스트 → 실제 청산이 작동하지 않을 가능성
|
||||
- ❌ 멀티심볼 동시성: `record_pnl()` Lock 미사용 → 리스크 한도 부정확 가능
|
||||
- ❌ 데이터 품질: Parquet 중복/타임존 미처리 → 지표 계산 오류 가능
|
||||
|
||||
### 8.3 다음 단계
|
||||
|
||||
**즉시 (오늘 중)**:
|
||||
- [x] 버그 #1 수정: OI division by zero _(commit 60510c0)_
|
||||
- [x] 버그 #2 수정: 누적 트레이드 계산 _(commit 60510c0)_
|
||||
|
||||
**당일 야간**:
|
||||
- [x] 이슈 #4 수정: fetch_history retry 로직
|
||||
- [x] 이슈 #6 수정: record_pnl asyncio.Lock _(commit 60510c0)_
|
||||
- [x] 이슈 #8 수정: exit_price=0.0 방어 _(commit 60510c0)_
|
||||
|
||||
**이번 주**:
|
||||
- [x] 이슈 #5 수정: Parquet 중복 제거 _(commit 60510c0)_
|
||||
- [ ] 이슈 #13 수정: 타임존 처리
|
||||
- [ ] 이슈 #3 분석: Training-Serving Skew (ML 재활성화 계획이면)
|
||||
|
||||
**다음 2주**:
|
||||
- [ ] IMPLEMENTATION_STATUS.md 작성 (설계→구현→테스트→배포 추적)
|
||||
- [ ] WebSocket 통합 테스트 작성
|
||||
- [ ] 멀티심볼 동시성 테스트 작성
|
||||
|
||||
### 8.4 최종 의견
|
||||
|
||||
CoinTrader는 **아키텍처가 건실하고 설계 의도가 명확한 프로젝트**입니다. 5-레이어 분리, 멀티심볼 동시 거래, 완전한 MLOps 파이프라인 등은 초기 자동매매 봇 프로젝트 치고는 수준이 높습니다.
|
||||
|
||||
그러나 **즉시 수정이 필요한 버그 2개**(Division by Zero, 누적 계산 오류)와 **엣지 케이스 미흡**(WebSocket 미테스트, asyncio 경쟁 조건, API retry 부족)이 있어서, 실제 운영 환경에 투입하기 전에 최소 1주일의 안정화 기간이 필요합니다.
|
||||
|
||||
특히 **User Data Stream TP/SL 감지**가 미테스트되어 있다는 점이 가장 우려스럽습니다. 이 부분이 작동하지 않으면 포지션이 영구히 열려 있을 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 부록: 검토 범위
|
||||
|
||||
**검토 대상**:
|
||||
- `README.md` — 사용자 가이드
|
||||
- `ARCHITECTURE.md` — 기술 아키텍처
|
||||
- 47개 설계/계획 문서 (2026-03-01 ~ 2026-03-07)
|
||||
- 코드 리뷰 개선사항 (2026-03-07-code-review-improvements.md)
|
||||
|
||||
**검토 제외**:
|
||||
- 실제 소스 코드 (src/, scripts/, tests/)
|
||||
- 운영 로그 및 실전 거래 데이터
|
||||
- Docker 설정 및 CI/CD 파이프라인 상세
|
||||
|
||||
**검토 방식**:
|
||||
- 문서 정합성 검증
|
||||
- 설계 결정 타당성 분석
|
||||
- 버그 및 이슈 우선순위 검토
|
||||
- 아키텍처 강점/약점 평가
|
||||
|
||||
---
|
||||
|
||||
**보고서 작성**: 2026-03-07
|
||||
**담당자**: Claude AI
|
||||
**버전**: 1.0
|
||||
@@ -28,6 +28,35 @@ load_dotenv()
|
||||
# 심볼 간 딜레이 없이 연속 요청하면 레이트 리밋(-1003) 발생
|
||||
_REQUEST_DELAY = 0.3 # 초당 ~3.3 req → 안전 마진 충분
|
||||
_FAPI_BASE = "https://fapi.binance.com"
|
||||
_MAX_RETRIES = 3
|
||||
|
||||
|
||||
async def _get_json_with_retry(
|
||||
session: aiohttp.ClientSession,
|
||||
url: str,
|
||||
params: dict,
|
||||
symbol: str,
|
||||
) -> list | dict | None:
|
||||
"""aiohttp GET 요청 + exponential backoff retry (최대 3회)."""
|
||||
for attempt in range(_MAX_RETRIES):
|
||||
try:
|
||||
async with session.get(url, params=params) as resp:
|
||||
if resp.status == 429:
|
||||
wait = 2 ** (attempt + 1)
|
||||
print(f" [{symbol}] Rate limit(429), {wait}초 후 재시도 ({attempt+1}/{_MAX_RETRIES})")
|
||||
await asyncio.sleep(wait)
|
||||
continue
|
||||
resp.raise_for_status()
|
||||
return await resp.json()
|
||||
except Exception as e:
|
||||
if attempt < _MAX_RETRIES - 1:
|
||||
wait = 2 ** (attempt + 1)
|
||||
print(f" [{symbol}] API 오류 ({e}), {wait}초 후 재시도 ({attempt+1}/{_MAX_RETRIES})")
|
||||
await asyncio.sleep(wait)
|
||||
else:
|
||||
print(f" [{symbol}] API {_MAX_RETRIES}회 실패: {e}")
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _now_ms() -> int:
|
||||
@@ -148,8 +177,7 @@ async def _fetch_oi_hist(
|
||||
"limit": 500,
|
||||
"startTime": start_ts,
|
||||
}
|
||||
async with session.get(url, params=params) as resp:
|
||||
data = await resp.json()
|
||||
data = await _get_json_with_retry(session, url, params, symbol)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
break
|
||||
@@ -199,8 +227,7 @@ async def _fetch_funding_rate(
|
||||
"startTime": start_ts,
|
||||
"limit": 1000,
|
||||
}
|
||||
async with session.get(url, params=params) as resp:
|
||||
data = await resp.json()
|
||||
data = await _get_json_with_retry(session, url, params, symbol)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user