Finance16 min read·

Yield Curve Bootstrapping: Step-by-Step With Worked Example

A practical, worked-example guide to constructing a zero-coupon yield curve by bootstrapping - from deposits and futures to swaps, with a Python implementation.

What Bootstrapping Solves

A yield curve is a function that assigns to every future maturity a discount factor or an equivalent zero-coupon interest rate. Every bond, swap, and interest-rate derivative price ultimately depends on some yield curve. The problem is that the market does not directly quote a full curve. It quotes prices for a selection of instruments — money-market deposits at short maturities, interest-rate futures or FRAs in the middle, and interest-rate swaps at longer maturities. Bootstrapping is the recipe for extracting a consistent zero-coupon curve from those quotes.

The core idea is simple. Start at the shortest maturity where the market quote directly implies a zero rate. Use that zero rate to strip the coupon component out of the next instrument, revealing the next zero rate. Repeat outward. Each step uses only zeros already solved for, so no simultaneous equation is needed — the curve is built one point at a time.

This article walks through the mechanics from the shortest instruments to a full swap curve, gives a complete Python implementation, and highlights the practical decisions that make real curves reproducible. The single-curve construction is standard in Hull (2021, ch. 4); the multi-curve framework that became market practice after 2008 is treated in detail in Brigo & Mercurio (2006) and Andersen & Piterbarg (2010).


Building Blocks

Three objects appear repeatedly:

  • Discount factor (D(t)) — the present value of £1 paid at time (t). (D(0) = 1); (D(t) < 1) for positive rates.
  • Zero rate (z(t)) — the continuously compounded rate for a zero-coupon bond of maturity (t): (D(t) = e^{-z(t) t}). Equivalently, on a simple-compounding basis (D(t) = 1 / (1 + z(t) t)) with the day-count convention appropriate to the currency.
  • Forward rate (f(t_1, t_2)) — the rate implied by the curve for borrowing between (t_1) and (t_2): (D(t_1) / D(t_2) = 1 + f(t_1, t_2) \tau) where (\tau) is the year-fraction between the two dates on the appropriate day count.

Bootstrapping produces (D(t)) at the maturities of the input instruments; interpolation fills in between them.


Instrument-by-Instrument Recipe

1. Money-Market Deposits (Overnight to ~1 Year)

A deposit paying rate (r) with tenor (\tau) implies:

D(tau) = 1 / (1 + r * tau)

Day count is usually Act/360 for USD, EUR, JPY money markets and Act/365F for GBP. For a 3-month USD deposit at 5% with (\tau = 92/360 \approx 0.2556):

D(0.2556) = 1 / (1 + 0.05 * 0.2556) = 1 / 1.01278 = 0.98738

This gives one point on the curve directly. In practice, only a handful of deposit tenors are used before switching to a more informative instrument.

2. Interest-Rate Futures or FRAs (~3 Months to ~2 Years)

Eurodollar futures, SOFR futures, and FRAs price forward rates for future accrual periods. A future maturing at (t_1) with reference rate for the period ([t_1, t_2]) has quoted rate (f_{\text{mkt}}). With a small convexity adjustment (c) (dropped here), the implied forward is (f = f_{\text{mkt}} - c), and:

D(t_2) = D(t_1) / (1 + f * (t_2 - t_1))

Because the previous step already gave (D(t_1)) from deposits (or from the previous future), this equation solves directly for (D(t_2)).

Convexity adjustment matters for futures but not FRAs. It arises because a futures contract is marked to market daily whereas an FRA is a forward contract. Standard practice is to compute the convexity adjustment from a short-rate model — Hull and White (1990) is the usual choice — and subtract it from the quoted rate before use (see also Hull, 2021, ch. 6).

3. Interest-Rate Swaps (~2 Years to 50+ Years)

The workhorse instrument at longer maturities is the par swap rate: the fixed rate that makes the fixed and floating legs of a vanilla interest-rate swap have equal present value at inception.

For an annual-paying, unit-notional swap of maturity (T = t_n) with fixed rate (S):

S * sum_{i=1..n} tau_i D(t_i) = 1 - D(t_n)

