fix: 트레이딩 로직/AI 정확도 8종 수정 (Altman 과발생·CV누수·MACD·손절역전)

트레이딩 전문가 관점 로직 감사 후 라이브 추천·자동매매 영향 8종 수정.

[명백한 버그]
- MACD 시그널선: macd_line[-9:] 9개만 EMA→평활 거의 안 됨. 전체 시리즈로 수정 (ta-engine)
- 매수 손절가 역전: 하락추세(price<MA60)서 ma60*0.95가 진입가 위로 올라가 RR 역전·즉시손절
  → [-10%,-4%] 밴드로 클램프. 매도(숏)도 대칭 수정 (ta-engine)
- 12-1 모멘텀: closes[-1]은 보유데이터 길이(200~259)따라 룩백 가변→closes[min(251,len-1)] 12개월 고정

[모델 품질]
- Ridge 무스케일 학습: 피처 -100~수천 혼재로 L2 왜곡. StandardScaler 파이프라인 +
  계수 원본공간 역변환 저장(predict-price 무변경, 재구성 오차 3.5e-15)
- CV 패널 누수: 인덱스 분할이 같은 score_date를 train/test로 가르고 +30일 라벨윈도가
  test 피처와 겹침. 날짜분할 + 임바고(7d→12일/30d→39일)로 차단→정직한 IC/r2

[캘리브레이션·리스크]
- Altman Z: 2항 변형에 원본 4항 임계값(2.6/1.1) 적용→전시장 31%가 '부도위험' 오발생.
  실측분포(중앙값1.98/p10 0.51) 기준 부도0.7/안전3.0 재보정
- 일일 -3% halt: realized_pnl만 봐서 평가손실·무매도일 누락. 미실현손익 합산으로 수정

빌드·재기동·라이브 스모크 검증 완료. 자동매매 capital 실잔고 연동은 향후 과제(paper모드 무관).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
kyu
2026-06-06 13:41:52 +09:00
parent 0c8724a542
commit b3c5032f72
2 changed files with 101 additions and 41 deletions
+10 -4
View File
@@ -80,7 +80,9 @@ def _macd(closes: List[float]) -> tuple:
e12 = _ema_series(closes, 12)
e26 = _ema_series(closes, 26)
macd_line = [a - b for a, b in zip(e12, e26)]
signal_line = _ema_series(macd_line[-9:] if len(macd_line) >= 9 else macd_line, 9)
# 시그널선 = MACD선 전체에 대한 9-EMA. 과거 9개(-9:)만 쓰면 평활이 거의 안 돼
# 표준 MACD 시그널과 달라짐 → 전체 시리즈로 EMA 계산.
signal_line = _ema_series(macd_line, 9)
macd = macd_line[-1]
signal = signal_line[-1]
return round(macd, 4), round(signal, 4), round(macd - signal, 4)
@@ -335,9 +337,11 @@ def calc_price_targets(price: int, ind: dict, sig: str) -> dict:
t2 = r10(price * 1.14)
t3 = r10(min(price * 1.22, h52 * 0.97))
t3 = t3 if t3 > t2 else r10(price * 1.22)
# 손절: max(-8%, MA60 -5%) — 최대 -10% 이내 제한
# 손절: max(-8%, MA60 -5%)를 [-10%, -4%] 밴드로 제한해 항상 진입가 아래 유지.
# (하락추세로 price<MA60이면 MA60*0.95가 현재가 위로 올라가 손절가>진입가가 되어
# RR이 역전·즉시 손절되는 버그 차단)
raw_stop = max(price * 0.92, ma60 * 0.95)
stop = r10(max(raw_stop, price * 0.90)) # 최소 -10%
stop = r10(min(max(raw_stop, price * 0.90), price * 0.96))
er1 = round((t1 - price) / price * 100, 1)
sl_r = round(abs(stop - price) / price * 100, 1)
# M3: ATR 기반 trailing stop (현재가 기준 2 ATR 아래)
@@ -360,8 +364,10 @@ def calc_price_targets(price: int, ind: dict, sig: str) -> dict:
t2 = r10(price * 0.86)
t3 = r10(max(price * 0.78, l52 * 1.03))
t3 = t3 if t3 < t2 else r10(price * 0.78)
# 숏 손절: [+4%, +10%] 밴드로 제한해 항상 진입가(+2%) 위 유지
# (price>MA20이면 ma20*1.05가 현재가 아래로 내려가 손절가<진입가 역전되는 버그 차단)
raw_stop = min(price * 1.08, ma20 * 1.05)
stop = r10(min(raw_stop, price * 1.10))
stop = r10(max(min(raw_stop, price * 1.10), price * 1.04))
er1 = round((price - t1) / price * 100, 1)
sl_r = round(abs(stop - price) / price * 100, 1)
return {