""" validate_alpha_execution_harness.py APEX Alpha Preservation Execution Harness V1/V5 전용 검증기. 기본 validate_harness_context.py는 기존 JSON 호환성을 위해 APEX 필드를 optional로 본다. 이 도구는 GAS/Harness V5 전환 후 APEX 필드를 의무 검증하거나, 부분 도입 상태를 감사할 때 사용한다. Usage: python tools/validate_alpha_execution_harness.py [--strict] python tools/validate_alpha_execution_harness.py --check breakout_quality_gate python tools/validate_alpha_execution_harness.py --check anti_whipsaw_gate python tools/validate_alpha_execution_harness.py --check smart_cash_raise_v2 python tools/validate_alpha_execution_harness.py --check determinism python tools/validate_alpha_execution_harness.py --check cla_harness cla_harness 체크 항목 (Section 13): [1] market_regime_state enum 검증 (ADVANCE|PULLBACK_IN_UPTREND|DISTRIBUTION|BREAKDOWN|UNKNOWN) [2] CLA(CLUSTER_HOLD_ONLY) 레짐 시 코어 종목(005930/000660/229200) SELL 차단 (REGIME_CLA-1) [3] semiconductor_cluster_json.cluster_state enum 검증 [4] buy_permission_json rs_verdict enum 검증 (LEADER|MARKET|LAGGARD|BROKEN|UNKNOWN) [5] buy_permission_json composite_verdict enum 검증 (5가지 판정값) [6] satellite_failure_gate_json.sfg_v1 enum 검증 (TRIGGERED|CLEAR) [7] sfg_v1=TRIGGERED 시 위성 ALLOW_* 차단 (SFG-2) [8] buy_permission_json.rag_v1 enum 검증 (PASS|FAIL|EXEMPT) [9] rag_v1=FAIL 시 ALLOW_* 불일치 오류 (RAG-2) """ from __future__ import annotations import json import sys from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] VALID_CHECK_MODES = {"breakout_quality_gate", "anti_whipsaw_gate", "smart_cash_raise_v2", "determinism", "cla_harness", "brt_harness", "factor_cap"} REQUIRED_V5_KEYS = [ "breakout_quality_gate_json", "breakout_quality_gate_lock", "anti_whipsaw_gate_json", "anti_whipsaw_gate_lock", "smart_cash_raise_json", "smart_cash_raise_route", ] REQUIRED_APEX_KEYS = [ "alpha_lead_lock", "alpha_lead_json", "follow_through_lock", "follow_through_json", "distribution_lock", "distribution_risk_json", "profit_preservation_lock", "profit_preservation_json", "smart_cash_raise_lock", "cash_raise_plan_json", "rebound_sell_trigger_json", "smart_sell_quantities_json", "execution_quality_lock", "execution_quality_json", "buy_permission_json", "limit_price_policy_json", "alpha_feedback_json", ] BUY_ACTIONS = {"BUY", "STAGED_BUY", "ADD_ON"} VALID_BUY_PERMISSION = {"ALLOW_PILOT", "ALLOW_ADD_ON", "WATCH", "BLOCKED"} # ── [2026-05-21_CLA_HARNESS_V1] Section 13 체크 상수 ────────────────────────── VALID_MARKET_REGIME_STATES = { "ADVANCE", "PULLBACK_IN_UPTREND", "DISTRIBUTION", "BREAKDOWN", "UNKNOWN" } VALID_RS_VERDICTS = {"LEADER", "MARKET", "LAGGARD", "BROKEN", "UNKNOWN"} VALID_COMPOSITE_VERDICTS = { "PRIME_CANDIDATE", "WATCH_CANDIDATE", "REDUCE_CANDIDATE", "EXIT_REVIEW", "CLOSE_POSITION" } VALID_BRT_VERDICTS = {"LEADER", "MARKET", "LAGGARD", "BROKEN", "UNKNOWN"} VALID_SAQG_STATES = {"ELIGIBLE", "WATCHLIST_ONLY", "EXCLUDED", "EXEMPT"} VALID_SAPG_STATUSES = {"PASS", "SAPG_ALERT", "SAPG_CRITICAL", "INSUFFICIENT_DATA"} SEMICONDUCTOR_CORE_TICKERS = {"005930", "000660", "229200"} # 삼성전자, SK하이닉스, KODEX반도체 def load_harness(path: Path) -> dict[str, Any]: payload = json.loads(path.read_text(encoding="utf-8")) if isinstance(payload, dict) and isinstance(payload.get("data"), dict): maybe = payload["data"].get("_harness_context") if isinstance(maybe, dict): return maybe return payload def parse_jsonish(value: Any) -> Any: if isinstance(value, (list, dict)): return value if isinstance(value, str) and value.strip(): return json.loads(value) return value def to_number(value: Any) -> float | None: if isinstance(value, (int, float)): return float(value) if isinstance(value, str): text = value.strip() if not text: return None try: return float(text) except ValueError: return None return None def as_rows(harness: dict[str, Any], key: str, errors: list[str]) -> list[dict[str, Any]]: value = parse_jsonish(harness.get(key)) if not isinstance(value, list): errors.append(f"{key}: must be a list") return [] rows: list[dict[str, Any]] = [] for idx, item in enumerate(value): if not isinstance(item, dict): errors.append(f"{key}[{idx}]: must be an object") continue rows.append(item) return rows def validate_required_keys(harness: dict[str, Any], errors: list[str]) -> None: for key in REQUIRED_APEX_KEYS: if key not in harness: errors.append(f"missing APEX key: {key}") def validate_buy_blocks(harness: dict[str, Any], errors: list[str]) -> None: permissions = as_rows(harness, "buy_permission_json", errors) permission_by_ticker = {str(r.get("ticker")): r for r in permissions if r.get("ticker")} for idx, row in enumerate(permissions): state = row.get("buy_permission_state") if state not in VALID_BUY_PERMISSION: errors.append(f"buy_permission_json[{idx}].buy_permission_state invalid: {state!r}") tranche = row.get("max_tranche_pct") if state == "ALLOW_PILOT" and isinstance(tranche, (int, float)) and tranche > 30: errors.append(f"buy_permission_json[{idx}].max_tranche_pct exceeds 30 for ALLOW_PILOT") late_chase = to_number(row.get("late_chase_risk_score")) if late_chase is not None and not (0 <= late_chase <= 100): errors.append(f"buy_permission_json[{idx}].late_chase_risk_score must be in [0,100]") follow_score = to_number(row.get("follow_through_score")) if follow_score is not None and not (0 <= follow_score <= 100): errors.append(f"buy_permission_json[{idx}].follow_through_score must be in [0,100]") orders = parse_jsonish(harness.get("order_blueprint_json")) if isinstance(orders, list): for idx, order in enumerate(orders): if not isinstance(order, dict): continue ticker = str(order.get("ticker") or "") order_type = str(order.get("order_type") or "") validation = str(order.get("validation_status") or "") state = (permission_by_ticker.get(ticker) or {}).get("buy_permission_state") if order_type in BUY_ACTIONS and validation == "PASS" and state not in {"ALLOW_PILOT", "ALLOW_ADD_ON"}: errors.append( f"order_blueprint_json[{idx}]: BUY PASS emitted while buy_permission_state={state!r}" ) def validate_distribution_blocks(harness: dict[str, Any], errors: list[str]) -> None: distribution = as_rows(harness, "distribution_risk_json", errors) blocked = { str(row.get("ticker")) for row in distribution if row.get("anti_distribution_state") == "BLOCK_BUY" or (isinstance(row.get("distribution_risk_score"), (int, float)) and row["distribution_risk_score"] >= 70) } orders = parse_jsonish(harness.get("order_blueprint_json")) if isinstance(orders, list): for idx, order in enumerate(orders): if not isinstance(order, dict): continue if str(order.get("ticker")) in blocked and str(order.get("order_type")) in BUY_ACTIONS: errors.append(f"order_blueprint_json[{idx}]: BUY action exists for distribution BLOCK_BUY ticker") def validate_cash_raise(harness: dict[str, Any], errors: list[str]) -> None: rows = as_rows(harness, "cash_raise_plan_json", errors) for idx, row in enumerate(rows): style = row.get("execution_style") immediate = row.get("immediate_qty") rebound = row.get("rebound_wait_qty") cap_pct = row.get("immediate_qty_cap_pct") if immediate is not None and not isinstance(immediate, int): errors.append(f"cash_raise_plan_json[{idx}].immediate_qty must be integer or null") if rebound is not None and not isinstance(rebound, int): errors.append(f"cash_raise_plan_json[{idx}].rebound_wait_qty must be integer or null") if cap_pct is not None and not isinstance(cap_pct, int): errors.append(f"cash_raise_plan_json[{idx}].immediate_qty_cap_pct must be integer or null") emergency = row.get("emergency_full_sell") is True if style == "OVERSOLD_REBOUND_SELL" and not (isinstance(rebound, int) and rebound > 0) and not emergency: errors.append( f"cash_raise_plan_json[{idx}]: OVERSOLD_REBOUND_SELL requires rebound_wait_qty > 0 " f"unless emergency_full_sell=true" ) def validate_execution_quality(harness: dict[str, Any], errors: list[str]) -> None: quality_rows = as_rows(harness, "execution_quality_json", errors) quality_by_ticker = {str(row.get("ticker")): row for row in quality_rows if row.get("ticker")} for idx, row in enumerate(quality_rows): status = row.get("execution_quality_status") if status not in {"PASS", "BLOCKED", "BLOCKED_ADV_3PCT", "SPLIT_REQUIRED", "BLOCKED_SPREAD"}: errors.append(f"execution_quality_json[{idx}].execution_quality_status invalid: {status!r}") orders = parse_jsonish(harness.get("order_blueprint_json")) if isinstance(orders, list): for idx, order in enumerate(orders): if not isinstance(order, dict): continue if str(order.get("validation_status")) != "PASS": continue ticker = str(order.get("ticker") or "") quality = quality_by_ticker.get(ticker) if quality and quality.get("execution_quality_status") != "PASS": errors.append( f"order_blueprint_json[{idx}]: PASS order while execution_quality_status={quality.get('execution_quality_status')!r}" ) for idx, row in enumerate(quality_rows): split_count = row.get("split_count") if split_count is not None and not isinstance(split_count, int): errors.append(f"execution_quality_json[{idx}].split_count must be integer or null") def validate_alpha_feedback_loop(harness: dict[str, Any], errors: list[str]) -> None: payload = parse_jsonish(harness.get("alpha_feedback_json")) if not isinstance(payload, dict): errors.append("alpha_feedback_json: must be an object") return if payload.get("formula_id") != "ALPHA_FEEDBACK_LOOP_V1": errors.append(f"alpha_feedback_json.formula_id must be ALPHA_FEEDBACK_LOOP_V1, found={payload.get('formula_id')!r}") if not isinstance(payload.get("cases_analyzed"), int) or payload["cases_analyzed"] < 0: errors.append("alpha_feedback_json.cases_analyzed must be a non-negative integer") if not isinstance(payload.get("grade_count"), int) or payload["grade_count"] < 0: errors.append("alpha_feedback_json.grade_count must be a non-negative integer") if payload.get("status") not in {"ANALYZED", "DATA_MISSING", "DATA_INSUFFICIENT"}: errors.append(f"alpha_feedback_json.status invalid: {payload.get('status')!r}") if payload.get("recommended_filter_adjustments") is not None and not isinstance(payload.get("recommended_filter_adjustments"), list): errors.append("alpha_feedback_json.recommended_filter_adjustments must be a list") if payload.get("grade_summary") is not None and not isinstance(payload.get("grade_summary"), list): errors.append("alpha_feedback_json.grade_summary must be a list") if payload.get("status") == "ANALYZED" and payload.get("cases_analyzed", 0) < 10: errors.append("alpha_feedback_json: ANALYZED requires cases_analyzed >= 10") # ── [2026-05-20_HARNESS_V5] 신규 검증 함수 ────────────────────────────────── def validate_breakout_quality_gate(harness: dict[str, Any], errors: list[str]) -> None: """H6: BREAKOUT_QUALITY_GATE_V2 — 뒷박 차단 게이트 검증.""" if "breakout_quality_gate_json" not in harness: errors.append("missing V5 key: breakout_quality_gate_json") return rows = as_rows(harness, "breakout_quality_gate_json", errors) valid_gates = {"PILOT_ALLOWED", "WATCH_COOLING_OFF", "BLOCKED_LATE_CHASE"} blocked_tickers: set[str] = set() for idx, row in enumerate(rows): gate = row.get("breakout_quality_gate") score = row.get("breakout_quality_score") if gate not in valid_gates: errors.append(f"breakout_quality_gate_json[{idx}].breakout_quality_gate invalid: {gate!r}") if score is not None and not (0 <= float(score) <= 100): errors.append(f"breakout_quality_gate_json[{idx}].breakout_quality_score must be 0-100") if gate == "BLOCKED_LATE_CHASE": blocked_tickers.add(str(row.get("ticker") or "")) orders = parse_jsonish(harness.get("order_blueprint_json")) if isinstance(orders, list): for idx, order in enumerate(orders): if not isinstance(order, dict): continue if str(order.get("ticker")) in blocked_tickers and str(order.get("order_type")) in BUY_ACTIONS: errors.append( f"order_blueprint_json[{idx}]: BUY exists for BLOCKED_LATE_CHASE ticker (QEH009)" ) def validate_anti_whipsaw_gate(harness: dict[str, Any], errors: list[str]) -> None: """H7: ANTI_WHIPSAW_HOLD_GATE_V1 — 가짜 매도 차단 게이트 검증.""" if "anti_whipsaw_gate_json" not in harness: errors.append("missing V5 key: anti_whipsaw_gate_json") return rows = as_rows(harness, "anti_whipsaw_gate_json", errors) valid_gates = { "WHIPSAW_SUSPECTED", "INCONCLUSIVE", "CONFIRMED_SELL", "WHIPSAW_CONFIRMED", "WHIPSAW_WEAKENING", "WHIPSAW_AUTO_RELEASED", } whipsaw_tickers: set[str] = set() for idx, row in enumerate(rows): gate = row.get("anti_whipsaw_gate") score = row.get("anti_whipsaw_score") hold_days = row.get("anti_whipsaw_hold_days") if gate not in valid_gates: errors.append(f"anti_whipsaw_gate_json[{idx}].anti_whipsaw_gate invalid: {gate!r}") if score is not None and not (-50 <= float(score) <= 100): errors.append(f"anti_whipsaw_gate_json[{idx}].anti_whipsaw_score must be in [-50,100]") if gate == "WHIPSAW_SUSPECTED" and hold_days != 1: errors.append(f"anti_whipsaw_gate_json[{idx}]: WHIPSAW_SUSPECTED must have hold_days=1") if gate == "WHIPSAW_SUSPECTED": whipsaw_tickers.add(str(row.get("ticker") or "")) orders = parse_jsonish(harness.get("order_blueprint_json")) SELL_ACTIONS = {"SELL", "TRIM", "EXIT_100", "EXIT_FULL"} if isinstance(orders, list): for idx, order in enumerate(orders): if not isinstance(order, dict): continue ticker = str(order.get("ticker") or "") order_type = str(order.get("order_type") or "") qty = order.get("quantity") if (ticker in whipsaw_tickers and order_type in SELL_ACTIONS and str(order.get("validation_status")) == "PASS"): errors.append( f"order_blueprint_json[{idx}]: full SELL emitted for WHIPSAW_SUSPECTED ticker (QEH010)" ) reentry_rows = parse_jsonish(harness.get("anti_whipsaw_reentry_json")) if isinstance(reentry_rows, list): for idx, row in enumerate(reentry_rows): if not isinstance(row, dict): continue tier = row.get("sell_tier") if tier not in {1, 2}: errors.append( f"anti_whipsaw_reentry_json[{idx}]: sell_tier must be 1 or 2, found={tier!r} (QEH010-TIER)" ) signal = row.get("reentry_signal") if signal not in {"REENTRY_CANDIDATE"}: errors.append( f"anti_whipsaw_reentry_json[{idx}].reentry_signal invalid: {signal!r}" ) def validate_smart_cash_raise_v2(harness: dict[str, Any], errors: list[str]) -> None: """H8: SMART_CASH_RAISE_V2 — 4경로 현금확보 라우터 검증.""" if "smart_cash_raise_json" not in harness: errors.append("missing V5 key: smart_cash_raise_json") return rows = as_rows(harness, "smart_cash_raise_json", errors) valid_routes = {"ROUTE_A", "ROUTE_B", "ROUTE_C", "ROUTE_D", "NO_ACTION"} portfolio_route = str(harness.get("smart_cash_raise_route") or "NO_ACTION") if portfolio_route not in valid_routes: errors.append(f"smart_cash_raise_route invalid: {portfolio_route!r}") for idx, row in enumerate(rows): route = row.get("smart_cash_raise_route") if route not in valid_routes: errors.append(f"smart_cash_raise_json[{idx}].smart_cash_raise_route invalid: {route!r}") rebound_wait = row.get("rebound_wait_pct") if route == "ROUTE_B" and rebound_wait != 50: errors.append(f"smart_cash_raise_json[{idx}]: ROUTE_B must have rebound_wait_pct=50") if route == "ROUTE_D": rationale = str(row.get("rationale") or "") emergency = row.get("emergency_full_sell") is True stop_gate = str(row.get("stop_breach_gate") or "") if not emergency and stop_gate != "BREACH" and ( "emergency" not in rationale.lower() and "breach" not in rationale.lower() ): errors.append( f"smart_cash_raise_json[{idx}]: ROUTE_D requires emergency or breach rationale (QEH011)" ) def validate_cla_harness(harness: dict[str, Any], errors: list[str]) -> None: """CLA_HARNESS_V1: CLA 레짐 위성 실패 게이트·RAG·RS 판정 검증.""" # ── [Section 13-1] market_regime_state enum 검증 ───────────────────────── regime_state = harness.get("market_regime_state") if regime_state is not None and regime_state not in VALID_MARKET_REGIME_STATES: errors.append( f"market_regime_state invalid: {regime_state!r} " f"(expected one of {sorted(VALID_MARKET_REGIME_STATES)})" ) # ── [Section 13-3] semiconductor_cluster_json cluster_state enum 검증 ──── semi_json = parse_jsonish(harness.get("semiconductor_cluster_json")) cluster_state: str | None = None if isinstance(semi_json, dict): cluster_state = str(semi_json.get("cluster_state") or "") if cluster_state not in {"CLUSTER_HOLD_ONLY", "CLUSTER_OPEN", "CLUSTER_BLOCK", ""}: errors.append( f"semiconductor_cluster_json.cluster_state invalid: {cluster_state!r} " "(expected CLUSTER_HOLD_ONLY | CLUSTER_OPEN | CLUSTER_BLOCK)" ) # ── [Section 13-2] CLA 레짐 시 코어 종목 SELL 차단 (REGIME_CLA-1) ──────── if cluster_state == "CLUSTER_HOLD_ONLY": decisions = parse_jsonish(harness.get("decisions_json")) if isinstance(decisions, list): for idx, dec in enumerate(decisions): if not isinstance(dec, dict): continue ticker = str(dec.get("ticker") or "") final_action = str(dec.get("final_action") or "").upper() if ticker in SEMICONDUCTOR_CORE_TICKERS and final_action == "SELL": errors.append( f"decisions_json[{idx}] ticker={ticker}: SELL emitted for core " "semiconductor in CLA (CLUSTER_HOLD_ONLY) regime — REGIME_CLA-1 violation" ) # ── [Section 13-4/5] buy_permission_json per-row rs_verdict/composite_verdict enum ─ permissions = as_rows(harness, "buy_permission_json", []) # 별도 errors 수집 불필요 for idx, bp in enumerate(permissions): rv = bp.get("rs_verdict") if rv is not None and rv not in VALID_RS_VERDICTS: errors.append( f"buy_permission_json[{idx}].rs_verdict invalid: {rv!r} " f"(expected one of {sorted(VALID_RS_VERDICTS)})" ) cv = bp.get("composite_verdict") if cv is not None and cv not in VALID_COMPOSITE_VERDICTS: errors.append( f"buy_permission_json[{idx}].composite_verdict invalid: {cv!r} " f"(expected one of {sorted(VALID_COMPOSITE_VERDICTS)})" ) # SFG-1: satellite_failure_gate_json 존재 및 sfg_v1 유효값 확인 if "satellite_failure_gate_json" not in harness: errors.append("missing CLA key: satellite_failure_gate_json") else: sfg = parse_jsonish(harness.get("satellite_failure_gate_json")) if not isinstance(sfg, dict): errors.append("satellite_failure_gate_json: must be an object") else: sfg_v1 = sfg.get("sfg_v1") if sfg_v1 not in {"TRIGGERED", "CLEAR"}: errors.append(f"satellite_failure_gate_json.sfg_v1 invalid: {sfg_v1!r} (expected TRIGGERED|CLEAR)") # SFG-2: TRIGGERED이면 위성 ALLOW_* buy_permission 없어야 함 if sfg_v1 == "TRIGGERED": permissions = as_rows(harness, "buy_permission_json", errors) for idx, bp in enumerate(permissions): state = str(bp.get("buy_permission_state") or "") pos_type = str(bp.get("position_type") or bp.get("cluster_label") or "") is_satellite = pos_type.lower() in {"satellite", "위성"} or ( pos_type == "" and bp.get("core_flag") is False ) if is_satellite and state.startswith("ALLOW_"): errors.append( f"buy_permission_json[{idx}]: satellite ALLOW_* ({state!r}) emitted while sfg_v1=TRIGGERED (SFG-2)" ) # RAG-1: buy_permission_json RAG 필드 일관성 permissions = as_rows(harness, "buy_permission_json", errors) for idx, bp in enumerate(permissions): rag = bp.get("rag_v1") if rag is not None and rag not in {"PASS", "FAIL", "EXEMPT"}: errors.append(f"buy_permission_json[{idx}].rag_v1 invalid: {rag!r}") # RAG-2: FAIL인데 ALLOW_* 상태면 오류 if rag == "FAIL" and str(bp.get("buy_permission_state") or "").startswith("ALLOW_"): errors.append( f"buy_permission_json[{idx}]: rag_v1=FAIL but buy_permission_state=ALLOW_* (RAG-2)" ) # RS-1: decisions_json 내 rs_verdict 존재 여부 — data_feed 시트 컬럼으로 관리되므로 경고만 출력 decisions = parse_jsonish(harness.get("decisions_json")) if isinstance(decisions, list): missing_rs = sum( 1 for d in decisions if isinstance(d, dict) and "rs_verdict" not in d and "composite_verdict" not in d ) if missing_rs == len(decisions) and len(decisions) > 0: # 전체 누락 시에만 경고 (data_feed 시트가 아직 갱신되지 않은 경우) print(f"[WARN] RS-1: decisions_json {missing_rs}/{len(decisions)} rows lack rs_verdict (GAS re-run needed)") def validate_determinism(harness: dict[str, Any], errors: list[str]) -> None: """결정론 검증: 하네스 락 필드와 JSON 출력 일관성 확인.""" lock_json_pairs = [ ("breakout_quality_gate_lock", "breakout_quality_gate_json"), ("anti_whipsaw_gate_lock", "anti_whipsaw_gate_json"), ("alpha_lead_lock", "alpha_lead_json"), ("distribution_lock", "distribution_risk_json"), ("profit_preservation_lock", "profit_preservation_json"), ("smart_cash_raise_lock", "cash_raise_plan_json"), ("execution_quality_lock", "execution_quality_json"), ] for lock_key, json_key in lock_json_pairs: lock_val = harness.get(lock_key) if str(lock_val).lower() == "true": json_val = parse_jsonish(harness.get(json_key)) if json_val is None: errors.append(f"determinism: {lock_key}=true but {json_key} is missing") elif isinstance(json_val, list) and len(json_val) == 0: errors.append(f"determinism: {lock_key}=true but {json_key} is empty list") decision_lock = harness.get("decision_lock") if str(decision_lock).lower() == "true": decisions = parse_jsonish(harness.get("decisions_json")) trace = parse_jsonish(harness.get("decision_trace_json")) if isinstance(decisions, list) and isinstance(trace, list): final_map: dict[str, set] = {} for d in decisions: if isinstance(d, dict) and d.get("ticker"): final_map.setdefault(str(d["ticker"]), set()).add(d.get("final_action")) for idx, t in enumerate(trace): if not isinstance(t, dict): continue ticker = str(t.get("ticker") or "") selected = t.get("selected_action") allowed = final_map.get(ticker) if selected and allowed and selected not in allowed: errors.append( f"determinism: decision_trace[{idx}].selected_action={selected!r} not in decisions_json.final_action={sorted(allowed)!r}" ) def validate_brt_harness(harness: dict[str, Any], errors: list[str]) -> None: brt = parse_jsonish(harness.get("benchmark_relative_timeseries_json")) if not isinstance(brt, list): errors.append("missing/invalid BRT key: benchmark_relative_timeseries_json") else: for idx, row in enumerate(brt): if not isinstance(row, dict): errors.append(f"benchmark_relative_timeseries_json[{idx}]: must be an object") continue verdict = row.get("brt_verdict") if verdict not in VALID_BRT_VERDICTS: errors.append(f"benchmark_relative_timeseries_json[{idx}].brt_verdict invalid: {verdict!r}") index_rows = parse_jsonish(harness.get("index_relative_health_json")) if not isinstance(index_rows, list): errors.append("missing/invalid BRT key: index_relative_health_json") else: for idx, row in enumerate(index_rows): if not isinstance(row, dict): errors.append(f"index_relative_health_json[{idx}]: must be an object") continue state = row.get("relative_health_state") if state not in {"HEALTHY", "OVER_EXTENDED", "UNDERPERFORMING", "DECOUPLED", "INSUFFICIENT_DATA"}: errors.append(f"index_relative_health_json[{idx}].relative_health_state invalid: {state!r}") saqg = parse_jsonish(harness.get("saqg_json")) if not isinstance(saqg, list): errors.append("missing/invalid BRT key: saqg_json") else: for idx, row in enumerate(saqg): if not isinstance(row, dict): errors.append(f"saqg_json[{idx}]: must be an object") continue state = row.get("saqg_v1") if state not in VALID_SAQG_STATES: errors.append(f"saqg_json[{idx}].saqg_v1 invalid: {state!r}") sapg = parse_jsonish(harness.get("sapg_json")) if not isinstance(sapg, dict): errors.append("missing/invalid BRT key: sapg_json") elif sapg.get("sapg_status") not in VALID_SAPG_STATUSES: errors.append(f"sapg_json.sapg_status invalid: {sapg.get('sapg_status')!r}") permissions = parse_jsonish(harness.get("buy_permission_json")) if isinstance(permissions, list): for idx, bp in enumerate(permissions): if not isinstance(bp, dict): continue if bp.get("saqg_v1") in {"EXCLUDED"} and str(bp.get("buy_permission_state") or "").startswith("ALLOW_"): errors.append(f"buy_permission_json[{idx}]: SAQG EXCLUDED but buy_permission_state=ALLOW_*") if bp.get("saqg_v1") == "WATCHLIST_ONLY" and str(bp.get("buy_permission_state") or "").startswith("ALLOW_"): errors.append(f"buy_permission_json[{idx}]: SAQG WATCHLIST_ONLY but buy_permission_state=ALLOW_*") def main() -> int: args = sys.argv[1:] if not args or len(args) > 3: print(__doc__) return 1 json_path = Path(args[0]) strict = "--strict" in args check_mode: str | None = None if "--check" in args: idx = args.index("--check") if idx + 1 >= len(args): print("--check requires a mode: breakout_quality_gate|anti_whipsaw_gate|smart_cash_raise_v2|determinism|cla_harness|brt_harness") return 1 check_mode = args[idx + 1] if check_mode not in VALID_CHECK_MODES: print(f"Unknown --check mode: {check_mode!r}. Valid: {', '.join(sorted(VALID_CHECK_MODES))}") return 1 harness = load_harness(json_path) errors: list[str] = [] if check_mode == "breakout_quality_gate": validate_breakout_quality_gate(harness, errors) label = "BREAKOUT_QUALITY_GATE" elif check_mode == "anti_whipsaw_gate": validate_anti_whipsaw_gate(harness, errors) label = "ANTI_WHIPSAW_GATE" elif check_mode == "smart_cash_raise_v2": validate_smart_cash_raise_v2(harness, errors) label = "SMART_CASH_RAISE_V2" elif check_mode == "determinism": validate_determinism(harness, errors) label = "DETERMINISM" elif check_mode == "cla_harness": validate_cla_harness(harness, errors) label = "CLA_HARNESS" elif check_mode == "brt_harness": validate_brt_harness(harness, errors) label = "BRT_HARNESS" elif check_mode == "factor_cap": # P1-1 (v11): PA1 단일팩터 50% 캡 검증 import json as _json pa_path = json_path.parent / "predictive_alpha_engine_v2.json" if not pa_path.exists(): pa_path = json_path.parent.parent / "Temp" / "predictive_alpha_engine_v2.json" label = "FACTOR_CAP" if pa_path.exists(): pa = _json.loads(pa_path.read_text(encoding="utf-8")) audit = pa.get("factor_cap_audit", {}) max_share = float(audit.get("single_factor_max_share_pct") or 0) pac_std = float(audit.get("pac_stddev") or 0) if max_share > 50.0: errors.append(f"single_factor_max_share_pct={max_share} > 50%") if pac_std < 5.0: errors.append(f"pac_stddev={pac_std} < 5.0") else: errors.append(f"predictive_alpha_engine_v2.json not found at {pa_path}") else: # Legacy / --strict mode apex_present = any(key in harness for key in REQUIRED_APEX_KEYS) if strict: validate_required_keys(harness, errors) # Also validate V5 keys in strict mode for key in REQUIRED_V5_KEYS: if key not in harness: errors.append(f"missing V5 key: {key}") validate_alpha_feedback_loop(harness, errors) if errors: print("ALPHA EXECUTION HARNESS FAIL") for err in errors: print(f"- {err}") return 1 elif not apex_present: print("ALPHA EXECUTION HARNESS SKIPPED: APEX fields not present (use --strict after GAS Harness V5 export)") return 0 validate_buy_blocks(harness, errors) validate_distribution_blocks(harness, errors) validate_cash_raise(harness, errors) validate_execution_quality(harness, errors) validate_alpha_feedback_loop(harness, errors) # V5 checks when keys present if "breakout_quality_gate_json" in harness: validate_breakout_quality_gate(harness, errors) if "anti_whipsaw_gate_json" in harness: validate_anti_whipsaw_gate(harness, errors) if "smart_cash_raise_json" in harness: validate_smart_cash_raise_v2(harness, errors) if "satellite_failure_gate_json" in harness: validate_cla_harness(harness, errors) if "benchmark_relative_timeseries_json" in harness: validate_brt_harness(harness, errors) label = "ALPHA EXECUTION HARNESS" if errors: print(f"{label} FAIL") for err in errors: print(f"- {err}") return 1 print(f"{label} OK") return 0 if __name__ == "__main__": raise SystemExit(main())