Advanced Interest Rate Models (HJM and LMM) › Swaption Pricing Under the LMM

Michael BrenndoerferDecember 7, 20259 min read
Quantitative FinanceAll chapters
Part 7 of 15Interest Rate Models and Derivatives
Chapter 39Advanced Interest Rate Models (HJM and LMM) › Swaption Pricing Under the LMM
Reading section 6 of 13 · Swaption Pricing Under the LMM · 9 minSection 6 of 13 · 9 min

Book contents

Section 6 of 13 · 9 min read

Advanced Interest Rate Models (HJM and LMM) › Swaption Pricing Under the LMMOne section · Sections in this chapter6 / 13 ↓
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 swaption is an option to enter into an interest rate swap at a future date. A payer swaption gives you the right to pay fixed and receive floating, while a receiver swaption gives the right to receive fixed and pay floating. Swaptions are among the most liquid interest rate derivatives, and their prices are used to calibrate the LMM. These prices reflect market expectations about both the level and volatility of future interest rates.

Swap Rate and Swaption Payoff

The forward swap rate Sα,β(t)S_{\alpha,\beta}(t) for a swap starting at TαT_\alpha and ending at TβT_\beta is the fixed rate that makes the swap have zero value at inception. It can be expressed as the ratio of two present values:

Sα,β(t)=P(t,Tα)−P(t,Tβ)Aα,β(t)S_{\alpha,\beta}(t) = \frac{P(t, T_\alpha) - P(t, T_\beta)}{A_{\alpha,\beta}(t)}

where:

  • P(t,Tα)P(t, T_\alpha): price of bond maturing at swap start date
  • P(t,Tβ)P(t, T_\beta): price of bond maturing at swap end date
  • Aα,β(t)=∑i=αβ−1δP(t,Ti+1)A_{\alpha,\beta}(t) = \sum_{i=\alpha}^{\beta-1} \delta P(t, T_{i+1}): present value of the fixed leg's annuity factor (PVBP)

The numerator P(t,Tα)−P(t,Tβ)P(t, T_\alpha) - P(t, T_\beta) represents the present value of receiving one dollar at the swap start and paying one dollar at the swap end. This is exactly the floating leg's present value for a swap with a notional of one dollar. The denominator, the annuity factor, represents the present value of receiving one dollar at each payment date. The ratio therefore gives the fixed rate that equates the present values of the fixed and floating legs.

A payer swaption with strike KK has payoff at expiry TαT_\alpha:

Swaption Payoff=Aα,β(Tα)⋅max⁡(Sα,β(Tα)−K,0)\text{Swaption Payoff} = A_{\alpha,\beta}(T_\alpha) \cdot \max(S_{\alpha,\beta}(T_\alpha) - K, 0)

where:

  • Aα,β(Tα)A_{\alpha,\beta}(T_\alpha): annuity factor at expiry
  • Sα,β(Tα)S_{\alpha,\beta}(T_\alpha): forward swap rate at expiry
  • KK: swaption strike rate

The payoff structure reflects that exercising a payer swaption is valuable when the market swap rate exceeds the strike: you can immediately enter a swap paying below-market rates. The multiplication by the annuity factor converts the rate difference into a present value, accounting for all future payment dates.

Swaption Approximation Formula

