변경 사항 ---- - _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>
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
#!/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}"
|