"""Macro economic data via OpenBB FRED provider.""" import asyncio import logging from typing import Any from openbb import obb logger = logging.getLogger(__name__) PROVIDER = "fred" # Key FRED series IDs SERIES = { "fed_funds_rate": "FEDFUNDS", "us_10y_treasury": "DGS10", "us_2y_treasury": "DGS2", "cpi_yoy": "CPIAUCSL", "unemployment_rate": "UNRATE", "gdp_growth": "A191RL1Q225SBEA", "sp500": "SP500", "vix": "VIXCLS", } def _to_dicts(result: Any) -> list[dict[str, Any]]: if result is None or result.results is None: return [] if isinstance(result.results, list): return [ item.model_dump() if hasattr(item, "model_dump") else vars(item) for item in result.results ] if hasattr(result.results, "model_dump"): return [result.results.model_dump()] return [vars(result.results)] async def get_series(series_id: str, limit: int = 10) -> list[dict[str, Any]]: """Get a FRED time series by ID.""" try: result = await asyncio.to_thread( obb.economy.fred_series, symbol=series_id, limit=limit, provider=PROVIDER, ) items = _to_dicts(result) for item in items: if "date" in item and not isinstance(item["date"], str): item = {**item, "date": str(item["date"])} return items except Exception: logger.warning("Failed to fetch FRED series %s", series_id, exc_info=True) return [] async def get_macro_overview() -> dict[str, Any]: """Get a summary of key macro indicators.""" tasks = { name: get_series(series_id, limit=1) for name, series_id in SERIES.items() } results = await asyncio.gather(*tasks.values(), return_exceptions=True) overview: dict[str, Any] = {} for name, result in zip(tasks.keys(), results): if isinstance(result, BaseException): logger.warning("Failed to fetch %s: %s", name, result) overview[name] = None elif result and len(result) > 0: entry = result[0] overview[name] = { "value": entry.get("value"), "date": str(entry.get("date", "")), } else: overview[name] = None return overview