Advanced Interest Rate Models (HJM and LMM) › Swaption Pricing Under the LMM
Quantitative FinanceAll chapters 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 for a swap starting at and ending at is the fixed rate that makes the swap have zero value at inception. It can be expressed as the ratio of two present values:
where:
- : price of bond maturing at swap start date
- : price of bond maturing at swap end date
- : present value of the fixed leg's annuity factor (PVBP)
The numerator 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 has payoff at expiry :
where:
- : annuity factor at expiry
- : forward swap rate at expiry
- : 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:
where:
- : initial annuity factor
- : initial forward swap rate
- : swaption strike rate
- : total swaption volatility ()
As with the caplet formula, this expression decomposes the value into interpretable components:
- is the expected value of the floating leg conditional on exercise
- is the expected value of the fixed leg conditional on exercise
- The annuity factor 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 is approximated by:
where:
- : weights representing the elasticity of the swap rate with respect to each forward rate
- : correlation between forward rates
- : volatility of forward rate
- : time to swaption expiry
The weights 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 ) and the covariance contributions from pairs of rates (when ).
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
)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.

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.
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,
)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.