8
$\begingroup$

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?

$\endgroup$

1 Answer 1

12
$\begingroup$

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.

$\endgroup$
2
  • $\begingroup$ do we shift the forecast curve to calculate the floating leg DVO1? $\endgroup$ Jan 12, 2022 at 23:36
  • $\begingroup$ I would check how it's defined in your context. $\endgroup$ Jan 13, 2022 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.