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

Date generation rule CDS2015 only works for multiples of 6M #727

Closed
francisduffy opened this issue Nov 22, 2019 · 3 comments · Fixed by #938
Closed

Date generation rule CDS2015 only works for multiples of 6M #727

francisduffy opened this issue Nov 22, 2019 · 3 comments · Fixed by #938

Comments

@francisduffy
Copy link
Contributor

There is documentation on the rule DateGeneration::CDS2015 given here. The document at that link is attached here also. The document has a table in section 11 describing the maturity dates of CDS with given tenors for valuation dates in six different periods using the CDS 2015 rule. If we use the code snippet below to generate this table using QuantLib, we get the table in the attached Excel workbook. The rule works for periods that are multiples of 6 months. However, the rule does not give the expected result for periods 3M, 9M, 15M, etc.

Before I start working on a fix for this, is there agreement that this is an issue and needs to be fixed or am I missing something?

The code crashes if a tenor of 0M is included but having this working with CDS2015 is more of a nice to have and could be treated as a separate issue of lesser importance.

#include <ql/termstructures/credit/defaultprobabilityhelpers.hpp>
#include <ql/termstructures/yield/flatforward.cpp>
#include <ql/time/calendars/weekendsonly.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <iostream>

using namespace QuantLib;
using namespace std;

#ifdef BOOST_MSVC
#include <ql/auto_link.hpp>
#endif

// Check CDS2015 rule against table:
// https://www.isda.org//2015/12/10/updated-faq-amend-single-name-on-the-run-frequency
int main(int argc, char** argv) {

    // Valuation dates that we wish to test
    vector<Date> dates = {
        Date(21, Sep, 2015),
        Date(21, Dec, 2015),
        Date(21, Mar, 2016),
        Date(21, Jun, 2016),
        Date(21, Sep, 2016),
        Date(21, Dec, 2016)
    };

    // CDS tenors that we wish to test
    vector<Period> tenors = {
        // 0 * Months,
        3 * Months,
        6 * Months,
        9 * Months,
        1 * Years,
        15 * Months,
        18 * Months,
        21 * Months,
        2 * Years,
        27 * Months,
        30 * Months,
        33 * Months,
        3 * Years,
        39 * Months,
        42 * Months,
        45 * Months,
        4 * Years,
        51 * Months,
        54 * Months,
        57 * Months,
        5 * Years
    };

    for (const auto& date : dates) {

        // Set the evaluation date to the date of interst
        Settings::instance().evaluationDate() = date;

        // Dummy yield term structure
        Handle<YieldTermStructure> yts(
            boost::make_shared<FlatForward>(0, NullCalendar(), 0.01, Actual365Fixed()));

        for (const auto& tenor : tenors) {
            
            // Create a CDS helper
            // Use NullCalendar here to compare with document. Should be WeekendsOnly.
            auto cdsHelper = boost::make_shared<SpreadCdsHelper>(0.0001, tenor, 1, NullCalendar(), Quarterly,
                Following, DateGeneration::CDS2015, Actual360(), 0.04, yts, true, true, Date(), Actual360(true));

            // Write out the maturity date of the CDS
            cout <<  tenor << "," << io::iso_date(date) << "," << io::iso_date(cdsHelper->latestDate()) << endl;
        }
    }

    return 0;
}

2015_isda_amend-single-name-on-the-run-frequency-faq-revised-as-of-12-10.pdf

compare_expected.xlsx

@thrasibule
Copy link
Contributor

I guess there is a bug indeed. When i submitted the CDS2015 date generation convention, I didn't check with non yearly maturities, I didn't know these existed, but it makes sense to match with the official ISDA guidelines.

benacook pushed a commit to benacook/QuantLib that referenced this issue Mar 1, 2020
benacook added a commit to benacook/QuantLib that referenced this issue Mar 1, 2020
starting to get my head around the code base and possible causes. this commit was reverted as I had the wrong user settings in.
@benacook
Copy link

benacook commented Mar 11, 2020

is this unit test still valid if the bug is fixed? specifically the end date.

Schedule s3 =  
        MakeSchedule().from(Date(20, March, 2017))  
                      .to(Date(20, March, 2017) + Period(5, Years))  
                     .withCalendar(WeekendsOnly())  
                      .withTenor(3*Months)  
                      .withConvention(ModifiedFollowing)  
                      .withTerminationDateConvention(Unadjusted)  
                      .withRule(DateGeneration::CDS2015);  
    BOOST_CHECK(s3.startDate() == Date(20, March, 2017));  
    BOOST_CHECK(s3.endDate() == Date(20, June, 2022));

from QuantLib\test-suite\schedule.cpp line 272

francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 16, 2020
The main addition is a function cdsMaturity(...) that can be used to
derive a CDS maturity date given a trade date, a valid CDS tenor and a
valid CDS date generation rule. The date generation is tested against
the dates provided in the ISDA documentation for CDS2015. It is also
tested for CDS and OldCDS. The logic for the 0M tenor is added for the
CDS and CDS2015 rules.
@francisduffy
Copy link
Contributor Author

is this unit test still valid if the bug is fixed? specifically the end date.

Schedule s3 =  
        MakeSchedule().from(Date(20, March, 2017))  
                      .to(Date(20, March, 2017) + Period(5, Years))  
                     .withCalendar(WeekendsOnly())  
                      .withTenor(3*Months)  
                      .withConvention(ModifiedFollowing)  
                      .withTerminationDateConvention(Unadjusted)  
                      .withRule(DateGeneration::CDS2015);  
    BOOST_CHECK(s3.startDate() == Date(20, March, 2017));  
    BOOST_CHECK(s3.endDate() == Date(20, June, 2022));

from QuantLib\test-suite\schedule.cpp line 272

@benacook I am only getting back to look at this issue now. Yes this unit test should still pass. I have kept it in the branch that I am working on, in commit 72c6064, along with some other tests. I hope to make a pull request from this branch but I need to check a few other downstream items i.e. CDS instrument, helper and engines before doing this.

francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
If the first coupon period would exclude the unadjusted effective date,
include the prior period. Only applies to CDS and CDS2015. This covers
trade dates on Sat or Sun for example and leaves the period from the
previous coupon payment date to the following Monday in the schedule.
There are tests added to show this. CDS and CDS2015 periods are always
regular.
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
Allow for specification of trade date and cash settlement days. There
was a built in assumption that if protection start was not given, it was
equal to the first date in the schedule. There was also an assumption
that cash settlement was 3 BD after protection start - 1D. This doesn't
really work after CDS Big Bang. The protection start is the trade date
itself, i.e. not T + 1 as it was before 2009.

The accrual rebate calculation needed to be corrected to be in line with
the ISDA docs. A unit test has been added to check this.

The code in the old 'if (rebatesAccrual) {}' block leads in some
scenarios to reads beyond the end of the leg vector and a crash.
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
- Update to ctor documentation, particularly settlementDays should
  typically be 0 after CDS Big Bang for correct treatment.
- Use cdsMaturity function if rule is a CDS rule to generate helper end
  date.
- Don't need to call CdsHelper::initializeDates() from
  UpfrontCdsHelper::initializeDates()
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
The upfront and accrual rebate are paid regardless of whether there is a
credit event between trade date and cash settlement date so removed the
survival probability component from the NPV calculation here.
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
francisduffy pushed a commit to francisduffy/QuantLib that referenced this issue Nov 17, 2020
@lballabio lballabio linked a pull request Nov 18, 2020 that will close this issue
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 a pull request may close this issue.

4 participants