Files
kis_bot/kis_trader/backtest/param_search_cli_common.py
2026-07-30 18:05:07 +09:00

111 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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="승률 하한(%%). Grid 기본 40 / Optuna CLI 는 set_defaults(0) 로 덮음",
)
parser.add_argument(
"--min_pf",
default=MIN_PF_DEFAULT,
type=float,
help="Profit Factor 하한. Grid 기본 1.0 / Optuna CLI 는 set_defaults(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}"