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:
Your Name
2026-07-21 07:50:24 +09:00
parent 74db49149f
commit 61bec4bd1d
86 changed files with 4811 additions and 297 deletions

View File

@@ -54,10 +54,12 @@ def resolve_scalp_universe(
try:
from kis_trader.database.db_manager import get_db as _get_ext_db
debounce_sec = scalp_universe_exit_debounce_sec()
history = _get_ext_db().get_universe_by_candle_time(
strategy_id=strategy_id,
start_ymd=start_ymd,
end_ymd=end_ymd,
exit_debounce_sec=debounce_sec,
)
if history:
return history, "history", len(history), 1
@@ -66,13 +68,79 @@ def resolve_scalp_universe(
return None, "all", 0, 1
def scalp_universe_exit_debounce_sec() -> int:
"""실매 ``CONDITION_EXIT_GRACE_SEC`` 정합 — 스냅샷 축소 시 N초 유지."""
from kis_trader.backtest.universe_timeline import universe_exit_debounce_sec_for_strategy
return universe_exit_debounce_sec_for_strategy("SCALP")
def scalp_backtest_candle_warmup_bars() -> int:
"""백테 RSI 등 warm-up — 실매 봉 버퍼와 같이 기간 시작 전 N봉 prepend."""
from kis_trader.utils.env import get_env_int
return max(0, int(get_env_int("SCALP_BACKTEST_CANDLE_WARMUP_BARS", 50)))
def prepend_scalp_candle_warmup(
db,
candles_by_code: Dict[str, List[Dict]],
period_start_key: str,
*,
warmup_bars: Optional[int] = None,
) -> int:
"""
``period_start_key``(YYYYMMDDHHMM) 이전 N봉을 종목별로 prepend.
RSI 판별용 — 포트폴리오 all_times 는 ``_backtest_period_start_key`` 로 기간만 사용.
"""
wb = (
scalp_backtest_candle_warmup_bars()
if warmup_bars is None
else max(0, int(warmup_bars))
)
if wb <= 0 or db is None or not period_start_key:
return 0
ps = str(period_start_key)[:12]
total_prepended = 0
for code, rows in list(candles_by_code.items()):
if not rows:
continue
first_period_idx = None
for i, r in enumerate(rows):
ct = str(r.get("candle_time") or "")
if ct >= ps:
first_period_idx = i
break
if first_period_idx is None:
continue
# 이미 기간 전 봉이 있으면 skip (idempotent)
if first_period_idx > 0:
continue
first_ct = str(rows[first_period_idx].get("candle_time") or "")
if not first_ct:
continue
warm_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 is_confirmed=1 "
"ORDER BY candle_time DESC LIMIT %s",
[code, first_ct, wb],
).fetchall()
if not warm_rows:
continue
prefix = [dict(r) for r in reversed(warm_rows)]
candles_by_code[code] = prefix + [dict(r) for r in rows]
total_prepended += len(prefix)
return total_prepended
def load_scalp_candles_by_code(
db,
start_key: str,
end_key: str,
rsi_period: int = 3,
) -> Tuple[Dict[str, List[Dict]], int]:
"""ws_candles 1분봉 전 종목 로드."""
"""ws_candles 1분봉 전 종목 로드 (+ 기간 전 웜업 prepend)."""
codes_raw = db.conn.execute(
"SELECT DISTINCT code FROM ws_candles WHERE timeframe=1 "
"AND candle_time >= %s AND candle_time <= %s ORDER BY code",
@@ -97,6 +165,7 @@ def load_scalp_candles_by_code(
candles_by_code[code] = [dict(r) for r in rows]
total_candles += len(rows)
prepend_scalp_candle_warmup(db, candles_by_code, str(start_key)[:12])
return candles_by_code, total_candles
@@ -137,6 +206,14 @@ def run_scalping_backtest_web_aligned(
engine_params.setdefault("scan_interval_min", 1)
engine_params.setdefault("portfolio_mode", True)
# 기간 시작 키 — 웜업 봉이 all_times 에 섞이지 않도록 (돌파·모멘텀과 동일)
_sk_w = str((meta_out or {}).get("start_key") or "")[:12]
if _sk_w:
engine_params["_backtest_period_start_key"] = _sk_w
_db_w = (meta_out or {}).get("db")
if _db_w is not None and str(mode).strip().lower() != "momentum":
prepend_scalp_candle_warmup(_db_w, candles_by_code, _sk_w)
from kis_trader.backtest.backtest_env_timeline import attach_backtest_env_timeline_to_params
attach_backtest_env_timeline_to_params(engine_params, meta_out, "SCALP")
@@ -194,6 +271,9 @@ def run_scalping_backtest_web_aligned(
meta_out["backtest_buy_source"] = "ohlc_fallback"
else:
meta_out["backtest_buy_source"] = "align"
if meta_out is not None:
meta_out["universe_exit_debounce_sec"] = scalp_universe_exit_debounce_sec()
meta_out["candle_warmup_bars"] = scalp_backtest_candle_warmup_bars()
attach_scalp_trade_pnl(
trades, fee_rate=fee_rate, sell_tax=sell_tax,