Files
All-in-one-Financial-Analysis/atlas-terminal/server/services/yield_fx_service.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

120 lines
3.1 KiB
Python

"""US 10Y minus peer 10Y spread vs FX pairs (yfinance)."""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional
import pandas as pd
from server.services.macro_fetcher import fetch_fred_series
_FRED_US10Y = "DGS10"
_FRED_JP10Y = "IRLTLT01JPM156N"
_FRED_EZ10Y = "IRLTLT01EZM156N"
_FRED_KR10Y = "IRLTLT01KRM156N"
_YF_PAIR = {
"usdjpy": "USDJPY=X",
"eurusd": "EURUSD=X",
"usdkrw": "KRW=X",
}
_PEER_FRED = {
"usdjpy": _FRED_JP10Y,
"eurusd": _FRED_EZ10Y,
"usdkrw": _FRED_KR10Y,
}
def _fred_to_daily_series(series_id: str) -> pd.Series:
rows = fetch_fred_series(series_id)
if not rows:
return pd.Series(dtype=float)
df = pd.DataFrame(rows)
df["dt"] = pd.to_datetime(df["date"])
df = df.sort_values("dt").drop_duplicates("dt", keep="last")
s = pd.Series(df["value"].astype(float).values, index=df["dt"])
return s.sort_index()
def _fred_to_monthly_end(series_id: str) -> pd.Series:
s = _fred_to_daily_series(series_id)
if s.empty:
return pd.Series(dtype=float)
# Monthly FRED series: normalize to month-end
m = s.resample("ME").last().dropna()
return m
def _us10y_monthly() -> pd.Series:
daily = _fred_to_daily_series(_FRED_US10Y)
if daily.empty:
return pd.Series(dtype=float)
return daily.resample("ME").last().dropna()
def _yf_fx(ticker: str, period: str = "2y") -> pd.Series:
try:
import yfinance as yf
t = yf.Ticker(ticker)
hist = t.history(period=period, auto_adjust=True)
if hist is None or hist.empty or "Close" not in hist:
return pd.Series(dtype=float)
s = hist["Close"].astype(float)
s.index = pd.to_datetime(s.index).tz_localize(None)
return s.resample("ME").last().dropna()
except Exception:
return pd.Series(dtype=float)
def get_yield_fx_pair(pair: str) -> Dict[str, Any]:
"""Return aligned monthly spread (US10Y - peer10Y) vs FX."""
key = (pair or "").strip().lower()
out: Dict[str, Any] = {
"pair": key,
"updated_at": datetime.utcnow().isoformat() + "Z",
"series": [],
"error": None,
}
if key not in _YF_PAIR:
out["error"] = "invalid_pair"
return out
peer_id = _PEER_FRED.get(key)
if not peer_id:
out["error"] = "missing_peer"
return out
us_m = _us10y_monthly()
peer_m = _fred_to_monthly_end(peer_id)
if us_m.empty or peer_m.empty:
out["error"] = "fred_empty"
return out
spread = us_m.sub(peer_m, fill_value=None)
spread = spread.dropna()
fx = _yf_fx(_YF_PAIR[key])
if fx.empty:
out["error"] = "fx_empty"
return out
df = pd.DataFrame({"spread_pct": spread}).join(
pd.DataFrame({"fx": fx}), how="inner"
)
df = df.dropna()
df = df.sort_index()
series: List[Dict[str, Any]] = []
for idx, row in df.iterrows():
series.append({
"date": idx.strftime("%Y-%m-%d"),
"spread_pct": round(float(row["spread_pct"]), 4),
"fx": round(float(row["fx"]), 6),
})
out["series"] = series
return out