feat(옵투나·웹): 후처리 재탐색·ob_modes·적용감사·수집통계

- Optuna web jobs/TPE/apply snapshot·틱로더 정합, jobs limit·감사로그
- 백테 UI 호가모드·후보 적용 흐름, feed_collect_stats API/탭
- 가설검증·교차검증 룰, 4전략 스모크·OB slot41 진단 스크립트

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name
2026-08-27 15:23:44 +09:00
parent 5e44b86f8b
commit 8fbba264ba
30 changed files with 2973 additions and 186 deletions

View File

@@ -2,6 +2,9 @@
"""
optuna_breakout_tpe_space.py — 돌파 Optuna 연속(TPE) 탐색 공간
기존 ``_breakout_grids()`` categorical 경로는 유지. ``skip_hts`` 항상 False.
호가 ON/OFF 는 trial 축이 아님 — CLI ``--orderbook-filter on|off`` 스터디 스위치.
(fixed/atr 손절과 같이 한 스터디에 섞지 않음 → 최대 fixed|atr × off|on = 4순차)
"""
from __future__ import annotations
@@ -10,9 +13,11 @@ from typing import Any, Dict, List, Optional
import optuna
from kis_trader.backtest.optuna_tpe_common import (
ORDERBOOK_TPE_THRESHOLD_KEYS,
RATCHET_TPE_AXIS_KEYS,
r1,
r2,
suggest_orderbook_thresholds_tpe,
suggest_ratchet_tiers_pct,
)
@@ -36,7 +41,11 @@ _BREAKOUT_BASE_KEYS: List[str] = [
"body_min_pct",
"max_hold_bars",
]
BREAKOUT_TPE_AXIS_KEYS: List[str] = list(_BREAKOUT_BASE_KEYS) + list(RATCHET_TPE_AXIS_KEYS)
# 돌파: 본축 + 래칫. 호가 ON/OFF·임계는 스터디 스위치(아래 breakout_tpe_axis_keys).
# 휩쏘는 전략 특성상 TPE 스킵 — 기존과 동일.
BREAKOUT_TPE_AXIS_KEYS: List[str] = (
list(_BREAKOUT_BASE_KEYS) + list(RATCHET_TPE_AXIS_KEYS)
)
def normalize_tpe_breakout_sl_mode(raw: Optional[Any] = None) -> str:
@@ -47,19 +56,43 @@ def normalize_tpe_breakout_sl_mode(raw: Optional[Any] = None) -> str:
return "fixed"
def breakout_tpe_axis_keys(sl_mode: Optional[str] = None) -> List[str]:
"""fixed=sl_pct 만, atr=atr_sl_mult 만. 한 스터디에 둘 다 넣지 않음."""
def normalize_tpe_breakout_ob_mode(raw: Optional[Any] = None) -> str:
"""돌파 TPE 호가 스터디 스위치. off | on (auto→on). trial 축 아님."""
s = str(raw or "off").strip().lower()
if s in ("on", "1", "true", "yes", "auto"):
return "on"
return "off"
def breakout_tpe_study_extra(
sl_mode: Optional[str] = None,
orderbook_filter: Optional[str] = None,
) -> str:
"""study 이름용 — 예: fixed_ob_off / atr_ob_on (한 스터디에 손절·호가 안 섞음)."""
sm = normalize_tpe_breakout_sl_mode(sl_mode)
om = normalize_tpe_breakout_ob_mode(orderbook_filter)
return f"{sm}_ob_{om}"
def breakout_tpe_axis_keys(
sl_mode: Optional[str] = None,
orderbook_filter: Optional[str] = None,
) -> List[str]:
"""fixed=sl_pct 만, atr=atr_sl_mult 만. 호가 ON 스터디만 임계값 축 추가."""
keys = list(BREAKOUT_TPE_AXIS_KEYS)
if normalize_tpe_breakout_sl_mode(sl_mode) == "atr":
keys.append("atr_sl_mult")
else:
keys.append("sl_pct")
if normalize_tpe_breakout_ob_mode(orderbook_filter) == "on":
keys.extend(list(ORDERBOOK_TPE_THRESHOLD_KEYS))
return keys
def suggest_breakout_params_tpe(
trial: optuna.Trial,
sl_mode: Optional[str] = None,
orderbook_filter: Optional[str] = None,
) -> Dict[str, Any]:
combo: Dict[str, Any] = {}
combo["max_daily_chg"] = r1(trial.suggest_float("max_daily_chg", 20.0, 55.0, step=1.0))
@@ -110,4 +143,10 @@ def suggest_breakout_params_tpe(
combo["max_hold_bars"] = trial.suggest_int("max_hold_bars", 0, 180, step=10)
combo["skip_hts_scan_dupes"] = False
# 호가: 스터디 스위치 (trial categorical ON/OFF 금지 — TPE가 OFF만 편애하던 구멍)
ob_on = normalize_tpe_breakout_ob_mode(orderbook_filter) == "on"
combo["_orderbook_filter_enabled"] = ob_on
combo["ob_filter_enabled"] = ob_on
if ob_on:
combo.update(suggest_orderbook_thresholds_tpe(trial))
return combo