Interest Rate Swap Valuation and Applications › The Swap Curve

Michael BrenndoerferUpdated January 5, 20269 min read
Quantitative FinanceAll chapters
Part 4 of 15Swaps and Credit Derivatives
Chapter 21Interest Rate Swap Valuation and Applications › The Swap Curve
Reading section 6 of 17 · The Swap Curve · 9 minSection 6 of 17 · 9 min

Book contents

Section 6 of 17 · 9 min read

Interest Rate Swap Valuation and Applications › The Swap CurveOne section · Sections in this chapter6 / 17 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

A par swap curve shows fixed rates for zero-initial-value swaps across maturities under specified contract conventions. Swap quotes are used to calibrate projection and discount curves for valuing interest-rate products. The resulting curves are valuation inputs; par quotes are not zero-coupon discount factors and do not by themselves specify every derivative's risks.

Why the Swap Curve Matters

Government bond yields can be affected by supply-demand imbalances, tax considerations, and liquidity preferences. Swap rates also contain more than expectations: their interpretation depends on the projection and discount curves, risk premia, and contract terms. For example, investor demand or central-bank purchases can make particular government bonds trade rich or cheap relative to comparable maturities; above-par or below-par price alone does not establish that effect. The swap curve is particularly important because:

  • It provides swap-rate quotes across maturities, often out to 30 years or more; a fitted curve connects those quotes without uniquely identifying expected future short-term rates.
  • Swap quotes are inputs to curve calibration. The resulting discount factors, together with appropriate floating-rate projection curves and risk-model inputs, support derivative valuation. A par swap quote is not itself a zero-coupon discount factor, and option valuation also needs assumptions about future rate variability.
  • Historically, unsecured IBOR-based swap curves reflected an interbank funding benchmark, including bank credit and liquidity components. RFR-based overnight index swap (OIS) curves instead reference overnight near-risk-free rates and do not generally measure banks' unsecured term borrowing costs (BIS, Beyond LIBOR).
  • Liquidity and quote freshness vary by market and tenor. A fitted curve may use interpolation or indicative inputs, so a displayed curve point is not necessarily a fresh executable quote.

Bootstrapping the Swap Curve

Bootstrapping is the process of extracting discount factors from market swap rates. The term "bootstrapping" refers to the iterative nature of the procedure: we use information derived from shorter maturities to solve for discount factors at longer maturities. The technique works iteratively, starting from the shortest maturity and working outward.

For a par swap with maturity TT and swap rate STS_T, the no-arbitrage condition requires that the value of the fixed leg equals the value of the floating leg. In the matching-reset single-curve model, the corresponding floating-rate bond including hypothetical principal is worth 1 per unit notional. A par swap therefore has a corresponding fixed-rate bond worth 1 as well:

1=∑i=1nST⋅δi⋅Z(0,ti)+Z(0,T)1 = \sum_{i=1}^{n} S_T \cdot \delta_i \cdot Z(0, t_i) + Z(0, T)

where:

  • STS_T: par swap rate for maturity TT
  • nn: number of payment periods up to maturity TT
  • δi\delta_i: day count fraction for period ii
  • Z(0,ti)Z(0, t_i): discount factor at coupon date tit_i. For the explicit last-factor solve, factors with i<ni<n must already be known or fixed by a stated interpolation rule; tn=Tt_n=T is the new endpoint.
  • Z(0,T)Z(0, T): discount factor for the final maturity TT (the unknown variable)

On a coupon-complete grid, earlier coupon-date discount factors are available from previous steps. Sparse quotes leave unquoted coupon dates: a 5-year annual-pay swap still pays at year 4 even if no 4-year swap is quoted. We therefore specify an interpolation rule and include those dates in the calibration. If their discounts depend on the new endpoint, we solve the full coupon equation for that endpoint.

We can rearrange the no-arbitrage condition to solve explicitly for the unknown discount factor Z(0,T)Z(0, T) by separating the final term and isolating Z(0,T)Z(0, T):

1=∑i=1n−1ST⋅δi⋅Z(0,ti)+ST⋅δn⋅Z(0,T)+Z(0,T)(separate final terms)1−∑i=1n−1ST⋅δi⋅Z(0,ti)=Z(0,T)(1+ST⋅δn)(group Z(0,T) terms)Z(0,T)=1−∑i=1n−1ST⋅δi⋅Z(0,ti)1+ST⋅δn(solve for Z(0,T))\begin{aligned} 1 &= \sum_{i=1}^{n-1} S_T \cdot \delta_i \cdot Z(0, t_i) + S_T \cdot \delta_n \cdot Z(0, T) + Z(0, T) && \text{(separate final terms)} \\ 1 - \sum_{i=1}^{n-1} S_T \cdot \delta_i \cdot Z(0, t_i) &= Z(0, T) (1 + S_T \cdot \delta_n) && \text{(group } Z(0, T) \text{ terms)} \\ Z(0, T) &= \frac{1 - \sum_{i=1}^{n-1} S_T \cdot \delta_i \cdot Z(0, t_i)}{1 + S_T \cdot \delta_n} && \text{(solve for } Z(0, T) \text{)} \end{aligned}

where:

  • Z(0,T)Z(0, T): discount factor for maturity TT
  • STS_T: par swap rate for maturity TT
  • nn: number of payment periods
  • δi\delta_i: day count fraction for period ii
  • Z(0,ti)Z(0, t_i): known discount factor for time tit_i
  • δn\delta_n: day count fraction for the final period
  • 1−∑i=1n−1ST⋅δi⋅Z(0,ti)1 - \sum_{i=1}^{n-1} S_T \cdot \delta_i \cdot Z(0, t_i): remaining value to be covered by the final payment
  • 1+ST⋅δn1 + S_T \cdot \delta_n: final coupon plus hypothetical principal per unit notional, the coefficient multiplying the final discount factor

