Calculating Swap DV01 and Distinguishing It from Fixed-Leg BPS
Summary
The document shows how to estimate an interest rate swap’s DV01 in QuantLib by repricing the swap after parallel upward and downward shifts to its yield curves, then taking the centered change in NPV. It explains why this result should not be compared directly with the fixed leg’s BPS. DV01 measures the effect of changing the curve, which changes discount factors and floating-rate forecasts; fixed-leg BPS measures the effect of changing the contractual fixed coupon while holding the curve unchanged. To find the fixed leg’s curve sensitivity, the answer reprices its cash flows under shifted discount curves.
The code example uses one curve for both forecasting and discounting. The answer identifies this as a simplifying assumption and notes that market practice uses separate forecast and discount curves. Whether both curves should be shifted together depends on the intended risk measure. The calculation is therefore useful as an illustration, but its interpretation depends on curve construction and the chosen shock convention.
Key ideas
- Swap DV01 can be estimated by repricing under positive and negative parallel curve shifts.
- Fixed-leg BPS measures coupon-rate sensitivity, whereas fixed-leg DV01 measures discount-curve sensitivity.
- The floating leg’s value can change when curve shifts alter projected fixings as well as discount factors.
- Using one curve for forecasting and discounting is a simplification; separate curves are common in practice.
- The meaning of a swap DV01 depends on which curves are shocked and how they are shifted.
Tags
Full text
# Use QuantLib Python to calculate Swap DV01
# Use QuantLib Python to calculate Swap DV01
I would want to use QuantLib Python to calculate DV01 of an interest rate swap.
Initially I was thinking of calculating the fixed leg DV01 and floating leg DV01 separately, then add both legs DV01 together to get the swap DV01. However, I don't know how to calculate the floating leg DV01 using QuantLib Python.
In the end I took a different approach to calculate interest rate swap DV01 using the following code:
```
from QuantLib import *
# global data
calendar = TARGET()
todaysDate = Date(6,November,2001);
Settings.instance().evaluationDate = todaysDate
settlementDate = Date(8,November,2001);
# market quotes
deposits = { (1,Weeks): 0.0382,
(1,Months): 0.0372,
(3,Months): 0.0363,
(6,Months): 0.0353,
(9,Months): 0.0348,
(1,Years): 0.0345 }
swaps = { (2,Years): 0.037125,
(3,Years): 0.0398,
(5,Years): 0.0443,
(10,Years): 0.05165,
(15,Years): 0.055175 }
# convert them to Quote objects
for n,unit in deposits.keys():
deposits[(n,unit)] = SimpleQuote(deposits[(n,unit)])
for n,unit in swaps.keys():
swaps[(n,unit)] = SimpleQuote(swaps[(n,unit)])
# build rate helpers
dayCounter = Actual360()
settlementDays = 2
depositHelpers = [ DepositRateHelper(QuoteHandle(deposits[(n,unit)]),
Period(n,unit), settlementDays,
calendar, ModifiedFollowing,
False, dayCounter)
for n, unit in [(1,Weeks),(1,Months),(3,Months),
(6,Months),(9,Months),(1,Years)] ]
fixedLegFrequency = Annual
fixedLegTenor = Period(1,Years)
fixedLegAdjustment = Unadjusted
fixedLegDayCounter = Thirty360()
floatingLegFrequency = Semiannual
floatingLegTenor = Period(6,Months)
floatingLegAdjustment = ModifiedFollowing
swapHelpers = [ SwapRateHelper(QuoteHandle(swaps[(n,unit)]),
Period(n,unit), calendar,
fixedLegFrequency, fixedLegAdjustment,
fixedLegDayCounter, Euribor6M())
for n, unit in swaps.keys() ]
# term structure handles
discountTermStructure = RelinkableYieldTermStructureHandle()
forecastTermStructure = RelinkableYieldTermStructureHandle()
# term-structure construction
helpers = depositHelpers + swapHelpers
depoSwapCurve = PiecewiseFlatForward(settlementDate, helpers, Actual360())
swapEngine = DiscountingSwapEngine(discountTermStructure)
# 5Y Swap
nominal = 1000000
maturity = calendar.advance(settlementDate,5,Years)
fixedLegFrequency = Annual
fixedLegAdjustment = Unadjusted
fixedLegDayCounter = Thirty360()
fixedRate = 0.04
floatingLegFrequency = Semiannual
spread = 0.0
fixingDays = 2
index = Euribor6M(forecastTermStructure)
floatingLegAdjustment = ModifiedFollowing
floatingLegDayCounter = index.dayCounter()
fixedSchedule = Schedule(settlementDate, maturity,
fixedLegTenor, calendar,
fixedLegAdjustment, fixedLegAdjustment,
DateGeneration.Forward, False)
floatingSchedule = Schedule(settlementDate, maturity,
floatingLegTenor, calendar,
floatingLegAdjustment, floatingLegAdjustment,
DateGeneration.Forward, False)
swap = VanillaSwap(VanillaSwap.Receiver, nominal,
fixedSchedule, fixedRate, fixedLegDayCounter,
floatingSchedule, index, spread,
floatingLegDayCounter)
swap.setPricingEngine(swapEngine)
discountTermStructure.linkTo(depoSwapCurve)
forecastTermStructure.linkTo(depoSwapCurve)
print('Fixed Leg DV01')
print(swap.fixedLegBPS())
shift = 0.0001
temp_fyc_handle = YieldTermStructureHandle(depoSwapCurve)
temp_dyc_handle = YieldTermStructureHandle(depoSwapCurve)
shiftedForwardCurve = ZeroSpreadedTermStructure(temp_fyc_handle, QuoteHandle(SimpleQuote(shift)))
shiftedDiscountCurve = ZeroSpreadedTermStructure(temp_dyc_handle, QuoteHandle(SimpleQuote(shift)))
discountTermStructure.linkTo(shiftedDiscountCurve)
forecastTermStructure.linkTo(shiftedForwardCurve)
P_p = swap.NPV()
temp_fyc_handle = YieldTermStructureHandle(depoSwapCurve)
temp_dyc_handle = YieldTermStructureHandle(depoSwapCurve)
shiftedForwardCurve = ZeroSpreadedTermStructure(temp_fyc_handle, QuoteHandle(SimpleQuote(-shift)))
shiftedDiscountCurve = ZeroSpreadedTermStructure(temp_dyc_handle, QuoteHandle(SimpleQuote(-shift)))
discountTermStructure.linkTo(shiftedDiscountCurve)
forecastTermStructure.linkTo(shiftedForwardCurve)
P_m = swap.NPV()
dv01 = (P_m - P_p) / 2.0
print('Swap DV01')
print(dv01)
```
Am I correct in how to use QuantLib Python to calculate interest rate swap DV01? The output of the code above shows that the fixed leg DV01 is smaller than the entire swap's DV01. Is that right?
## Answer by Luigi Ballabio (score 13, accepted)
https://quant.stackexchange.com/a/37117
No, I'm afraid you're comparing apples with oranges. Your calculation of the DV01 of the swap is correct (with a caveat, see below), but the figure returned from `swap.fixedLegBPS` is not comparable.
The DV01 tells you what happens to the NPV if the interest-rate curve change; in the case of the fixed leg, this affects the discount factors used to discount the coupon amounts, but not the amounts themselves which are fixed.
The BPS tells you what happens to the NPV if the rate of the fixed coupons increases by 1 bps and the interest rates (and thus the discount factors) stay the same. It's useful to calculate the fair rate, or while you're defining the deal, but probably not so much once the swap is built and its rate is fixed. So: different things.
If you want to calculate the DV01 of the fixed leg alone, you can do something like:
```
shift = 0.0001
temp_dyc_handle = YieldTermStructureHandle(depoSwapCurve)
shiftedDiscountCurve = ZeroSpreadedTermStructure(temp_dyc_handle, QuoteHandle(SimpleQuote(shift)))
discountTermStructure.linkTo(shiftedDiscountCurve)
P_p = CashFlows.npv(swap.fixedLeg(), discountTermStructure, False, settlementDate)
temp_dyc_handle = YieldTermStructureHandle(depoSwapCurve)
shiftedDiscountCurve = ZeroSpreadedTermStructure(temp_dyc_handle, QuoteHandle(SimpleQuote(-shift)))
discountTermStructure.linkTo(shiftedDiscountCurve)
P_m = CashFlows.npv(swap.fixedLeg(), discountTermStructure, False, settlementDate)
dv01 = (P_m - P_p) / 2.0
print('Fixed Leg DV01')
print(dv01)
```
About the caveat I mentioned above: you're doing
```
discountTermStructure.linkTo(depoSwapCurve)
forecastTermStructure.linkTo(depoSwapCurve)
```
that is, you're using the same curve for forecasting the floating-rate fixing and discounting the cash flows, which is what we do in the example included in the QuantLib release. We should really update that; nowadays, the practice is to use two different curves for forecasting and discounting. In that case, it probably still makes sense to define the DV01 as the variation of NPV when you shift both curves, as you do now; but make sure that this is what you want.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.