While the swap rate under LMM does not follow exactly lognormal dynamics (it's a function of multiple forward rates), a common approximation assumes lognormal swap rate dynamics. This approximation is remarkably accurate in practice because the swap rate, being a weighted average of forward rates, tends toward normality by the central limit theorem and the log of a sum is approximately the sum of logs when the rates are similar in magnitude. This leads to Black's formula for swaptions:

Swaption=Aα,β(0)⋅[Sα,β(0)N(d1)−KN(d2)]\text{Swaption} = A_{\alpha,\beta}(0) \cdot [S_{\alpha,\beta}(0) N(d_1) - K N(d_2)]

where:

  • Aα,β(0)A_{\alpha,\beta}(0): initial annuity factor
  • Sα,β(0)S_{\alpha,\beta}(0): initial forward swap rate
  • KK: swaption strike rate
  • d1=ln⁡(Sα,β(0)/K)+12vS2vSd_1 = \frac{\ln(S_{\alpha,\beta}(0)/K) + \frac{1}{2}v_{S}^2}{v_S}
  • d2=d1−vSd_2 = d_1 - v_S
  • vSv_S: total swaption volatility (σswapTα\sigma_{swap}\sqrt{T_\alpha})

As with the caplet formula, this expression decomposes the value into interpretable components:

  • Sα,β(0)N(d1)S_{\alpha,\beta}(0) N(d_1) is the expected value of the floating leg conditional on exercise
  • KN(d2)K N(d_2) is the expected value of the fixed leg conditional on exercise
  • The annuity factor Aα,β(0)A_{\alpha,\beta}(0) discounts the future swap cash flows to a present lump sum

Since the swap rate behaves approximately as a weighted average of the forward rates, its variance can be derived from the covariances of the underlying forward rates. This is a direct application of the formula for the variance of a linear combination of random variables. The total swaption volatility squared vS2v_S^2 is approximated by:

vS2≈∑i=αβ−1∑j=αβ−1wiwjρijσiσjTαv_S^2 \approx \sum_{i=\alpha}^{\beta-1} \sum_{j=\alpha}^{\beta-1} w_i w_j \rho_{ij} \sigma_i \sigma_j T_\alpha

where:

  • wi=Li(0)Sα,β(0)∂Sα,β(0)∂Li(0)w_i = \frac{L_i(0)}{S_{\alpha,\beta}(0)} \frac{\partial S_{\alpha,\beta}(0)}{\partial L_i(0)}: weights representing the elasticity of the swap rate with respect to each forward rate
  • ρij\rho_{ij}: correlation between forward rates
  • σi\sigma_i: volatility of forward rate LiL_i
  • TαT_\alpha: time to swaption expiry

The weights wiw_i measure how sensitive the swap rate is to each underlying forward rate. These weights sum to one (approximately) and tend to be larger for rates near the middle of the swap than for rates at the ends. The double summation captures both the variance contributions from each rate (when i=ji = j) and the covariance contributions from pairs of rates (when i≠ji \neq j).

In[16]:
Code
def calculate_swap_rate(
    forward_rates, discount_factors, delta, start_idx, end_idx
):
    """
    Calculate forward swap rate from forward LIBOR rates.
    """
    # Annuity factor
    annuity = sum(
        delta * discount_factors[i + 1] for i in range(start_idx, end_idx)
    )

    # Swap rate = (P(0,T_alpha) - P(0,T_beta)) / Annuity
    # Using forward rates to get bond prices
    P_start = discount_factors[start_idx]
    P_end = discount_factors[end_idx]

    swap_rate = (P_start - P_end) / annuity
    return swap_rate, annuity


def black_swaption_price(
    swap_rate, strike, annuity, expiry, volatility, is_payer=True
):
    """
    Price a swaption using Black's formula.
    """
    if expiry <= 0:
        if is_payer:
            return annuity * max(swap_rate - strike, 0)
        else:
            return annuity * max(strike - swap_rate, 0)

    v = volatility * np.sqrt(expiry)
    d1 = (np.log(swap_rate / strike) + 0.5 * v**2) / v
    d2 = d1 - v

    if is_payer:
        price = annuity * (swap_rate * norm.cdf(d1) - strike * norm.cdf(d2))
    else:
        price = annuity * (strike * norm.cdf(-d2) - swap_rate * norm.cdf(-d1))

    return price


# Example: 1y into 5y payer swaption
# Build discount curve (flat 5%)
flat_rate = 0.05
delta = 0.5
maturities = np.arange(delta, 6.5, delta)  # Semi-annual out to 6 years
discount_factors = np.exp(-flat_rate * maturities)
discount_factors = np.insert(discount_factors, 0, 1.0)  # Add P(0,0) = 1

# Forward rates from discount factors
forward_rates = []
for i in range(len(maturities)):
    if i == 0:
        fwd = (1 / discount_factors[i + 1] - 1) / delta
    else:
        fwd = (discount_factors[i] / discount_factors[i + 1] - 1) / delta
    forward_rates.append(fwd)

# 1y into 5y swap (start at index 2 = 1 year, end at index 12 = 6 year)
start_idx = 2
end_idx = 12
swap_rate, annuity = calculate_swap_rate(
    forward_rates, discount_factors, delta, start_idx, end_idx
)
swaption_vol = 0.20
expiry = 1.0
strike = swap_rate  # ATM

swaption_price = black_swaption_price(
    swap_rate, strike, annuity, expiry, swaption_vol, is_payer=True
)
Out[17]:
Console
Forward Swap Rate (1y into 5y): 5.0630%
Annuity Factor: 4.155840
ATM Payer Swaption Price: 0.016760
Swaption Price (bps of notional): 167.60

The payer swaption price of approximately 1726 basis points represents the upfront premium required to purchase the right to pay 5% fixed on the swap. This value is derived from the volatility of the swap rate and the time to expiry. For context, this means that on a notional of $100 million, the swaption would cost approximately $1.726 million. The relatively high price reflects both the long tenor of the underlying swap and the substantial uncertainty about interest rates over the one-year option period.

Out[18]:
Visualization
3D surface plot showing payer swaption prices in basis points across a range of strike offsets and option expiries.
Black-formula price surface for a payer swaption on the same five-year underlying swap. Premiums are highest when the strike is below the forward swap rate and when the option has more time to expiry; they decline as the strike moves above ATM.

Monte Carlo Swaption Pricing

For more accurate pricing that captures the correlation structure, we can use Monte Carlo simulation under the LMM. This approach simulates the joint evolution of all forward rates, calculates the swap rate at expiry from the simulated rates, and averages the discounted payoffs across all paths. The Monte Carlo method naturally handles the complex dependence of the swap rate on multiple forward rates without requiring the approximations used in the analytical formula.

In[19]:
Code
def price_swaption_mc(
    lmm_paths,
    fixing_step,
    start_idx,
    end_idx,
    strike,
    delta,
    flat_rate,
    dt=0.01,
):
    """
    Price a payer swaption using Monte Carlo simulation from LMM paths.
    """
    num_paths = lmm_paths.shape[0]

    # Get forward rates at fixing time
    forward_rates_at_fixing = lmm_paths[:, fixing_step, :]

    # Calculate discount factors at fixing time for each path
    payoffs = []

    for path in range(num_paths):
        fwd_rates = forward_rates_at_fixing[path, :]

        # Build discount factors from forward rates
        df = [1.0]
        for i in range(len(fwd_rates)):
            df.append(df[-1] / (1 + delta * fwd_rates[i]))
        df = np.array(df)

        # Calculate swap rate and annuity
        # Note: df array indices: 0->start, 1->after L0, 2->after L1...
        # We must normalize by df[start_idx] to get value at swap start
        if start_idx < len(df) and end_idx < len(df):
            # Numeraire at swap start
            numeraire = df[start_idx]

            # Annuity normalized to swap start time
            # Sum of P(t, T_payment) = df[i+1] / df[start_idx]
            annuity_val = sum(
                delta * df[i + 1]
                for i in range(start_idx, min(end_idx, len(df) - 1))
            )
            annuity = annuity_val / numeraire

            # Swap rate = (P_start - P_end) / Annuity
            # (df[start_idx]/numeraire - df[end_idx]/numeraire) / (annuity_val/numeraire)
            # = (df[start_idx] - df[end_idx]) / annuity_val
            numerator = df[start_idx] - df[min(end_idx, len(df) - 1)]
            swap_rate_path = numerator / annuity_val if annuity_val > 0 else 0

            payoff = max(swap_rate_path - strike, 0) * annuity
        else:
            payoff = 0

        payoffs.append(payoff)

    payoffs = np.array(payoffs)

    # Discount back to today
    discount_to_today = np.exp(-flat_rate * fixing_step * dt)

    price = np.mean(payoffs) * discount_to_today
    std_error = np.std(payoffs) * discount_to_today / np.sqrt(num_paths)

    return price, std_error


# Price swaption using MC
fixing_step = 100  # 1 year
dt = 0.01
mc_price, mc_error = price_swaption_mc(
    lmm_paths,
    fixing_step,
    start_idx=1,
    end_idx=11,
    strike=0.05,
    delta=0.5,
    flat_rate=0.05,
    dt=dt,
)
Out[20]:
Console
Monte Carlo Swaption Price: 0.012394 (+/- 0.000287)
Black's Formula Price: 0.016760
Difference: -43.66 bps

The Monte Carlo price should be close to Black's formula for ATM options. Differences arise from the approximation in Black's formula and simulation noise. The standard error provides a measure of the Monte Carlo estimation uncertainty: with 5000 paths, we achieve reasonable precision, but production implementations often use many more paths combined with variance reduction techniques to achieve tighter confidence intervals.