fix: 매수 차단·백테스트 왜곡 유발한 버그 5종 수정

수개월간 매수 0건 + 백테스트가 손실로 보이던 근본 원인들을 수정.

- ADX 발산(mean_reversion_strategy, run_backtest): Wilder 평활에 /period
  누락으로 ADX가 100 초과 발산 → 실서비스 SIDEWAYS 게이트(ADX<20) 영구
  무력화. 공식 복원(표준 Wilder와 대수적으로 동일 검증).
- 백테스트 워밍업(run_backtest): 국면판정 220봉을 start_date부터 세서
  14개월 미만 백테스트의 ~90%가 강제 BEAR → 가짜 손실. start_date 340일
  전부터 프리로드하고 성과는 sim_start_date부터만 집계. CAGR 분모도 워밍업
  제외한 실제 거래일로 수정. → 최근1년 -0.33%→+6.95%, 가산성 회복.
- ML HOLD 게이트(trade_executor): 신뢰도 무관 매수 차단 → 데이터 부족으로
  ML이 실패(conf=0)한 HOLD까지 모든 매수를 막던 것을 conf>=0.6 조건부로.
- 빈 Redis 추천 캐시(swing/scheduler): 빈 캐시를 '히트'로 처리해 DB 폴백을
  건너뛰던 것을 recommendations_by_rule 비면 DB 폴백하도록.
- alembic 로깅(env.py, main.py): lifespan의 alembic upgrade가 fileConfig로
  앱 로거를 꺼 startup 이후 로그 전멸 → 스킵(스키마는 create_all 담당).

검증: code-verifier 통과(실전 경로 CONFIRMED 버그 없음), walk-forward 6/9구간
양(+)·과적합 아님(조건부 견고). 서바이버십 편향은 별도 이슈로 검증 진행 중.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBmzf1HDnvFHLraJ8eaaKJ
This commit is contained in:
2026-09-16 10:03:56 +09:00
parent 3965760021
commit a0973812fb
7 changed files with 112 additions and 26 deletions

View File

@@ -155,11 +155,15 @@ async def swing_scheduler() -> None:
# ── Phase 3: 추천 조회 + 진입 실행 (별도 세션) ──
# 4. Redis 캐시 확인 → cache miss 시 DB 조회
rec = await get_json("recommendations:latest")
if rec:
# [FIX] Redis 캐시가 존재해도 recommendations_by_rule이 비어있으면(배치가 빈
# 추천을 캐싱한 경우) 기존엔 '히트'로 처리해 DB 폴백을 건너뛰고 스킵했다.
# 빈 캐시가 30분 TTL 동안 DB의 유효 후보를 가려 매매를 막으므로, 비어있으면
# 캐시 미스와 동일하게 DB 조회로 폴백한다.
if rec and rec.get("recommendations_by_rule"):
rec = _normalize_recommendations(rec)
logger.info(f"[Swing] Redis 캐시 히트 (추천 {len(rec.get('recommendations_by_rule', []))}그룹)")
else:
logger.info("[Swing] Redis 캐시 미스 → DB 조회")
logger.info("[Swing] Redis 캐시 미스/빈값 → DB 조회")
async with AsyncSessionLocal() as rec_session:
rec = await get_rule_based_recommendations_from_db(rec_session)