lballabio / QuantLib

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

OISRateHelper use in curve bootstrapping throwing exception TODAY, likely something to do with Labor Day holiday #2065

Open jbschaer opened 2 weeks ago

jbschaer commented 2 weeks ago

This is with actual data from today, 1st day this has failed. Removing the OISRateHelper adds to "helpers", does not fail. If I move the businessDate forward to Sep 3 (and add some fixings), exception does not occur. Dates Aug 29, Aug 30 also fail.

{
    struct SOFRQuotes {
        QuantLib::Frequency freq;
        QuantLib::Month month;
        QuantLib::Year year;
        QuantLib::Real price;
        QuantLib::Real convx;
    };

    const QuantLib::Integer OISSettlementDays{ 2 };
    const QuantLib::Real SOFRConvx = 0.0000;
    QuantLib::Calendar calendar = QuantLib::UnitedStates(QuantLib::UnitedStates::SOFR);
    QuantLib::Date today = QuantLib::Date(28, QuantLib::August, 2024);
    QuantLib::Date businessDate = today;
    while (!calendar.isBusinessDay(businessDate))
        businessDate--;
    QuantLib::Settings::instance().evaluationDate() = businessDate;

    auto sofrIndex = QuantLib::ext::make_shared<QuantLib::Sofr>();
    sofrIndex->clearFixings();

#if 1
    sofrIndex->addFixing(QuantLib::Date(27, QuantLib::August, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(26, QuantLib::August, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(23, QuantLib::August, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(22, QuantLib::August, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(21, QuantLib::August, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(20, QuantLib::August, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(19, QuantLib::August, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(16, QuantLib::August, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(15, QuantLib::August, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(14, QuantLib::August, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(13, QuantLib::August, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(12, QuantLib::August, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(9, QuantLib::August, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(8, QuantLib::August, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(7, QuantLib::August, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(6, QuantLib::August, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(5, QuantLib::August, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(2, QuantLib::August, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(1, QuantLib::August, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(31, QuantLib::July, 2024), 0.0538);
    sofrIndex->addFixing(QuantLib::Date(30, QuantLib::July, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(29, QuantLib::July, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(26, QuantLib::July, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(25, QuantLib::July, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(24, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(23, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(22, QuantLib::July, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(19, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(18, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(17, QuantLib::July, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(16, QuantLib::July, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(15, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(12, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(11, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(10, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(9, QuantLib::July, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(8, QuantLib::July, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(5, QuantLib::July, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(3, QuantLib::July, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(2, QuantLib::July, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(1, QuantLib::July, 2024), 0.0540);
    sofrIndex->addFixing(QuantLib::Date(28, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(27, QuantLib::June, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(26, QuantLib::June, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(25, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(24, QuantLib::June, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(21, QuantLib::June, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(20, QuantLib::June, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(18, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(17, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(14, QuantLib::June, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(13, QuantLib::June, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(12, QuantLib::June, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(11, QuantLib::June, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(10, QuantLib::June, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(7, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(6, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(5, QuantLib::June, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(4, QuantLib::June, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(3, QuantLib::June, 2024), 0.0535);
    sofrIndex->addFixing(QuantLib::Date(31, QuantLib::May, 2024), 0.0534);
    sofrIndex->addFixing(QuantLib::Date(30, QuantLib::May, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(29, QuantLib::May, 2024), 0.0533);
    sofrIndex->addFixing(QuantLib::Date(28, QuantLib::May, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(24, QuantLib::May, 2024), 0.0532);
    sofrIndex->addFixing(QuantLib::Date(23, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(22, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(21, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(20, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(17, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(16, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(15, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(14, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(13, QuantLib::May, 2024), 0.0531);
    sofrIndex->addFixing(QuantLib::Date(10, QuantLib::May, 2024), 0.0531);
#endif

    const SOFRQuotes SOFRQuotes[] = {
        {QuantLib::Monthly, QuantLib::Aug, 2024, 94.66375, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Sep, 2024, 94.7875, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Oct, 2024, 94.9775, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Nov, 2024, 95.2325, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Dec, 2024, 95.4475, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Jan, 2025, 95.6725, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Feb, 2025, 95.9475, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Mar, 2025, 96.0575, SOFRConvx},
        {QuantLib::Monthly, QuantLib::Apr, 2025, 96.2325, SOFRConvx},
        {QuantLib::Monthly, QuantLib::May, 2025, 96.3775, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Jun, 2024, 94.62625, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Jul, 2024, 94.72375, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Aug, 2024, 94.8925, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Sep, 2024, 95.09125, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Oct, 2024, 95.295, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Nov, 2024, 95.58, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Dec, 2024, 95.7825, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Jan, 2025, 95.9675, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Feb, 2025, 96.1525, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Mar, 2025, 96.2975, SOFRConvx},
        {QuantLib::Quarterly, QuantLib::Jun, 2025, 96.6075, SOFRConvx},
    };

    std::vector<QuantLib::ext::shared_ptr<QuantLib::RateHelper>> helpers;

    for (const auto& sofrQuote : SOFRQuotes)
        helpers.push_back(QuantLib::ext::make_shared<QuantLib::SofrFutureRateHelper>(QuantLib::Real(sofrQuote.price), sofrQuote.month, sofrQuote.year, sofrQuote.freq, sofrQuote.convx / 100));

#if 1
    const auto OIS3YR = QuantLib::ext::make_shared<QuantLib::SimpleQuote>(3.4820 / 100);
    const auto OIS5YR = QuantLib::ext::make_shared<QuantLib::SimpleQuote>(3.3530 / 100);
    const auto OIS7YR = QuantLib::ext::make_shared<QuantLib::SimpleQuote>(3.3460 / 100);
    const auto OIS10YR = QuantLib::ext::make_shared<QuantLib::SimpleQuote>(3.3850 / 100);
    const auto OIS15YR = QuantLib::ext::make_shared<QuantLib::SimpleQuote>(3.4560 / 100);
    const auto OIS30YR = QuantLib::ext::make_shared<QuantLib::SimpleQuote>(3.3190 / 100);

    helpers.push_back(QuantLib::ext::make_shared<QuantLib::OISRateHelper>(OISSettlementDays, QuantLib::Period(3, QuantLib::Years), QuantLib::Handle<QuantLib::Quote>(OIS3YR), sofrIndex));
    helpers.push_back(QuantLib::ext::make_shared<QuantLib::OISRateHelper>(OISSettlementDays, QuantLib::Period(5, QuantLib::Years), QuantLib::Handle<QuantLib::Quote>(OIS5YR), sofrIndex));
    helpers.push_back(QuantLib::ext::make_shared<QuantLib::OISRateHelper>(OISSettlementDays, QuantLib::Period(7, QuantLib::Years), QuantLib::Handle<QuantLib::Quote>(OIS7YR), sofrIndex));
    helpers.push_back(QuantLib::ext::make_shared<QuantLib::OISRateHelper>(OISSettlementDays, QuantLib::Period(10, QuantLib::Years), QuantLib::Handle<QuantLib::Quote>(OIS10YR), sofrIndex));
    helpers.push_back(QuantLib::ext::make_shared<QuantLib::OISRateHelper>(OISSettlementDays, QuantLib::Period(15, QuantLib::Years), QuantLib::Handle<QuantLib::Quote>(OIS15YR), sofrIndex));
    helpers.push_back(QuantLib::ext::make_shared<QuantLib::OISRateHelper>(OISSettlementDays, QuantLib::Period(30, QuantLib::Years), QuantLib::Handle<QuantLib::Quote>(OIS30YR), sofrIndex));
#endif

    auto oisTermStructureDayCounter = QuantLib::Actual360();
    auto curve1 = QuantLib::ext::make_shared<QuantLib::PiecewiseYieldCurve<QuantLib::Discount, QuantLib::Cubic, QuantLib::IterativeBootstrap>>(businessDate, helpers, oisTermStructureDayCounter, QuantLib::Cubic(QuantLib::CubicInterpolation::Spline, true, QuantLib::CubicInterpolation::SecondDerivative, 0.0, QuantLib::CubicInterpolation::SecondDerivative, 0.0));
    curve1->enableExtrapolation();

    for (int y = 2025; y <= 2025; y++)
    {
        for (int m = 1; m <= 12; m++)
        {
            try
            {
                auto rate = curve1->zeroRate(QuantLib::Date(1, QuantLib::Month(m), y), oisTermStructureDayCounter, QuantLib::Compounded, QuantLib::Monthly).rate();
            }
            catch (QuantLib::Error& e) {
                TRACE((std::string("yield curve building failed for curve ") + e.what()).c_str());
            }
        }
    }
    for (int y = 2026; y <= 2050; y++)
    {
        try
        {
            auto rate = curve1->zeroRate(QuantLib::Date(1, QuantLib::Month(1), y), oisTermStructureDayCounter, QuantLib::Compounded, QuantLib::Monthly).rate();
        }
        catch (QuantLib::Error& e) {
            TRACE((std::string("yield curve building failed for curve ") + e.what()).c_str());
        }
    }
}
jbschaer commented 2 weeks ago

the exception being thrown looks like QuantLib PiecewiseYieldCurve::zeroRate failed, Expiration Sep[20240916], 2nd iteration: failed at 1st alive instrument, pillar September 1st, 2024, maturity September 1st, 2024, reference date August 28th, 2024: positive compound factor required

jbschaer commented 1 day ago

this is failing again with today's business date (20240912) when utilizing OISRateHelper. There is a bug.