bootstrappingquantlibcds

How to use directly in QuantLib a discount or zero-rate curve from Bloomberg, instead of building one from the underlying instruments


I'm just starting out with QuantLib, and I'm hoping to use it to bootstrap hazard rates and survival probability curves from CDS spreads. To this end, I was perusing the examples (that come with QuantLib) in Examples/CDS/CDS.cpp which appear to do exactly what I need. However, and this is my question, I want to bypass the construction of the ISDA yield curve from the underlying instruments and, instead, construct a yield curve (to be used with the SpreadCDSHelpers) from a vector of dates and a vector of discount factors (or zero rates). For example, looking at the 3rd example in the aforementioned file (CDS.cpp) for the sake of concreteness, the code is as follows:

void example03() {

Date tradeDate(13,June,2011);

Settings::instance().evaluationDate() = tradeDate;

// -- BEGINNING

ext::shared_ptr<DepositRateHelper> dp1m =
    ext::make_shared<DepositRateHelper>(0.00445, 1 * Months, 2,
                                          WeekendsOnly(), ModifiedFollowing,
                                          false, Actual360());
ext::shared_ptr<DepositRateHelper> dp2m =
    ext::make_shared<DepositRateHelper>(0.00949, 2 * Months, 2,
                                          WeekendsOnly(), ModifiedFollowing,
                                          false, Actual360());
ext::shared_ptr<DepositRateHelper> dp3m =
    ext::make_shared<DepositRateHelper>(0.01234, 3 * Months, 2,
                                          WeekendsOnly(), ModifiedFollowing,
                                          false, Actual360());
ext::shared_ptr<DepositRateHelper> dp6m =
    ext::make_shared<DepositRateHelper>(0.01776, 6 * Months, 2,
                                          WeekendsOnly(), ModifiedFollowing,
                                          false, Actual360());
ext::shared_ptr<DepositRateHelper> dp9m =
    ext::make_shared<DepositRateHelper>(0.01935, 9 * Months, 2,
                                          WeekendsOnly(), ModifiedFollowing,
                                          false, Actual360());
ext::shared_ptr<DepositRateHelper> dp1y =
    ext::make_shared<DepositRateHelper>(0.02084, 12 * Months, 2,
                                          WeekendsOnly(), ModifiedFollowing,
                                          false, Actual360());

// this index is probably not important since we are not using
// IborCoupon::usingAtParCoupons() == false 
// - define it "isda compliant" anyway
ext::shared_ptr<IborIndex> euribor6m = ext::make_shared<IborIndex>(
    "IsdaIbor", 6 * Months, 2, EURCurrency(), WeekendsOnly(),
    ModifiedFollowing, false, Actual360());

ext::shared_ptr<SwapRateHelper> sw2y = ext::make_shared<SwapRateHelper>(
    0.01652, 2 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw3y = ext::make_shared<SwapRateHelper>(
    0.02018, 3 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw4y = ext::make_shared<SwapRateHelper>(
    0.02303, 4 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw5y = ext::make_shared<SwapRateHelper>(
    0.02525, 5 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw6y = ext::make_shared<SwapRateHelper>(
    0.02696, 6 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw7y = ext::make_shared<SwapRateHelper>(
    0.02825, 7 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw8y = ext::make_shared<SwapRateHelper>(
    0.02931, 8 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw9y = ext::make_shared<SwapRateHelper>(
    0.03017, 9 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw10y = ext::make_shared<SwapRateHelper>(
    0.03092, 10 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw11y = ext::make_shared<SwapRateHelper>(
    0.03160, 11 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw12y = ext::make_shared<SwapRateHelper>(
    0.03231, 12 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw15y = ext::make_shared<SwapRateHelper>(
    0.03367, 15 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw20y = ext::make_shared<SwapRateHelper>(
    0.03419, 20 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw25y = ext::make_shared<SwapRateHelper>(
    0.03411, 25 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);
ext::shared_ptr<SwapRateHelper> sw30y = ext::make_shared<SwapRateHelper>(
    0.03412, 30 * Years, WeekendsOnly(), Annual, ModifiedFollowing, Thirty360(),
    euribor6m);

std::vector<ext::shared_ptr<RateHelper> > isdaYieldHelpers;

isdaYieldHelpers.push_back(dp1m);
isdaYieldHelpers.push_back(dp2m);
isdaYieldHelpers.push_back(dp3m);
isdaYieldHelpers.push_back(dp6m);
isdaYieldHelpers.push_back(dp9m);
isdaYieldHelpers.push_back(dp1y);
isdaYieldHelpers.push_back(sw2y);
isdaYieldHelpers.push_back(sw3y);
isdaYieldHelpers.push_back(sw4y);
isdaYieldHelpers.push_back(sw5y);
isdaYieldHelpers.push_back(sw6y);
isdaYieldHelpers.push_back(sw7y);
isdaYieldHelpers.push_back(sw8y);
isdaYieldHelpers.push_back(sw9y);
isdaYieldHelpers.push_back(sw10y);
isdaYieldHelpers.push_back(sw11y);
isdaYieldHelpers.push_back(sw12y);
isdaYieldHelpers.push_back(sw15y);
isdaYieldHelpers.push_back(sw20y);
isdaYieldHelpers.push_back(sw25y);
isdaYieldHelpers.push_back(sw30y);

// build yield curve
Handle<YieldTermStructure> isdaYts = Handle<YieldTermStructure>(
        ext::make_shared<PiecewiseYieldCurve<Discount, LogLinear> >(
            0, WeekendsOnly(), isdaYieldHelpers, Actual365Fixed()));
isdaYts->enableExtrapolation();

// -- END

CreditDefaultSwap::PricingModel model = CreditDefaultSwap::ISDA;
ext::shared_ptr<CdsHelper> cds6m(new SpreadCdsHelper(
    0.007927, 6 * Months, 1, WeekendsOnly(), Quarterly, Following,
    DateGeneration::CDS, Actual360(), 0.4, isdaYts, true, true, Date(),
    Actual360(true), true, model));
ext::shared_ptr<CdsHelper> cds1y(new SpreadCdsHelper(
    0.007927, 1 * Years, 1, WeekendsOnly(), Quarterly, Following,
    DateGeneration::CDS, Actual360(), 0.4, isdaYts, true, true, Date(),
    Actual360(true), true, model));
ext::shared_ptr<CdsHelper> cds3y(new SpreadCdsHelper(
    0.012239, 3 * Years, 1, WeekendsOnly(), Quarterly, Following,
    DateGeneration::CDS, Actual360(), 0.4, isdaYts, true, true, Date(),
    Actual360(true), true, model));
ext::shared_ptr<CdsHelper> cds5y(new SpreadCdsHelper(
    0.016979, 5 * Years, 1, WeekendsOnly(), Quarterly, Following,
    DateGeneration::CDS, Actual360(), 0.4, isdaYts, true, true, Date(),
    Actual360(true), true, model));
ext::shared_ptr<CdsHelper> cds7y(new SpreadCdsHelper(
    0.019271, 7 * Years, 1, WeekendsOnly(), Quarterly, Following,
    DateGeneration::CDS, Actual360(), 0.4, isdaYts, true, true, Date(),
    Actual360(true), true, model));
ext::shared_ptr<CdsHelper> cds10y(new SpreadCdsHelper(
    0.020860, 10 * Years, 1, WeekendsOnly(), Quarterly, Following,
    DateGeneration::CDS, Actual360(), 0.4, isdaYts, true, true, Date(),
    Actual360(true), true, model));

std::vector<ext::shared_ptr<DefaultProbabilityHelper> > isdaCdsHelpers;

isdaCdsHelpers.push_back(cds6m);
isdaCdsHelpers.push_back(cds1y);
isdaCdsHelpers.push_back(cds3y);
isdaCdsHelpers.push_back(cds5y);
isdaCdsHelpers.push_back(cds7y);
isdaCdsHelpers.push_back(cds10y);

// build credit curve
Handle<DefaultProbabilityTermStructure> isdaCts =
Handle<DefaultProbabilityTermStructure>(ext::make_shared<
    PiecewiseDefaultCurve<SurvivalProbability, LogLinear> >(
        0, WeekendsOnly(), isdaCdsHelpers, Actual365Fixed()));

// set up isda engine
ext::shared_ptr<IsdaCdsEngine> isdaPricer =
    ext::make_shared<IsdaCdsEngine>(
        isdaCts, 0.4, isdaYts);

// check the curves
std::cout << "ISDA yield curve:" << std::endl;
std::cout << "date;time;zeroyield" << std::endl;
for (auto& isdaYieldHelper : isdaYieldHelpers) {
    Date d = isdaYieldHelper->latestDate();
    Real t = isdaYts->timeFromReference(d);
    std::cout << d << ";" << t << ";"
              << isdaYts->zeroRate(d, Actual365Fixed(), Continuous).rate()
              << std::endl;
}

std::cout << "ISDA credit curve:" << std::endl;
std::cout << "date;time;survivalprob" << std::endl;
for (auto& isdaCdsHelper : isdaCdsHelpers) {
    Date d = isdaCdsHelper->latestDate();
    Real t = isdaCts->timeFromReference(d);
    std::cout << d << ";" << t << ";" << isdaCts->survivalProbability(d)
              << std::endl;
}}

and it compiles and runs fine. Now, I would like to replace the code between the comments // -- BEGINNING and // -- END with something like the following

Handle<YieldTermStructure> isdaSwapDiscountCurve = Handle<YieldTermStructure>(
        ext::make_shared<DiscountCurve>(
            isdaSwapDates,
            isdaSwapDiscountFactors,
            Actual365Fixed(),
            LogLinear()
        )
    );
    isdaSwapZeroCurve->enableExtrapolation();

where std::vector<Date> isdaSwapDates and std::vector<Real> isdaSwapDiscountFactors are vectors I can provide. With this modification, however, the example does not work anymore (in fact, compiles but fails at runtime).

Any pointer in the right direction will be highly appreciated. Thanks in advance.


Solution

  • you might try the more sophisticated InterpolatedDiscountCurve instead:

    Handle<YieldTermStructure> isdaSwapDiscountCurve = 
        boost::shared_ptr<YieldTermStructure>(
           new InterpolatedDiscountCurve<LogLinear>(
                isdaSwapDates,
                isdaSwapDiscountFactors,
                Actual365Fixed(),
                LogLinear()
            )
        );
        isdaSwapZeroCurve->enableExtrapolation();
    

    You can use any Interpolator along with the definition, e.g. Linear, LogLinear, Cubic, LogCubic, etc.

    I presume you replaced all occurrences of isdaYts in the example-code with isdaSwapDiscountCurve, right?