Files
AutoCoinTrader2/docs/code_review_report_v5.md

20 KiB

AutoCoinTrader Code Review Report (v5)

1. 개요 (Overview)

본 보고서는 AutoCoinTrader 프로젝트의 전체 코드베이스에 대한 v5 종합 심층 분석입니다. v4 리포트 이후의 변경사항을 반영하고, Python 전문가전문 암호화폐 트레이더 관점에서 단계별로 꼼꼼하게 검토하였습니다.

분석 범위:

  • 14개 핵심 소스 모듈 (총 ~5,500줄)
  • 15개 테스트 파일
  • 아키텍처, 코드 품질, 성능, 안정성, 트레이딩 로직, 리스크 관리

분석 방법론:

  1. 정적 코드 분석 (Static Analysis)
  2. 논리 흐름 추적 (Control Flow Analysis)
  3. 동시성 패턴 검토 (Concurrency Review)
  4. 트레이딩 전략 유효성 검토 (Strategy Validation)

2. 🚨 긴급 수정 필요 사항 (CRITICAL Issues)

CRITICAL-001: order.py 구문 오류 (Syntax Error)

파일: src/order.py (라인 789-792)

문제점:

# 현재 코드 (오류)
            if attempt == max_retries:
                raise
        time.sleep(ORDER_RETRY_DELAY)
            continue  # ← IndentationError: 들여쓰기 오류
    except (requests.exceptions.RequestException, ValueError, TypeError, OSError) as e:

영향:

  • Python 인터프리터 오류: 이 파일을 import하면 IndentationError 발생
  • 전체 시스템 작동 불가: 매도 주문 기능 완전 실패

수정 방안:

# 수정된 코드
            if attempt == max_retries:
                raise
            time.sleep(ORDER_RETRY_DELAY)
            continue
        except (requests.exceptions.RequestException, ValueError, TypeError, OSError) as e:

우선순위: 🔴 즉시 수정 필수 (P0)


CRITICAL-002: holdings.py 중복 return 문

파일: src/holdings.py (라인 510-513)

문제점:

        if not new_holdings_map:
            return {}

            return {}  # ← 접근 불가능한 코드 (dead code)

영향:

  • 코드는 실행되지만, 죽은 코드(dead code)가 존재
  • 유지보수 혼란 및 코드 품질 저하

수정 방안: 중복 return {} 제거

우선순위: 🟡 빠른 수정 권장 (P1)


3. Python 전문가 관점 분석 (Technical Review)

3.1 아키텍처 및 디자인 패턴

우수한 점

패턴 적용 내용 평가
단일 책임 원칙 (SRP) 각 모듈이 명확한 역할 수행 (order.py=주문, signals.py=신호, holdings.py=보유관리) Excellent
불변 설정 객체 RuntimeConfig dataclass (frozen=True)로 설정 불변성 보장 Best Practice
상태 분리 StateManager로 봇 상태와 거래소 캐시 분리 v4 대비 개선
원자적 파일 쓰기 임시 파일 → os.replace()os.fsync() 패턴 일관 적용 Production Ready

⚠️ 개선 필요 영역

1. 순환 의존성 잠재 리스크 (Medium)

graph TD
    A[order.py] --> B[holdings.py]
    A --> C[signals.py]
    A --> D[common.py]
    B --> E[state_manager.py]
    C --> A
    C --> B
  • order.pysignals.py 간 상호 참조 존재
  • TYPE_CHECKING으로 런타임 순환 회피 중이나, 리팩토링 시 주의 필요

2. 모듈 크기 불균형 (Low)

모듈 라인 수 권장
order.py 1,289 ⚠️ 500~700 권장 (분할 고려)
signals.py 960 ⚠️ 분할 고려
holdings.py 700 적정
common.py 413 적정

권장: order.pyorder_buy.py, order_sell.py, order_monitor.py로 분할


3.2 코드 품질 및 스타일

우수한 점

1. 타입 힌팅 (Type Hinting) - 95%+ 커버리지

def place_buy_order_upbit(market: str, amount_krw: float, cfg: RuntimeConfig) -> dict:

2. 정밀도 관리 (Precision Handling)

# Decimal 사용으로 부동소수점 오차 방지
getcontext().prec = 28
d_price = Decimal(str(price))
volume = (d_amount / d_price).quantize(Decimal("0.00000001"), rounding=ROUND_DOWN)

