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

CDS ISDA pricing engine #112

Merged
merged 90 commits into from
Sep 13, 2017
Merged

CDS ISDA pricing engine #112

merged 90 commits into from
Sep 13, 2017

Conversation

thrasibule
Copy link
Contributor

Comments/reviews welcome! This closes #108 .

@tangent360
Copy link

I know this pull request has been dormant for some time but I wanted to run some tests and hopefully reignite the discussion on merging the changes into the master branch. However, I'm getting several compile errors (VS2015 Comm) which appear to be caused by the changes to ql/time/daycounters/actual360.hpp

They are all of the following nature:

ql/time/daycounters/actual360.hpp(46): error C2555: 'QuantLib::Actual360::Impl::dayCount': overriding virtual function return type differs and is not covariant from 'QuantLib::DayCounter::Impl::dayCount' (compiling source file ql\indexes\ibor\euribor.cpp)

Anyone else getting this?

@lballabio
Copy link
Owner

@thrasibule , may you give me permission to push a few commits to this PR? Instructions at https://help.github.com/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork/

@thrasibule
Copy link
Contributor Author

@lballabio done! Let me know if this doesn't work.

@lballabio
Copy link
Owner

Yes, it worked. Let me know if the treatment of the NPV date in RiskyBond makes sense.

paysAtDefaultTime_(paysAtDefaultTime),
lastPeriodDC_(lastPeriodDayCounter), rebatesAccrual_(rebatesAccrual),
model_(model), isdaNumericalFix_(IsdaCdsEngine::Taylor),
isdaAccrualBias_(IsdaCdsEngine::NoBias),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is HalfDayBias everywhere except here. Is this on purpose?

@pcaspers
Copy link
Contributor

pcaspers commented Sep 11, 2017

It's been a while, but looking at the ISDA engine ctor we have a comment there

 +            AccrualBias / ForwardsInCouponPeriod corresponding
 +            to the standard model implementation C code are
 +
 +            prior 1.8.2    HalfDayBias / Flat
 +            1.8.2          NoBias / Flat
 +
 +            The theoretical correct setting would be NoBias / Piecewise
 +
 +            Todo: Clarify in which version of the standard model
 +            implementation C code the numerical problem of zero denominators
 +            is solved and how exactly.

so I'd say the default values should be "HalfDayBias / Flat / Taylor" everywhere to match the current actual standard implementation (assuming it has the "Taylor" fix). @thrasibule, @japari what do you think? Do you an updated information on what is actually implemented?

@thrasibule
Copy link
Contributor Author

thrasibule commented Sep 12, 2017

As far as I can see, the latest version of the isda model C code (1.8.2) implements HalfDayBias/Piecewise/Taylor.
The Taylor numerical fix was introduced in 1.7.2. Actually they do both None and Taylor (add 1e-50 and do a Taylor expansion for small values).
The Piecewise/Flat toggle is pretty useless at the moment. The ISDA code always did piecewise. Markit published a fix, which is an incorrect way of doing piecewise. The code got added as a comment but is not turned on. As far as I can see Flat doesn't correspond to any published code. We could add PiecewiseFix enum to the possible values, which implements the above pdf, and set Piecewise as default.
We can also change it to always also add 1e-50 when doing Taylor which would be closer to the official code, what do you think?

@lballabio
Copy link
Owner

Ok, how about I hard-code HalfDayBias/Piecewise/Taylor for this release so it covers the latest model, get it out, and then we think about how to extend it?

@thrasibule
Copy link
Contributor Author

Fine by me!

@pcaspers
Copy link
Contributor

Ok!

@lballabio
Copy link
Owner

Done. Please check that you get the output you expect from the CDS example.

@lballabio lballabio merged commit 8126b0a into lballabio:master Sep 13, 2017
lballabio added a commit that referenced this pull request Sep 13, 2017
@codingyesiamalway
Copy link

I am wondering if anyone can match ISDA calculation with this change. I could match the pv of the coupon/premium leg exactly, but the protection leg is off by about $200 for the $10mm notional cds I am testing. Anyone else had any luck?

@thrasibule
Copy link
Contributor Author

thrasibule commented Jan 2, 2018 via email

@codingyesiamalway
Copy link

codingyesiamalway commented Jan 7, 2018

