from __future__ import annotations import argparse import json import subprocess from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] DEFAULT_JSON = ROOT / "GatherTradingData.json" DEFAULT_REBOUND = ROOT / "Temp" / "rebound_sell_efficiency_v1.json" DEFAULT_OUT = ROOT / "Temp" / "smart_cash_recovery_v5.json" TEMP_V4 = ROOT / "Temp" / "_smart_cash_recovery_v4_for_v5.json" def _load(path: Path) -> dict[str, Any]: if not path.exists(): return {} try: obj = json.loads(path.read_text(encoding="utf-8")) except Exception: return {} return obj if isinstance(obj, dict) else {} def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--json", default=str(DEFAULT_JSON)) ap.add_argument("--rebound", default=str(DEFAULT_REBOUND)) ap.add_argument("--out", default=str(DEFAULT_OUT)) args = ap.parse_args() jp = Path(args.json) rp = Path(args.rebound) op = Path(args.out) if not jp.is_absolute(): jp = ROOT / jp if not rp.is_absolute(): rp = ROOT / rp if not op.is_absolute(): op = ROOT / op cmd = [ "python", "tools/build_smart_cash_recovery_v4.py", "--json", str(jp), "--rebound", str(rp), "--out", str(TEMP_V4), ] proc = subprocess.run(cmd, cwd=str(ROOT), text=True, capture_output=True, encoding="utf-8", errors="replace") if proc.returncode != 0: print((proc.stdout or "") + (proc.stderr or "")) return proc.returncode v4 = _load(TEMP_V4) result = dict(v4) selected_combo = [] for row in result.get("selected_sell_combo") or []: if isinstance(row, dict): enriched = dict(row) execution_allowed = bool(result.get("execution_allowed")) enriched["hts_candidate"] = execution_allowed enriched["ledger_only"] = not execution_allowed enriched["execution_allowed"] = execution_allowed enriched["execution_block_reason"] = ( "VALUE_DAMAGE_GT_10" if not execution_allowed and float(result.get("value_damage_pct_avg") or 0.0) > 10.0 else ("SHORTFALL_UNCOVERED" if not bool(result.get("cash_shortfall_covered")) else "") ) selected_combo.append(enriched) if selected_combo: result["selected_sell_combo"] = selected_combo result["formula_id"] = "SMART_CASH_RECOVERY_V5" result["upgraded_from"] = "SMART_CASH_RECOVERY_V4" result["v5_enforced"] = { "value_damage_pct_avg_max": 10.0, "cash_shortfall_covered_required": True, "hts_candidate_rows": len([r for r in selected_combo if r.get("hts_candidate")]), "ledger_only_rows": len([r for r in selected_combo if r.get("ledger_only")]), } op.parent.mkdir(parents=True, exist_ok=True) op.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps(result, ensure_ascii=False, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())