Valuation of Interest Rate Derivatives › Caps and Floors

Michael BrenndoerferDecember 8, 202518 min read
Quantitative FinanceAll chapters
Part 7 of 15Interest Rate Models and Derivatives
Chapter 40Valuation of Interest Rate Derivatives › Caps and Floors
Reading section 3 of 13 · Caps and Floors · 18 minSection 3 of 13 · 18 min

Book contents

Section 3 of 13 · 18 min read

Valuation of Interest Rate Derivatives › Caps and FloorsOne section · Sections in this chapter3 / 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 cap is a portfolio of interest rate call options, each called a caplet, that provides you with payments whenever a reference rate exceeds a predetermined strike rate. A floor is the corresponding portfolio of put options, called floorlets, that pays when the reference rate falls below the strike. Understanding how these instruments work requires us to first examine the building blocks, namely individual caplets and floorlets, before assembling them into complete cap and floor structures.

Caplet and Floorlet Mechanics

To understand how caplets work, consider that you are a borrower with floating-rate debt who worries about rising interest rates. Each quarter, your interest payment resets based on market rates. If rates spike unexpectedly, you face a sudden increase in interest expense. A caplet provides insurance against exactly this scenario: it pays you whenever the market rate exceeds a predetermined ceiling.

Consider a caplet that settles at time TiT_i based on the interest rate observed at time Ti−1T_{i-1}. The timing distinction matters here: the rate is determined, or "fixed," at Ti−1T_{i-1}, but the actual payment occurs at TiT_i, typically three or six months later. This structure mirrors how floating-rate loans work, where the rate for each period is set at the beginning but interest is paid at the end. If the reference rate Li−1L_{i-1} (typically a LIBOR or SOFR rate) exceeds the strike rate KK, you receive a payment. The payment occurs at TiT_i and equals:

Caplet Payoff=N⋅τi⋅max⁡(Li−1−K,0)\text{Caplet Payoff} = N \cdot \tau_i \cdot \max(L_{i-1} - K, 0)

where:

  • NN: notional principal
  • τi\tau_i: day count fraction between Ti−1T_{i-1} and TiT_i
  • Li−1L_{i-1}: reference interest rate (e.g., LIBOR or SOFR) observed at time Ti−1T_{i-1}
  • KK: strike rate

Notice how the payoff is structured. The max⁡(Li−1−K,0)\max(L_{i-1} - K, 0) term ensures the payment is never negative: when rates are below the strike, the caplet simply expires worthless, and you owe nothing. When rates exceed the strike, you receive the difference. The notional principal NN and day count fraction τi\tau_i convert this rate difference into an actual dollar amount, following the same conventions used in the underlying floating-rate loan.

The caplet payoff looks exactly like a call option on an interest rate. Just as a stock call option pays the excess of the stock price over the strike, a caplet pays the excess of the interest rate over the strike rate. This allows us to adapt option pricing theory to interest rates.

The floorlet payoff follows the same structure but uses a put option:

Floorlet Payoff=N⋅τi⋅max⁡(K−Li−1,0)\text{Floorlet Payoff} = N \cdot \tau_i \cdot \max(K - L_{i-1}, 0)

where:

  • NN: notional principal
  • τi\tau_i: day count fraction
  • KK: strike rate
  • Li−1L_{i-1}: reference interest rate

A floorlet protects against falling rates rather than rising rates. Consider that you have made fixed-rate loans funded by floating-rate deposits. If rates fall, you continue receiving fixed payments from borrowers but pay less on deposits, which is favorable. However, if rates rise, the opposite occurs. A floorlet provides the mirror-image protection to a cap, paying you when rates fall below the strike level.

A cap consists of multiple caplets covering consecutive periods. For example, a 3-year cap on quarterly rates would contain 12 caplets, one for each quarter. The first caplet typically starts at the first reset date (often 3 or 6 months from inception), which means the first fixing is already known at inception and the corresponding caplet has no optionality. This first caplet is often called the "stub" period, and market conventions vary on whether it is included in the quoted cap price.

