Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matching Bloomberg's CDSO using quantlib CdsOption #1341

Open
laj007 opened this issue Apr 3, 2022 · 6 comments
Open

Matching Bloomberg's CDSO using quantlib CdsOption #1341

laj007 opened this issue Apr 3, 2022 · 6 comments

Comments

@laj007
Copy link

laj007 commented Apr 3, 2022

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
Copy link

boring-cyborg bot commented Apr 3, 2022

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
Copy link
Contributor

github-actions bot commented Jun 3, 2022

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 github-actions bot added the stale label Jun 3, 2022
@lballabio lballabio removed the stale label Jun 3, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Aug 3, 2022

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 github-actions bot added the stale label Aug 3, 2022
@lballabio lballabio removed the stale label Aug 3, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Oct 3, 2022

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
Copy link

qmarsun commented Oct 17, 2022

@thrasibule
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants