71 lines
2.1 KiB
Python
71 lines
2.1 KiB
Python
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())
|