Files
All-in-one-Financial-Analysis/atlas-terminal/server/services/oecd_cycle.py
T
shawnkim1997andClaude Opus 4.6 51cbaf7f8d feat: major codebase audit — 21 routers, 37 services, 12 pages fully documented
- Add missing numpy, scipy, dbnomics to requirements.txt (fixes ImportError on fresh install)
- Sync claude.md with actual codebase: §3 file structure (37 services, 21 routers),
  §5 API endpoints (92 routes), §6 frontend pages (12), §13 TODO status
- Update README.md with current architecture (92 API routes, 21 routers, 37 services),
  multi-asset overview, research grid, macro dashboard, screener+backtest,
  multi-jurisdiction filings, and 2026-03-26 changelog entry
- Add new routers: dart, edinet, fmp, macro, research
- Add new services: cache, dart_fetcher, dart_filing_service, economic_calendar,
  ecos_fetcher, edinet_filing_service, fmp_client, global_macro_quadrant,
  kpi_history_service, macro_cycle, macro_fetcher, oecd_cycle,
  peer_comparison_service, research_dashboard, smart_money_service, yield_fx_service
- Add new frontend: macro page, screener+backtest, research grid components,
  overview (Equity/ETF/Commodity), filings (SEC/DART/EDINET), error boundaries
- Remove 6 unused services: copilot_context, crypto_fetcher, fx_fetcher,
  gemini_analysis, market_data, technical_analysis
- Remove obsolete docs: .agent/, AGENT.md, ATLAS_EVALUATION.md, docs/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 21:39:07 +00:00

129 lines
3.8 KiB
Python

"""OECD leading indicator service via DBnomics."""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional
from server.services.cache import cached
OECD_COUNTRIES = {
"USA": "United States",
"EA": "Euro Area",
"GBR": "United Kingdom",
"DEU": "Germany",
"FRA": "France",
"JPN": "Japan",
"KOR": "South Korea",
"CHN": "China",
"IND": "India",
"BRA": "Brazil",
}
_DATASET = "DSD_STES@DF_CLI"
def _fetch_cli(iso: str, limit: int = 36) -> List[Dict[str, Any]]:
"""Fetch CLI (normalized) for a country from DBnomics."""
code = f"{iso}.M.LI.IX._Z.NOR.IX._Z.H"
return _fetch_dbn(_DATASET, code, limit)
def _fetch_bci(iso: str) -> Optional[float]:
code = f"{iso}.M.BCICP.IX._Z.AA.IX._Z.H"
series = _fetch_dbn(_DATASET, code, 3)
return series[-1]["value"] if series else None
def _fetch_cci(iso: str) -> Optional[float]:
code = f"{iso}.M.CCICP.IX._Z.AA.IX._Z.H"
series = _fetch_dbn(_DATASET, code, 3)
return series[-1]["value"] if series else None
def _fetch_dbn(dataset: str, series_code: str, limit: int) -> List[Dict[str, Any]]:
"""Fetch from DBnomics, with REST fallback."""
try:
import dbnomics
df = dbnomics.fetch_series(
provider_code="OECD", dataset_code=dataset, series_code=series_code,
)
if df is None or df.empty:
return []
df = df.dropna(subset=["value"]).tail(limit)
return [
{"date": str(row["period"])[:7], "value": round(float(row["value"]), 4)}
for _, row in df.iterrows()
]
except Exception:
return _fetch_dbn_rest(dataset, series_code, limit)
def _fetch_dbn_rest(dataset: str, series_code: str, limit: int) -> List[Dict[str, Any]]:
"""REST fallback when the dbnomics package fails."""
import requests
url = f"https://api.db.nomics.world/v22/series/OECD/{dataset}/{series_code}?observations=1&limit=1"
try:
resp = requests.get(url, timeout=15)
data = resp.json()
docs = data.get("series", {}).get("docs", [])
if not docs:
return []
obs = docs[0]
periods = obs.get("period", [])
values = obs.get("value", [])
result = []
for p, v in zip(periods[-limit:], values[-limit:]):
if v is not None and str(v) != "NA":
try:
result.append({"date": str(p)[:7], "value": round(float(v), 4)})
except (TypeError, ValueError):
continue
return result
except Exception:
return []
def _direction(series: List[Dict[str, Any]]) -> str:
if len(series) < 2:
return "unknown"
last = series[-1]["value"]
prev = series[-2]["value"]
if last > 100 and last > prev:
return "expanding"
if last < 100 and last < prev:
return "contracting"
if last > prev:
return "recovering"
return "slowing"
@cached("oecd_cli_snapshot", ttl_seconds=21600)
def get_oecd_cli_snapshot() -> Dict[str, Any]:
"""Fetch OECD CLI for major economies via DBnomics."""
countries: List[Dict[str, Any]] = []
for iso, name in OECD_COUNTRIES.items():
series = _fetch_cli(iso, limit=36)
cli_current = series[-1]["value"] if series else None
cli_prev = series[-2]["value"] if len(series) >= 2 else None
bci = _fetch_bci(iso)
cci = _fetch_cci(iso)
countries.append({
"country": name,
"iso": iso,
"cli_current": cli_current,
"cli_prev": cli_prev,
"direction": _direction(series) if series else "unknown",
"bci": bci,
"cci": cci,
"series": series,
})
return {
"updated_at": datetime.utcnow().isoformat() + "Z",
"countries": countries,
}