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

46
tmp_mom_smoke_web.py Normal file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""웹 /api/backtest/momentum 스모크 — Flask test_client (서버 불필요)."""
import json
import sys
sys.path.insert(0, "/home/hoon/kis_bot")
from backtest_web import app # noqa: E402
QS = {
"start": "2026-06-01",
"end": "2026-06-14",
"universe": "sim",
"pattern_breakout": "1",
"pattern_pullback": "1",
"chase_lookback_min": "10",
"pullback_lookback_min": "15",
"pullback_min_pct": "0.3",
"pullback_max_pct": "3.0",
"use_ema_filter": "1",
"use_rsi_max_filter": "0",
"use_high_chase_filter": "0",
"use_daily_range_filter": "0",
"mom_max_from_open_pct": "999",
"mom_min_from_open_pct": "-999",
}
with app.test_client() as c:
r = c.get("/api/backtest/momentum", query_string=QS)
print("HTTP", r.status_code)
d = r.get_json(silent=True) or {}
if d.get("error"):
print("ERROR:", d["error"])
sys.exit(1)
s = d.get("summary") or {}
p = d.get("params") or {}
print("--- V2 스모크 (6/1~14, universe=sim) ---")
print(f"패턴: 돌파={p.get('pattern_breakout')} 눌림={p.get('pattern_pullback')} chase={p.get('chase_lookback_min')}")
print(f"RSI≥{p.get('mom_rsi_min')} vol×{p.get('mom_vol_mult')} EMA={p.get('use_ema_filter')} ({p.get('ema_fast_period')}/{p.get('ema_slow_period')})")
print(f"거래: {s.get('total_trades')}건 | 승률 {s.get('win_rate')}% | PF {s.get('profit_factor')}")
print(f"총손익: {s.get('total_pnl'):+,}원 | MDD {s.get('max_drawdown'):,}원 | 평균보유 {s.get('avg_hold_min')}")
print(f"종목수: {p.get('codes_analyzed')} | 슬롯 {p.get('max_stocks')} × {p.get('slot_money'):,.0f}")
reasons = d.get("reasons") or {}
if reasons:
top = sorted(reasons.items(), key=lambda x: -x[1])[:8]
print("청산사유:", ", ".join(f"{k}={v}" for k, v in top))