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:
2026-07-06 01:27:00 +09:00
parent d8ba01afa4
commit 61c72a8a4c
171 changed files with 176914 additions and 7329 deletions

View File

@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""
kis_trader/backtest/param_search_cli_common.py — 포트폴리오 파라서치 공통 CLI
=============================================================================
꼬리·스캘핑·모멘텀·돌파는 동일한 run 옵션 패턴을 쓴다 (하락매수 UPDOW 제외).
예시 (전략 파일만 바꿈):
python3 kis_trader/backtest/param_search_momentum.py \\
--start 2026-05-01 --end 2026-05-31 \\
--slot-money 200000 --max-stocks 20 --total-budget 2000000 \\
--time-start 830 --time-end 1530 \\
--mode fast
"""
from __future__ import annotations
import argparse
from typing import Any, Dict, Optional
# 파라서치 공통 필터 기본값 (정렬은 각 스크립트 total_pnl 유지)
MIN_WIN_RATE_DEFAULT = 40.0
MIN_PF_DEFAULT = 1.0
def combo_passes_search_filters(
*,
win_rate: float,
pf: float,
min_win_rate: float,
min_pf: float,
) -> bool:
"""승률·PF 하한 — 워커·머지 단계 공통."""
if float(win_rate) < float(min_win_rate):
return False
if float(pf) < float(min_pf):
return False
return True
def add_search_filter_cli_args(parser: argparse.ArgumentParser) -> None:
"""승률·PF 하한 CLI (--min_win_rate, --min_pf)."""
parser.add_argument(
"--min_win_rate",
default=MIN_WIN_RATE_DEFAULT,
type=float,
help="승률 하한(%%). 기본 40",
)
parser.add_argument(
"--min_pf",
default=MIN_PF_DEFAULT,
type=float,
help="Profit Factor 하한 (총익/|총손|). 기본 1.0",
)
def add_portfolio_cli_args(parser: argparse.ArgumentParser) -> None:
"""1회투자·동시보유·총한도·매매시간 — tail/scalp/momentum/breakout 공통."""
parser.add_argument(
"--time-start", type=int, default=None, dest="time_start",
help="매수 시작 HHMM (미지정 시 DB·전략 기본값)",
)
parser.add_argument(
"--time-end", type=int, default=None, dest="time_end",
help="매수 종료 HHMM (미지정 시 DB·전략 기본값)",
)
parser.add_argument(
"--slot-money", type=float, default=None, dest="slot_money",
help="1회투자금(원). 미지정 시 DB 전략별 SLOT_*",
)
parser.add_argument(
"--max-stocks", type=int, default=None, dest="max_stocks",
help="동시보유 종목 수. 미지정 시 DB 전략 MAX_*_STOCKS",
)
parser.add_argument(
"--total-budget", type=float, default=None, dest="total_budget",
help="총 운용한도(원). 0/미지정 시 동시×1회투자",
)
def apply_session_to_fixed(
fixed: Dict[str, Any],
*,
time_start_hm: Optional[int] = None,
time_end_hm: Optional[int] = None,
) -> None:
"""CLI 매매시간 → 백테 base fixed dict (in-place)."""
if time_start_hm is not None:
fixed["time_start_hm"] = int(time_start_hm)
if time_end_hm is not None:
fixed["time_end_hm"] = int(time_end_hm)
def search_json_meta(
portfolio: Dict[str, Any],
fixed: Dict[str, Any],
) -> Dict[str, Any]:
"""JSON 저장·--apply meta — 포트폴리오 + 매매시간."""
return {
"slot_money": portfolio.get("slot_money"),
"max_stocks": portfolio.get("max_stocks"),
"total_budget_krw": portfolio.get("total_budget_krw"),
"time_start_hm": fixed.get("time_start_hm"),
"time_end_hm": fixed.get("time_end_hm"),
"portfolio": dict(portfolio),
}
def format_session_hm(fixed: Dict[str, Any]) -> str:
ts = int(fixed.get("time_start_hm") or 930)
te = int(fixed.get("time_end_hm") or 1530)
return f"{ts:04d}-{te:04d}"