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

@@ -737,6 +737,8 @@ def evaluate_scalp_param_combo(
ticks_by_code: Any = None,
orderbook_by_code: Any = None,
program_by_code: Any = None,
start_key: str = "",
end_key: str = "",
) -> Optional[Dict[str, Any]]:
"""단일 스캘핑(reversal) 조합 백테 — Grid 워커·Optuna objective 공통.
@@ -757,6 +759,19 @@ def evaluate_scalp_param_combo(
engine_params["_bt_program_by_code"] = program_by_code
meta: Dict[str, Any] = {}
sk = str(start_key or "").strip()
ek = str(end_key or "").strip()
if len(sk) >= 12:
meta["start_key"] = sk[:12]
engine_params["_backtest_period_start_key"] = sk[:12]
elif len(sk) >= 8:
meta["start_key"] = sk[:8] + "0000"
engine_params["_backtest_period_start_key"] = meta["start_key"]
if len(ek) >= 12:
meta["end_key"] = ek[:12]
elif len(ek) >= 8:
meta["end_key"] = ek[:8] + "2359"
trades = sbc.run_scalping_backtest_web_aligned(
codes_candles, engine_params, universe_by_slot,
slot_money=slot_money, fee_rate=fee_rate, sell_tax=sell_tax,
@@ -920,12 +935,14 @@ def _evaluate_scalp_chunk(
"""워커: 청크 내 조합 평가 — 시각순 포트폴리오·총한도 (scalping_backtest_common)."""
shared = worker_shared_get()
ticks_preloaded = None
period_start_key = ""
if shared:
if codes_candles is None:
codes_candles = shared.get("codes_candles") or {}
if universe_by_slot is None:
universe_by_slot = shared.get("universe_by_slot")
ticks_preloaded = shared.get("ticks_by_code")
period_start_key = str(shared.get("start_key") or "")[:12]
# ws_ticks 공유메모리(opt-in): descriptor 로 read-only attach (워커당 1회 재사용).
if not ticks_preloaded:
_desc = shared.get("ticks_shared_descriptor")
@@ -948,8 +965,12 @@ def _evaluate_scalp_chunk(
engine_params["max_stocks"] = int(max_stocks)
engine_params["total_budget_krw"] = float(total_budget_krw)
engine_params["portfolio_mode"] = True
if period_start_key:
engine_params["_backtest_period_start_key"] = period_start_key
meta: Dict[str, Any] = {}
if period_start_key:
meta["start_key"] = period_start_key
trades = sbc.run_scalping_backtest_web_aligned(
codes_candles, engine_params, universe_by_slot,
slot_money=slot_money, fee_rate=fee_rate, sell_tax=sell_tax,
@@ -1043,6 +1064,11 @@ def _load_candles_for_search(start: str, end: str, rsi_period: int) -> dict:
if len(rows) < rsi_period + 5:
continue
codes_candles[code] = [dict(r) for r in rows]
if codes_candles:
from kis_trader.backtest.scalping_backtest_common import (
prepend_scalp_candle_warmup,
)
prepend_scalp_candle_warmup(db, codes_candles, start_key[:12])
finally:
db.close()
return codes_candles
@@ -1186,17 +1212,13 @@ def run_search(start: str, end: str, mode: str, top_n: int,
print("📌 [유니버스] --fallback-universe: 저장 이력 무시 → 시뮬레이션 유니버스 (조합별 거래 수 확대)")
elif start_ymd and end_ymd:
try:
# 신봇 이력 (초단위 event_time) → 1분 캔들 시각으로 리샘플링
from kis_trader.database.db_manager import get_db as _get_ext_db # type: ignore
_ext = _get_ext_db()
history = _ext.get_universe_by_candle_time(
strategy_id="SCALP",
start_ymd=start_ymd,
end_ymd=end_ymd,
# Optuna/웹과 동일 — resolve_scalp_universe (EXIT debounce 포함)
from kis_trader.backtest.scalping_backtest_common import resolve_scalp_universe
history, _src, n_bins, _scan_iv = resolve_scalp_universe(
start_ymd, end_ymd, use_saved_history=True, strategy_id="SCALP",
)
if history:
universe_by_slot = history
n_bins = len(history)
avg = sum(len(v) for v in history.values()) / max(1, n_bins)
print(
f"✅ 유니버스: 신봇 이력 사용 (event_time → 1분 캔들 리샘플링) | "
@@ -1271,6 +1293,8 @@ def run_search(start: str, end: str, mode: str, top_n: int,
"ticks_by_code": ticks_by_code,
"ticks_shared_descriptor": (shared_tick_store.descriptor() if shared_tick_store else None),
"tick_backtest_meta": tick_backtest_meta,
"start_key": start_key[:12] if start_key else "",
"end_key": end_key[:12] if end_key else "",
})
payload_bytes = shared.estimate_bytes()
n_cpu = os.cpu_count() or 4