89 lines
3.3 KiB
Python
89 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
optuna_scalping_tpe_space.py — 스캘핑 Optuna 연속(TPE) 탐색 공간
|
|
기존 ``_scalp_grids()`` categorical 유지. ``use_macd_cross=False``, ``skip_hts=False``.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List
|
|
|
|
import optuna
|
|
|
|
from kis_trader.backtest.optuna_tpe_common import r1, r2, r3
|
|
|
|
# 매수 시간창(time_*) 은 TPE 제외 — 운영 DB 고정값 사용(과적합·apply 후 진입 잠금 방지).
|
|
SCALP_TPE_AXIS_KEYS: List[str] = [
|
|
"rsi_period",
|
|
"rsi_oversold",
|
|
"rsi_overbought",
|
|
"sl_pct",
|
|
"tp_pct",
|
|
"tp_max_pct",
|
|
"drop_rate",
|
|
"shoulder_min_high",
|
|
"shoulder_cut_pct",
|
|
"cooldown_min",
|
|
"high_chase_thr",
|
|
"max_daily_chg",
|
|
"min_price",
|
|
"vol_mult",
|
|
"use_defense_filters",
|
|
"require_reversal_candle",
|
|
"max_loss_krw",
|
|
"min_drop_pct_for_loss_cut",
|
|
"min_margin",
|
|
"min_hold_sec",
|
|
"max_daily",
|
|
]
|
|
|
|
|
|
def scalp_tpe_axis_keys() -> List[str]:
|
|
return list(SCALP_TPE_AXIS_KEYS)
|
|
|
|
|
|
def suggest_scalp_params_tpe(trial: optuna.Trial) -> Dict[str, Any]:
|
|
combo: Dict[str, Any] = {}
|
|
combo["rsi_period"] = trial.suggest_int("rsi_period", 3, 14)
|
|
combo["rsi_oversold"] = r1(trial.suggest_float("rsi_oversold", 15.0, 30.0, step=0.5))
|
|
combo["rsi_overbought"] = r1(trial.suggest_float("rsi_overbought", 65.0, 85.0, step=0.5))
|
|
if combo["rsi_oversold"] >= combo["rsi_overbought"]:
|
|
raise optuna.TrialPruned("rsi oversold >= overbought")
|
|
|
|
combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 1.5, 4.5, step=0.1))
|
|
combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 1.5, 4.0, step=0.1))
|
|
combo["tp_max_pct"] = r2(trial.suggest_float("tp_max_pct", 2.0, 8.0, step=0.5))
|
|
if combo["tp_max_pct"] + 1e-9 < combo["tp_pct"]:
|
|
raise optuna.TrialPruned("tp_max < tp")
|
|
|
|
combo["drop_rate"] = r2(trial.suggest_float("drop_rate", 1.0, 6.0, step=0.1))
|
|
combo["shoulder_min_high"] = r2(
|
|
trial.suggest_float("shoulder_min_high", 0.3, 4.0, step=0.1),
|
|
)
|
|
combo["shoulder_cut_pct"] = r2(
|
|
trial.suggest_float("shoulder_cut_pct", 0.05, 0.6, step=0.05),
|
|
)
|
|
combo["cooldown_min"] = trial.suggest_int("cooldown_min", 0, 15)
|
|
combo["high_chase_thr"] = r3(
|
|
trial.suggest_float("high_chase_thr", 0.95, 1.0, step=0.005),
|
|
)
|
|
combo["max_daily_chg"] = r1(trial.suggest_float("max_daily_chg", 10.0, 50.0, step=1.0))
|
|
combo["min_price"] = trial.suggest_int("min_price", 1000, 8000, step=500)
|
|
combo["vol_mult"] = r2(trial.suggest_float("vol_mult", 0.0, 2.0, step=0.1))
|
|
combo["use_defense_filters"] = trial.suggest_categorical(
|
|
"use_defense_filters", [False, True],
|
|
)
|
|
combo["require_reversal_candle"] = trial.suggest_categorical(
|
|
"require_reversal_candle", [False, True],
|
|
)
|
|
combo["max_loss_krw"] = trial.suggest_int("max_loss_krw", 50000, 300000, step=25000)
|
|
combo["min_drop_pct_for_loss_cut"] = r3(
|
|
trial.suggest_float("min_drop_pct_for_loss_cut", 0.005, 0.03, step=0.001),
|
|
)
|
|
combo["min_margin"] = r2(trial.suggest_float("min_margin", 0.05, 0.5, step=0.05))
|
|
combo["min_hold_sec"] = trial.suggest_int("min_hold_sec", 0, 120, step=10)
|
|
combo["max_daily"] = trial.suggest_int("max_daily", 10, 100, step=5)
|
|
|
|
combo["use_macd_cross"] = False
|
|
combo["skip_hts_scan_dupes"] = False
|
|
return combo
|