Out[2]:
Visualization
Line chart of caplet payoff in thousands of dollars versus reference rate, showing zero payoff below 5% strike and linearly increasing payoff above it.
Caplet payoff diagram showing the asymmetric payoff structure. The caplet pays when the reference rate exceeds the strike (5%), and gives one-sided protection with zero downside.
Line chart of floorlet payoff in thousands of dollars versus reference rate, showing linearly decreasing payoff below 5% strike and zero payoff above it.
Floorlet payoff diagram showing the mirror-image structure to a caplet. The floorlet pays when the reference rate falls below the strike (5%), and gives protection against falling rates.

Black's Model for Caplet Pricing

To value these payments, we use Black's model, an extension of the Black-Scholes framework.

Black's model treats the forward rate for each period as the underlying asset. Rather than modeling how interest rates evolve continuously over time, Black's approach takes the forward rate as given and asks: what is the fair price for an option on this forward rate?

To make this approach work, Black's model assumes the forward rate for each period follows a lognormal distribution under a specific probability measure, known as the forward measure associated with that period's payment date. The lognormal assumption means that forward rates are always positive and that percentage changes in rates are normally distributed. While this assumption has limitations, particularly when rates are near zero, it provides tractable closed-form solutions that form the foundation of market practice.

Black's Model for Caplets

Under Black's model, the price of a caplet paying at time TiT_i based on the rate set at Ti−1T_{i-1} is:

Caplet=N⋅τi⋅P(0,Ti)⋅[Fi⋅N(d1)−K⋅N(d2)]\text{Caplet} = N \cdot \tau_i \cdot P(0, T_i) \cdot [F_i \cdot N(d_1) - K \cdot N(d_2)]

where:

  • NN: notional principal
  • τi\tau_i: day count fraction
  • P(0,Ti)P(0, T_i): today's discount factor to time TiT_i
  • FiF_i: forward rate from Ti−1T_{i-1} to TiT_i
  • KK: strike rate
  • N(⋅)N(\cdot): cumulative standard normal distribution function
  • σi\sigma_i: volatility of the forward rate
  • Ti−1T_{i-1}: time to rate fixing
  • d1=ln⁡(Fi/K)+12σi2Ti−1σiTi−1d_1 = \frac{\ln(F_i/K) + \frac{1}{2}\sigma_i^2 T_{i-1}}{\sigma_i \sqrt{T_{i-1}}}: measures how many standard deviations the forward rate is above the strike, adjusted for time and volatility
  • d2=d1−σiTi−1d_2 = d_1 - \sigma_i \sqrt{T_{i-1}}: a risk-adjusted measure accounting for volatility over the time to fixing
  • FiN(d1)F_i N(d_1): expected value of the forward rate, conditional on the option being in-the-money
  • KN(d2)K N(d_2): strike rate weighted by the probability of exercise

Let's carefully unpack each component of this formula to build intuition for why it takes this form.

The term N⋅τi⋅P(0,Ti)N \cdot \tau_i \cdot P(0, T_i) outside the brackets converts the rate-based payoff into a present value. The notional and day count fraction translate a rate difference into a cash amount, while the discount factor P(0,Ti)P(0, T_i) brings this future cash payment back to today's dollars. Notice that we discount to the payment date TiT_i, not the fixing date Ti−1T_{i-1}, because that is when cash changes hands.

Inside the brackets, we find the familiar Black-Scholes structure adapted to forward rates. The term Fi⋅N(d1)F_i \cdot N(d_1) represents the expected payoff from the forward rate component, weighted by the probability that the option finishes in the money (after a specific risk adjustment). The term K⋅N(d2)K \cdot N(d_2) represents what we expect to "give up" by paying the strike, again weighted by exercise probability. The difference between these two terms gives the option's expected payoff under the forward measure.

The formula closely resembles the Black-Scholes formula from Chapter 6, with key differences. The underlying is a forward rate rather than a stock price, and the discount factor P(0,Ti)P(0, T_i) appears outside the main expression rather than being embedded through a risk-free rate term. The time to expiration is Ti−1T_{i-1}, the rate-setting date, not TiT_i, the payment date. This distinction is critical: the option's uncertainty resolves when the rate is fixed, even though the payment comes later.