The left side is the present value of the fixed leg; the right side is the present value of the floating leg minus the notional exchange, which under standard conventions reduces to (1 - D(t_n)).

Rearranging:

D(t_n) = ( 1 - S * sum_{i=1..n-1} tau_i D(t_i) ) / ( 1 + S * tau_n )

The sum runs over payment dates that are earlier than (t_n), all of which have already been solved for. This is the bootstrap iteration: at each swap maturity, the equation depends only on previously known discount factors and yields the new one directly.

4. Interpolation Between Nodes

Real curves need discount factors at maturities other than the input instruments. The most common conventions:

  • Linear interpolation on zero rates. Simple, but produces non-smooth forwards.
  • Linear interpolation on log-discount factors. Equivalent to piecewise-constant forwards, which is a common choice.
  • Monotone cubic on log-discount factors. Preserves monotonicity and produces smoother forwards.
  • Piecewise-constant on forward rates. The most stable choice for curve calibration and delta calculations; the industry default in many risk systems.

The choice matters for forward-sensitive products (caps, floors, forward-starting swaps) but is neutral for spot-priced bullet cash flows.


A Worked Bootstrap

Consider a stylised set of instruments (approximate, illustrative):

InstrumentTenorRate
Deposit3M5.00%
Deposit6M5.05%
Deposit12M5.10%
Swap2Y4.80%
Swap5Y4.50%
Swap10Y4.40%

Assume swaps pay annually, all with Act/365F day count and unit notional.

Step 1: Deposit-Implied Discount Factors

D(0.25) = 1 / (1 + 0.0500 * 0.25) = 1 / 1.01250 = 0.98765

D(0.50) = 1 / (1 + 0.0505 * 0.50) = 1 / 1.02525 = 0.97538

D(1.00) = 1 / (1 + 0.0510 * 1.00) = 1 / 1.05100 = 0.95147

Step 2: 2-Year Swap

The 2Y swap pays 4.80% annually. Payment dates are 1Y and 2Y. Using the swap identity:

0.048 * (1.00 * D(1) + 1.00 * D(2)) = 1 - D(2)

0.048 * D(1) + 0.048 * D(2) = 1 - D(2)

D(2) = (1 - 0.048 * D(1)) / (1 + 0.048)

Substituting (D(1) = 0.95147):

D(2) = (1 - 0.048 * 0.95147) / 1.048 = (1 - 0.04567) / 1.048 = 0.95433 / 1.048 = 0.91062

The implied 2-year zero rate is (z_2 = -\ln(0.91062) / 2 = 0.04680), roughly 4.68%.

Step 3: 5-Year Swap

The 5Y swap pays 4.50% at 1Y, 2Y, 3Y, 4Y, 5Y. (D(3)) and (D(4)) are not yet known. Two options:

  1. Add 3Y and 4Y swaps as inputs (they are usually available).
  2. Interpolate (D(3)) and (D(4)) from (D(2)) and the eventual (D(5)), which requires solving a fixed-point.

Realistic curves use option 1: every year up to 10Y typically has a quoted swap, so no interpolation is needed inside the bootstrap. The illustrative table above skips 3Y and 4Y for brevity, and in practice you would fill them in.

Assuming they were provided and give (say) (D(3) = 0.87189) and (D(4) = 0.83400), the 5Y bootstrap step is:

D(5) = (1 - 0.045 * (D(1) + D(2) + D(3) + D(4))) / (1 + 0.045)

D(5) = (1 - 0.045 * (0.95147 + 0.91062 + 0.87189 + 0.83400)) / 1.045

D(5) = (1 - 0.045 * 3.56798) / 1.045 = (1 - 0.16056) / 1.045 = 0.80329

Zero rate: (z_5 = -\ln(0.80329) / 5 = 0.04381), roughly 4.38%.

Step 4: 10-Year Swap and Beyond

The same recipe applies. (D(t_n)) at each additional swap maturity depends only on previously solved discount factors.


Python Implementation

The following implementation covers deposits and swaps, sufficient for a first bootstrap. Extending it to futures with convexity adjustment is a mechanical addition.

