0
$\begingroup$

I am getting different results from the CreditDefaultSwap().impliedHazardRate() method, depending on whether the discount curve passed is "cold" i.e. just created and never used or "warm" i.e used at least once.

Using the standard ISDA engine example as a starting point: isda-engine.py

For simplicity, I picked

termDate = ql.Date(20, 6, 2019)
spread = 0.001
recovery = 0.4

and added a for _ in range(4) statement just above the quotedTrade.impliedHazardRate call to loop over that part of the code. See code below.

Surprisingly, the output of the first pass is different from the rest, despite the inputs being apparently the same:

Hazard: 0.0016898148745633896 Upfront: 795519.1252566372
Hazard: 0.0016827677048894731 Upfront: 795915.9786476545
Hazard: 0.0016827677048894731 Upfront: 795915.9786476545
Hazard: 0.0016827677048894731 Upfront: 795915.9786476545

If I "warm up" the yield curve by adding a

_ = discountCurve.discount(tradeDate+1)

before the loop, the problem disappears and the outputs are consistent. Does anyone know what is causing this behaviour?

Thanks

code below:

import QuantLib as ql

tradeDate = ql.Date(21,5,2009)
ql.Settings.instance().setEvaluationDate(tradeDate)

dep_tenors = [1,2,3,6,9,12]
dep_quotes = [0.003081,0.005525,0.007163,0.012413,0.014,0.015488]
isdaRateHelpers = [ql.DepositRateHelper(dep_quotes[i],
                                        dep_tenors[i]*ql.Period(ql.Monthly),
                                        2,ql.WeekendsOnly(),
                                        ql.ModifiedFollowing,
                                        False,ql.Actual360())
                   for i in range(len(dep_tenors))]

swap_tenors = [2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30]
swap_quotes = [0.011907,
               0.01699,
               0.021198,
               0.02444,
               0.026937,
               0.028967,
               0.030504,
               0.031719,
               0.03279,
               0.034535,
               0.036217,
               0.036981,
               0.037246,
               0.037605]

isda_ibor = ql.IborIndex('IsdaIbor',3*ql.Period(ql.Monthly),2,
                         ql.USDCurrency(),ql.WeekendsOnly(),
                         ql.ModifiedFollowing,False,ql.Actual360())
isdaRateHelpers = isdaRateHelpers + [
    ql.SwapRateHelper(swap_quotes[i],swap_tenors[i]*ql.Period(ql.Annual),
                      ql.WeekendsOnly(),ql.Semiannual,ql.ModifiedFollowing,
                      ql.Thirty360(),isda_ibor)
    for i in range(len(swap_tenors))]

discountCurve = ql.RelinkableYieldTermStructureHandle()
discountCurve.linkTo(ql.PiecewiseLogLinearDiscount(0,ql.WeekendsOnly(),
                                                   isdaRateHelpers,
                                                   ql.Actual365Fixed()))
probabilityCurve = ql.RelinkableDefaultProbabilityTermStructureHandle()
termDate = ql.Date(20, 6, 2019)
spread = 0.001
recovery = 0.4
tolerance = 1.0e-6

cdsSchedule = ql.Schedule(tradeDate+1, termDate,
                          3*ql.Period(ql.Monthly),
                          ql.WeekendsOnly(),
                          ql.Following, ql.Unadjusted,
                          ql.DateGeneration.CDS, False)

quotedTrade = ql.CreditDefaultSwap(
    ql.Protection.Buyer,10000000,0,spread,cdsSchedule,
    ql.Following,ql.Actual360(),True,True,tradeDate+1,
    ql.WeekendsOnly().advance(tradeDate,3*ql.Period(ql.Daily)),
    ql.FaceValueClaim(), ql.Actual360(True))

for _ in range(4):
    h = quotedTrade.impliedHazardRate(0,discountCurve,ql.Actual365Fixed(),
                                      recovery,1e-10,
                                      ql.CreditDefaultSwap.ISDA)

    probabilityCurve.linkTo(
        ql.FlatHazardRate(0,ql.WeekendsOnly(),
                          ql.QuoteHandle(ql.SimpleQuote(h)),
                          ql.Actual365Fixed()))

    engine = ql.IsdaCdsEngine(probabilityCurve,recovery,discountCurve)
    conventionalTrade = ql.CreditDefaultSwap(
        ql.Protection.Buyer,10000000,0,0.01,cdsSchedule,
        ql.Following,ql.Actual360(),True,True,tradeDate+1,
        ql.WeekendsOnly().advance(tradeDate,3*ql.Period(ql.Daily)),
        ql.FaceValueClaim(), ql.Actual360(True))
    conventionalTrade.setPricingEngine(engine)

    upfront = conventionalTrade.notional() * conventionalTrade.fairUpfront()
    print("Hazard:",h, "Upfront:", upfront)
```
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.