ㅇ Changes: - Introduced the DART strategy to the trading system, including its configuration and integration into the existing framework. - Updated the database schema to include DART-specific tables for disclosures and watchlists. - Enhanced the backtesting and parameter search functionalities to support the DART strategy. - Implemented new rules for browser verification and API interactions to ensure compliance with the updated DART strategy. Impact: - These additions expand the trading capabilities of the system, allowing for more comprehensive analysis and execution of DART-related strategies, while maintaining system integrity and performance.
105 lines
3.0 KiB
Python
105 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""kis_trader/backtest/optuna_dart.py — DART Optuna TPE."""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import Any, Dict, Optional
|
|
|
|
import optuna
|
|
from optuna.samplers import TPESampler
|
|
|
|
from database import TradeDB
|
|
from kis_trader.backtest.optuna_common import (
|
|
announce_optuna_json_path,
|
|
resolve_optuna_storage_url,
|
|
)
|
|
from kis_trader.backtest.param_search_dart import (
|
|
_dart_grids,
|
|
apply_params_to_db,
|
|
evaluate_dart_param_combo,
|
|
)
|
|
from kis_trader.engine import dart_engine as de
|
|
|
|
logger = logging.getLogger("param_search_optuna")
|
|
_FAIL = -1e18
|
|
|
|
|
|
def prepare_dart_search_context(start: str, end: str, mode: str) -> Optional[Dict[str, Any]]:
|
|
grids = _dart_grids()
|
|
if mode not in grids:
|
|
logger.error("DART mode: %s (fast/coarse/fine)", mode)
|
|
return None
|
|
db = TradeDB()
|
|
try:
|
|
env_row = db.get_merged_env_snapshot() or {}
|
|
finally:
|
|
db.close()
|
|
return {
|
|
"start": start,
|
|
"end": end,
|
|
"mode": mode,
|
|
"grid": grids[mode],
|
|
"env_row": env_row,
|
|
"base": de.get_dart_defaults_from_db(env_row=env_row),
|
|
}
|
|
|
|
|
|
def run_dart_optuna(
|
|
ctx: Dict[str, Any],
|
|
*,
|
|
n_trials: int,
|
|
storage_url: Optional[str] = None,
|
|
study_name: Optional[str] = None,
|
|
min_trades: int = 1,
|
|
sampler_name: str = "tpe",
|
|
seed: int = 42,
|
|
show_progress: bool = False,
|
|
**_kwargs: Any,
|
|
) -> optuna.Study:
|
|
grid: Dict[str, Any] = ctx["grid"]
|
|
storage = storage_url or resolve_optuna_storage_url()
|
|
name = study_name or "dart_%s_%s" % (
|
|
ctx["mode"], datetime.now().strftime("%Y%m%d_%H%M%S"),
|
|
)
|
|
sampler = TPESampler(seed=seed) if sampler_name != "random" else optuna.samplers.RandomSampler(seed=seed)
|
|
study = optuna.create_study(
|
|
study_name=name,
|
|
storage=storage,
|
|
direction="maximize",
|
|
sampler=sampler,
|
|
load_if_exists=True,
|
|
)
|
|
|
|
def objective(trial: optuna.Trial) -> float:
|
|
params = dict(ctx["base"])
|
|
for axis, values in grid.items():
|
|
params[axis] = trial.suggest_categorical(axis, list(values))
|
|
res = evaluate_dart_param_combo(
|
|
params, start=ctx["start"], end=ctx["end"], env_row=ctx["env_row"],
|
|
)
|
|
n = int(res.get("trade_count") or 0)
|
|
pnl = float(res.get("total_pnl") or 0)
|
|
trial.set_user_attr("trade_count", n)
|
|
trial.set_user_attr("total_pnl", pnl)
|
|
trial.set_user_attr("win_rate", float(res.get("win_rate") or 0))
|
|
if n < min_trades:
|
|
return _FAIL
|
|
return pnl
|
|
|
|
study.optimize(objective, n_trials=n_trials, show_progress_bar=show_progress)
|
|
announce_optuna_json_path("dart", study)
|
|
return study
|
|
|
|
|
|
def apply_best_dart_trial(study: optuna.Study) -> bool:
|
|
try:
|
|
best = study.best_trial
|
|
except Exception:
|
|
return False
|
|
if float(best.value or _FAIL) <= 0:
|
|
logger.warning("DART best PnL<=0 — DB 미적용")
|
|
return False
|
|
apply_params_to_db(dict(best.params))
|
|
return True
|