Files
kis_bot/scripts/mom_bt_one_day_trial120.py
Your Name b5d7c5d848 fix(옵투나·백테웹): 휩쏘 키 통일 + 엔진 뱃지 + 잡 삭제 + race·후처리 시그니처
휩쏘 키 통일 (bt-form-postfilter-key-parity.mdc 신설)
- 저장키 vs 조회키 불일치 해소: whipsaw_enabled(옛) → whipsaw_filter_enabled(표준)
- backtest_web.py: params[..] 세팅, static/js/backtest.js: 폼 파라미터
- optuna_web_jobs·param_search_apply_snapshot·param_search_scalping: 옛 alias 제거
- bt_post_filters: 로컬 변수도 whipsaw_filter_enabled 로 통일
- scripts/scratch: 관련 스크립트 동기 반영

엔진 뱃지 (optuna-job-engine-label.mdc 신설)
- optuna_web_jobs: use_rust 명시값만 신뢰, 미상은  (전략명 하드코딩 폴백 금지)
- 스캘핑/모멘텀/돌파 Rust 포팅 확장에도 안전한 라벨링

Optuna 웹 잡 UX
- save_job: tmp 파일 PID 접미 (멀티 프로세스 race 해소)
- _spawn_job_reaper: mode_refine 완료 시 phase1/phase2 JSON 자동 등록
  → 예전 UX 복원 (실행 잡ID 위에 결과 잡ID 노출)
- delete_job / delete_jobs_bulk 신설 + API + UI 삭제 버튼
  실행 중 잡 거부, 로그·원본 결과 JSON 보존

후처리 시그니처 수정
- bt_post_filters.orderbook_reject_for_entry 호출:
  price= → current_price=, 단일 대입 → 튜플 unpack
  (매 trial 마다 TypeError 삼켜지고 후처리 무효화되던 버그)

버그 조사·룰 강화
- verify-before-conclude-cross-strategy.mdc: 함수 추적 딥다이브 → 근본 대안 3개
- .cursorrules: 룰 27~29 추가 (해외 UI 정합·휩쏘 키·엔진 뱃지)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 19:23:22 +09:00

147 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""Apply trial#120 params → 1일 모멘텀 백테 (호가 ON). DB 미변경."""
from __future__ import annotations
import json
import sys
import time
from pathlib import Path
_ROOT = Path(__file__).resolve().parents[1]
if str(_ROOT) not in sys.path:
sys.path.insert(0, str(_ROOT))
def main() -> int:
t0 = time.time()
start = end = "2026-08-25"
out_json = _ROOT / "logs" / "mom_bt_20260825_trial120_ob_on.json"
print(f"start prepare {start} OB=on", flush=True)
from database import TradeDB
from kis_trader.backtest.optuna_momentum import prepare_momentum_search_context
from kis_trader.backtest.param_search_momentum import evaluate_momentum_param_combo
db = TradeDB()
row = db.conn.execute(
"SELECT payload_json FROM optuna_study_result WHERE study_name=%s",
("momentum_tpe_20260818_20260821_20260823_192516",),
).fetchone()
payload = json.loads(row["payload_json"])
t120 = next(x for x in payload["results_gated"] if x.get("optuna_trial_number") == 120)
combo = dict(t120["params"])
print(
"trial120 ob=%s whip=%s sl=%s tp=%s" % (
combo.get("_orderbook_filter_enabled"),
combo.get("whipsaw_filter_enabled"),
combo.get("sl_pct"),
combo.get("tp_pct"),
),
flush=True,
)
ctx = prepare_momentum_search_context(
start,
end,
"tpe",
orderbook_filter="on",
market="KR",
)
if ctx is None:
print("PREPARE FAILED", flush=True)
return 1
print(
"ctx ready codes=%d ticks=%s ob_codes=%d fee=%s slot=%s max=%s budget=%s"
% (
len(ctx.codes_candles),
bool(ctx.ticks_by_code),
len(ctx.orderbook_by_code or {}),
ctx.fee_rate,
ctx.slot_money,
ctx.max_stocks,
ctx.total_budget_krw,
),
flush=True,
)
base = dict(ctx.base_fixed)
base["_orderbook_filter_enabled"] = True
base["ob_filter_enabled"] = True
grid_keys = list(payload.get("grid_keys") or [])
if not grid_keys:
grid_keys = [k for k in combo.keys() if not str(k).startswith("_")]
result = evaluate_momentum_param_combo(
combo,
base_fixed=base,
grid_keys=grid_keys,
codes_candles=ctx.codes_candles,
min_trades=0,
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=1,
cache_holder=ctx.cache_holder,
ticks_by_code=ctx.ticks_by_code,
orderbook_by_code=ctx.orderbook_by_code,
program_by_code=getattr(ctx, "program_by_code", None),
start_key="20260825",
end_key="20260825",
include_trades=True,
)
print("elapsed_sec", round(time.time() - t0, 1), flush=True)
if not result:
print("NO RESULT", flush=True)
return 2
trades = result.get("_trades") or result.get("trades") or []
out = {
"ok": True,
"date": start,
"source": "optuna trial#120 + prepare orderbook_filter=on",
"study": "momentum_tpe_20260818_20260821_20260823_192516",
"total_trades": result.get("total_trades"),
"win_rate": result.get("win_rate"),
"total_pnl": result.get("total_pnl"),
"pf": result.get("pf"),
"trades": trades,
}
print(
"RESULT trades=%s WR=%s PnL=%s PF=%s"
% (out["total_trades"], out["win_rate"], out["total_pnl"], out["pf"]),
flush=True,
)
for tr in trades:
code = str(tr.get("code") or "")
line = (
" %s->%s %s %s pnl=%s entry=%s exit=%s"
% (
tr.get("buy_time") or tr.get("entry_time"),
tr.get("sell_time") or tr.get("exit_time"),
code,
tr.get("sell_reason"),
tr.get("pnl"),
tr.get("entry_price") or tr.get("buy_price"),
tr.get("exit_price") or tr.get("sell_price"),
)
)
print(line, flush=True)
if code == "417010":
print(" *** NANOTEAM IN BACKTEST ***", flush=True)
out_json.write_text(json.dumps(out, ensure_ascii=False, indent=2, default=str))
print("JSON", out_json, flush=True)
print("DONE", flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())