from __future__ import annotations import json import sys from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] DEFAULT_JSON = ROOT / "GatherTradingData.json" VALID_EXECUTION_STATUSES = {"PROPOSAL_ONLY", "EXECUTION_WAIT", "EXECUTION_READY"} REQUIRED_FIELDS = { "account", "ticker", "name", "proposal_type", "proposed_limit_price_krw", "proposed_price_basis", "proposed_quantity", "proposed_quantity_basis", "stop1_price_krw", "stop1_quantity", "stop2_price_krw", "stop2_quantity", "stop3_price_krw", "stop3_quantity", "tp1_price_krw", "tp1_quantity", "tp2_price_krw", "tp2_quantity", "tp3_price_krw", "tp3_quantity", "execution_status", "block_reason", } 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_bool(value: Any) -> bool | None: if isinstance(value, bool): return value if isinstance(value, str): lowered = value.strip().lower() if lowered in {"true", "1", "y", "yes"}: return True if lowered in {"false", "0", "n", "no"}: return False return None 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 None def validate_rows(rows: Any) -> list[str]: errors: list[str] = [] if not isinstance(rows, list): return ["proposal_reference_json: must be a list"] for idx, row in enumerate(rows): if not isinstance(row, dict): errors.append(f"proposal_reference_json[{idx}]: must be an object") continue missing = sorted(REQUIRED_FIELDS - set(row)) if missing: errors.append(f"proposal_reference_json[{idx}] missing fields: {missing}") for field in ( "proposed_limit_price_krw", "proposed_quantity", "stop1_price_krw", "stop1_quantity", "stop2_price_krw", "stop2_quantity", "stop3_price_krw", "stop3_quantity", "tp1_price_krw", "tp1_quantity", "tp2_price_krw", "tp2_quantity", "tp3_price_krw", "tp3_quantity", ): value = row.get(field) if value is not None and not isinstance(value, int): errors.append(f"proposal_reference_json[{idx}].{field}: must be integer or null") status = row.get("execution_status") if status is not None and status not in VALID_EXECUTION_STATUSES: errors.append( f"proposal_reference_json[{idx}].execution_status invalid: {status!r}" ) return errors def main() -> int: args = sys.argv[1:] require = "--require" in args positional = [arg for arg in args if arg != "--require"] path = Path(positional[0]) if positional else DEFAULT_JSON if not path.is_absolute(): path = ROOT / path harness = load_harness(path) lock = parse_bool(harness.get("proposal_reference_lock")) raw = harness.get("proposal_reference_json") if raw is None: if require: print("PROPOSAL REFERENCE FAIL") print("- proposal_reference_json missing") return 1 print("PROPOSAL REFERENCE SKIP: proposal_reference_json not present yet") return 0 if lock is not True: if require: print("PROPOSAL REFERENCE FAIL") print(f"- proposal_reference_lock must be true when proposal_reference_json exists, found={harness.get('proposal_reference_lock')!r}") return 1 print("PROPOSAL REFERENCE SKIP: proposal_reference_json present but proposal_reference_lock is not true") return 0 try: rows = parse_jsonish(raw) except Exception as exc: print("PROPOSAL REFERENCE FAIL") print(f"- proposal_reference_json invalid JSON: {exc}") return 1 errors = validate_rows(rows) if errors: print("PROPOSAL REFERENCE FAIL") for err in errors: print(f"- {err}") return 1 print(f"PROPOSAL REFERENCE OK: rows={len(rows)}") return 0 if __name__ == "__main__": raise SystemExit(main())