ㅇ Changes: - Introduced the DART strategy to the trading system, including its configuration and integration into the existing framework. - Updated the database schema to include DART-specific tables for disclosures and watchlists. - Enhanced the backtesting and parameter search functionalities to support the DART strategy. - Implemented new rules for browser verification and API interactions to ensure compliance with the updated DART strategy. Impact: - These additions expand the trading capabilities of the system, allowing for more comprehensive analysis and execution of DART-related strategies, while maintaining system integrity and performance.
143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
#!/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,
|
|
}
|