#!/usr/bin/env python3 """ scripts/test_live_execution_validation.py ========================================= Optuna 호가 합의 파라미터가 적용된 실매매(Live) 엔진의 구동 및 정합성을 100% 검증합니다. 1. 핵심 매매 모듈(전략/주문/호가 엔진) 구문 및 의존성 무결성 테스트 2. 실매매 루프의 DB 스냅샷 실시간 연동 및 필터 개통(ON) 상태 실측 3. 전략별 모멘텀/돌파 수익/손실 호가 컷 실매매 개통 구조 실측 4. 주가 진입 직전 가상 호가 데이터(정상 수급 vs 비합리 수급) 주입을 통한 주문 차단/통과 시뮬레이션 """ import sys from pathlib import Path import logging ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s") lg = logging.getLogger("LiveValidation") def run_validation(): print("=" * 78) print("🚀 [실매매 엔진 호가 수급 통합 가동 검증 5단계 로드맵]") print("=" * 78) # ------------------------------------------------------------------------- # 1. 핵심 모듈 임포트 무결성 검사 # ------------------------------------------------------------------------- print("\n🔍 [1단계] 핵심 실매매 전략 및 인프라 모듈 구문/의존성 무결성 검증...") try: from database import TradeDB from kis_trader.strategies.momentum import MomentumStrategy from kis_trader.strategies.breakout import BreakoutStrategy from kis_trader.engine.orderbook_filter import orderbook_reject_for_entry, orderbook_filter_enabled from kis_trader.engine.orderbook_env import load_orderbook_threshold_cfg from kis_trader.ws.orderbook_cache import OrderbookSnapshot, OrderbookLevel from kis_trader.engine.momentum_hts_logic import need_ob_or_history print(" ✅ 모든 핵심 실매매 모듈(Momentum, Breakout, OrderbookFilter) 정상 로드 완료.") except Exception as exc: print(f" ❌ [1단계 실패] 모듈 임포트 중 오류 발생: {exc}") return False # ------------------------------------------------------------------------- # 2. 실매매 루프의 DB 스냅샷 실시간 연동 검증 # ------------------------------------------------------------------------- print("\n🔗 [2단계] 실매매 루프 DB 스냅샷 바인딩 상태 실측 (하드코딩 배제 검증)...") db = TradeDB() strategies = ["MOMENTUM", "BREAKOUT", "SCALP", "TAIL"] for strat in strategies: is_on = orderbook_filter_enabled(strat) cfg = load_orderbook_threshold_cfg(strat, {}) print(f" 👉 [{strat:8s}] 실매매 호가필터 ON 여부: {is_on} | 스프레드 제한: {cfg.get('max_spread_pct')}%, 잔량비 제한: {cfg.get('min_bid_ask_ratio')}") if not is_on: print(f" ⚠️ [경고] {strat} 전략의 호가 필터가 OFF로 되어있습니다. DB 값이 맞는지 확인하십시오.") # ------------------------------------------------------------------------- # 3. 전략별 모멘텀/돌파 수익/손실 호가 컷 실매매 개통 구조 실측 # ------------------------------------------------------------------------- print("\n🛡️ [3단계] 모멘텀 & 돌파 전략 실매매 청산 엔진 호가 컷 개통 실측...") try: mom_strat = MomentumStrategy(db=db, client=None, ws=None, order_mgr=None) brk_strat = BreakoutStrategy(db=db, client=None, ws=None, order_mgr=None) # 돌파 전략 객체 속성 검증 print(f" 👉 [BREAKOUT] 객체 속성 바인딩 -> 수익구간 호가매도 ON: {brk_strat.exit_ob_enabled} | 손절호가 ON: {brk_strat.stop_ob_enabled}") # 모멘텀 전략 실매매 청산 함수 동적 개통 검증 from kis_trader.utils.env import get_env_bool mom_exit_on = get_env_bool("MOMENTUM_EXIT_OB_ENABLED", False) mom_stop_on = get_env_bool("MOMENTUM_STOP_OB_ENABLED", False) mom_need_ob = need_ob_or_history({"exit_ob_enabled": mom_exit_on, "stop_ob_enabled": mom_stop_on}) print(f" 👉 [MOMENTUM] 실시간 엔진 판정 -> 수익구간 호가매도 ON: {mom_exit_on} | 실시간 호가 추적 활성: {mom_need_ob}") print(" ✅ 실매매 인스턴스 및 엔진에서 DB 수급 컷 설정을 100% 정상 인식하고 있습니다.") except Exception as exc: print(f" ❌ [3단계 실패] 실매매 클래스 및 엔진 검증 중 오류 발생: {exc}") import traceback traceback.print_exc() return False # ------------------------------------------------------------------------- # 4. 가상 실시간 호가 스냅샷 주입을 통한 실매매 진입 필터 차단/통과 시뮬레이션 # ------------------------------------------------------------------------- print("\n🧪 [4단계] 실매매 TRIGGER 진입 시점 호가 필터 작동 정밀 시뮬레이션...") # 가상 스냅샷 생성 함수 def make_mock_snap(spread_ratio: float, total_bid: int, total_ask: int) -> OrderbookSnapshot: cur = 10000 ask1 = int(cur + (cur * spread_ratio / 100.0)) bid1 = int(cur) # 1~10호가 레벨 구성 (OrderbookLevel 규격) bids = [OrderbookLevel(price=bid1 - i*10, qty=int(total_bid/10)) for i in range(10)] asks = [OrderbookLevel(price=ask1 + i*10, qty=int(total_ask/10)) for i in range(10)] snap = OrderbookSnapshot( code="005930", bids=bids, asks=asks, total_bid_qty=total_bid, total_ask_qty=total_ask, ts=1234567890.0, source="kiwoom_0d", ) return snap # Case A: 정상 황금 수급 종목 (스프레드 0.1%, 매수잔량 6,000 > 매도잔량 2,000 -> 잔량비 3.0, 매도벽 및 두께 적합) good_snap = make_mock_snap(0.1, 6000, 2000) # Case B: 비합리 위험 종목 (스프레드 3.5%, 매수물량 500 / 매도 50,000 -> 스프레드 과다 및 위험 수급) toxic_snap = make_mock_snap(3.5, 500, 50000) for strat in ["MOMENTUM", "BREAKOUT", "SCALP", "TAIL"]: # (1) Good Liquidity Test (예수금 1,000만원 기준 1000주 진입 -> 매도벽 3000주 이내) params_good = {"_backtest_orderbook_snapshot": good_snap, "slot_money": 10_000_000} rej_code_good, _ = orderbook_reject_for_entry(params_good, strat, current_price=10000.0) # (2) Toxic Liquidity Test params_toxic = {"_backtest_orderbook_snapshot": toxic_snap, "slot_money": 10_000_000} rej_code_toxic, rej_msg = orderbook_reject_for_entry(params_toxic, strat, current_price=10000.0) good_status = "✅ 정상 통과" if not rej_code_good else f"❌ 의심 탈락 ({rej_code_good})" toxic_status = f"✅ 방어 차단 완수 ({rej_code_toxic}: {rej_msg})" if rej_code_toxic else "❌ 위험 종목 미차단 (오류)" print(f" 👉 [{strat:8s}] 우량 호가 주입: {good_status} | 위험 호가 주입: {toxic_status}") # ------------------------------------------------------------------------- # 5. 웹소켓 단절 예외 및 유니버스 교집합 0건 방어 정합성 검증 # ------------------------------------------------------------------------- print("\n⚠️ [5단계] 웹소켓 단절 예외(None 수신) 및 유니버스 교집합 기형(0건 탈락) 방어 검증...") from kis_trader.utils.live_portfolio_common import live_universe_slot_align_enabled, resolve_live_universe_history_source # (1) 웹소켓 단절 시 Fail-Open / Close 동작 검열 print(" [5-A] 웹소켓 단절 및 스냅샷 결석(None) 시 실매매 호가 필터 안전성 검열:") for strat in ["MOMENTUM", "BREAKOUT", "SCALP", "TAIL"]: params_none = {"_backtest_orderbook_snapshot": None, "slot_money": 10_000_000} rej_code_none, _ = orderbook_reject_for_entry(params_none, strat, current_price=10000.0) action_desc = "✅ Fail-Open (REST 429 과부하/지연 방지를 위해 필터 스킵 후 주문 통과)" if rej_code_none is None else f"🛡️ Fail-Close (주문 차단: {rej_code_none})" print(f" 👉 [{strat:8s}] WS 단절(None 스냅샷): {action_desc}") # (2) 유니버스 교집합 정합 스위치 상태 및 전량 탈락(Wipeout) 경보 인식 실측 print(" [5-B] 유니버스 교집합 정합(ALIGN) 스위치 실측 (키움 WS 0건 발생 시 전량 탈락 원인 판독):") for strat in ["MOMENTUM", "BREAKOUT", "SCALP", "TAIL"]: align_on = live_universe_slot_align_enabled(strat) hist_src = resolve_live_universe_history_source(strat, universe_source="kiwoom_condition" if strat in ["MOMENTUM", "TAIL"] else "ls_condition") status_str = "🟢 ON (DB 히스토리와 실시간 후보 교집합 적용 중 -> 키움 장애 시 universe_wipe 발생)" if align_on else "⚪ OFF (실시간 후보 단독 승인 -> 교집합 탈락 위험 없음)" print(f" 👉 [{strat:8s}] 유니버스 교집합 정합(ALIGN): {status_str} (기준소스: {hist_src})") # ------------------------------------------------------------------------- # 6. 스냅샷 수집 스위치(호가, 프로그램) 하드코딩 방지 검열 # ------------------------------------------------------------------------- print("\n📸 [6단계] 매수 검토 스냅샷(호가/프로그램) 수집 스위치 정상 연동 검열...") import kis_trader.engine.trigger_eval_collect as tec from unittest.mock import patch # DB 값을 True/False로 강제했을 때 스위치 함수가 이를 똑같이 따라오는지 확인 (하드코딩 차단) with patch("kis_trader.engine.trigger_eval_collect.get_env_bool") as mock_get_env_bool, \ patch("kis_trader.engine.trigger_eval_collect.get_env_from_db", return_value=""), \ patch("kis_trader.engine.trigger_eval_collect.trigger_eval_save_master_enabled", return_value=True): # Test True mock_get_env_bool.return_value = True if not tec.orderbook_collect_enabled({}, "MOMENTUM") or not tec.program_collect_enabled({}, "MOMENTUM"): print(" ❌ [치명적 오류] 호가 또는 프로그램 수집 스위치가 코드 내부에 하드코딩되어 DB 설정(ON)을 무시합니다!") return False # Test False mock_get_env_bool.return_value = False if tec.orderbook_collect_enabled({}, "MOMENTUM") or tec.program_collect_enabled({}, "MOMENTUM"): print(" ❌ [치명적 오류] 호가 또는 프로그램 수집 스위치가 코드 내부에 하드코딩되어 DB 설정(OFF)을 무시합니다!") return False print(" ✅ 호가 및 프로그램 스냅샷 수집 스위치가 DB 설정과 100% 정상 연동 확인 완료 (하드코딩 없음)") print("\n=" * 78) print("👑 [최종 판정] 실매매 엔진 6단계 무결성 및 예외 방어 검증 완결!") print("=" * 78) return True if __name__ == "__main__": success = run_validation() sys.exit(0 if success else 1)