The parameters d1d_1 and d2d_2 measure how far the forward rate sits from the strike in standardized units. When FiF_i equals KK, the caplet is at-the-money, and d1d_1 and d2d_2 depend only on volatility and time. When FiF_i exceeds KK, the forward rate is above the strike, making the caplet in-the-money and increasing both d1d_1 and d2d_2. The volatility parameter σi\sigma_i captures how uncertain we are about where the forward rate will settle by the fixing date. Higher volatility means more dispersion, which increases option value because you benefit from upside while being protected from downside.

For floorlets, the pricing formula uses the put option version:

Floorlet=N⋅τi⋅P(0,Ti)⋅[K⋅N(−d2)−Fi⋅N(−d1)]\text{Floorlet} = N \cdot \tau_i \cdot P(0, T_i) \cdot [K \cdot N(-d_2) - F_i \cdot N(-d_1)]

where:

  • NN: notional principal
  • τi\tau_i: day count fraction
  • P(0,Ti)P(0, T_i): discount factor to time TiT_i
  • FiF_i: forward rate
  • KK: strike rate
  • σi\sigma_i: volatility of the forward rate
  • N(−d1),N(−d2)N(-d_1), N(-d_2): probabilities associated with the put option calculation

The floorlet formula mirrors the caplet formula with the roles reversed. Here N(−d2)N(-d_2) represents the probability of exercise (rates falling below the strike), and the terms are arranged so that the payoff is positive when KK exceeds FiF_i. The symmetry between the formulas reflects the relationship between call and put options.

Cap and Floor Valuation

With the caplet pricing formula in hand, valuing a complete cap becomes straightforward. Because each caplet pays independently based on the rate for its specific period, there is no path dependence or interaction between caplets. The total cap value is simply the sum of its parts.

The price of a complete cap equals the sum of its constituent caplet prices:

Cap=∑i=1nCapleti\text{Cap} = \sum_{i=1}^{n} \text{Caplet}_i

where:

  • nn: total number of periods in the cap
  • Capleti\text{Caplet}_i: price of the ii-th caplet

This additive structure reflects the fact that each caplet is a separate option that either pays off or expires worthless independently of the others. A three-year quarterly cap contains twelve such options, and you pay upfront for the entire portfolio of protection.

In practice, we often quote caps using a single implied volatility for all caplets, called a flat volatility. This doesn't mean the market believes all forward rates have the same volatility. Instead, it's a quoting convention that simplifies communication. When you quote "20% vol on a 3-year cap," you mean that plugging 20% into Black's formula for every caplet reproduces the quoted cap price. More sophisticated analysis uses spot volatilities, where each caplet has its own volatility parameter calibrated to market prices. The spot volatility curve typically shows higher volatilities for shorter-dated caplets and lower volatilities for longer-dated ones, which reflects the mean-reverting nature of interest rates.

In[3]:
Code
import numpy as np
from scipy.stats import norm


def black_caplet_price(
    forward_rate,
    strike,
    volatility,
    time_to_expiry,
    discount_factor,
    day_count_fraction,
    notional=1.0,
):
    """
    Price a caplet using Black's model.

    Parameters:
    - forward_rate: Forward rate for the caplet period
    - strike: Strike rate (as decimal, e.g., 0.05 for 5%)
    - volatility: Implied volatility of the forward rate
    - time_to_expiry: Time to rate fixing (in years)
    - discount_factor: Discount factor to payment date
    - day_count_fraction: Year fraction for the caplet period
    - notional: Notional principal
    """
    if time_to_expiry <= 0:
        # Expired caplet, return intrinsic value
        return (
            notional
            * day_count_fraction
            * discount_factor
            * max(forward_rate - strike, 0)
        )

    d1 = (
        np.log(forward_rate / strike) + 0.5 * volatility**2 * time_to_expiry
    ) / (volatility * np.sqrt(time_to_expiry))
    d2 = d1 - volatility * np.sqrt(time_to_expiry)

    price = (
        notional
        * day_count_fraction
        * discount_factor
        * (forward_rate * norm.cdf(d1) - strike * norm.cdf(d2))
    )
    return price


