What Is the Yield Curve?
The US government borrows money by selling Treasury bonds. It sells bonds that mature (pay back) in different timeframes — 2 years, 5 years, 10 years, 30 years. Each has its own interest rate, called a yield.
The yield curve is simply a line connecting all those yields from shortest to longest. Normally the line slopes upward — you get paid more interest for lending longer, because you're taking more risk. It makes sense: if you lend the government money for 30 years instead of 2 years, you want more compensation for that extra risk.
Here's what normal and inverted curves look like:
Why Does Inversion Matter?
When short-term rates are higher than long-term rates, it tells us something important: the bond market thinks things will get worse before they get better. Investors are rushing into long-term bonds as a safe haven — driving long-term yields down — while short-term rates stay high because the Federal Reserve hasn't cut yet.
The 2s10s spread (2-year yield minus 10-year yield) is the most-watched measure. When it goes negative — inversion — it has preceded every US recession since the 1950s, typically by 6–24 months.
Historical Inversions & Recessions
| Inversion Year | Recession Followed |
|---|---|
| 1978 | 1980 recession |
| 1988 | 1990 recession |
| 2000 | 2001 recession |
| 2006 | 2008–09 financial crisis |
| 2019 | 2020 recession |
| 2022–2023 | Ongoing monitoring |
Note: Past inversions are historical data, not guarantees of future recessions.
What OpenClaw Does
OpenClaw fetches the 2-year and 10-year Treasury yields from FRED every morning. It calculates the spread, plots the full curve shape, and sends you a briefing. If the curve inverts — or if the spread crosses a threshold you've set — you get an alert.
You set up your fi-config.yaml once, and OpenClaw handles the rest. No API keys to juggle, no spreadsheet gymnastics, no manual checking. Just automated insights delivered to your inbox.
Getting Your Free FRED API Key
FRED (Federal Reserve Economic Data) publishes Treasury yields for free. Getting started is simple:
Step-by-Step Setup
- Visit
fred.stlouisfed.org - Create a free account (30 seconds)
- Click My Account → API Keys → "Request API Key"
- Copy your key into
fi-config.yaml
Test Your Connection
Install the required library and verify FRED is working:
# Install the required library
pip install fredapi pandas pyyaml
# Verify it works
python3 -c "
from fredapi import Fred
import os
fred = Fred(api_key='your_key_here')
print('10-year yield:', fred.get_series('DGS10').iloc[-1], '%')
print('✅ FRED connection working')
"
The Yield Curve Config
Save this to fi-config.yaml in your OpenClaw project directory:
# fi-config.yaml — yield curve section
fred:
api_key: "your_fred_api_key_here"
yield_curve:
maturities:
- { label: "2-Year", series: "DGS2" }
- { label: "5-Year", series: "DGS5" }
- { label: "10-Year", series: "DGS10" }
- { label: "30-Year", series: "DGS30" }
alerts:
inversion: true # Alert when 2yr yield > 10yr yield
spread_threshold: -0.25 # Alert if 2s10s drops below -0.25%
steepening_move: 0.50 # Alert if spread widens 0.50% in a week
alerts:
email: "you@example.com"
daily_digest: true
Replace your_fred_api_key_here with the key you just requested. Replace you@example.com with your actual email.
The Yield Curve Script
This is the core OpenClaw yield curve monitor. It fetches Treasury yields from FRED, calculates the spread, and checks alert conditions:
# fi_yield_curve.py — OpenClaw Yield Curve Monitor
# Fetches Treasury yields from FRED and checks for inversion
import yaml
from fredapi import Fred
import pandas as pd
from datetime import datetime, timedelta
# ── Step 1: Load your config ──────────────────────────────────────
def load_config(path: str = "fi-config.yaml") -> dict:
with open(path) as f:
return yaml.safe_load(f)
cfg = load_config()
fred = Fred(api_key=cfg["fred"]["api_key"])
YC = cfg["yield_curve"]
print(f"🦞 OpenClaw Yield Curve Monitor")
print(f" {datetime.now().strftime('%Y-%m-%d %H:%M')}")
print("─" * 45)
# ── Step 2: Fetch current yields from FRED ────────────────────────
def fetch_yields() -> dict:
"""
Fetches the latest available yield for each maturity.
FRED updates daily — we take the most recent non-null value.
"""
yields = {}
for item in YC["maturities"]:
series = fred.get_series(item["series"], observation_start="2020-01-01")
latest = series.dropna().iloc[-1] # most recent value
yields[item["label"]] = round(float(latest), 3)
print(f" {item['label']:<10} {latest:.3f}%")
return yields
yields = fetch_yields()
# ── Step 3: Calculate the key spread ─────────────────────────────
two_year = yields.get("2-Year", 0)
ten_year = yields.get("10-Year", 0)
spread_2s10s = round(ten_year - two_year, 3)
print(f"\n 2s10s Spread: {spread_2s10s:+.3f}%")
if spread_2s10s < 0:
print(f" ⚠️ INVERTED — short-term rates exceed long-term")
else:
print(f" ✅ Normal — curve is positively sloped")
# ── Step 4: Check alert conditions ───────────────────────────────
def check_alerts(yields: dict, spread: float) -> list:
"""Check configured alert thresholds. Returns list of alert messages."""
alerts_cfg = YC.get("alerts", {})
fired = []
# Inversion alert
if alerts_cfg.get("inversion") and spread < 0:
fired.append({
"type": "INVERSION",
"message": f"2s10s spread is INVERTED at {spread:+.3f}% — 2yr ({yields['2-Year']}%) > 10yr ({yields['10-Year']}%)",
"severity": "HIGH"
})
# Threshold alert
threshold = alerts_cfg.get("spread_threshold", -0.25)
if spread < threshold:
fired.append({
"type": "SPREAD_BELOW_THRESHOLD",
"message": f"2s10s spread {spread:+.3f}% is below your threshold of {threshold:+.3f}%",
"severity": "MEDIUM"
})
return fired
alerts = check_alerts(yields, spread_2s10s)
if alerts:
print(f"\n 🚨 {len(alerts)} alert(s) triggered:")
for a in alerts:
print(f" [{a['severity']}] {a['message']}")
else:
print(f"\n ✓ No alerts triggered today")
The Weekly Trend
Understanding whether the spread is improving or worsening is crucial. This snippet pulls the last 90 days of spread history and shows you the trend:
def fetch_spread_history(days: int = 90) -> pd.Series:
"""
Fetches 2s10s spread history for the past N days.
Useful for seeing whether the inversion is deepening or recovering.
"""
start = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
two_yr = fred.get_series("DGS2", observation_start=start).dropna()
ten_yr = fred.get_series("DGS10", observation_start=start).dropna()
# Align the two series (some days one may be missing)
spread = (ten_yr - two_yr).dropna()
spread.name = "2s10s_spread"
return spread
history = fetch_spread_history(90)
# Show last 10 readings
print(f"\n📅 2s10s Spread — Last 10 trading days:")
print(f" {'Date':<14} {'Spread':>8} {'Status'}")
print(" " + "─" * 35)
for date, val in history.tail(10).items():
status = "⚠️ Inverted" if val < 0 else "✅ Normal"
print(f" {str(date.date()):<14} {val:>+7.3f}% {status}")
# Trend check: is it getting better or worse?
recent_avg = history.tail(5).mean()
previous_avg = history.tail(15).head(10).mean()
trend = "improving" if recent_avg > previous_avg else "worsening"
print(f"\n 5-day avg: {recent_avg:+.3f}% | 10-day prior avg: {previous_avg:+.3f}%")
print(f" Trend: {trend.upper()}")
Sample Morning Brief Output
Here's what your OpenClaw automated brief looks like each morning:
This brief is automatically emailed to you every morning at 7:00 AM ET, before US market open. No intervention needed — OpenClaw handles it all.
Scheduling Daily Checks
To run your yield curve monitor automatically every weekday morning (before US market open at 9:30 AM ET), use cron:
# crontab -e
# Run every weekday at 7:00 AM local time
0 7 * * 1-5 cd /path/to/openclaw && python3 fi_yield_curve.py >> fi_brief.log 2>&1
This creates a log file fi_brief.log where you can review past briefs and debug any issues.
0 7) to match your timezone and preferences. The format is minute hour day month weekday.
FRED Series Reference
These are the official FRED series IDs for US Treasury yields. They update daily after market close:
| Series ID | Description | Update Frequency |
|---|---|---|
DGS3MO |
3-Month Treasury Bill Rate | Daily |
DGS2 |
2-Year Treasury Constant Maturity Rate | Daily |
DGS5 |
5-Year Treasury Constant Maturity Rate | Daily |
DGS10 |
10-Year Treasury Constant Maturity Rate | Daily |
DGS30 |
30-Year Treasury Constant Maturity Rate | Daily |
For a complete list of all FRED series, visit fred.stlouisfed.org.