- 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>