def black_floorlet_price(
    forward_rate,
    strike,
    volatility,
    time_to_expiry,
    discount_factor,
    day_count_fraction,
    notional=1.0,
):
    """
    Price a floorlet using Black's model.
    """
    if time_to_expiry <= 0:
        return (
            notional
            * day_count_fraction
            * discount_factor
            * max(strike - forward_rate, 0)
        )

    d1 = (
        np.log(forward_rate / strike) + 0.5 * volatility**2 * time_to_expiry
    ) / (volatility * np.sqrt(time_to_expiry))
    d2 = d1 - volatility * np.sqrt(time_to_expiry)

    price = (
        notional
        * day_count_fraction
        * discount_factor
        * (strike * norm.cdf(-d2) - forward_rate * norm.cdf(-d1))
    )
    return price

Let's price a 2-year quarterly cap with a 5% strike. We'll assume a flat yield curve at 4% for simplicity:

In[4]:
Code
# Market data setup
strike = 0.05  # 5% strike rate
notional = 1_000_000  # $1 million notional
flat_vol = 0.20  # 20% implied volatility
flat_rate = 0.04  # 4% flat yield curve

# Generate quarterly periods for 2 years (8 periods)
# Payment dates at 0.25, 0.5, 0.75, ..., 2.0 years
payment_dates = np.arange(0.25, 2.25, 0.25)
day_count_fractions = np.full(len(payment_dates), 0.25)

# Forward rates (in a flat curve, forward = spot)
forward_rates = np.full(len(payment_dates), flat_rate)

# Discount factors
discount_factors = np.exp(-flat_rate * payment_dates)

# Time to expiry for each caplet (rate fixing is at start of each period)
times_to_expiry = payment_dates - 0.25

# Price each caplet
caplet_prices = []
for i in range(len(payment_dates)):
    price = black_caplet_price(
        forward_rate=forward_rates[i],
        strike=strike,
        volatility=flat_vol,
        time_to_expiry=times_to_expiry[i],
        discount_factor=discount_factors[i],
        day_count_fraction=day_count_fractions[i],
        notional=notional,
    )
    caplet_prices.append(price)

caplet_prices = np.array(caplet_prices)
cap_price = caplet_prices.sum()
Out[5]:
Console
2-Year Quarterly Cap Valuation
==================================================
Strike Rate: 5.00%
Implied Volatility: 20.00%
Notional: $1,000,000

Caplet Breakdown:
--------------------------------------------------
Period     Expiry     Forward    Price ($)   
--------------------------------------------------
1          0.00       4.00%      0.00        
2          0.25       4.00%      4.89        
3          0.50       4.00%      37.50       
4          0.75       4.00%      86.61       
5          1.00       4.00%      141.01      
6          1.25       4.00%      196.15      
7          1.50       4.00%      250.17      
8          1.75       4.00%      302.28      
--------------------------------------------------
Total Cap Price:               $1,018.61

The first caplet has zero value because its fixing occurs at inception (time 0), and with the forward rate at 4% below the 5% strike, it's out of the money with no time for rates to move. Subsequent caplets have progressively higher values due to longer time to expiration, giving more opportunity for rates to rise above the strike. This pattern illustrates a fundamental principle of option pricing: time has value because it provides opportunity for favorable movements in the underlying.

Out[6]:
Visualization
Bar chart showing caplet prices in dollars for each of the 8 quarterly periods of a 2-year cap, with dollar value labels on each bar and increasing prices from period 1 (zero) through period 8.
Individual caplet prices across the 8 quarterly periods of a 2-year cap. The first caplet has zero value because it fixes immediately at inception. Subsequent caplets increase in value with longer time to expiry, which reflects greater opportunity for rates to rise above the 5% strike.

Cap-Floor Parity

A fundamental relationship connects caps, floors, and swaps. This relationship, known as cap-floor parity, provides a consistency check for pricing.

