Files
kis_bot/kis_trader/backtest/optuna_breakout_tpe_space.py
2026-07-30 18:05:07 +09:00

97 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""
optuna_breakout_tpe_space.py — 돌파 Optuna 연속(TPE) 탐색 공간
기존 ``_breakout_grids()`` categorical 경로는 유지. ``skip_hts`` 항상 False.
"""
from __future__ import annotations
from typing import Any, Dict, List
import optuna
from kis_trader.backtest.optuna_tpe_common import (
RATCHET_TPE_AXIS_KEYS,
r1,
r2,
suggest_ratchet_tiers_pct,
)
# 매수 시간창(time_end_hm) 은 TPE 제외 — 운영 DB/골든타임 고정. 다단래칫·청산축만 탐색.
_BREAKOUT_BASE_KEYS: List[str] = [
"max_daily_chg",
"vol_mult",
"vol_window",
"min_turnover_1m_pct",
"prev_chg_min",
"prev_chg_max",
"min_price",
"tp_pct",
"sl_pct",
"sl_mode",
"atr_sl_mult",
"trail_pct",
"trail_arm_pct",
"shoulder_min_high_pct",
"shoulder_cut_pct",
"lookback_min",
"confirm_margin_pct",
"body_min_pct",
"max_hold_bars",
]
BREAKOUT_TPE_AXIS_KEYS: List[str] = list(_BREAKOUT_BASE_KEYS) + list(RATCHET_TPE_AXIS_KEYS)
_SL_MODE_CHOICES = ["fixed", "atr"]
def breakout_tpe_axis_keys() -> List[str]:
return list(BREAKOUT_TPE_AXIS_KEYS)
def suggest_breakout_params_tpe(trial: optuna.Trial) -> 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))
combo["vol_mult"] = r2(trial.suggest_float("vol_mult", 1.5, 4.0, step=0.1))
combo["vol_window"] = trial.suggest_int("vol_window", 1, 15)
combo["min_turnover_1m_pct"] = r2(
trial.suggest_float("min_turnover_1m_pct", 0.05, 0.5, step=0.05),
)
combo["prev_chg_min"] = r2(trial.suggest_float("prev_chg_min", 0.2, 1.0, step=0.1))
combo["prev_chg_max"] = r1(trial.suggest_float("prev_chg_max", 5.0, 30.0, step=0.5))
if combo["prev_chg_min"] >= combo["prev_chg_max"]:
raise optuna.TrialPruned("prev_chg invalid")
combo["min_price"] = trial.suggest_int("min_price", 1000, 5000, step=500)
combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 2.0, 18.0, step=0.5))
combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 2.0, 6.0, step=0.1))
combo["sl_mode"] = trial.suggest_categorical("sl_mode", _SL_MODE_CHOICES)
combo["atr_sl_mult"] = r2(trial.suggest_float("atr_sl_mult", 1.5, 3.5, step=0.1))
combo["trail_pct"] = r2(trial.suggest_float("trail_pct", 1.0, 4.0, step=0.1))
combo["trail_arm_pct"] = r2(trial.suggest_float("trail_arm_pct", 1.0, 4.0, step=0.1))
combo["shoulder_min_high_pct"] = r2(
trial.suggest_float("shoulder_min_high_pct", 1.0, 6.0, step=0.1),
)
combo["shoulder_cut_pct"] = r2(
trial.suggest_float("shoulder_cut_pct", 0.3, 1.5, step=0.1),
)
combo.update(
suggest_ratchet_tiers_pct(
trial,
off_token="",
n_max=3,
gain_low=2.0,
gain_high=15.0,
gain_step=0.5,
cut_low=0.5,
cut_high=3.0,
cut_step=0.1,
),
)
combo["lookback_min"] = trial.suggest_int("lookback_min", 1, 10)
combo["confirm_margin_pct"] = r2(
trial.suggest_float("confirm_margin_pct", 0.0, 1.0, step=0.1),
)
combo["body_min_pct"] = r2(trial.suggest_float("body_min_pct", 0.0, 0.5, step=0.1))
combo["max_hold_bars"] = trial.suggest_int("max_hold_bars", 0, 180, step=10)
combo["skip_hts_scan_dupes"] = False
return combo