3. 방어적 프로그래밍 (Defensive Programming)

# None-safe 포매팅
def _safe_format(value, precision: int = 2, default: str = "N/A") -> str:
    if value is None:
        return default
    if pd.isna(value):
        return default

⚠️ 개선 필요 영역

1. 광범위한 Exception 처리 (High)

현재 여러 곳에서 Exception 전체를 catch하고 있음:

# 문제점: 예상치 못한 버그를 숨길 수 있음
except Exception as e:
    logger.error("오류: %s", e)
    return None

권장: 구체적 예외 타입 지정

except (requests.exceptions.RequestException, json.JSONDecodeError, ValueError) as e:
    logger.error("알려진 오류: %s", e)
except Exception as e:
    logger.exception("예상치 못한 오류 - 재발생: %s", e)
    raise  # 또는 특정 처리

영향 범위:

  • holdings.py: 9개소
  • order.py: 12개소
  • signals.py: 7개소

2. 매직 넘버 하드코딩 (Medium)

# 현재
time.sleep(0.5)  # 무슨 의미?
if len(calls) >= 590:  # 왜 590?

# 권장
TELEGRAM_RATE_LIMIT_DELAY = 0.5  # Telegram API 초당 제한 대응
UPBIT_MINUTE_RATE_LIMIT = 590  # Upbit 분당 600회 제한의 안전 마진

3. 일관성 없는 로그 포맷 (Low)

# 혼재된 스타일
logger.info("[INFO] [%s] 매수 성공", symbol)  # [INFO] 중복
logger.info("[%s] 매수 성공", symbol)  # 권장 스타일
logger.info(f"[{symbol}] 매수 성공")  # f-string 스타일

3.3 동시성 및 스레드 안전성

우수한 점

1. 리소스별 Lock 분리 (Best Practice)

# 각 리소스에 전용 Lock 할당 → 데드락 위험 최소화
holdings_lock = threading.RLock()      # holdings.json 보호
_state_lock = threading.RLock()        # bot_state.json 보호
_cache_lock = threading.Lock()         # 가격/잔고 캐시 보호
_pending_order_lock = threading.Lock() # 대기 주문 보호
krw_balance_lock = threading.RLock()   # KRW 잔고 조회 직렬화
recent_sells_lock = threading.RLock()  # recent_sells.json 보호

2. KRW 예산 관리자 (Token 기반)

class KRWBudgetManager:
    """동일 심볼 다중 주문도 안전하게 지원"""
    def allocate(self, symbol, amount_krw, ...) -> tuple[bool, float, str | None]:
        # 고유 토큰으로 각 주문 구분
        token = secrets.token_hex(8)

3. Rate Limiter (Token Bucket)

# 초당 8회 + 분당 590회 동시 제한
api_rate_limiter = RateLimiter(
    max_calls=8,
    period=1.0,
    additional_limits=[(590, 60.0)]
)

⚠️ 개선 필요 영역

1. Lock 획득 순서 미문서화 (Medium)

여러 Lock을 동시에 획득하는 경우가 있으나, 획득 순서가 문서화되지 않음.

# 잠재적 데드락 시나리오
# Thread A: holdings_lock → _state_lock
# Thread B: _state_lock → holdings_lock

# 권장: Lock 획득 순서 규약 문서화
# 1. holdings_lock
# 2. _state_lock
# 3. _cache_lock

2. 캐시 만료 시 경합 (Low)

# 캐시 TTL 만료 시 여러 스레드가 동시에 API 호출 가능
if (now - ts) > PRICE_CACHE_TTL:
    # 여러 스레드가 이 조건을 동시에 통과할 수 있음
    price = pyupbit.get_current_price(market)

권장: Double-checked locking 또는 cache stampede prevention


3.4 예외 처리 및 회복력

우수한 점

1. Circuit Breaker 패턴

class CircuitBreaker:
    """API 장애 시 자동 차단"""
    STATES = ["closed", "open", "half_open"]
    # 연속 3회 실패 → 5분 차단 → 점진적 복구

2. 지수 백오프 재시도 (Exponential Backoff with Jitter)

sleep_time = base_backoff * (2 ** (attempt - 1))
sleep_time += random.uniform(0, jitter_factor * sleep_time)

3. ReadTimeout 복구 로직