import numpy as np import pandas as pd def bootstrap_curve( deposits: list[tuple[float, float]], swaps: list[tuple[float, float]], ) -> pd.DataFrame: """Bootstrap a zero-coupon curve from deposits and annual-paying swaps. Parameters ---------- deposits : list of (tenor, rate) Money-market deposits. Rates are simple-compounding on Act/365F. swaps : list of (tenor, rate) Annual-paying swaps. Fixed leg pays on Act/365F. Assumes tenors are integer years covering every year in ascending order. Returns ------- pd.DataFrame with columns [tenor, discount_factor, zero_rate_cc]. """ discounts: dict[float, float] = {} for tenor, rate in deposits: discounts[tenor] = 1.0 / (1.0 + rate * tenor) swap_tenors = [t for t, _ in swaps] swap_rates = {t: r for t, r in swaps} for tenor in swap_tenors: annuity = sum(discounts[t] for t in range(1, int(tenor)) if t in discounts) rate = swap_rates[tenor] discounts[tenor] = (1 - rate * annuity) / (1 + rate) tenors_sorted = sorted(discounts) df = pd.DataFrame( { "tenor": tenors_sorted, "discount_factor": [discounts[t] for t in tenors_sorted], "zero_rate_cc": [-np.log(discounts[t]) / t for t in tenors_sorted], } ) return df deposits = [(0.25, 0.0500), (0.50, 0.0505), (1.00, 0.0510)] swaps = [ (2, 0.0480), (3, 0.0470), (4, 0.0460), (5, 0.0450), (7, 0.0440), (10, 0.0440), ] curve = bootstrap_curve(deposits, swaps) print(curve.to_string(index=False, float_format=lambda v: f"{v:.6f}"))

The output is a table of discount factors and zero rates at each input node. Interpolation between nodes is then applied by the pricing library.


Multi-Curve Reality (Post-2008)

The description above is a single-curve bootstrap: one curve is used for both discounting and forward projection. Since 2008, market convention has moved to multi-curve frameworks:

  • Discount curve. Built from OIS (overnight indexed swap) rates. Since collateralised trades are discounted at the collateral rate (usually the overnight rate in the currency of the collateral), OIS is the correct discount reference for cleared and CSA-governed derivatives.
  • Projection curves. One curve per index tenor: 1M LIBOR curve, 3M LIBOR curve, 6M LIBOR curve. Each is bootstrapped from swaps referencing that specific index tenor.

The construction is the same in each curve: peel off zero rates one instrument at a time. The subtlety is that swap valuation now uses one curve for discounting and another for forward projection, so the bootstrap of the projection curve treats the discount curve as a fixed external input.

With the transition away from LIBOR to overnight-based RFRs (SOFR, ESTR, SONIA, SARON, TONA), most projection curves have collapsed to a single overnight-compounded reference in each currency, simplifying the multi-curve setup considerably. Legacy LIBOR curves are still maintained for the tail of existing trades but are no longer market-standard for new business.


Practical Considerations

Day Counts and Business-Day Conventions

Real curves involve day counts (Act/360, Act/365F, 30/360), business-day roll conventions (Modified Following, Preceding), and calendar effects (holidays in each currency). Getting these right is essential to reproducing broker quotes. A curve that is bootstrapped with the wrong day count will price a par swap at a small but non-zero PV even at inception — a clear sign of a specification mismatch.

Weekend and Holiday Effects

The overnight rate on a Friday earns three days of interest for the weekend, not one. Bootstrapping a nightly overnight curve requires the correct handling of these weight-days. Most curve libraries have this built in; hand-built curves must be careful.

Bid-Offer and Mid Marks

Broker quotes are two-way. Curves are usually bootstrapped from mid marks — the average of bid and offer — but the discretion to shade toward one side or the other appears when hedging: a book that is systematically long risk to a maturity might be marked closer to bid; short might be closer to offer.

Smoothness and Locality

Different interpolation choices produce different forward-rate shapes for the same input quotes. Piecewise-constant forwards are stable but not smooth. Monotone cubic on log-discount factors is smoother but can cause unwanted forward-rate sensitivities in adjacent buckets. The choice is a trade-off between smoothness of forwards and locality of curve sensitivities.

Multi-Currency Consistency

