#!/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)