lballabio / QuantLib

The QuantLib C++ library
http://quantlib.org
Other
5.37k stars 1.8k forks source link

Matching Bloomberg's CDSO using quantlib CdsOption #1341

Open laj007 opened 2 years ago

laj007 commented 2 years ago

Hello - I'm trying to use CdsOption in order to price CDS payer/receiver options. Been through pretty much everything online, but still can't match what I see in BBG's CDSO function.

For instance, with valuation date 4/1/2022, reference spread 66bps, strike 75bps, expiry 6/15/2022, and vol 50%, one should see a price in the 0.1687 area. The way I do it (see below) gets me closer to 0.1272 (NPV). Similarly my forward spread is off too (~65.4bps vs BBG's ~68.2bps).

I'm using all usual input (e.g. discount curve) that prices/matches the underlying CDS perfectly, so am quite sure the issue is not on that front, but rather in the logic I'm using.

For instance, I get a little confused as to how to specify the strike given it's not an explicit input in CdsOption. Another potential source of issue is that CdsOption seems to only take CDS defined on a running-spread basis rather than conventional pricing.

Code below.

Thanks so much for the help !

side= 'Pay'
ref = 65
cpn = 100
recovery = 0.4
tradeDate = ql.Date(1, 4, 2022)   
expiry = ql.Date(15,  6, 2022) 
tenor = 5
vol = 0.5
strike = 75

ref /= 1e4
cpn /= 1e4
strike /= 1e4

if side == 'Pay':

    sideCDS = ql.Protection.Buyer

elif side == 'Rec': 

    sideCDS = ql.Protection.Seller

termDate    = cdsMat(tenor,runDate)

ql.Settings.instance().setEvaluationDate(tradeDate)

# underlying cds to get probabilityCurve

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

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

h = quotedTrade.impliedHazardRate(0, # target NPV
                                  discCurve,
                                  ql.Actual365Fixed(), # dayCounter
                                  recovery,
                                  1e-10, # accuracy
                                  ql.CreditDefaultSwap.ISDA) # model

probabilityCurve = ql.RelinkableDefaultProbabilityTermStructureHandle()

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

engine = ql.IsdaCdsEngine(probabilityCurve,recovery,discCurve)

quotedTrade.setPricingEngine(engine)

# underlying CDS - for option pricer (using strike as spread ?)

cds = ql.CreditDefaultSwap(sideCDS, 100, strike, cdsSchedule, ql.Following, ql.Actual365Fixed())
engine = ql.IsdaCdsEngine(probabilityCurve,recovery,discCurve)
cds.setPricingEngine(engine)

vol = ql.QuoteHandle(ql.SimpleQuote(vol))
expiry = ql.Date(expiry.day,expiry.month,expiry.year) 
exercise = ql.EuropeanExercise(expiry)

cds_option = ql.CdsOption(cds, exercise, knocksOut=True)
cds_option.setPricingEngine((ql.BlackCdsOptionEngine(probabilityCurve, recovery, discCurve, vol)))

cds_option.NPV()
cds_option.atmRate()*1e4
boring-cyborg[bot] commented 2 years ago

Thanks for posting! It might take a while before we look at your issue, so don't worry if there seems to be no feedback. We'll get to it.

github-actions[bot] commented 2 years ago

This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.

github-actions[bot] commented 2 years ago

This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.

github-actions[bot] commented 2 years ago

This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.

qmarsun commented 2 years ago

what you get using mathworks. https://www.mathworks.com/help/fininst/cdsoptprice.html Click Button "Try This example" https://www.mathworks.com/help/fininst/cdsoptprice.html#cdsoptprice_example1

thrasibule commented 1 year ago

I think you should be using knocksOut=False. There is no knockout for indices.