except requests.exceptions.ReadTimeout:
    # 1단계: 중복 주문 확인
    is_dup, dup_order = _has_duplicate_pending_order(...)
    if is_dup:
        return dup_order  # 중복 방지

    # 2단계: 최근 주문 조회
    found = _find_recent_order(...)
    if found:
        return found  # 이미 체결된 주문 반환

⚠️ 개선 필요 영역

1. 중복 주문 검증 정확도 (Medium)

# 현재: 수량/가격만으로 중복 판단
if abs(order_vol - volume) < 1e-8:
    if price is None or abs(order_price - price) < 1e-4:
        return True, order

# 문제: 다른 사유로 동일 수량/가격 주문이 존재할 수 있음
# 권장: UUID 캐시 또는 client_order_id 사용

3.5 테스트 커버리지 분석

테스트 파일 현황 (15개)

테스트 파일 대상 모듈 커버리지
test_order.py order.py 핵심 기능
test_evaluate_sell_conditions.py signals.py 매도 조건
test_krw_budget_manager.py common.py 예산 관리
test_concurrent_buy_orders.py 동시성 로직 경합 조건
test_circuit_breaker.py circuit_breaker.py 복구 로직
test_state_reconciliation.py holdings.py/state_manager.py 상태 동기화
test_boundary_conditions.py 다수 경계값
test_critical_fixes.py 다수 주요 버그픽스
test_recent_sells.py common.py 재매수 방지
test_holdings_cache.py holdings.py 캐시 로직
...

⚠️ 개선 필요 영역

1. 통합 테스트 부재 (High)

  • 현재: 대부분 단위 테스트
  • 권장: 매수→보유→매도 전체 플로우 통합 테스트

2. 엣지 케이스 미커버 (Medium)

  • 네트워크 단절 중 여러 주문 동시 발생
  • API Rate Limit 도달 시 동작
  • 파일 시스템 권한 오류

3. 모킹 의존도 높음 (Low)

  • pyupbit 모킹으로 실제 API 동작 검증 불가
  • 권장: 별도 샌드박스 환경 테스트

4. 전문 트레이더 관점 분석 (Trading Logic Review)

4.1 진입 전략 (Entry Strategy)

복합 매수 조건 (Triple Confirmation)

graph TD
    A[매수조건1] -->|MACD 상향 돌파| B{SMA5 > SMA200}
    B -->|Yes| C{ADX > 25}
    C -->|Yes| D[매수 신호]

    E[매수조건2] -->|SMA 골든크로스| F{MACD > Signal}
    F -->|Yes| G{ADX > 25}
    G -->|Yes| D

    H[매수조건3] -->|ADX 상향 돌파| I{SMA5 > SMA200}
    I -->|Yes| J{MACD > Signal}
    J -->|Yes| D

분석:

  • 다중 확인으로 False Signal 감소
  • ADX 필터로 추세 확인 (횡보장 회피)
  • ⚠️ 보수적 접근으로 진입 기회 감소 가능

재매수 방지 (Rebuy Cooldown)

def can_buy(symbol: str, cooldown_hours: int = 24) -> bool:
    """매도 후 24시간 쿨다운"""

장점: 감정적 재진입 방지, Pump & Dump 피해 최소화

⚠️ 개선 필요 영역

1. 볼륨 확인 부재 (High)

현재 MACD/SMA/ADX만 확인하고 거래량 확인 없음:

# 현재
cross_macd_signal = prev_macd < prev_signal and curr_macd > curr_signal

# 권장: 거래량 동반 확인
volume_surge = curr_volume > sma_volume * 1.5  # 평균 대비 150%
valid_signal = cross_macd_signal and volume_surge

2. 시장 상태 필터 부재 (Medium)

# 권장: 비트코인 방향성 확인
def is_btc_bullish():
    btc_data = fetch_ohlcv("KRW-BTC", "1d", 20)
    return btc_data["close"].iloc[-1] > btc_data["close"].rolling(20).mean().iloc[-1]

3. 진입 가격 최적화 부재 (Low)

  • 현재: 신호 발생 시 시장가/지정가 즉시 매수
  • 권장: VWAP 또는 지지선 근처 지정가 대기

4.2 청산 전략 (Exit Strategy)

계층적 트레일링 스탑 (Tiered Trailing Stop)

# 구간별 차등 스탑 설정
저수익 구간 (< 10%):  최고점 대비 -5%  전량 매도
중간 구간 (10~30%):   수익률 10% 이하 복귀  전량 매도
                      또는 최고점 대비 -5%  전량 매도
