"""Routes for quantitative analysis: risk metrics, CAPM, normality, unit root.""" from fastapi import APIRouter, Path, Query from models import ApiResponse from route_utils import safe, validate_symbol import quantitative_service router = APIRouter(prefix="/api/v1") @router.get("/stock/{symbol}/performance", response_model=ApiResponse) @safe async def stock_performance( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=30, le=3650), ): """Performance metrics: Sharpe, Sortino, max drawdown, volatility.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_performance_metrics(symbol, days=days) return ApiResponse(data=data) @router.get("/stock/{symbol}/capm", response_model=ApiResponse) @safe async def stock_capm(symbol: str = Path(..., min_length=1, max_length=20)): """CAPM: beta, alpha, systematic and idiosyncratic risk.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_capm(symbol) return ApiResponse(data=data) @router.get("/stock/{symbol}/normality", response_model=ApiResponse) @safe async def stock_normality( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=30, le=3650), ): """Normality tests: Jarque-Bera, Shapiro-Wilk on returns.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_normality_test(symbol, days=days) return ApiResponse(data=data) @router.get("/stock/{symbol}/unitroot", response_model=ApiResponse) @safe async def stock_unitroot( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=30, le=3650), ): """Unit root tests: ADF, KPSS for stationarity.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_unitroot_test(symbol, days=days) return ApiResponse(data=data) # --- Extended Quantitative (Group J) --- @router.get("/stock/{symbol}/sortino", response_model=ApiResponse) @safe async def stock_sortino( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=30, le=3650), ): """Sortino ratio -- risk-adjusted return penalizing only downside deviation.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_sortino(symbol, days=days) return ApiResponse(data=data) @router.get("/stock/{symbol}/omega", response_model=ApiResponse) @safe async def stock_omega( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=30, le=3650), ): """Omega ratio -- probability-weighted gain vs loss.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_omega(symbol, days=days) return ApiResponse(data=data) @router.get("/stock/{symbol}/rolling/{stat}", response_model=ApiResponse) @safe async def stock_rolling( symbol: str = Path(..., min_length=1, max_length=20), stat: str = Path(..., pattern="^(variance|stdev|mean|skew|kurtosis|quantile)$"), days: int = Query(default=365, ge=30, le=3650), window: int = Query(default=30, ge=5, le=252), ): """Rolling statistics: variance, stdev, mean, skew, kurtosis, quantile.""" symbol = validate_symbol(symbol) data = await quantitative_service.get_rolling_stat(symbol, stat=stat, days=days, window=window) return ApiResponse(data=data)