import math from fastapi import APIRouter, Query from services.database import ( get_portfolio_exposure, get_pnl_timeline, get_risk_clusters, get_pattern_correlations, compute_kelly_sizing, get_risk_dashboard, ) router = APIRouter(prefix="/api/risk", tags=["risk"]) def _sanitize(obj): """Replace NaN/Inf with None recursively for JSON compliance.""" if isinstance(obj, dict): return {k: _sanitize(v) for k, v in obj.items()} if isinstance(obj, list): return [_sanitize(v) for v in obj] if isinstance(obj, float) and (math.isnan(obj) or math.isinf(obj)): return None return obj @router.get("/exposure") def portfolio_exposure(): """Exposure by asset class + risk factor for open positions.""" return get_portfolio_exposure() @router.get("/timeline") def pnl_timeline(days: int = Query(default=90, ge=7, le=365)): """Daily aggregated P&L timeline for equity curve.""" return {"timeline": get_pnl_timeline(days=days)} @router.get("/clusters") def risk_clusters(): """Risk factor clustering + saturation detection + prompt context.""" return get_risk_clusters() @router.get("/correlations") def pattern_correlations(): """Pearson correlation matrix between patterns (mature trades only).""" return get_pattern_correlations() @router.get("/kelly/{pattern_id}") def kelly_sizing( pattern_id: str, capital: float = Query(default=10000.0, ge=100, le=10_000_000), fractional: float = Query(default=0.33, ge=0.1, le=1.0), ): """Fractional Kelly position sizing for a pattern.""" return compute_kelly_sizing(pattern_id=pattern_id, capital_available=capital, fractional=fractional) @router.get("/dashboard") def risk_dashboard(): """Full portfolio risk snapshot: concentration, diversification, expected drawdown, recommendation.""" return get_risk_dashboard() @router.get("/radar") def risk_radar(): """5-axis risk radar (Concentration/Volatility/Correlation/Exposure/Drawdown) for the real portfolio.""" from services.portfolio_risk import compute_real_portfolio_risk_radar return _sanitize(compute_real_portfolio_risk_radar())