Files
kis_bot/scripts/momentum_optuna_parity_merged_snap.py

178 lines
5.8 KiB
Python

#!/usr/bin/env python3
"""Optuna JSON 재평가 — 당시 merged_params 고정축 스냅샷 사용 (DB 미변경)."""
from __future__ import annotations
import argparse
import json
import time
import traceback
from pathlib import Path
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument(
"--json",
default="kis_trader/backtest/results/optuna_momentum_tpe_20260821_220227.json",
)
ap.add_argument("--out", required=True)
args = ap.parse_args()
t0 = time.time()
json_path = Path(args.json)
out_path = Path(args.out)
d = json.loads(json_path.read_text())
start, end = d["start"], d["end"]
slot = float(d["slot_money"])
max_stocks = int(d["max_stocks"])
budget = float(d["total_budget_krw"])
grid_keys = list(d["grid_keys"])
t199 = next(x for x in d["results_all"] if x.get("optuna_trial_number") == 199)
mp = dict(t199["merged_params"])
p199 = dict(t199["params"])
mode_params = dict(d["mode_combo"]["params"])
# Optuna 당시 고정축 (지금 DB 기본값과 다를 수 있음 — RSI 등)
fixed_snap = {k: v for k, v in mp.items() if k not in p199}
fixed_snap["_orderbook_filter_enabled"] = False
for k in list(fixed_snap.keys()):
if "whipsaw" in k.lower() and isinstance(fixed_snap[k], (bool, int)):
fixed_snap[k] = False if isinstance(fixed_snap[k], bool) else 0
from kis_trader.backtest.optuna_momentum import prepare_momentum_search_context
from kis_trader.backtest.param_search_momentum import evaluate_momentum_param_combo
print(
f"prepare {start}~{end} OB=off merged-fixed "
f"rsi={fixed_snap.get('mom_rsi_min')}/{fixed_snap.get('mom_rsi_max')}",
flush=True,
)
ctx = prepare_momentum_search_context(
start,
end,
"tpe",
slot_money=slot,
max_stocks=max_stocks,
total_budget_krw=budget,
orderbook_filter="off",
market="KR",
history_source="kiwoom",
)
if ctx is None:
print("prepare failed", flush=True)
return 1
base = dict(ctx.base_fixed)
base.update(fixed_snap)
base["_orderbook_filter_enabled"] = False
def run_one(label, combo, recorded):
print(f"--- eval {label} ---", flush=True)
r = evaluate_momentum_param_combo(
combo,
base_fixed=base,
grid_keys=grid_keys,
codes_candles=ctx.codes_candles,
min_trades=1,
min_win_rate=0.0,
min_pf=0.0,
universe_by_slot=ctx.universe_by_slot,
slot_money=ctx.slot_money,
max_stocks=ctx.max_stocks,
total_budget_krw=ctx.total_budget_krw,
fee_rate=ctx.fee_rate,
sell_tax=ctx.sell_tax,
period_days=ctx.period_days,
cache_holder=ctx.cache_holder,
ticks_by_code=ctx.ticks_by_code,
orderbook_by_code=ctx.orderbook_by_code,
program_by_code=ctx.program_by_code,
log_verdict_by_code=ctx.log_verdict_by_code,
start_key=ctx.start_key,
end_key=ctx.end_key,
include_trades=False,
)
if not r:
return {"label": label, "ok": False, "error": "None"}
out = {
"label": label,
"ok": True,
"reeval": {
"total_pnl": r.get("total_pnl"),
"total_trades": r.get("total_trades"),
"win_rate": r.get("win_rate"),
"pf": r.get("pf"),
},
"recorded": recorded,
"delta_pnl": float(r.get("total_pnl") or 0) - float(recorded.get("total_pnl") or 0),
"delta_trades": int(r.get("total_trades") or 0)
- int(recorded.get("total_trades") or 0),
"match": (
int(r.get("total_pnl") or 0) == int(recorded.get("total_pnl") or 0)
and int(r.get("total_trades") or 0)
== int(recorded.get("total_trades") or 0)
),
}
print(json.dumps(out, ensure_ascii=False), flush=True)
return out
rows = [
run_one(
"#199",
p199,
{
"total_pnl": t199["total_pnl"],
"total_trades": t199["total_trades"],
"win_rate": t199["win_rate"],
"pf": t199["pf"],
},
),
run_one(
"mode_combo",
mode_params,
{
"total_pnl": d["mode_combo"]["backtest"]["total_pnl"],
"total_trades": d["mode_combo"]["backtest"]["total_trades"],
"win_rate": d["mode_combo"]["backtest"]["win_rate"],
"pf": d["mode_combo"]["backtest"]["pf"],
},
),
]
report = {
"method": "merged_fixed_snapshot + combo",
"source_json": str(json_path),
"db_touched": False,
"elapsed_sec": round(time.time() - t0, 1),
"fixed_rsi": [fixed_snap.get("mom_rsi_min"), fixed_snap.get("mom_rsi_max")],
"comparisons": rows,
}
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(report, ensure_ascii=False, indent=2))
print("WROTE", out_path, flush=True)
print(
"FINAL",
json.dumps(
{
r["label"]: {
"match": r.get("match"),
"delta_pnl": r.get("delta_pnl"),
"delta_trades": r.get("delta_trades"),
"reeval": r.get("reeval"),
}
for r in rows
},
ensure_ascii=False,
),
flush=True,
)
return 0 if all(r.get("match") for r in rows if r.get("ok")) else 2
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception:
traceback.print_exc()
raise SystemExit(1)