고수익 구간 (> 30%):  수익률 30% 이하 복귀  전량 매도
                      또는 최고점 대비 -15%  전량 매도

분석:

  • 수익 구간별 차등 보호 (전문가 수준)
  • 상승 여력 확보하면서 수익 보호
  • max_price 영구 저장으로 재시작 시에도 유지

분할 매도 (Partial Profit Taking)

# 10% 달성 시 50% 부분 익절 (1회 제한)
if not partial_sell_done and profit_rate >= 10.0:
    return {"status": "stop_loss", "sell_ratio": 0.5, "set_partial_sell_done": True}

분석:

  • 리스크 감소 + 나머지로 큰 수익 추구
  • partial_sell_done 플래그로 중복 방지
  • 최소 주문 금액 미만 시 자동 전량 매도 전환

손절 즉시 실행

# is_stop_loss 플래그로 확인 절차 건너뜀
bypass_confirmation = not confirm_via_file or (final_is_stop_loss and not confirm_stop_loss)

⚠️ 개선 필요 영역

1. ATR 기반 동적 스탑 미적용 (Medium)

# 현재: 고정 퍼센티지
drawdown_1 = 5.0  # 모든 코인에 동일

# 권장: ATR 기반 동적 스탑
def calculate_dynamic_stop(symbol, atr_multiplier=2.0):
    atr = ta.atr(df["high"], df["low"], df["close"], length=14).iloc[-1]
    current_price = df["close"].iloc[-1]
    stop_distance = (atr / current_price) * atr_multiplier * 100
    return max(3.0, min(stop_distance, 15.0))  # 3~15% 범위 제한

2. 시간 기반 청산 미적용 (Low)

  • 현재: 가격 조건만 확인
  • 권장: 장기 횡보 시 기회비용 고려 청산

4.3 리스크 관리 (Risk Management)

우수한 점

리스크 관리 항목 구현 상태 평가
API 장애 대응 (Circuit Breaker) Excellent
KRW 잔고 경쟁 방지 Excellent
부분 매수 지원 Good
Rate Limiting Excellent
슬리피지 관리 Good
최소 주문 금액 검증 Excellent

⚠️ 개선 필요 영역

1. 최대 보유 종목 수 제한 없음 (High)

# 현재: symbols.txt의 모든 심볼 매수 가능
# 문제: 과도한 분산 투자 → 관리 어려움

# 권장: 최대 보유 종목 수 제한
MAX_HOLDINGS = 5

def can_open_new_position(holdings):
    return len(holdings) < MAX_HOLDINGS

2. 포트폴리오 손실 한도 부재 (High)

# 권장: 일일/주간 손실 한도 모니터링
def check_portfolio_drawdown(holdings, initial_balance):
    current_value = calculate_portfolio_value(holdings)
    drawdown = (current_value - initial_balance) / initial_balance * 100

    if drawdown <= -20:  # 20% 손실 시
        logger.error("포트폴리오 손실 한도 도달: %.2f%%", drawdown)
        # 신규 매수 차단 또는 전량 청산

3. 심볼별 상관관계 미고려 (Medium)

  • 현재: 각 심볼 독립적으로 처리
  • 문제: BTC 하락 시 대부분 알트코인 동반 하락
  • 권장: 상관관계 높은 종목 그룹화하여 동시 보유 제한

4. 변동성 기반 포지션 사이징 미적용 (Medium)

# 현재: 고정 금액
buy_amount_krw = 50000

# 권장: 변동성 역비례 사이징
def calculate_position_size(symbol, base_amount, max_volatility=5.0):
    volatility = calculate_volatility(symbol)  # ATR 기반
    if volatility > max_volatility:
        return base_amount * (max_volatility / volatility)
    return base_amount

5. v4 → v5 변경사항 및 신규 발견

🟢 v4 이후 확인된 개선사항

  1. StateManager 안정화: max_price 영구 저장 안정적 동작
  2. Stop-Loss 즉시 실행: is_stop_loss 플래그 정상 작동
  3. KRW 예산 관리: 토큰 기반 독립 할당으로 동시 주문 안전
  4. 테스트 커버리지 증가: 15개 테스트 파일

🔴 v5에서 새로 발견된 문제