Cross-currency basis swaps introduce another layer. A single-currency USD curve bootstrapped from USD swaps and a single-currency EUR curve from EUR swaps are individually correct but will not price a USD/EUR cross-currency swap at zero unless the FX basis is incorporated. In practice, at least one currency's discount curve is adjusted to accommodate observed cross-currency basis quotes.


Applications

  • Bond and swap pricing. Every fixed-income cash-flow product is priced against a bootstrapped curve.
  • Curve risk / DV01. Sensitivities are typically computed by bumping each input instrument's quote and re-bootstrapping.
  • Forward-rate calculation. Caps, floors, and forward-starting swaps depend on the forward-rate curve derived from the discount curve.
  • Basis risk management. Differences between OIS and LIBOR (or SOFR) curves quantify basis and can be hedged with basis swaps.
  • Cross-currency valuation. The FX basis in cross-currency curves is a first-order input to XVA calculations.

References

  • Andersen, L. B. G., & Piterbarg, V. V. (2010). Interest Rate Modeling (Vols. 1–3). Atlantic Financial Press.
  • Brigo, D., & Mercurio, F. (2006). Interest Rate Models — Theory and Practice (2nd ed.). Springer.
  • Hull, J. C. (2021). Options, Futures, and Other Derivatives (11th ed.). Pearson.
  • Hull, J., & White, A. (1990). Pricing interest-rate-derivative securities. Review of Financial Studies, 3(4), 573–592.
  • Tuckman, B., & Serrat, A. (2022). Fixed Income Securities: Tools for Today's Markets (4th ed.). Wiley.

Frequently Asked Questions

Why do we need bootstrapping instead of just using the swap rates as zero rates?

Because a swap rate is a par coupon rate, not a zero rate. A 5-year par swap rate of 4.5% is the fixed rate that makes the swap have zero present value given the actual discount factors at each payment date. Treating it as a 5-year zero rate would be wrong — it implicitly assumes the earlier payment dates discount at the same rate, which is not true if the curve has any shape.

How does bootstrapping handle interest-rate futures?

Futures give forward rates for specific accrual periods. Given a discount factor at the start of the period (from earlier bootstrapped points) and the futures-implied forward rate for the period (with convexity adjustment), the discount factor at the end of the period is direct. Futures typically fill the 3-month to 2-year part of the curve, before swap instruments become quoted at every relevant maturity.

What is the convexity adjustment for interest-rate futures?

Futures are settled daily to cash; forwards are not. Daily settlement means the futures rate carries a small positive drift relative to the corresponding forward under any interest-rate model with non-zero volatility. The convexity adjustment corrects for this — the size depends on the volatility of the short rate and the futures maturity, typically a few basis points at short maturities rising to tens of basis points at 5+ years. Hull-White is the standard model for computing it.

Can I use a single-curve bootstrap for post-2008 pricing?

For educational purposes, yes. For actual pricing of cleared or collateralised trades, no. A single-curve framework misvalues trades by the OIS-LIBOR basis, which was tens of basis points during the 2008-2015 era and remains non-trivial post-LIBOR. All institutional risk systems use multi-curve frameworks.

How stable is a bootstrapped curve to small changes in inputs?

Fairly stable at the level of discount factors and zero rates, less stable at the level of forwards. Small quote changes at one node can produce visible zigzags in adjacent forward-rate buckets if the interpolation is not smoothed. This is one reason many practitioners prefer piecewise-constant forwards: bumps to individual instruments produce forward-rate changes localised to a single bucket, which is much easier to interpret and hedge.

What is the difference between bootstrapping and curve fitting?

Bootstrapping is a local procedure that peels off one instrument at a time and reproduces every input quote exactly. Curve fitting is a global procedure that adjusts a smooth functional form (Nelson-Siegel, Svensson, or splines) to minimise pricing error across all instruments simultaneously, typically not fitting every quote exactly. Bootstrapping is standard for swap-curve construction; fitted curves are more common in government bond markets where the noise across many bonds needs to be smoothed.

Want to go deeper on Yield Curve Bootstrapping: Step-by-Step With Worked Example?

This article covers the essentials — next, open your free Quantt prep workspace: a real course lesson, interview practice, and a preview of your personalised plan.

Free lesson + interview practice · No credit card required