Skip to content
All library documents

Calculating Swap Key-Rate DV01 by Bumping Curve Tenors

Article Quant Q&A · Author: amitbisai

Summary

The document explains how to estimate tenor-specific DV01, or key-rate risk, for a swap in QuantLib. Instead of shifting the entire yield curve to obtain aggregate sensitivity, it reprices the swap after increasing each market par-rate quote individually, then records the change in value for that tenor. The example builds a curve from swap-rate helpers, prices a vanilla swap, and reports the resulting sensitivities across the quoted maturities.

The method is presented as a way to create a hedge basket that offsets PV exposure at individual curve points. The example shows most sensitivity concentrated around the swap’s maturity, with small impacts at other listed tenors. It is a one-sided bump illustration and uses par-rate quotes; the document notes that zero rates or forward rates could be bumped instead. Results depend on the curve construction and interpolation, and the post does not provide a general treatment of bump size, central differences, or validation for other swap conventions.

Key ideas

  • Key-rate DV01 can be estimated by repricing a swap after bumping one curve tenor at a time.
  • The example applies bumps to par-rate quotes used to construct a QuantLib yield curve.
  • Individual tenor sensitivities can help build a hedge basket that offsets curve exposure point by point.
  • Zero rates, par rates, or forward rates can be used as the bumped curve inputs.
  • The reported risk depends on curve construction and the chosen bumping approach.

Tags

Full text
# Calculate tenor wise DV01 of a Swap in Quantlib Python, i.e. Key-rate Duration


# Calculate tenor wise DV01 of a Swap in Quantlib Python, i.e. Key-rate Duration












Is there a way I can get DV01 breakup of a Swap across different tenors of the deal in Quantlib-Python. The question asked here, follows the basic method of re-valuing the swap by shifting the curve through 1-bips and summing positive and negative impacts.

By the above method we can get net DV01, for each leg(fixed/float) of a derivative instrument. But if we drill-down we could see that net DV01 of a leg is an aggregration of differential DV01s accrued at tenor point where cash flow occurs (each cash flow tenor point might not have DV contribution). In short, we generally calculate Modified Duration, but I am looking for Key-rate Duration.

I have to create a hedge basket, where I get differential DV01s for each tenor points. Such that I can buy/sell instruments to net the PV impact for individual tenors.

As an illustration, this is how my Pricer provides tenor-wise DV01s.

Is there a function/module which does this, or it has to be created from the scratch. I have done the steps explained in this post, but it illustrates for a flat rate curve. I need to evaluate for a swap with floating rates. I am not sure how it will handle interpolation of floating rates.

## Answer by David Duarte (score 2, accepted)

https://quant.stackexchange.com/a/50728

You would have to "create it from scratch" although it's not too complicated. What you want to do is value your swap with your market curve and then revalue it after shifting each tenor. The difference between the market value and the value you get after bumping a particular tenor will be the key risk for that tenor.

There are different ways to do this. You could bump the zero rates, the par rates or forward rates.

In this example I am bumping the par rates.

```
import QuantLib as ql

today = ql.Date().todaysDate()
tenors = (1,2,3,4,5,6,7)
quotes = [ql.SimpleQuote(0.01) for n in tenors]
handles = [ql.QuoteHandle(quote) for quote in quotes]
dates = [today + ql.Period(y, ql.Years) for y in tenors]

helpers = []
yts = ql.RelinkableYieldTermStructureHandle()
euribor6m = ql.Euribor6M(yts)
for quote, tenor in zip(handles, tenors):
    helpers.append( ql.SwapRateHelper(quote,
                        ql.Period(tenor, ql.Years), ql.TARGET(),
                        ql.Annual, ql.Unadjusted,
                        ql.Thirty360(ql.Thirty360.BondBasis),
                        euribor6m)
                  )
curve = ql.PiecewiseLogCubicDiscount(0, ql.TARGET(), helpers, ql.Actual365Fixed())
yts.linkTo(curve)
engine = ql.DiscountingSwapEngine(yts)

swap = ql.MakeVanillaSwap(ql.Period('5y'), euribor6m, 0.01, ql.Period('0d'), Nominal=5e6)
swap.setPricingEngine(engine)

npv = swap.NPV()
key_risk = []
for quote in quotes:
    value = quote.value()
    quote.setValue(value + 0.0001)
    key_risk.append( npv - swap.NPV() )
    quote.setValue(value)

for tenor, risk in zip(tenors, key_risk):
    print("{}y: {:>12,.2f}".format(tenor, risk))
```

The output would be:

```
1y:         -0.00
2y:          0.00
3y:         -0.08
4y:          0.05
5y:     -2,426.15
6y:         -0.00
7y:         -0.00
```

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.