from __future__ import annotations import argparse import datetime as dt import json from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] DEFAULT_JSON = ROOT / "GatherTradingData.json" DEFAULT_OUT = ROOT / "Temp" / "strategy_decision_result_v3.json" def _as_obj(value: Any) -> dict[str, Any]: if isinstance(value, dict): return value if isinstance(value, str): try: parsed = json.loads(value) return parsed if isinstance(parsed, dict) else {} except Exception: return {} return {} def _as_rows(value: Any) -> list[dict[str, Any]]: if isinstance(value, list): return [v for v in value if isinstance(v, dict)] if isinstance(value, str): try: parsed = json.loads(value) return _as_rows(parsed) except Exception: return [] return [] def _load_json(path: Path) -> dict[str, Any]: try: data = json.loads(path.read_text(encoding="utf-8")) except Exception: return {} return data if isinstance(data, dict) else {} def _safe_bool(v: Any, default: bool = False) -> bool: if isinstance(v, bool): return v if isinstance(v, (int, float)): return bool(v) if isinstance(v, str): return v.strip().lower() in ("1", "true", "y", "yes") return default def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--json", default=str(DEFAULT_JSON)) ap.add_argument("--out", default=str(DEFAULT_OUT)) args = ap.parse_args() json_path = Path(args.json) if not json_path.is_absolute(): json_path = ROOT / json_path out_path = Path(args.out) if not out_path.is_absolute(): out_path = ROOT / out_path payload = _load_json(json_path) if not payload: print("STRATEGY_DECISION_V3_FAIL: input json missing/invalid") return 1 data = _as_obj(payload.get("data")) apex = _as_obj(payload.get("hApex")) hctx = _as_obj(data.get("_harness_context")) h = dict(hctx) h.update(apex) export_gate_obj = _as_obj(h.get("export_gate_json")) export_allowed = export_gate_obj.get("hts_entry_allowed") if export_allowed is True: export_gate = "PASS" elif export_allowed is False: export_gate = "BLOCKED" else: export_gate = "REVIEW_ONLY" blueprint = _as_rows(h.get("order_blueprint_json")) shadow = [row for row in blueprint if str(row.get("validation_status", "")).upper() != "PASS"] oq_obj = _as_obj(h.get("outcome_quality_score_v1_json")) oq_gate = str(oq_obj.get("gate") or "") buy_allowed = export_allowed is True and oq_gate != "CRITICAL_MODE" sell_allowed = "ALLOW" if export_allowed is True else "REVIEW_ONLY" out = { "schema_version": "strategy-decision-result-v3", "as_of": dt.datetime.now(dt.timezone.utc).isoformat(), "source_truth": { "price_basis": "HARNESS_ONLY", "cash_basis": "D_PLUS_2" }, "route": { "route_id": str(h.get("request_route") or "PIPELINE_EOD_BATCH"), "serving_mode": "JSON_ONLY_LLM_EXPLAIN" }, "global_gates": { "export_gate": export_gate, "llm_numeric_generation_allowed": False }, "portfolio_decision": { "buy_allowed": _safe_bool(buy_allowed, False), "sell_allowed": sell_allowed }, "order_blueprint": blueprint, "shadow_ledger": shadow, "audit_ledger": [ { "formula_id": "STRATEGY_DECISION_RESULT_V3", "source_json": str(json_path.name), "blueprint_rows": len(blueprint), "shadow_rows": len(shadow) } ] } out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(json.dumps(out, ensure_ascii=False, indent=2), encoding="utf-8") print(f"STRATEGY_DECISION_V3_OK: {out_path}") return 0 if __name__ == "__main__": raise SystemExit(main())