feat(Core/UI): 백테스트 전역 엔진 선택 버튼 추가 및 스캘핑 Rust 방어로직 포팅 1차 완료

This commit is contained in:
Your Name
2026-09-03 02:49:14 +09:00
parent ac1190f1fc
commit 3b7c21cde4
316 changed files with 56952 additions and 1 deletions

View File

@@ -674,6 +674,11 @@ def run_scalping_backtest(
universe_by_slot이 주어지면, 5분마다 해당 슬롯의 후보 종목에서만 매수 신호를 검사
(실매매의 target_candidates 5분 갱신과 동일한 유니버스 시뮬레이션).
"""
if _to_bool(params.get("use_rust"), False):
from kis_trader.utils.logger import get_logger
get_logger("kis_trader.scalping_engine").info("🚀 Rust 엔진(Experimental)으로 스캘핑 시뮬레이션을 실행합니다.")
return run_scalping_backtest_rust_experimental(codes_candles, params)
if _to_bool(params.get("portfolio_mode"), True):
from kis_trader.backtest.scalping_portfolio_backtest import run_scalping_backtest_portfolio
return run_scalping_backtest_portfolio(
@@ -1352,4 +1357,87 @@ def check_sell_signal_live(
if reason:
return (reason, exit_price)
return None
return None
def run_scalping_backtest_rust_experimental(
codes_candles: Dict[str, List[Dict]],
params: Dict[str, Any],
) -> List[Dict]:
"""
Rust 엔진 (kis_rust_core) 을 통한 초고속 스캘핑 백테스트 (실험).
"""
try:
import kis_rust_core
from kis_rust_core import ScalpParams, CandleData
except ImportError as e:
from kis_trader.utils.logger import get_logger
get_logger("kis_trader.scalping_engine").error(f"Rust core import failed: {e}")
return []
use_defense_filters = str(params.get("use_defense_filters", True)).strip().lower() in ("1", "true", "t", "y", "yes", "on")
# skip_hts_scan_dupes 는 HTS 조건검색 엔진 쓸 때 낙폭/RSI 중복을 끌지 여부.
skip_hts = False
if "skip_hts_scan_dupes" in params:
skip_hts = str(params.get("skip_hts_scan_dupes")).strip().lower() in ("1", "true", "t", "y", "yes", "on")
else:
src = str(params.get("SCALP_UNIVERSE_SOURCE", "condition")).strip().lower()
skip_hts = src in ("kiwoom_condition", "condition")
rp = ScalpParams(
rsi_period=int(params.get("rsi_period", 3)),
rsi_oversold=float(params.get("rsi_oversold", 25.0)),
rsi_overbought=float(params.get("rsi_overbought", 75.0)),
sl_pct=abs(float(params.get("sl_pct", 0.015))),
tp_pct=effective_tp_pct_from_params(params),
drop_rate=float(params.get("drop_rate", 0.015)),
cooldown_min=float(params.get("cooldown_min", 10.0)),
max_daily=int(params.get("max_daily", 3)),
high_chase_thr=float(params.get("high_chase_thr", 0.96)),
max_daily_chg=float(params.get("max_daily_chg", 20.0)),
min_price=float(params.get("min_price", 1000.0)),
vol_mult=float(params.get("vol_mult", 0.0)),
use_defense_filters=use_defense_filters,
skip_hts=skip_hts,
time_start_hm=int(params.get("time_start_hm", 900)),
time_end_hm=int(params.get("time_end_hm", 1530))
)
all_trades = []
for code, rows in codes_candles.items():
if not rows:
continue
# 캔들 변환
rust_candles = []
for r in rows:
rust_candles.append(CandleData(
str(r["candle_time"]),
float(r["open"]),
float(r["high"]),
float(r["low"]),
float(r["close"]),
float(r.get("volume", 0)),
float(r.get("rsi", 50.0)) # TODO: rust 내부에서 rsi 계산하도록 변경 필요
))
res = kis_rust_core.run_scalp_backtest_fast(code, rust_candles, rp)
for t in res:
all_trades.append({
"code": t.code,
"buy_time": t.buy_time,
"sell_time": t.sell_time,
"buy_price": t.buy_price,
"sell_price": t.sell_price,
"qty": 1, # 임시
"pnl": 0, # PnL 부착부에서 재계산
"profit_rate": round(t.pnl_pct, 2),
"hold_min": 0,
"sell_reason": t.reason,
"rsi_entry": round(t.rsi_entry, 1),
"is_rust_core": True,
})
return all_trades