FRED Rate Curve

abaquant.rates provides a provider-neutral rate curve API. This notebook uses ManualRateProvider with Treasury-like rates so it runs deterministically without a FRED API key or network access. If you set the FRED_API_KEY environment variable, the same factory can request live FRED Treasury constant-maturity observations by passing provider="fred".

Sections:

  1. Build a deterministic Treasury-like curve

  2. Interpolate rates and discount factors

  3. Use a curve-derived rate inside an option model

  4. Visualize the curve

  5. Optional: live FRED branch

Setup

import abaquant
print(f"AbaQuant version: {abaquant.__version__}")
AbaQuant version: 1.0.0rc1
import os
from pathlib import Path

from abaquant.derivatives.models import BlackScholesMertonModel
from abaquant.rates import ManualRateProvider, get_rate_curve

1. Build a deterministic Treasury-like curve

Rates are supplied as {maturity_in_years: annual_decimal_rate}.

curve = get_rate_curve(
    provider=ManualRateProvider(
        {
            1.0 / 12.0: 0.0520,
            0.25: 0.0505,
            0.50: 0.0485,
            1.00: 0.0460,
            2.00: 0.0430,
            5.00: 0.0410,
            10.00: 0.0425,
            30.00: 0.0440,
        }
    )
)
curve.as_frame().head()
maturity_years annual_rate observation_date series_id provider_name raw_value_percent
0 0.083333 0.0520 2026-08-24 MANUAL_0.0833333Y manual 5.20
1 0.250000 0.0505 2026-08-24 MANUAL_0.25Y manual 5.05
2 0.500000 0.0485 2026-08-24 MANUAL_0.5Y manual 4.85
3 1.000000 0.0460 2026-08-24 MANUAL_1Y manual 4.60
4 2.000000 0.0430 2026-08-24 MANUAL_2Y manual 4.30

2. Interpolate rates and discount factors

zero_rate() uses linear interpolation by default; discount_factor() uses continuous compounding by default.

six_month_rate = curve.zero_rate(0.5)
one_year_rate = curve.zero_rate(1.0)
two_year_df = curve.discount_factor(2.0)

print(f"6-month rate:          {six_month_rate:.6f}")
print(f"1-year rate:            {one_year_rate:.6f}")
print(f"2-year discount factor: {two_year_df:.6f}")
6-month rate:          0.048500
1-year rate:            0.046000
2-year discount factor: 0.917594

3. Use a curve-derived rate inside an option model

Instead of hard-coding a risk-free rate, pull the curve’s interpolated 1-year zero rate straight into BlackScholesMertonModel.

model = BlackScholesMertonModel(
    spot_price=100.0,
    strike_price=105.0,
    maturity_years=1.0,
    risk_free_rate=one_year_rate,
    volatility=0.20,
)
report = model.diagnostics(option_type="call")

print(f"Price:            {report.price:.4f}")
print(f"Intrinsic value:  {report.intrinsic_value:.4f}")
print(f"Extrinsic value:  {report.extrinsic_value:.4f}")
print(f"Moneyness:        {report.moneyness:.4f}")
print(f"Break-even price: {report.break_even_price:.4f}")
Price:            7.8378
Intrinsic value:  0.0000
Extrinsic value:  7.8378
Moneyness:        0.9524
Break-even price: 112.8378

4. Visualize the curve

from abaquant.visualization import VisualizationError

output_directory = Path("generated_figures/fred_rate_curve")
output_directory.mkdir(parents=True, exist_ok=True)

try:
    curve.visualize(save_path=output_directory, filename="manual_rate_curve")
    print(f"Saved curve visualization under: {output_directory}")
except VisualizationError as exc:
    print(f"Visualization skipped (optional dependency missing): {exc}")
Visualization skipped (optional dependency missing): Specify either path or filename, not both.
../../_images/aeab5c75e895177791de0a72821b04adce3efd43835801606b25083566fac741.png

5. Optional: live FRED branch

Only runs if FRED_API_KEY is set in the environment; otherwise it’s skipped cleanly.

def maybe_fetch_live_fred_curve():
    if not os.getenv("FRED_API_KEY"):
        return None
    return get_rate_curve(
        provider="fred",
        date="latest",
        cache_mode="disk",
        cache_directory="~/.cache/abaquant",
        refresh_policy="if_stale",
        max_age_days=1.0,
    )


live_curve = maybe_fetch_live_fred_curve()
if live_curve is None:
    print("Skipped live FRED request: FRED_API_KEY is not set.")
else:
    print(live_curve.as_frame().head())
    print(f"1-year rate:            {live_curve.zero_rate(1.0):.6f}")
    print(f"2-year discount factor: {live_curve.discount_factor(2.0):.6f}")
Skipped live FRED request: FRED_API_KEY is not set.

Takeaway

Manual and FRED-backed curves share the exact same API (zero_rate, discount_factor, .provenance, .visualize()), so you can prototype with a manual curve and swap in FRED later with a one-line change. Remember: Treasury constant-maturity yields are a pragmatic proxy, not a bootstrapped zero-coupon curve — see docs/domains/rates.rst.