#!/usr/bin/env python3 """ kis_trader/backtest/dart_backtest_common.py — DART 웹백테 정렬 """ from __future__ import annotations from typing import Any, Dict, List, Optional from database import TradeDB from kis_trader.backtest import scalping_backtest_common as sbc from kis_trader.engine import dart_engine as de def load_dart_events(db: TradeDB, start: str, end: str) -> List[Dict[str, Any]]: try: from kis_trader.scan.dart_order_tracker import ensure_dart_disclosure_columns ensure_dart_disclosure_columns(db) except Exception: pass rows = db.conn.execute( """ SELECT rcept_no, stock_code, corp_name, report_nm, rcept_dt, first_seen_at FROM dart_disclosures WHERE stock_code <> '' AND (filter_ok IS NULL OR filter_ok = 1) AND ( (first_seen_at >= %s AND first_seen_at <= %s) OR (rcept_dt >= %s AND rcept_dt <= %s) ) ORDER BY first_seen_at ASC """, ( start + " 00:00:00", end + " 23:59:59", start.replace("-", ""), end.replace("-", ""), ), ).fetchall() return [dict(r) for r in rows] def event_to_candle_time(ev: Dict[str, Any]) -> str: fs = str(ev.get("first_seen_at") or "") if fs: s = fs.replace("-", "").replace(":", "").replace(" ", "") if len(s) >= 12: return s[:12] rd = str(ev.get("rcept_dt") or "") if len(rd) == 8: return rd + "0900" return "" def _load_code_candles(db: TradeDB, code: str, start: str, end: str) -> List[Dict]: sk = start.replace("-", "") + "0900" ek = end.replace("-", "") + "1530" rows = db.conn.execute( """ SELECT candle_time, open, high, low, close, volume FROM ws_candles WHERE timeframe=1 AND code=%s AND candle_time >= %s AND candle_time <= %s ORDER BY candle_time ASC """, (code, sk, ek), ).fetchall() out = [] for r in rows: out.append({ "candle_time": str(r["candle_time"]), "open": float(r["open"] or 0), "high": float(r["high"] or 0), "low": float(r["low"] or 0), "close": float(r["close"] or 0), "volume": float(r["volume"] or 0), "is_confirmed": 1, }) return out def run_dart_backtest_web_aligned( *, start: str, end: str, params: Optional[Dict[str, Any]] = None, env_row: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: p = dict(params or de.get_dart_defaults_from_db(env_row=env_row)) db = TradeDB() events: List[Dict[str, Any]] = [] all_trades: List[Dict[str, Any]] = [] try: fee_rate, sell_tax, slot = sbc.fee_and_slot_from_env(env_row, strategy="DART") if params and params.get("slot_money"): slot = float(params["slot_money"]) events = load_dart_events(db, start, end) codes_done = set() for ev in events: code = str(ev.get("stock_code") or "").strip() if not code: continue ect = event_to_candle_time(ev) if not ect: continue key = (code, ect[:8]) if key in codes_done: continue codes_done.add(key) candles = _load_code_candles(db, code, start, end) if not candles: continue trades = de.run_dart_backtest_code( candles, p, event_candle_time=ect, slot_money=slot, fee_rate=fee_rate, sell_tax=sell_tax, ) for t in trades: t["code"] = code t["name"] = ev.get("corp_name") or code t["rcept_no"] = ev.get("rcept_no") all_trades.append(t) finally: db.close() wins = sum(1 for t in all_trades if float(t.get("pnl") or 0) > 0) n = len(all_trades) pnl = sum(float(t.get("pnl") or 0) for t in all_trades) return { "ok": True, "strategy": "DART", "start": start, "end": end, "trades": all_trades, "trade_count": n, "win_rate": (wins / n * 100.0) if n else 0.0, "total_pnl": pnl, "events": len(events), "params": p, }