Recall from Chapter 12 that a payer swap involves paying fixed and receiving floating. If we decompose this:

  • Buying a cap with strike KK gives us the positive part of (Li−K)(L_i - K)
  • Selling a floor with strike KK gives us the negative part of (Li−K)(L_i - K)

The combination yields exactly (Li−1−K)(L_{i-1} - K) for each period. To see why this works, consider what happens in each possible state of the world. When the floating rate exceeds the strike, the cap pays us the difference while the floor expires worthless. When the floating rate falls below the strike, the cap expires worthless but we owe the floor buyer the difference. In either case, our net position equals the floating rate minus the strike.

We can see this mathematically by decomposing the payoff across the two possible states for the reference rate Li−1L_{i-1}:

Payoff=max⁡(Li−1−K,0)−max⁡(K−Li−1,0)={(Li−1−K)−0=Li−1−Kif Li−1≥K0−(K−Li−1)=Li−1−Kif Li−1<K=Li−1−K\begin{aligned} \text{Payoff} &= \max(L_{i-1} - K, 0) - \max(K - L_{i-1}, 0) \\ &= \begin{cases} (L_{i-1} - K) - 0 = L_{i-1} - K & \text{if } L_{i-1} \ge K \\ 0 - (K - L_{i-1}) = L_{i-1} - K & \text{if } L_{i-1} < K \end{cases} \\ &= L_{i-1} - K \end{aligned}

where:

  • Li−1L_{i-1}: reference interest rate observed at time Ti−1T_{i-1}
  • KK: strike rate

Regardless of rate movements, a long cap and short floor deliver the floating rate minus the strike. This is precisely the payoff of receiving floating and paying fixed in a swap.

The right-hand side represents the floating minus fixed cashflow of a payer swap. Summing these values gives us cap-floor parity:

Cap(K)−Floor(K)=Payer Swap(K)\text{Cap}(K) - \text{Floor}(K) = \text{Payer Swap}(K)

where:

  • Cap(K)\text{Cap}(K): price of the cap with strike KK
  • Floor(K)\text{Floor}(K): price of the floor with strike KK
  • Payer Swap(K)\text{Payer Swap}(K): value of a payer swap with fixed rate KK

When KK equals the current swap rate, the payer swap has zero value (it's at-the-money), so the cap and floor have equal prices. This at-the-money volatility is often the benchmark for quoting interest rate option prices. The relationship also provides a powerful arbitrage constraint: if cap and floor prices diverge from parity, you can profit by buying the underpriced combination and selling the overpriced one.

In[7]:
Code
# Verify cap-floor parity
floorlet_prices = []
for i in range(len(payment_dates)):
    price = black_floorlet_price(
        forward_rate=forward_rates[i],
        strike=strike,
        volatility=flat_vol,
        time_to_expiry=times_to_expiry[i],
        discount_factor=discount_factors[i],
        day_count_fraction=day_count_fractions[i],
        notional=notional,
    )
    floorlet_prices.append(price)

floor_price = np.sum(floorlet_prices)

# Calculate swap value (receive float, pay fixed at strike)
# Swap value = sum of (forward - strike) * dcf * df
swap_value = sum(
    notional
    * day_count_fractions[i]
    * discount_factors[i]
    * (forward_rates[i] - strike)
    for i in range(len(payment_dates))
)
Out[8]:
Console
Cap-Floor Parity Verification
==================================================
Cap Price:    $1,018.61
Floor Price:  $20,143.58
Cap - Floor:  $-19,124.97
Swap Value:   $-19,124.97

Parity Error: $0.0000
Out[9]:
Visualization
Line chart showing cap and floor payoffs in thousands of dollars versus reference rate, with both lines meeting at the 5% strike.
Cap and floor payoff profiles showing the asymmetric one-sided protection each provides. The cap pays when rates exceed the 5% strike and the floor pays when rates fall below it.
Line chart showing cap minus floor payoff and payer swap payoff overlapping exactly, showing cap-floor parity.
Cap minus floor payoff overlaid on a payer swap payoff, showing cap-floor parity: the combination of long cap and short floor produces the identical linear payoff of a payer swap.