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:
21in7
2026-03-07 16:00:52 +09:00
parent afdbacaabd
commit 97aef14d6c
3 changed files with 539 additions and 5 deletions

View File

@@ -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 다음 스프린트)
## 목표

View 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

View File

@@ -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