옵투나 8방 후처리를 재탐색으로 변경하기 전전
This commit is contained in:
205
scripts/momentum_optuna_parity_reeval.py
Normal file
205
scripts/momentum_optuna_parity_reeval.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Optuna JSON(#199·mode) vs 동일 엔진 재평가 — DB 미변경.
|
||||
|
||||
Usage:
|
||||
python3 -u scripts/momentum_optuna_parity_reeval.py \\
|
||||
--json kis_trader/backtest/results/optuna_momentum_tpe_20260821_220227.json \\
|
||||
--out logs/momentum_optuna_parity_OUT.json
|
||||
"""
|
||||
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)
|
||||
mode_params = dict(d["mode_combo"]["params"])
|
||||
p199 = dict(t199["params"])
|
||||
|
||||
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} slot={slot} max={max_stocks} budget={budget} OB=off",
|
||||
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",
|
||||
)
|
||||
if ctx is None:
|
||||
print("prepare failed", flush=True)
|
||||
return 1
|
||||
|
||||
# Optuna 학습과 동일: 호가 OFF. 휩쏘 키는 있으면 OFF (DB ON이어도 비교용 고정)
|
||||
base = dict(ctx.base_fixed)
|
||||
base["_orderbook_filter_enabled"] = False
|
||||
for k in list(base.keys()):
|
||||
if "whipsaw" in k.lower() and isinstance(base[k], (bool, int)):
|
||||
base[k] = False if isinstance(base[k], bool) else 0
|
||||
|
||||
print(
|
||||
f"ctx ready candles={len(ctx.codes_candles)} "
|
||||
f"ticks={'yes' if ctx.ticks_by_code else 'no'} "
|
||||
f"ob_snaps={len(ctx.orderbook_by_code)} fee={ctx.fee_rate} tax={ctx.sell_tax}",
|
||||
flush=True,
|
||||
)
|
||||
print(
|
||||
f"base tick_entry={base.get('backtest_use_tick_entry')} "
|
||||
f"tick_exit={base.get('backtest_use_tick_exit')} "
|
||||
f"ob={base.get('_orderbook_filter_enabled')} "
|
||||
f"whip_keys={[k for k in base if 'whip' in k.lower()]}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
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": "evaluate returned 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"),
|
||||
"mdd": r.get("mdd"),
|
||||
},
|
||||
"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 = {
|
||||
"source_json": str(json_path),
|
||||
"period": {"start": start, "end": end},
|
||||
"portfolio": {
|
||||
"slot_money": slot,
|
||||
"max_stocks": max_stocks,
|
||||
"total_budget_krw": budget,
|
||||
},
|
||||
"orderbook": "off",
|
||||
"db_touched": False,
|
||||
"elapsed_sec": round(time.time() - t0, 1),
|
||||
"comparisons": rows,
|
||||
"note": (
|
||||
"Optuna evaluate 경로 재실행"
|
||||
"(웹엔진=run_momentum_backtest_web_aligned). DB 미변경."
|
||||
),
|
||||
"web_bt_note": (
|
||||
"지금 웹폼(호가ON+휩쏘ON)과 숫자 비교가 아님. "
|
||||
"정합=JSON기록 vs 동일조건 재시뮬."
|
||||
),
|
||||
}
|
||||
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)
|
||||
Reference in New Issue
Block a user