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

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

Open
jbschaer opened this issue Aug 28, 2024 · 4 comments

Comments

@jbschaer
Copy link

jbschaer commented Aug 28, 2024

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

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

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

@jbschaer jbschaer reopened this Sep 12, 2024
@lballabio lballabio added this to the Release 1.36 milestone Sep 18, 2024
@lballabio
Copy link
Owner

@marcin-rybacki — is there any chance you can have a look and see if this is related to the recent work on OIS? Thanks!

@marcin-rybacki
Copy link
Contributor

Hi Luigi,

I had a brief look and I see that the curve is constructed using cubic spline interpolation on discount factors, which perhaps may cause troubles more often than other interpolation schemes.

Also, the curve seems to be breaking on the monthly August 2024 futures. If only this instrument is excluded the curve bootstraps well. Interestingly, this is the case only on version 1.35.

I get the same error if I use SwapRateHelper instead of OISRateHelper , so this suggests that the issue is not caused by the changes in the OvernightIndexedCoupon.

Additionally, if I reconstruct this curve (including both futures and swaps as constituents) using different interpolation schemes (more reliable for bootstrapping) I get the following results:

image

The forwards are a bit irregular though, which could lead to bootstrapping problems for some schemes.

In summary, I think the issue may be caused by SOFR futures, however it only resurfaces using the interpolation scheme that I would not generally use for bootstrapping. Under some other, more common, schemes the curve builds well.

What do you think?

@lballabio lballabio removed this from the Release 1.36 milestone Oct 3, 2024
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

No branches or pull requests

3 participants