feat(tests): 신규 키움 웹소켓 조건검색 및 실시간 조건검색 테스트 추가
변경 사항 ---- - _test_kiwoom_condition_list.py: 키움 웹소켓 조건검색 '목록조회' 기능을 단독으로 테스트하는 스크립트 추가 - _test_kiwoom_condition_realtime.py: 'momentum' 조건식을 실시간으로 등록하고 초기 매칭 종목 리스트 및 실시간 편입/이탈을 수신하는 테스트 스크립트 추가 - _verify_columnar_bitid.py, _verify_shared_e2e_breakout.py, _verify_shared_e2e.py: 공유 메모리 및 dict 간의 데이터 일관성을 검증하는 테스트 추가 영향 ---- - 신규 테스트 스크립트 추가로 키움 웹소켓 API의 기능 검증 및 안정성을 높임 - 기존 기능에 대한 영향 없음 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
107
kis_trader/backtest/momentum_universe_timeline.py
Normal file
107
kis_trader/backtest/momentum_universe_timeline.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
모멘텀 백테 유니버스 — 스캔 시각(초)별 ``get_universe_at`` 재현 + EXIT 디바운스.
|
||||
|
||||
실매: 조건검색 변동 시 초단위 스냅샷 → 10초 루프에서 그 시각 최신 유니버스.
|
||||
백테(기존): 1분 슬롯 strict 집계 → 깜빡임 EXIT·분봉 끝 집계로 실매와 어긋남.
|
||||
|
||||
※ 초단위 타임라인 로직은 전략 공통 ``universe_timeline.UniverseTimeline`` 으로 통일했다.
|
||||
이 모듈은 모멘텀 전용 wrapper(debounce/scan_at env·attach)만 유지하며,
|
||||
``MomentumUniverseTimeline`` 은 공통 클래스의 alias 다. (기존 import 무변경)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from kis_trader.engine.momentum_engine import MOMENTUM_STRATEGY_ID
|
||||
|
||||
# 전략 공통 타임라인 (단일 구현) — 기존 이름 재노출/alias 로 모멘텀 코드 무변경 유지.
|
||||
from kis_trader.backtest.universe_timeline import ( # noqa: F401
|
||||
UniverseTimeline,
|
||||
build_universe_timeline,
|
||||
debounce_universe_snapshots,
|
||||
normalize_universe_events,
|
||||
)
|
||||
|
||||
# 하위호환 alias — 모멘텀 코드/외부 import 가 계속 쓰는 이름.
|
||||
MomentumUniverseTimeline = UniverseTimeline
|
||||
|
||||
|
||||
def momentum_universe_exit_debounce_sec(params: Optional[Dict[str, Any]] = None) -> int:
|
||||
"""조건검색 짧은 EXIT→재편입 깜빡임 무시(초). grace 스냅샷 재생 시 기본 0."""
|
||||
if params is not None and params.get("universe_exit_debounce_sec") is not None:
|
||||
try:
|
||||
return max(0, int(float(params["universe_exit_debounce_sec"])))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
from kis_trader.backtest.momentum_backtest_common import (
|
||||
momentum_universe_exit_debounce_sec as _deb_from_common,
|
||||
)
|
||||
return _deb_from_common()
|
||||
|
||||
|
||||
def momentum_backtest_universe_scan_at_enabled(params: Optional[Dict[str, Any]] = None) -> bool:
|
||||
"""백테 스캔 루프: 분 슬롯 대신 스캔 시각 유니버스 (기본 ON)."""
|
||||
if params is not None and params.get("backtest_universe_scan_at") is not None:
|
||||
s = str(params.get("backtest_universe_scan_at")).strip().lower()
|
||||
if s in ("1", "true", "t", "y", "yes", "on"):
|
||||
return True
|
||||
if s in ("0", "false", "f", "n", "no", "off", ""):
|
||||
return False
|
||||
from kis_trader.utils.env import get_env_bool
|
||||
return get_env_bool("MOMENTUM_BACKTEST_UNIVERSE_SCAN_AT", True)
|
||||
|
||||
|
||||
def build_momentum_universe_timeline(
|
||||
*,
|
||||
strategy_id: str,
|
||||
start_ymd: str,
|
||||
end_ymd: str,
|
||||
debounce_sec: int = 30,
|
||||
strict: bool = False,
|
||||
strict_lag_minutes: int = 1,
|
||||
) -> Optional[UniverseTimeline]:
|
||||
"""모멘텀 유니버스 타임라인 — 전략 공통 ``build_universe_timeline`` 사용."""
|
||||
return build_universe_timeline(
|
||||
strategy_id=strategy_id,
|
||||
start_ymd=start_ymd,
|
||||
end_ymd=end_ymd,
|
||||
debounce_sec=debounce_sec,
|
||||
strict=strict,
|
||||
strict_lag_minutes=strict_lag_minutes,
|
||||
)
|
||||
|
||||
|
||||
def attach_momentum_universe_timeline_to_params(
|
||||
params: Dict[str, Any],
|
||||
*,
|
||||
start_ymd: str,
|
||||
end_ymd: str,
|
||||
strategy_id: str = MOMENTUM_STRATEGY_ID,
|
||||
use_saved_history: bool = True,
|
||||
) -> Optional[UniverseTimeline]:
|
||||
"""백테 params에 ``_momentum_universe_timeline`` 부착 (스캔 시각 유니버스)."""
|
||||
if not use_saved_history:
|
||||
return None
|
||||
if not momentum_backtest_universe_scan_at_enabled(params):
|
||||
return None
|
||||
debounce_sec = momentum_universe_exit_debounce_sec(params)
|
||||
# scan_at = 실매 get_universe_at(초) — 분봉 strict lag 는 minute 슬롯 전용
|
||||
timeline = build_momentum_universe_timeline(
|
||||
strategy_id=strategy_id,
|
||||
start_ymd=start_ymd,
|
||||
end_ymd=end_ymd,
|
||||
debounce_sec=debounce_sec,
|
||||
strict=False,
|
||||
strict_lag_minutes=0,
|
||||
)
|
||||
if timeline is not None:
|
||||
params["_momentum_universe_timeline"] = timeline
|
||||
params["_universe_timeline_meta"] = {
|
||||
"debounce_sec": debounce_sec,
|
||||
"strict": False,
|
||||
"strict_lag_minutes": 0,
|
||||
"snapshots": timeline.snapshot_count,
|
||||
"mode": "scan_at",
|
||||
}
|
||||
return timeline
|
||||
Reference in New Issue
Block a user