feat: Add DART strategy and related configurations
ㅇ 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.
This commit is contained in:
142
kis_trader/backtest/dart_backtest_common.py
Normal file
142
kis_trader/backtest/dart_backtest_common.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#!/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,
|
||||
}
|
||||
Reference in New Issue
Block a user