I am comparing with bloomberg, but couldnt match the numbers. The discount curve matches bloomberg exactly, but the default probability from credit curve is off. I posted the code in here. I have spent many hours on this, and couldnt figure what is causing the difference. Also as I am looking for documentations, I couldnt find much info related to the Bootstraperror class. Specifically, what does curve -> data (used in the operator()) represent? Is the ith element the ith iteration of something like a hazard rate?

@lballabio
Copy link
Owner

@thrasibule , any chance you can enter the discussion on the mailing list and compare figures? Thanks!

@thrasibule
Copy link
Contributor Author

@lballabio and @codingyesiamalway Just got back from vacation, I'll try to check the numbers later today and chime in on the mailing list.

@lballabio
Copy link
Owner

Thanks!

@codingyesiamalway
Copy link

@lballabio @thrasibule Many thanks!

@laj007
Copy link

laj007 commented Mar 4, 2021

Hi guys - i've just built a similar code trying to replicate BBG / ISDA Fair Value model (based on piecewise flat hazard curve from a CDS curve, which pass into the ISDA CDS engine) - same issue; I get close but not really on it.

If I build on a 6m tenor only (i.e. just one point on the curve), I'm dead on. The moment I add another tenor, it starts deviating.

It's surprising given that the similar implied-hazard rate toolkit works perfectly from points upfront to conventional spread (and vice-versa).

Were there any recent developments / fixes ?

Thanks!

@thrasibule
Copy link
Contributor Author

thrasibule commented Mar 4, 2021 via email

@laj007
Copy link

laj007 commented Mar 4, 2021

Yes - exactly - from the par spread curve (each separate tenors, agree), the goal is to use piecewise flat hazard rates and come up with points upfront (PV).

Here I'm using 6m to 10y points, trying to price a 5Y CDS (termDate = 12/20/2025) on tradeDate = 3/3/2021, with the traditional ISDA discount curve (discCurve). Bloomberg would return value of -2.60 but I consistently land in the -2.74 to -2.76 range.

Do you see anything wrong in the code structure / use of classes ?

Many thanks.

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

cdsTenors = [ql.Period(6, ql.Months), ql.Period(1, ql.Years), ql.Period(2, ql.Years), ql.Period(3, ql.Years),
              ql.Period(4, ql.Years), ql.Period(5, ql.Years),ql.Period(7, ql.Years),ql.Period(10, ql.Years)]

cdsCurve = [6.68, 8.82, 12.85, 21.26, 32.28, 45.28, 63.45, 84.94]

cdsHelpers = [ql.SpreadCdsHelper((CDS_spread/1e4), CDS_tenor, 0, ql.TARGET(), ql.Quarterly, ql.Following,
              ql.DateGeneration.TwentiethIMM, ql.Actual365Fixed(), recovery, discCurve)
              for CDS_spread, CDS_tenor in zip(cdsCurve, cdsTenors)]

pdCurve = ql.PiecewiseFlatHazardRate(tradeDate, cdsHelpers, ql.Actual365Fixed())
probabilityCurve = ql.RelinkableDefaultProbabilityTermStructureHandle()
probabilityCurve.linkTo(pdCurve)

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

conventionalTrade = ql.CreditDefaultSwap(ql.Protection.Seller,100,0,cpn,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)

p = conventionalTrade.notional() * conventionalTrade.fairUpfront()`

@thrasibule
Copy link
Contributor Author

First your yield curve needs to match with the ISDA yield curve on bloomberg, which would be the first thing to check.
Then you're building your cdshelpers wrong. See this example. And actually needs to use CDS2015, not CDS. But this would be a better discussion for the mailing list.

@thrasibule thrasibule deleted the CDS_clean branch March 10, 2021 16:38
@laj007
Copy link

laj007 commented Mar 10, 2021

Thanks for helping out with this - really appreciated.

First, when I follow your example - using Python - I get a function overload problem (TypeError: new_SpreadCdsHelper expected at most 12 arguments, got 16). Further, my function above has 10 arguments and, if I try adding only one, the most important I believe (model = ql.CreditDefaultSwap.ISDA), the Python function does not recognize the keyword argument (even if explicitly used here : https://quantlib-python-docs.readthedocs.io/en/latest/helpers/credit.html). How could it be fixed it in a Python context ?

Second, when I do use CDS2015, it gets me almost very very close to the BBG number, so perhaps solving my first question won't impact much.

Thanks again!

@thrasibule
Copy link
Contributor Author

thrasibule commented Mar 10, 2021 via email

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

Successfully merging this pull request may close these issues.

CDS ISDA pricing engine
7 participants