Curves
Discount factor, zero rate, forward rate, spread, composite, parametric, and implied curve classes.
All types are available via flat import:
from vade import DiscountCurve, LineCurve, ForwardCurve, SpreadCurve, CompositeCurve, TurnOfYearCurve, FXImpliedCurve, NelsonSiegel, NelsonSiegelSvensson, SmithWilson, IRImpliedCurve, CreditImpliedCurve, FittedBondCurveSee Conventions for all accepted string parameter values.
Contents: DiscountCurve | LineCurve | ForwardCurve | SpreadCurve | CompositeCurve | TurnOfYearCurve | FXImpliedCurve | NelsonSiegel | NelsonSiegelSvensson | SmithWilson | IRImpliedCurve | CreditImpliedCurve | FittedBondCurve
See also: Curve Building Guide for end-to-end curve construction workflows.
DiscountCurve
Discount factor curve with interpolated DF values. Rust-backed.
Constructor
DiscountCurve(nodes, *, interpolation="log_linear", id="curve", convention="act360", modifier="modified_following", calendar=None, t=None, endpoints="not_a_knot")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
nodes | NodeValues | required | Discount factor nodes as {date: float} dict or ([dates], [values]) tuple. Positional-only. |
interpolation | str | "log_linear" | Interpolation method for discount factors |
id | str | "curve" | Curve identifier |
convention | str | "act360" | Day count convention for rate calculations |
modifier | str | "modified_following" | Business day adjustment rule |
calendar | BusinessCalendar or None | None | Business day calendar |
t | list[datetime.date] or None | None | Knot dates for B-spline interpolation |
endpoints | str | "not_a_knot" | Endpoint condition for spline methods |
NodeValues accepts Dict[datetime.date, float | Dual | Dual2] or Tuple[List[datetime.date], List[float | Dual | Dual2]].
See Conventions for accepted interpolation values. See Conventions for accepted convention values.
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Continuously compounded zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | DiscountCurve | Parallel shift by bp basis points |
.translate(days) | DiscountCurve | Translate curve forward by N days |
.roll(days) | DiscountCurve | Roll curve forward (time decay) |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | DiscountCurve | Deserialize from JSON string |
Return types are Union[float, Dual, Dual2] -- see the type system guide for when each variant appears.
Example
import datetime
from vade import DiscountCurve
nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.97,
datetime.date(2026, 1, 1): 0.94,
}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")
df = curve.discount_factor(datetime.date(2024, 7, 1))
assert 0.97 < df < 1.0 # interpolated mid-year discount factor
zr = curve.zero_rate(datetime.date(2024, 1, 1), datetime.date(2025, 1, 1))
assert 0.02 < zr < 0.04 # zero rate implied by DF=0.97
shifted = curve.shift(10) # shift by 10 basis pointsLineCurve
Value-based curve for zero rates, spreads, or other quantities. Rust-backed.
Constructor
LineCurve(nodes, *, interpolation="linear", id="curve", convention="act360", modifier="modified_following", calendar=None, t=None, endpoints="not_a_knot")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
nodes | NodeValues | required | Value nodes as {date: float} dict or ([dates], [values]) tuple. Positional-only. |
interpolation | str | "linear" | Interpolation method |
id | str | "curve" | Curve identifier |
convention | str | "act360" | Day count convention |
modifier | str | "modified_following" | Business day adjustment rule |
calendar | BusinessCalendar or None | None | Business day calendar |
t | list[datetime.date] or None | None | Knot dates for B-spline interpolation |
endpoints | str | "not_a_knot" | Endpoint condition for spline methods |
See Conventions for accepted interpolation values.
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date (derived from stored values) |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | LineCurve | Parallel shift by bp basis points |
.translate(days) | LineCurve | Translate curve forward by N days |
.roll(days) | LineCurve | Roll curve forward (time decay) |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | LineCurve | Deserialize from JSON string |
Example
import datetime
from vade import LineCurve
nodes = {
datetime.date(2024, 1, 1): 0.030,
datetime.date(2025, 1, 1): 0.035,
datetime.date(2026, 1, 1): 0.040,
}
curve = LineCurve(nodes, interpolation="linear", convention="act365f")
zr = curve.zero_rate(datetime.date(2024, 1, 1), datetime.date(2024, 7, 1))
assert 0.03 <= zr <= 0.035 # interpolated mid-year rate
fr = curve.forward_rate(datetime.date(2024, 1, 1), datetime.date(2025, 1, 1))
assert fr > 0 # positive forward rateForwardCurve
Forward rate curve with native forward rate representation. Rust-backed.
Nodes represent instantaneous forward rates (not discount factors). Uses Gauss-Legendre quadrature for discount factor integration.
Constructor
ForwardCurve(nodes, *, interpolation="flat_forward", id="forward_curve", convention="act365f", modifier="modified_following", calendar=None, t=None, endpoints="not_a_knot")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
nodes | NodeValues | required | Forward rate nodes as {date: float} dict or ([dates], [values]) tuple. Positional-only. |
interpolation | str | "flat_forward" | Interpolation method for forward rates |
id | str | "forward_curve" | Curve identifier |
convention | str | "act365f" | Day count convention |
modifier | str | "modified_following" | Business day adjustment rule |
calendar | BusinessCalendar or None | None | Business day calendar |
t | list[datetime.date] or None | None | Knot dates for B-spline interpolation |
endpoints | str | "not_a_knot" | Endpoint condition for spline methods |
See Conventions for accepted interpolation values.
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date (integrated from forward rates) |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | ForwardCurve | Parallel shift by bp basis points |
.translate(days) | ForwardCurve | Translate curve forward by N days |
.roll(days) | ForwardCurve | Roll curve forward (time decay) |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | ForwardCurve | Deserialize from JSON string |
Example
import datetime
from vade import ForwardCurve
# Instantaneous forward rates (per-day units matching internal x-axis)
nodes = {
datetime.date(2024, 1, 1): 8.2e-5,
datetime.date(2025, 1, 1): 9.0e-5,
datetime.date(2026, 1, 1): 1.0e-4,
}
curve = ForwardCurve(nodes, interpolation="flat_forward", convention="act365f")
df = curve.discount_factor(datetime.date(2025, 1, 1))
assert 0.9 < df < 1.0 # discount factor integrated from forward rates
fr = curve.forward_rate(datetime.date(2024, 1, 1), datetime.date(2025, 1, 1))
assert 0.02 < fr < 0.04 # annualized forward rateSpreadCurve
Spread curve: base curve plus additive or multiplicative spread. Rust-backed.
The base curve is passed as a JSON string (from curve.to_json()). Spread nodes are zero-rate spreads at specified dates.
Constructor
SpreadCurve(base_json, spread_nodes, *, mode="additive", spread_interpolation="linear", convention="act365f", id="spread_curve")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
base_json | str | required | JSON string of the base curve (from .to_json()). Positional-only. |
spread_nodes | NodeValues | required | Spread values as {date: float} dict or ([dates], [values]) tuple. Positional-only. |
mode | str | "additive" | Spread mode: "additive" or "multiplicative" |
spread_interpolation | str | "linear" | Interpolation method for spread values |
convention | str | "act365f" | Day count convention |
id | str | "spread_curve" | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date (base + spread) |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | SpreadCurve | Deserialize from JSON string |
Note: SpreadCurve does not support .shift(), .translate(), or .roll().
Example
import datetime
from vade import DiscountCurve, SpreadCurve
base_nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.97,
datetime.date(2026, 1, 1): 0.94,
}
base = DiscountCurve(base_nodes, interpolation="log_linear", convention="act365f")
base_json = base.to_json()
spread_nodes = {
datetime.date(2024, 1, 1): 0.001,
datetime.date(2025, 1, 1): 0.002,
datetime.date(2026, 1, 1): 0.003,
}
spread = SpreadCurve(base_json, spread_nodes, mode="additive")
df = spread.discount_factor(datetime.date(2025, 1, 1))
assert 0.9 < df < 1.0 # spread-adjusted discount factorAdvanced Example
Multiplicative mode and JSON roundtrip:
import datetime
from vade import DiscountCurve, SpreadCurve
# Base OIS curve with realistic discount factors
base = DiscountCurve(
{
datetime.date(2025, 6, 16): 1.0,
datetime.date(2026, 6, 16): 0.9615,
datetime.date(2027, 6, 16): 0.9240,
datetime.date(2028, 6, 16): 0.8880,
},
interpolation="log_linear",
convention="act360",
)
# Credit spread term structure (in zero-rate space)
spread_nodes = {
datetime.date(2025, 6, 16): 0.005,
datetime.date(2026, 6, 16): 0.008,
datetime.date(2027, 6, 16): 0.012,
datetime.date(2028, 6, 16): 0.015,
}
# Additive spread: zero_rate = base_zero_rate + spread
additive = SpreadCurve(base.to_json(), spread_nodes, mode="additive")
df_add = additive.discount_factor(datetime.date(2027, 6, 16))
assert 0.85 < float(df_add) < 0.95 # lower DF than base due to spread
# Multiplicative spread: discount_factor = base_df * spread_df
multiplicative = SpreadCurve(base.to_json(), spread_nodes, mode="multiplicative")
df_mult = multiplicative.discount_factor(datetime.date(2027, 6, 16))
assert 0.85 < float(df_mult) < 0.95
# JSON roundtrip preserves all parameters
restored = SpreadCurve.from_json(additive.to_json())
df_restored = restored.discount_factor(datetime.date(2027, 6, 16))
assert abs(float(df_restored) - float(df_add)) < 1e-12See also: Curve Building Guide for spread curve usage in multi-curve frameworks.
CompositeCurve
Composite curve combining multiple curves. Rust-backed.
Discount factors are computed as the product of discount factors from all component curves.
Constructor
CompositeCurve(curves, id=None)Parameters
| Name | Type | Default | Description |
|---|---|---|---|
curves | list | required | List of curve objects to combine |
id | str or None | None | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Product of component discount factors at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | CompositeCurve | Parallel shift by bp basis points |
.translate(days) | CompositeCurve | Translate curve forward by N days |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | CompositeCurve | Deserialize from JSON string |
Note: CompositeCurve does not support .roll().
Example
import datetime
from vade import DiscountCurve, CompositeCurve
nodes1 = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.98,
datetime.date(2026, 1, 1): 0.96,
}
nodes2 = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.99,
datetime.date(2026, 1, 1): 0.98,
}
curve1 = DiscountCurve(nodes1, convention="act365f")
curve2 = DiscountCurve(nodes2, convention="act365f")
composite = CompositeCurve([curve1, curve2])
df = composite.discount_factor(datetime.date(2025, 1, 1))
assert 0.96 < df < 0.98 # product of 0.98 * 0.99Advanced Example
DF multiplicativity proof and IRS pricing with a composite funding curve:
import datetime
from vade import DiscountCurve, CompositeCurve, IRS
# OIS discount curve (risk-free rate ~4%)
ois = DiscountCurve(
{datetime.date(2025, 6, 16): 1.0, datetime.date(2026, 6, 16): 0.9615,
datetime.date(2027, 6, 16): 0.9240},
convention="act360", id="ois",
)
# Funding spread curve (credit spread ~20bps)
spread = DiscountCurve(
{datetime.date(2025, 6, 16): 1.0, datetime.date(2026, 6, 16): 0.998,
datetime.date(2027, 6, 16): 0.996},
convention="act360", id="spread",
)
# Composite = OIS x Spread (DF is product of components)
funding = CompositeCurve([ois, spread], id="funding")
df_composite = funding.discount_factor(datetime.date(2026, 6, 16))
df_ois = ois.discount_factor(datetime.date(2026, 6, 16))
df_spread = spread.discount_factor(datetime.date(2026, 6, 16))
assert abs(float(df_composite) - float(df_ois) * float(df_spread)) < 1e-10
# Use composite curve for IRS discounting
irs = IRS(
effective=datetime.date(2025, 6, 16),
termination="2y",
frequency="s",
convention="act360",
fixed_rate=4.0,
notional=10_000_000,
currency="usd",
)
npv = irs.npv(funding)
assert isinstance(float(npv), float)TurnOfYearCurve
Turn-of-year adjusted curve with date-specific bumps. Rust-backed.
The inner curve is passed as a JSON string (from curve.to_json()). Bumps are {date: bp_adjustment} for turn-of-year effects.
Constructor
TurnOfYearCurve(inner_json, bumps, *, convention="act365f", calendar=None, id="toy_curve")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
inner_json | str | required | JSON string of the inner curve (from .to_json()). Positional-only. |
bumps | Dict[datetime.date, float] | required | Date-specific basis point adjustments. Positional-only. |
convention | str | "act365f" | Day count convention |
calendar | BusinessCalendar or None | None | Business day calendar |
id | str | "toy_curve" | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date (inner + bumps) |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | TurnOfYearCurve | Deserialize from JSON string |
Note: TurnOfYearCurve does not support .shift(), .translate(), or .roll().
Example
import datetime
from vade import DiscountCurve, TurnOfYearCurve
nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.97,
datetime.date(2026, 1, 1): 0.94,
}
inner = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")
inner_json = inner.to_json()
bumps = {
datetime.date(2024, 12, 31): 5.0,
datetime.date(2025, 12, 31): 3.0,
}
toy = TurnOfYearCurve(inner_json, bumps, convention="act365f")
df = toy.discount_factor(datetime.date(2025, 1, 1))
assert 0.9 < df < 1.0 # adjusted discount factorAdvanced Example
Year-end effect on discount factors and JSON roundtrip:
import datetime
from vade import DiscountCurve, TurnOfYearCurve
# Smooth underlying curve
inner = DiscountCurve(
{
datetime.date(2025, 6, 16): 1.0,
datetime.date(2026, 6, 16): 0.9615,
datetime.date(2027, 6, 16): 0.9240,
datetime.date(2028, 6, 16): 0.8880,
},
interpolation="log_linear",
convention="act365f",
)
# Year-end bumps in basis points (5bp at 2025 year-end, 3bp at 2026 year-end)
bumps = {
datetime.date(2025, 12, 31): 5.0,
datetime.date(2026, 12, 31): 3.0,
}
toy = TurnOfYearCurve(inner.to_json(), bumps, convention="act365f")
# Discount factors reflect year-end bumps
df_after_ye = toy.discount_factor(datetime.date(2026, 1, 2))
inner_df_after = inner.discount_factor(datetime.date(2026, 1, 2))
# TurnOfYear curve produces lower DFs than inner curve around year-end
assert float(df_after_ye) < float(inner_df_after)
# JSON roundtrip preserves bumps
restored = TurnOfYearCurve.from_json(toy.to_json())
df_check = restored.discount_factor(datetime.date(2026, 1, 2))
assert abs(float(df_check) - float(df_after_ye)) < 1e-12See also: Curve Building Guide for incorporating turn-of-year effects in curve construction.
FXImpliedCurve
Proxy curve for FX-adjusted discounting. Rust-backed.
Used in cross-currency frameworks where discounting in one currency is proxied through another via FX rates and interest rate differentials.
See also: FX Rates & Forwards Guide for FX implied curve construction.
Constructor
FXImpliedCurve(cashflow_ccy, collateral_ccy, fx_rates, fx_settlements, collateral_curve, cashflow_curve, id=None)Parameters
| Name | Type | Default | Description |
|---|---|---|---|
cashflow_ccy | str | required | Currency of the cashflows (e.g., "eur") |
collateral_ccy | str | required | Currency of the collateral (e.g., "usd") |
fx_rates | FXRates | required | FX rates object for currency conversion |
fx_settlements | Dict[str, datetime.date] | required | Settlement dates per currency |
collateral_curve | DiscountCurve | required | Discount curve in collateral currency |
cashflow_curve | DiscountCurve | required | Discount curve in cashflow currency |
id | str or None | None | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | FX-adjusted discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | FXImpliedCurve | Parallel shift by bp basis points |
.translate(days) | FXImpliedCurve | Translate curve forward by N days |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | FXImpliedCurve | Deserialize from JSON string |
Note: FXImpliedCurve does not support .roll().
Example
import datetime
from vade import DiscountCurve, FXRates, FXImpliedCurve
usd_nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.96,
datetime.date(2026, 1, 1): 0.92,
}
eur_nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.97,
datetime.date(2026, 1, 1): 0.94,
}
usd_curve = DiscountCurve(usd_nodes, convention="act365f", id="usd")
eur_curve = DiscountCurve(eur_nodes, convention="act365f", id="eur")
fx = FXRates({"eurusd": 1.10}, datetime.date(2024, 1, 1))
settlements = {"usd": datetime.date(2024, 1, 3), "eur": datetime.date(2024, 1, 3)}
fx_implied = FXImpliedCurve("eur", "usd", fx, settlements, usd_curve, eur_curve)
df = fx_implied.discount_factor(datetime.date(2025, 1, 1))
assert 0.9 < float(df) < 1.0 # FX-adjusted discount factor (returns Dual due to FXRates AD)NelsonSiegel
Nelson-Siegel parametric yield curve model. Rust-backed.
See also: Bootstrap & Parametric Guide for parametric curve fitting workflows.
Models the yield curve as a function of three factors (level, slope, curvature) with a single decay parameter tau.
Constructor
NelsonSiegel(beta0, beta1, beta2, tau, base_date, convention="act365f", id="")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
beta0 | float | Dual | Dual2 | required | Level (long-term rate) |
beta1 | float | Dual | Dual2 | required | Slope (short-term component) |
beta2 | float | Dual | Dual2 | required | Curvature (medium-term hump) |
tau | float | Dual | Dual2 | required | Decay parameter |
base_date | datetime.date | required | Base date for the curve |
convention | str | "act365f" | Day count convention |
id | str | "" | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | NelsonSiegel | Parallel shift by bp basis points |
.translate(days) | NelsonSiegel | Translate curve forward by N days |
.roll(days) | NelsonSiegel | Roll curve forward (time decay) |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | NelsonSiegel | Deserialize from JSON string |
Example
import datetime
from vade import NelsonSiegel
ns = NelsonSiegel(
beta0=0.05,
beta1=-0.02,
beta2=0.01,
tau=1.5,
base_date=datetime.date(2024, 1, 1),
convention="act365f",
)
df = ns.discount_factor(datetime.date(2025, 1, 1))
assert 0.9 < df < 1.0 # discount factor from parametric model
zr = ns.zero_rate(datetime.date(2024, 1, 1), datetime.date(2034, 1, 1))
assert 0.04 < zr < 0.06 # long-term rate converges to beta0NelsonSiegelSvensson
Nelson-Siegel-Svensson parametric yield curve model. Rust-backed.
Extends Nelson-Siegel with a second curvature term and decay parameter for more flexible curve shapes.
Constructor
NelsonSiegelSvensson(beta0, beta1, beta2, beta3, tau1, tau2, base_date, convention="act365f", id="")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
beta0 | float | Dual | Dual2 | required | Level (long-term rate) |
beta1 | float | Dual | Dual2 | required | Slope (short-term component) |
beta2 | float | Dual | Dual2 | required | First curvature term |
beta3 | float | Dual | Dual2 | required | Second curvature term |
tau1 | float | Dual | Dual2 | required | First decay parameter |
tau2 | float | Dual | Dual2 | required | Second decay parameter |
base_date | datetime.date | required | Base date for the curve |
convention | str | "act365f" | Day count convention |
id | str | "" | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | NelsonSiegelSvensson | Parallel shift by bp basis points |
.translate(days) | NelsonSiegelSvensson | Translate curve forward by N days |
.roll(days) | NelsonSiegelSvensson | Roll curve forward (time decay) |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | NelsonSiegelSvensson | Deserialize from JSON string |
Example
import datetime
from vade import NelsonSiegelSvensson
nss = NelsonSiegelSvensson(
beta0=0.05,
beta1=-0.02,
beta2=0.01,
beta3=0.005,
tau1=1.5,
tau2=5.0,
base_date=datetime.date(2024, 1, 1),
convention="act365f",
)
df = nss.discount_factor(datetime.date(2025, 1, 1))
assert 0.9 < df < 1.0 # discount factor from parametric model
zr = nss.zero_rate(datetime.date(2024, 1, 1), datetime.date(2034, 1, 1))
assert 0.04 < zr < 0.06 # long-term rate converges to beta0SmithWilson
Smith-Wilson parametric curve for regulatory yield curve extrapolation. Rust-backed.
Used in insurance regulation (e.g., Solvency II) for extrapolating yield curves beyond the last liquid point toward an ultimate forward rate (UFR).
Constructor
SmithWilson(dates, rates, ufr, alpha, base_date, convention="act365f", id="")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
dates | list[datetime.date] | required | Knot dates for observed rates |
rates | list[float] | required | Observed zero rates at knot dates |
ufr | float | required | Ultimate forward rate (long-term target rate) |
alpha | float | required | Speed of convergence to UFR |
base_date | datetime.date | required | Base date for the curve |
convention | str | "act365f" | Day count convention |
id | str | "" | Curve identifier |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | SmithWilson | Parallel shift by bp basis points |
.translate(days) | SmithWilson | Translate curve forward by N days |
.roll(days) | SmithWilson | Roll curve forward (time decay) |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | SmithWilson | Deserialize from JSON string |
Example
import datetime
from vade import SmithWilson
sw = SmithWilson(
dates=[
datetime.date(2025, 1, 1),
datetime.date(2029, 1, 1),
datetime.date(2034, 1, 1),
datetime.date(2044, 1, 1),
],
rates=[0.030, 0.032, 0.035, 0.038],
ufr=0.035,
alpha=0.1,
base_date=datetime.date(2024, 1, 1),
convention="act365f",
)
df = sw.discount_factor(datetime.date(2029, 1, 1))
assert 0.8 < df < 1.0 # discount factor from Smith-Wilson model
zr = sw.zero_rate(datetime.date(2024, 1, 1), datetime.date(2029, 1, 1))
assert 0.02 < zr < 0.05 # zero rate at 5-year pointIRImpliedCurve
Implied curve for bucket-level delta risk reporting. Rust-backed.
Constructs a curve from a source DiscountCurve with custom tenor buckets. Used to compute bucket-level risk sensitivities.
See also: Risk Guide for IRImpliedCurve usage in delta/gamma computation.
Constructor
IRImpliedCurve(source, ref_date, tenors, calendar, convention="act365f")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
source | DiscountCurve | required | Source discount curve to re-bucket |
ref_date | datetime.date | required | Reference date for tenor resolution |
tenors | list[str] | required | Tenor bucket labels (e.g., ["1Y", "2Y", "5Y", "10Y"]) |
calendar | BusinessCalendar | required | Calendar for tenor date resolution |
convention | str | "act365f" | Day count convention |
See Conventions for tenor string format.
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.bucket_delta(instrument) | polars.DataFrame | Compute bucket-level delta risk for an instrument |
.tenor_labels() | list[str] | Return the tenor bucket labels |
Note: IRImpliedCurve does not support .shift(), .translate(), .roll(), .to_json(), or .from_json().
Example
import datetime
from vade import DiscountCurve, IRImpliedCurve, BusinessCalendar
nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.97,
datetime.date(2026, 1, 1): 0.94,
datetime.date(2029, 1, 1): 0.85,
datetime.date(2034, 1, 1): 0.72,
}
source = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")
implied = IRImpliedCurve(
source=source,
ref_date=datetime.date(2024, 1, 1),
tenors=["1Y", "2Y", "5Y", "10Y"],
calendar=BusinessCalendar("NYC"),
)
implied.tenor_labels() # ['1Y', '2Y', '5Y', '10Y']
df = implied.discount_factor(datetime.date(2025, 1, 1))
assert 0.96 < df < 0.98 # consistent with source curveCreditImpliedCurve
Credit implied curve with piecewise flat hazard rates. Rust-backed.
Models credit risk through survival probabilities derived from hazard rates. Used for CDS pricing and credit risk analysis.
See also: Credit Curves & CDS Guide for credit curve calibration workflows.
Constructor
CreditImpliedCurve(nodes, convention="act360", recovery_rate=0.4, id="credit_curve")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
nodes | NodeValues | required | Survival probability nodes as {date: float} dict or ([dates], [values]) tuple |
convention | str | "act360" | Day count convention |
recovery_rate | float | 0.4 | Recovery rate assumption (0.0 to 1.0) |
id | str | "credit_curve" | Curve identifier |
Properties
| Property | Type | Description |
|---|---|---|
.id | str | Curve identifier |
.recovery_rate | float | Recovery rate assumption |
Methods
| Method | Returns | Description |
|---|---|---|
.survival_probability(date) | float | Dual | Dual2 | Survival probability at date |
.discount_factor(date) | float | Dual | Dual2 | Discount factor at date |
.zero_rate(start, end) | float | Dual | Dual2 | Zero rate between dates |
.forward_rate(start, end) | float | Dual | Dual2 | Forward rate between dates |
.shift(bp) | CreditImpliedCurve | Parallel shift by bp basis points |
.translate(days) | CreditImpliedCurve | Translate curve forward by N days |
.to_json() | str | Serialize to JSON string |
.from_json(s) (static) | CreditImpliedCurve | Deserialize from JSON string |
Note: CreditImpliedCurve does not support .roll().
Example
import datetime
from vade import CreditImpliedCurve
nodes = {
datetime.date(2024, 1, 1): 1.0,
datetime.date(2025, 1, 1): 0.98,
datetime.date(2026, 1, 1): 0.95,
datetime.date(2029, 1, 1): 0.88,
}
credit = CreditImpliedCurve(nodes, convention="act360", recovery_rate=0.4)
sp = credit.survival_probability(datetime.date(2025, 1, 1))
assert 0.0 < sp < 1.0 # survival probability at 1 year
credit.id # 'credit_curve'
credit.recovery_rate # 0.4FittedBondCurve
Parametric curve fitted to bond market prices via nonlinear least-squares. Python wrapper composing NS/NSS/SmithWilson fitting.
Fits a NelsonSiegel, NelsonSiegelSvensson, or SmithWilson curve to observed bond clean prices. Uses Levenberg-Marquardt optimization with inverse-duration weighting.
See also: Fitted Curves Guide for end-to-end fitting workflows.
Constructor
FittedBondCurve(bonds, clean_prices, settlement, model="NS", *, convention="act365f", base_date=None, weights=None, initial_params=None, max_iter=200, func_tol=1e-10, ufr=None, alpha=None, id="")Parameters
| Name | Type | Default | Description |
|---|---|---|---|
bonds | list[FixedRateBond] | required | Portfolio of bonds to fit. Positional. |
clean_prices | list[float] | required | Observed clean prices for each bond. Positional. |
settlement | datetime.date | required | Settlement/valuation date. Positional. |
model | str | "NS" | Model type: "NS" (Nelson-Siegel), "NSS" (Nelson-Siegel-Svensson), or "SW" (Smith-Wilson). Positional. |
convention | str | "act365f" | Day count convention for rate calculations |
base_date | datetime.date or None | None | Base date for curve (defaults to settlement) |
weights | list[float] or None | None | Custom fitting weights (overrides inverse-duration default) |
initial_params | list[float] or None | None | Initial parameter guess for optimizer |
max_iter | int | 200 | Maximum Levenberg-Marquardt iterations |
func_tol | float | 1e-10 | Function tolerance for convergence |
ufr | float or None | None | Ultimate forward rate (required for SW model) |
alpha | float or None | None | Convergence speed (required for SW model) |
id | str | "" | Curve identifier |
Properties
| Property | Type | Description |
|---|---|---|
.rmse | float | Root mean square pricing error |
.converged | bool | Whether optimization converged |
.iterations | int | Number of iterations used |
.objective | float | Final objective function value |
Methods
| Method | Returns | Description |
|---|---|---|
.discount_factor(date) | float | Discount factor at date |
.zero_rate(start, end) | float | Zero rate between dates |
.forward_rate(start, end) | float | Forward rate between dates |
.pricing_errors() | list[float] | Per-bond pricing errors (model price - market price) |
.curve() | NelsonSiegel | NelsonSiegelSvensson | SmithWilson | Extract the fitted inner curve |
Example
import datetime
from vade import FittedBondCurve, FixedRateBond, NelsonSiegel
settlement = datetime.date(2024, 1, 15)
# Create a portfolio of bonds with different maturities
bonds = []
for maturity_year, coupon in [(2026, 0.03), (2027, 0.035), (2029, 0.04), (2031, 0.042), (2034, 0.045)]:
bond = FixedRateBond(
effective=settlement,
termination=datetime.date(maturity_year, 1, 15),
frequency="s",
convention="act365f",
coupon=coupon,
face_value=100.0,
)
bonds.append(bond)
# Observed clean prices
clean_prices = [99.5, 99.0, 98.5, 97.0, 95.0]
# Fit Nelson-Siegel model
fc = FittedBondCurve(bonds, clean_prices, settlement, model="NS")
assert fc.converged
assert fc.rmse < 1.0 # good fit quality
# Query the fitted curve
df = fc.discount_factor(datetime.date(2025, 1, 15))
assert 0.9 < df < 1.0 # reasonable 1Y discount factor
zr = fc.zero_rate(settlement, datetime.date(2029, 1, 15))
assert 0.0 < zr < 0.10 # positive 5Y zero rate
# Per-bond pricing errors
errors = fc.pricing_errors()
assert len(errors) == 5 # one per bond
# Extract the fitted inner curve
inner = fc.curve()
assert isinstance(inner, NelsonSiegel)Advanced Example
Convergence diagnostics, per-bond pricing errors, and inner curve extraction:
import datetime
from vade import FittedBondCurve, FixedRateBond
bonds = [
FixedRateBond(effective=datetime.date(2024, 6, 15), termination="2y",
fixed_rate=3.5, frequency="s", convention="act365f", currency="usd"),
FixedRateBond(effective=datetime.date(2024, 6, 15), termination="3y",
fixed_rate=3.75, frequency="s", convention="act365f", currency="usd"),
FixedRateBond(effective=datetime.date(2024, 6, 15), termination="5y",
fixed_rate=4.0, frequency="s", convention="act365f", currency="usd"),
FixedRateBond(effective=datetime.date(2024, 6, 15), termination="7y",
fixed_rate=4.25, frequency="s", convention="act365f", currency="usd"),
FixedRateBond(effective=datetime.date(2024, 6, 15), termination="10y",
fixed_rate=4.5, frequency="s", convention="act365f", currency="usd"),
]
clean_prices = [99.5, 99.0, 98.5, 97.5, 96.0]
fc = FittedBondCurve(
bonds=bonds,
clean_prices=clean_prices,
settlement=datetime.date(2025, 6, 16),
model="NS",
)
# Check convergence diagnostics
assert fc.converged
assert fc.iterations < 100
assert fc.rmse < 1.0 # RMSE in price terms
# pricing_errors() returns per-bond fitting errors
errors = fc.pricing_errors()
assert len(errors) == 5
for err in errors:
assert abs(err) < 2.0 # each bond fits within 2 price points
# .curve() extracts the underlying parametric curve
inner_curve = fc.curve()
df = inner_curve.discount_factor(datetime.date(2030, 6, 16))
assert 0.70 < float(df) < 1.0Tip: Use
pricing_errors()to identify bonds that fit poorly -- these may indicate data issues or bonds with special features (e.g., on-the-run premiums). Consider excluding or down-weighting them via theweightsparameter.