feat(Core/UI): 백테스트 전역 엔진 선택 버튼 추가 및 스캘핑 Rust 방어로직 포팅 1차 완료
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user