fix: JSON serialization crash on NaN floats in cycle context snapshot

- portfolio_context.py: add _safe_float() helper (converts NaN/Inf → None);
  use .squeeze().dropna() on yfinance closes before computing moves;
  guard division by checking closes.iloc[-2] != 0
- cycle.py: add _sanitize_floats() recursive sanitizer applied to the full
  snapshot before FastAPI serializes it — catches any remaining NaN from
  iv_rank, technical indicators, or other sources

Fixes 500 on GET /api/cycle/contexts/{run_id} when yfinance returns NaN
weekend data for portfolio positions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-21 19:48:19 +02:00
parent 96327bec8f
commit d4bc4e6624
2 changed files with 38 additions and 10 deletions

View File

@@ -91,13 +91,25 @@ def list_context_snapshots(limit: int = 30):
return {"snapshots": list_cycle_context_snapshots(limit=limit)}
def _sanitize_floats(obj):
"""Recursively replace NaN/Inf floats with None for JSON-safe serialization."""
import math
if isinstance(obj, float):
return None if (math.isnan(obj) or math.isinf(obj)) else obj
if isinstance(obj, dict):
return {k: _sanitize_floats(v) for k, v in obj.items()}
if isinstance(obj, list):
return [_sanitize_floats(v) for v in obj]
return obj
@router.get("/contexts/{run_id}")
def get_context_snapshot(run_id: str):
"""Return the full context snapshot for a given cycle run_id."""
snap = get_cycle_context_snapshot(run_id)
if not snap:
raise HTTPException(404, "Snapshot non trouvé pour ce cycle")
return snap
return _sanitize_floats(snap)
@router.get("/ai-calls/{run_id}")