The explicit quotient requires 1+STδn≠01+S_T\delta_n\ne0 and a positive solved discount factor. It applies when the earlier coupon factors are fixed inputs. For this sparse-quote example, we instead interpolate continuously compounded zero rates linearly between quoted nodes, keep the first zero rate flat before the first node, and solve each full coupon equation with the trial endpoint included. Integer payment indices generate every regular coupon date. The resulting curve must reprice every input quote using those same conventions. This specifies a model curve; finite quotes do not uniquely determine its values at all other dates.

In[10]:
Code
def bootstrap_swap_curve(swap_rates, maturities, payment_freq=0.5):
    """Calibrate a regular-coupon single curve to hypothetical par quotes.

    payment_freq is the accrual interval in years, not payments per year.
    Maturities must be positive increasing multiples of that interval.
    Continuous zero rates are linear between quoted nodes and flat before
    the first node. Each trial includes the current endpoint when valuing
    every contractual coupon, including unquoted intermediate dates.
    This teaching example does not support stubs or multiple curves.
    """
    from scipy.optimize import brentq

    swap_rates = np.asarray(swap_rates, dtype=float)
    maturities = np.asarray(maturities, dtype=float)
    delta = float(payment_freq)
    if (
        swap_rates.ndim != 1
        or maturities.ndim != 1
        or len(swap_rates) != len(maturities)
        or not len(maturities)
        or not np.all(np.isfinite(swap_rates))
        or not np.all(np.isfinite(maturities))
        or not np.isfinite(delta)
        or delta <= 0
        or np.any(maturities <= 0)
        or np.any(np.diff(maturities) <= 0)
    ):
        raise ValueError(
            "Use finite aligned quotes and positive increasing maturities."
        )
    period_counts = np.rint(maturities / delta).astype(int)
    if (
        np.any(period_counts < 1)
        or not np.allclose(period_counts * delta, maturities, rtol=0, atol=1e-10)
        or np.any(1 + swap_rates * delta <= 0)
    ):
        raise ValueError(
            "Use regular whole-period maturities and admissible coupon rates."
        )

    zero_rates = np.empty(len(maturities))
    for i, (rate, maturity, periods) in enumerate(
        zip(swap_rates, maturities, period_counts)
    ):
        times = delta * np.arange(1, periods + 1)
        nodes = maturities[: i + 1]

        def par_residual(trial_zero):
            trial_rates = np.append(zero_rates[:i], trial_zero)
            discounts = np.exp(-np.interp(times, nodes, trial_rates) * times)
            return rate * delta * np.sum(discounts) + discounts[-1] - 1

        # Explicit finite bracket for this educational rate range; reject
        # inconsistent/unbracketed quotes rather than return a false curve.
        lower, upper = -0.5, 1.0
        if par_residual(lower) * par_residual(upper) > 0:
            raise ValueError(
                "Quote has no root in the stated illustrative zero-rate bracket."
            )
        zero_rates[i] = brentq(par_residual, lower, upper, xtol=1e-14)

    discount_factors = np.exp(-zero_rates * maturities)
    return discount_factors, zero_rates


# Hypothetical par swap quotes; single-curve regular-coupon exercise
market_maturities = np.array([1, 2, 3, 5, 7, 10])
market_swap_rates = np.array([0.047, 0.050, 0.052, 0.055, 0.057, 0.058])
In[11]:
Code
# Simplified bootstrap for illustration
def simple_bootstrap(swap_rates, maturities):
    """Annual-payment specialization of the full-coupon calibration above."""
    return bootstrap_swap_curve(swap_rates, maturities, payment_freq=1.0)


# Bootstrap the curve
boot_disc_factors, boot_zero_rates = simple_bootstrap(
    market_swap_rates, market_maturities
)
Out[12]:
Console
Bootstrapped Swap Curve
=================================================================
Maturity    Swap Rate      Discount Factor         Zero Rate
-----------------------------------------------------------------
     1 yr        4.700%           0.955110          4.5929%
     2 yr        5.000%           0.906900          4.8862%
     3 yr        5.200%           0.858532          5.0844%
     5 yr        5.500%           0.763759          5.3901%
     7 yr        5.700%           0.675665          5.6008%
    10 yr        5.800%           0.565404          5.7021%
Out[13]:
Visualization
Line chart showing rising par swap quotes from 4.7% to 5.8% and rising calibrated continuous zero rates from 4.59% to 5.70%, with all six maturity points within the displayed 3% to 7% range.
Comparison of hypothetical annual-pay par swap quotes with calibrated continuously compounded zero rates. Par quotes rise from 4.7% to 5.8%; zero rates rise from 4.59% at one year to 5.70% at ten years. All quoted maturity points are shown.

This figure compares annual-pay par quotes with continuously compounded zero rates. For the stated hypothetical data, the calibrated zeros increase from 4.5929% at one year to 5.7021% at ten years. They are not interchangeable with par coupons: the latter price a full coupon schedule, whereas each zero rate describes a single payment.

Out[14]:
Visualization
Line chart comparing the hypothetical par quotes with the calibrated continuous-zero rates and interval-average continuous forward rates at the six quoted maturity nodes.
Hypothetical par quotes, calibrated continuously compounded zero rates and continuously compounded interval forwards. The latter average each interval between consecutive quote nodes and are plotted at the interval end; they differ from the earlier simple-accrual FRA rates and are not an unqualified forecast.