feat: add 67 new endpoints across 10 feature groups
Prerequisite refactor: - Consolidate duplicate _to_dicts into shared obb_utils.to_list - Add fetch_historical and first_or_empty helpers to obb_utils Phase 1 - Local computation (no provider risk): - Group I: 12 technical indicators (ATR, ADX, Stoch, OBV, Ichimoku, Donchian, Aroon, CCI, Keltner, Fibonacci, A/D, Volatility Cones) - Group J: Sortino, Omega ratios + rolling stats (variance, stdev, mean, skew, kurtosis, quantile via generic endpoint) - Group H: ECB currency reference rates Phase 2 - FRED/Federal Reserve providers: - Group C: 10 fixed income endpoints (treasury rates, yield curve, auctions, TIPS, EFFR, SOFR, HQM, commercial paper, spot rates, spreads) - Group D: 11 economy endpoints (CPI, GDP, unemployment, PCE, money measures, CLI, HPI, FRED search, balance of payments, Fed holdings, FOMC documents) - Group E: 5 survey endpoints (Michigan, SLOOS, NFP, Empire State, BLS search) Phase 3 - SEC/stockgrid/FINRA providers: - Group B: 4 equity fundamental endpoints (management, dividends, SEC filings, company search) - Group A: 4 shorts/dark pool endpoints (short volume, FTD, short interest, OTC dark pool) - Group F: 3 index/ETF enhanced (S&P 500 multiples, index constituents, ETF N-PORT) Phase 4 - Regulators: - Group G: 5 regulatory endpoints (COT report, COT search, SEC litigation, institution search, CIK mapping) Security hardening: - Service-layer allowlists for all getattr dynamic dispatch - Regex validation on date, country, security_type, form_type params - Exception handling in fetch_historical - Callable guard on rolling stat dispatch Total: 32 existing + 67 new = 99 endpoints, all free providers.
This commit is contained in:
107
routes_economy.py
Normal file
107
routes_economy.py
Normal file
@@ -0,0 +1,107 @@
|
||||
"""Routes for expanded economy data."""
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from models import ApiResponse
|
||||
from route_utils import safe
|
||||
import economy_service
|
||||
|
||||
router = APIRouter(prefix="/api/v1")
|
||||
|
||||
|
||||
# --- Structured macro indicators (Group D) ---
|
||||
|
||||
|
||||
@router.get("/macro/cpi", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_cpi(country: str = Query(default="united_states", max_length=50, pattern=r"^[a-z_]+$")):
|
||||
"""Consumer Price Index (multi-country)."""
|
||||
data = await economy_service.get_cpi(country=country)
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/macro/gdp", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_gdp(
|
||||
type: str = Query(default="real", pattern="^(nominal|real|forecast)$"),
|
||||
):
|
||||
"""GDP: nominal, real, or forecast."""
|
||||
data = await economy_service.get_gdp(gdp_type=type)
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/macro/unemployment", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_unemployment(
|
||||
country: str = Query(default="united_states", max_length=50, pattern=r"^[a-z_]+$"),
|
||||
):
|
||||
"""Unemployment rate (multi-country, with demographic breakdowns)."""
|
||||
data = await economy_service.get_unemployment(country=country)
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/macro/pce", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_pce():
|
||||
"""Personal Consumption Expenditures (Fed preferred inflation measure)."""
|
||||
data = await economy_service.get_pce()
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/macro/money-measures", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_money_measures():
|
||||
"""M1/M2 money supply, currency in circulation."""
|
||||
data = await economy_service.get_money_measures()
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/macro/cli", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_cli(country: str = Query(default="united_states", max_length=50, pattern=r"^[a-z_]+$")):
|
||||
"""Composite Leading Indicator (predicts recessions 6-9 months ahead)."""
|
||||
data = await economy_service.get_composite_leading_indicator(country=country)
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/macro/house-price-index", response_model=ApiResponse)
|
||||
@safe
|
||||
async def macro_hpi(country: str = Query(default="united_states", max_length=50, pattern=r"^[a-z_]+$")):
|
||||
"""Housing price index (multi-country)."""
|
||||
data = await economy_service.get_house_price_index(country=country)
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
# --- Economy data endpoints ---
|
||||
|
||||
|
||||
@router.get("/economy/fred-search", response_model=ApiResponse)
|
||||
@safe
|
||||
async def economy_fred_search(query: str = Query(..., min_length=1, max_length=100)):
|
||||
"""Search FRED series by keyword (800K+ economic series)."""
|
||||
data = await economy_service.fred_search(query=query)
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/economy/balance-of-payments", response_model=ApiResponse)
|
||||
@safe
|
||||
async def economy_bop():
|
||||
"""Balance of payments: current/capital/financial account."""
|
||||
data = await economy_service.get_balance_of_payments()
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/economy/central-bank-holdings", response_model=ApiResponse)
|
||||
@safe
|
||||
async def economy_fed_holdings():
|
||||
"""Fed SOMA portfolio: holdings by security type."""
|
||||
data = await economy_service.get_central_bank_holdings()
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.get("/economy/fomc-documents", response_model=ApiResponse)
|
||||
@safe
|
||||
async def economy_fomc(year: int = Query(default=None, ge=2000, le=2099)):
|
||||
"""FOMC meeting documents: minutes, projections, press conferences."""
|
||||
data = await economy_service.get_fomc_documents(year=year)
|
||||
return ApiResponse(data=data)
|
||||
Reference in New Issue
Block a user