ID 심각도 문제 영향
CRITICAL-001 🔴 Critical order.py 구문 오류 시스템 작동 불가
CRITICAL-002 🟡 High holdings.py 중복 return 죽은 코드
HIGH-001 🟡 High 광범위한 Exception 처리 버그 은폐 가능
HIGH-002 🟡 High 최대 보유 종목 수 제한 없음 과도한 분산
HIGH-003 🟡 High 포트폴리오 손실 한도 부재 대손 리스크
HIGH-004 🟡 High 볼륨 확인 부재 (매수) False Signal
MEDIUM-001 🟠 Medium Lock 획득 순서 미문서화 잠재적 데드락
MEDIUM-002 🟠 Medium 매직 넘버 하드코딩 유지보수 어려움
MEDIUM-003 🟠 Medium ATR 동적 스탑 미적용 비최적 청산

6. 코드 품질 지표 요약

항목 v4 평가 v5 평가 변화
아키텍처 설계 유지
타입 안전성 유지
예외 처리 유지 (개선 필요)
동시성 안전성 유지
테스트 커버리지 유지 (15개 파일)
코드 품질 ⬇️ (구문 오류 발견)
트레이딩 로직 ⬇️ (볼륨 미확인)
리스크 관리 ⬇️ (포트폴리오 관리 부재)

종합 평가: (4.2/5.0) - v4 대비 0.5점 하락 (구문 오류 발견으로 인한 감점)


7. 우선순위별 권장사항

🚨 즉시 수정 (P0 - 24시간 이내)

  1. CRITICAL-001 수정: order.py 구문 오류 해결
  2. CRITICAL-002 수정: holdings.py 중복 return 제거

🔴 긴급 개선 (P1 - 1주 이내)

  1. 최대 보유 종목 수 제한 추가 (MAX_HOLDINGS = 5)
  2. 포트폴리오 손실 한도 모니터링 추가
  3. 광범위한 Exception 처리 구체화

🟡 단기 개선 (P2 - 1개월 이내)

  1. 거래량 확인 로직 추가 (매수 신호)
  2. ATR 기반 동적 스탑 구현
  3. Lock 획득 순서 문서화
  4. 통합 테스트 추가

🟢 중장기 개선 (P3 - 분기 이내)

  1. order.py 모듈 분할 (1,289줄 → 3개 파일)
  2. 백테스팅 프레임워크 구축
  3. 매직 넘버 상수화
  4. 심볼 상관관계 분석 추가

8. 파일별 상세 분석

핵심 모듈 (Critical Path)

파일 라인 수 역할 품질 주요 이슈
order.py 1,289 주문 실행 ⚠️ 구문 오류, 크기 과대
signals.py 960 신호 분석 크기 적정 초과
holdings.py 700 보유 관리 중복 return 문
state_manager.py 106 상태 관리 없음
common.py 413 공통 유틸 없음
config.py 328 설정 관리 없음

보조 모듈

파일 라인 수 역할 품질
indicators.py 172 기술 지표
notifications.py 183 알림 전송
circuit_breaker.py 79 장애 복구
retry_utils.py 62 재시도 유틸
constants.py 60 상수 정의
threading_utils.py 207 스레드 유틸
metrics.py 30 메트릭 수집 ⚠️ (미사용)
main.py 388 엔트리포인트

9. 결론

강점

  • 아키텍처: 모듈화, 단일 책임 원칙, 상태 분리 우수
  • 동시성: RLock, 토큰 기반 예산 관리, Rate Limiter 완비
  • 정밀도: Decimal 기반 계산, 원자적 파일 쓰기
  • 복구력: Circuit Breaker, 지수 백오프, ReadTimeout 복구

⚠️ 개선 필요

  • 긴급: 구문 오류 수정 필수 (시스템 작동 불가)
  • 리스크 관리: 포트폴리오 레벨 관리 부재
  • 트레이딩: 거래량 확인 및 동적 스탑 미적용
  • 코드 품질: 광범위한 Exception, 모듈 크기 과대

🎯 최종 권장사항

  1. 즉시: CRITICAL-001/002 수정 후 재배포
  2. 1주 내: 최대 보유 종목 및 손실 한도 추가
  3. 1개월 내: 거래량 확인 및 통합 테스트 추가
  4. 장기: 백테스팅으로 전략 검증 후 파라미터 최적화

보고서 작성일: 2025-12-10 작성자: AI Code Reviewer (Python Expert + Crypto Trader Perspective) 버전: v5.0