fix(daytrading): 트레일링 고점이 진입 전 고가로 오염되던 버그

- highest_price 를 max(candles_5m[-3:].high) 대신 진입+60초 이후 시작한 1분봉 고가 + 현재가로만 갱신
  (진입 전 스파이크가 +1% 트리거를 오발동시키고 고점-1.5×ATR 손절선을 부풀리던 문제)
- 날짜+시각 KST 비교로 전일 캔들 제외, entry_time 부재·UTC 저장 추정 시 현재가만 사용
- 파라미터 변경 없음. 하드손절·시간손절·EOD·폴백 손절 경로 불변
- CLAUDE.md §7 시간손절 서술을 실제 동작(30분 경과 후 상한 없이 -0.3% 미만)으로 정정
- 회귀 테스트 13건 (전체 909 passed)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 17:04:07 +09:00
parent e4346d30fb
commit 599de3b52e
3 changed files with 275 additions and 6 deletions

View File

@@ -195,8 +195,9 @@ adaptive_strategy(_MODE_PARAMS) > strategy_cfg(DB: strategy_configs) > setti
- **최소 손절폭 보장(`DAY_TRADE_MIN_STOP_LOSS_PCT` = 2%)은 §5.2의 "타이트한 쪽" 규칙의 의도적 예외**다. 장중 전략에서 손절이 너무 타이트하면 노이즈에 털리므로, `min(raw_stop, min_stop)` 으로 **더 느슨한 쪽**을 고른다. 이 `min()` 을 "버그"로 판단해 뒤집지 말 것.
- 사이징: 종목당 **6%**(`DAY_TRADE_MAX_CAPITAL_RATIO`), 동시 **5종목**(`DAY_TRADE_MAX_POSITIONS`) = 5×6% = 30%. confidence 조정 `예산 × min(1.2, confidence/0.7)` 후 **세 한도(종목당 6% / DT 잔여 30% / 가용현금)로 재클램프**된다.
- 청산: **1.5R**(`DAY_TRADE_TARGET_MULTIPLIER`) 도달 시 50% 분할 → 잔량 트레일링(ATR×**1.5**, 발동 최소수익 1%) / 진입 30분(6봉)간 **-0.3% 미만**이면 시간손절(수익 중이면 보유 유지) / 5분 RSI ≥70 & 수수료 이상 수익 시 / **15:20 EOD 전량 청산** / **전일 미청산 포지션은 장중 발견 즉시 전량 청산**(`close_stale_positions`)
- 청산: **1.5R**(`DAY_TRADE_TARGET_MULTIPLIER`) 도달 시 50% 분할 → 잔량 트레일링(ATR×**1.5**, 발동 최소수익 1%) / **진입 후 30분(`DAY_TRADE_TIME_STOP_MINUTES`) 경과 이후 언제든** 수익률이 **-0.3% 미만**(`DAY_TRADE_TIME_STOP_MIN_GAIN`)이면 시간손절 — "30분 동안"이 아니라 30분 시점부터 EOD 까지 상한 없이 매 사이클 판정한다(`day_trading_strategy.check_day_trade_exit`). -0.3% 이상이면 보유 유지. 경과시간이 음수/390분 초과면 entry_time 기준 불일치로 보고 판정하지 않는다(`_elapsed_minutes`) / 5분 RSI ≥70 & 수수료 이상 수익 시 / **15:20 EOD 전량 청산** / **전일 미청산 포지션은 장중 발견 즉시 전량 청산**(`close_stale_positions`)
- **전일 포지션 청산 손익은 당일 실현손익에 넣지 않는다**(`_finalize_exit_impl(count_daily_pnl=False)`). 넣으면 당일 매매 0건인데 -3% 손실한도가 발동해 정상 포지션까지 전량 청산된다. 전일 손익은 `get_stale_realized_pnl()` + warning 로그 + TradeHistory로 추적한다. stale 청산 종목은 `closed_today` 에 추가해 당일 재진입을 막는다(주문 실패 시에도).
- **트레일링 고점(`highest_price`)은 진입 이후에 시작한 1분봉 고가 + 현재가로만 갱신한다**(`daytrading/exit_service._post_entry_candle_high`, 2026-10-01). 진입이 속한 봉은 진입 전 체결이 섞여 제외하고, 라벨 ≥ 진입+60초 봉만 인정하며 날짜까지 비교한다. entry_time 이 없거나 정규장 밖(UTC 저장 추정)이면 현재가만 쓴다. 이전의 `max(candles_5m[-3:].high)` 는 진입 전 고가로 +1% 트리거를 오발동시켰다.
- **분봉 조회 실패 시 폴백 손절**이 있다(`_check_fallback_stop_loss`). 현재가만으로 손절선 이탈을 판정해 전량 청산한다. 폴백 손절가는 `min()` 으로 고른 가장 타이트한 하드캡(-2%) 기준이며, 현재가조차 못 구하면 `logger.error` 로 수동 확인을 요청한다.
- 제한: **15:00 이후 신규 마감**(`DAY_TRADE_ENTRY_CUTOFF_*`) / 일일 **-3%** 손실 시 당일 중단(`DAY_TRADE_DAILY_LOSS_LIMIT`) / 당일 3연패 쿨다운(position_manager) / 전량 매도 시 loss_tracker 기록 + `save_to_db()`
- 감시종목: 09:00에 당일 분석이 없으면 최근 분석일 결과로 선채움(ORB 수집 보장), 자동 목록은 30분마다 갱신(수동 설정 목록은 유지)