Interest Rate Swap Valuation and Applications › Swap Risk Measures

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

Book contents

Section 12 of 17 · 6 min read

Interest Rate Swap Valuation and Applications › Swap Risk MeasuresOne section · Sections in this chapter12 / 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

Understanding how swap values respond to rate changes matters for risk management. You need to know how much money could be gained or lost as interest rates fluctuate. The key measures are DV01 (dollar value of a basis point) and swap duration, both of which quantify interest rate sensitivity.

DV01: Dollar Value of a Basis Point

DV01 is the signed local, first-order value sensitivity per one basis point (0.01%) parallel shift in the specified yield curve. It approximates a small value change; finite moves are repriced and can differ because of curvature. Comparisons across positions require the same shock, fixing convention and normalization, or explicit conversion to a common basis. The mathematical definition is:

DV01=−∂V∂y×0.0001\text{DV01} = -\frac{\partial V}{\partial y} \times 0.0001

where:

  • DV01\text{DV01}: dollar value of a basis point
  • VV: value of the swap
  • yy: yield curve level (interest rate)
  • 0.00010.0001: one basis point scaling factor

The minus sign makes DV01 positive for a position that gains from a small decline in the specified curve. The five-year receive-fixed example below has positive DV01 under its stated curve and fixed-current-coupon convention. That is a property of its remaining cash-flow exposure, not of the receive-fixed label alone. Preserve any already observed floating fixing in rate scenarios.

For a counterexample, take one remaining annual payment with notional N=100N=100, fixed rate K=1%K=1\%, known floating rate L=5%L=5\%, and discount factor D=1/1.05D=1/1.05. The net cash flow is N(K−L)=−4N(K-L)=-4, so its value is V=−4D=−3.80952V=-4D=-3.80952. A lower continuous zero rate increases DD and makes this negative value more negative. With one year remaining, the signed local DV01 is V×0.0001=−0.000380952V\times0.0001=-0.000380952 per 100 notional.

In[19]:
Code
def calculate_swap_dv01(notional, fixed_rate, maturities, zero_rates, bump=0.0001):
    """Signed one-basis-point sensitivity for this annual-coupon example.

    Value immediately after the current annual floating fixing. Set that
    fixing from the base curve once and preserve the known coupon in every
    parallel continuous-zero-rate shock. Future floating coupons continue
    to reset. The supplied dates are the complete annual grid 1,...,T.
    """
    maturities = np.asarray(maturities, dtype=float)
    zero_rates = np.asarray(zero_rates, dtype=float)
    if (
        maturities.ndim != 1
        or zero_rates.ndim != 1
        or len(maturities) != len(zero_rates)
        or not len(maturities)
        or not np.all(np.isfinite(maturities))
        or not np.all(np.isfinite(zero_rates))
        or not np.allclose(maturities, np.arange(1, len(maturities) + 1))
        or not np.isfinite(notional)
        or notional <= 0
        or not np.isfinite(fixed_rate)
        or not np.isfinite(bump)
        or bump <= 0
    ):
        raise ValueError(
            "Use a full annual grid, finite inputs, positive notional and bump."
        )
    df_original = np.exp(-zero_rates * maturities)
    df_up = np.exp(-(zero_rates + bump) * maturities)
    df_down = np.exp(-(zero_rates - bump) * maturities)
    known_first_fixing = np.expm1(zero_rates[0] * maturities[0])

    def swap_value(discounts):
        fixed_bond = notional * (fixed_rate * np.sum(discounts) + discounts[-1])
        floating_bond = notional * (1 + known_first_fixing) * discounts[0]
        return fixed_bond - floating_bond

    v_original = swap_value(df_original)
    v_up = swap_value(df_up)
    v_down = swap_value(df_down)
    dv01 = (v_down - v_up) / (2 * bump) * 0.0001
    return dv01, v_original, v_up, v_down


# Calculate DV01 for a 5-year swap
maturities_5y = np.array([1, 2, 3, 4, 5])
rates_5y = np.array([0.045, 0.048, 0.050, 0.052, 0.053])

dv01, v_base, v_up, v_down = calculate_swap_dv01(
    10_000_000, 0.052, maturities_5y, rates_5y
)
Out[20]:
Console
Swap DV01 Calculation
==================================================

5-Year Receive-Fixed Swap
  Notional:     $10,000,000
  Fixed Rate:   5.20%

Scenario Analysis:
  Base Value:      $  -89,557.62
  Rates +1bp:      $  -93,039.50
  Rates -1bp:      $  -86,073.69

  DV01:            $    3,482.91

Swap Duration

A par swap has zero net market value, so ordinary modified duration, −∂V/∂y /V-\partial V/\partial y\,/V, is undefined there. We use a different, explicit reference: notional-normalized rate sensitivity,

DN=−1N∂V∂y=DV01N×0.0001.D_N = -\frac{1}{N}\frac{\partial V}{\partial y} = \frac{\text{DV01}}{N\times0.0001}.

This has units of years and does not depend on the position's notional scale. Net dollar sensitivity is fixed-bond sensitivity minus floating-bond sensitivity. In this annual example, the current floating coupon is already fixed, so the corresponding floating bond retains exposure through its next payment. It is not held at par in every shocked scenario. A short time to the next reset can justify a small-floating-duration approximation, but that is a separate stated approximation.

In[21]:
Code
# Calculate notional-normalized sensitivity D_N
def calculate_swap_duration(dv01, notional):
    """Return D_N = DV01 / (notional * 0.0001), not duration divided by net MTM."""
    if not np.isfinite(notional) or notional <= 0 or not np.isfinite(dv01):
        raise ValueError("Use finite DV01 and positive finite notional.")
    return dv01 / (notional * 0.0001)


swap_duration = calculate_swap_duration(dv01, 10_000_000)
Out[22]:
Console

Swap Duration Analysis
==================================================
  5-Year Notional-Normalized Sensitivity: 3.48 years
Out[23]:
Visualization
Bar chart of signed DV01 in thousands of dollars for fixed-5% receive-fixed swaps with 1, 2, 3, 5, 7 and 10 annual payments, using the declared curve and unchanged first fixing.
Signed DV01 for hypothetical fixed-5% receive-fixed swaps on the calibrated annual curve, holding the current observed floating coupon fixed under parallel continuous-zero shocks. Compare actual bar heights rather than assuming proportional growth with tenor.
Bar chart of notional-normalized sensitivity in years for the same six swap tenors. Numeric endpoints depend on the calibrated curve and known-fixing convention.
Notional-normalized sensitivity D_N = DV01/(N × 0.0001) for the same swaps and fixing convention. This is not ordinary duration divided by the swap's net market value; a par swap has zero net value.