"""Routes for ETF, index, crypto, currency, and derivatives data.""" from fastapi import APIRouter, Path, Query from models import ApiResponse from route_utils import safe, validate_symbol import market_service router = APIRouter(prefix="/api/v1") # --- ETF --- # NOTE: /etf/search MUST be registered before /etf/{symbol} to avoid shadowing. @router.get("/etf/search", response_model=ApiResponse) @safe async def etf_search(query: str = Query(..., min_length=1, max_length=100)): """Search for ETFs by name or keyword.""" data = await market_service.search_etf(query) return ApiResponse(data=data) @router.get("/etf/{symbol}/info", response_model=ApiResponse) @safe async def etf_info(symbol: str = Path(..., min_length=1, max_length=20)): """Get ETF profile and info.""" symbol = validate_symbol(symbol) data = await market_service.get_etf_info(symbol) return ApiResponse(data=data) @router.get("/etf/{symbol}/historical", response_model=ApiResponse) @safe async def etf_historical( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=1, le=3650), ): """Get ETF price history.""" symbol = validate_symbol(symbol) data = await market_service.get_etf_historical(symbol, days=days) return ApiResponse(data=data) # --- Index --- @router.get("/index/available", response_model=ApiResponse) @safe async def index_available(): """List available market indices.""" data = await market_service.get_available_indices() return ApiResponse(data=data) @router.get("/index/{symbol}/historical", response_model=ApiResponse) @safe async def index_historical( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=1, le=3650), ): """Get index price history (e.g., ^GSPC, ^DJI, ^IXIC).""" symbol = validate_symbol(symbol) data = await market_service.get_index_historical(symbol, days=days) return ApiResponse(data=data) # --- Crypto --- # NOTE: /crypto/search MUST be registered before /crypto/{symbol} to avoid shadowing. @router.get("/crypto/search", response_model=ApiResponse) @safe async def crypto_search(query: str = Query(..., min_length=1, max_length=100)): """Search for cryptocurrencies.""" data = await market_service.search_crypto(query) return ApiResponse(data=data) @router.get("/crypto/{symbol}/historical", response_model=ApiResponse) @safe async def crypto_historical( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=1, le=3650), ): """Get cryptocurrency price history (e.g., BTC-USD).""" symbol = validate_symbol(symbol) data = await market_service.get_crypto_historical(symbol, days=days) return ApiResponse(data=data) # --- Currency --- @router.get("/currency/{symbol}/historical", response_model=ApiResponse) @safe async def currency_historical( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=1, le=3650), ): """Get forex price history (e.g., EURUSD, USDSEK).""" symbol = validate_symbol(symbol) data = await market_service.get_currency_historical(symbol, days=days) return ApiResponse(data=data) # --- Derivatives --- @router.get("/options/{symbol}/chains", response_model=ApiResponse) @safe async def options_chains(symbol: str = Path(..., min_length=1, max_length=20)): """Get options chain data.""" symbol = validate_symbol(symbol) data = await market_service.get_options_chains(symbol) return ApiResponse(data=data) @router.get("/futures/{symbol}/historical", response_model=ApiResponse) @safe async def futures_historical( symbol: str = Path(..., min_length=1, max_length=20), days: int = Query(default=365, ge=1, le=3650), ): """Get futures price history.""" symbol = validate_symbol(symbol) data = await market_service.get_futures_historical(symbol, days=days) return ApiResponse(data=data) @router.get("/futures/{symbol}/curve", response_model=ApiResponse) @safe async def futures_curve(symbol: str = Path(..., min_length=1, max_length=20)): """Get futures term structure/curve.""" symbol = validate_symbol(symbol) data = await market_service.get_futures_curve(symbol) return ApiResponse(data=data)