Yield Curve Construction and Bootstrapping
Yield-curve bootstrapping infers discount factors or zero rates from liquid instruments so each instrument reprices to its market quote. It is not simply plotting quoted yields. A usable curve requires correct cashflow conventions, an explicit interpolation space, and separate discounting and forwarding curves where collateral and index projection differ.
Choose the primitive
Discount factors are the safest internal primitive:
P(0,T) = present value of 1 paid at T
z(T) = -log(P(0,T)) / T
f(t1,t2) = log(P(0,t1)/P(0,t2)) / (t2-t1)
Zero rates, forwards, and par swap rates can all be derived from P. Storing only zero rates invites day-count and compounding ambiguity. A curve object should retain dates, discount factors, day-count basis, interpolation convention, and source instrument metadata.
| Quote | Primary unknown solved | Typical short-end role |
|---|---|---|
| Deposit / overnight | earliest discount factor | anchor |
| OIS swap | discount factors | collateral curve |
| FRA / futures | forward discount ratios | index projection |
| IRS | longer forward nodes | projection curve |
The bootstrap identity
For a fixed-for-floating par swap with fixed rate c, accruals alphai, and discount factors Pi, the fixed-leg PV is:
PV_fixed = c * sum_i alpha_i P_i
For a simple single-curve par swap, the floating leg PV is approximately:
PV_float = 1 - P_n
Set them equal and solve the last unknown discount factor:
P_n = [1 - c * sum_(i=1)^(n-1) alpha_i P_i] / [1 + c * alpha_n]
This is sequential only when all earlier nodes are known. Irregular coupons, stubs, and instruments whose payments cross unsolved regions require an interpolation-and-root-solve approach.
def bootstrap_par_swap(par_rate, accruals, known_dfs):
"""Solve final discount factor for a simple annualized par swap."""
assert len(accruals) == len(known_dfs) + 1
known_annuity = sum(a * df for a, df in zip(accruals[:-1], known_dfs))
final_alpha = accruals[-1]
return (1.0 - par_rate * known_annuity) / (1.0 + par_rate * final_alpha)
The code assumes one curve and a floating-leg identity. In a multi-curve setup, forecast each floating coupon from its index forward curve and discount it on the collateral curve.
Multi-curve construction
Post-crisis collateralization broke the single-curve approximation. For a collateralized swap:
PV = sum_i P_OIS(0, t_i) * alpha_i * [F_index(t_(i-1),t_i) - fixed_rate]
Construct the OIS discount curve first. Then bootstrap each forwarding curve from its own FRAs, futures, and swaps while discounting all cashflows on OIS. Basis swaps constrain relationships between projection curves. Mixing discount and projection curves can create pricing errors that look like “model risk” but are actually a simple curve-ID bug.
Interpolation is a risk decision
No interpolation is uniquely correct between liquid pillars. Common choices include linear zero rates, linear log discount factors, and monotone cubic splines on instantaneous forwards. Their hedge behavior differs.
| Space | Strength | Main concern |
|---|---|---|
| log discount factor | positive DFs, simple | forwards may jump |
| zero rate | intuitive display | compounding dependence |
| instantaneous forward | smooth curve controls | can oscillate or go negative |
Select a convention based on product sensitivity and market practice, then treat it as a model parameter. Interpolation sensitivity is often large for short-dated forward-starting trades.
Futures, dates, and conventions
Futures quotes need convexity adjustment before becoming FRA-style forwards when rates are stochastic:
FRA_rate ≈ futures_implied_rate - convexity_adjustment
The adjustment’s sign and size depend on the model and contract. Equally important are calendars, business-day adjustment, payment lags, day count, reset timing, and compounding. A curve calibrated to “rates” without real cashflow schedules is not calibrated at all.
Production validation
Reprice every input instrument from generated cashflows, not the formulas used to bootstrap it. Then run:
- Discount-factor positivity and sensible monotonicity checks (negative-rate regimes may have
P>1). - No unexplained discontinuities in implied forwards at pillars.
- PV01 bucket stability under a one-tick quote bump.
- End-to-end agreement with independent market-price snapshots.
def df_to_simple_forward(df_start, df_end, accrual):
return (df_start / df_end - 1.0) / accrual
If input instruments fail to reprice within tolerance, stop. Smoothing a curve before identifying a date or convention mismatch hides the fault and contaminates every downstream valuation.
Key takeaways
- Build around dated discount factors, then derive zeros and forwards consistently.
- Each quoted instrument supplies a cashflow equation; bootstrapping solves those equations.
- OIS discounting and index projection require separate curves in collateralized markets.
- Interpolation determines forward and hedge behavior between liquid pillars.
- Reprice inputs with independent cashflow generation and validate conventions explicitly.
