Skip to content

Commit

Permalink
Account for possible adjustments (e.g. on Juneteenth)
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio committed Jun 24, 2024
1 parent c7b4109 commit 1cf9e8e
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 21 deletions.
14 changes: 7 additions & 7 deletions ql/termstructures/yield/overnightindexfutureratehelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ namespace QuantLib {
namespace {

Date getValidSofrStart(Month month, Year year, Frequency freq) {
return freq == Monthly ?
UnitedStates(UnitedStates::GovernmentBond).adjust(Date(1, month, year)) :
Date::nthWeekday(3, Wednesday, month, year);
static auto calendar = UnitedStates(UnitedStates::SOFR);
return calendar.adjust(freq == Monthly ? Date(1, month, year) :
Date::nthWeekday(3, Wednesday, month, year));
}

Date getValidSofrEnd(Month month, Year year, Frequency freq) {
static auto calendar = UnitedStates(UnitedStates::SOFR);
if (freq == Monthly) {
Calendar dc = UnitedStates(UnitedStates::GovernmentBond);
Date d = dc.endOfMonth(Date(1, month, year));
return dc.advance(d, 1*Days);
Date d = calendar.endOfMonth(Date(1, month, year));
return calendar.advance(d, 1*Days);
} else {
Date d = getValidSofrStart(month, year, freq) + Period(freq);
return Date::nthWeekday(3, Wednesday, d.month(), d.year());
return calendar.adjust(Date::nthWeekday(3, Wednesday, d.month(), d.year()));
}

}
Expand Down
74 changes: 60 additions & 14 deletions test-suite/sofrfutures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ struct SofrQuotes {
Month month;
Year year;
Real price;
RateAveraging::Type averagingMethod;
};


Expand All @@ -49,19 +48,19 @@ BOOST_AUTO_TEST_CASE(testBootstrap) {
Settings::instance().evaluationDate() = today;

const SofrQuotes sofrQuotes[] = {
{Monthly, Oct, 2018, 97.8175, RateAveraging::Simple},
{Monthly, Nov, 2018, 97.770, RateAveraging::Simple},
{Monthly, Dec, 2018, 97.685, RateAveraging::Simple},
{Monthly, Jan, 2019, 97.595, RateAveraging::Simple},
{Monthly, Feb, 2019, 97.590, RateAveraging::Simple},
{Monthly, Mar, 2019, 97.525, RateAveraging::Simple},
{Quarterly, Mar, 2019, 97.440, RateAveraging::Compound},
{Quarterly, Jun, 2019, 97.295, RateAveraging::Compound},
{Quarterly, Sep, 2019, 97.220, RateAveraging::Compound},
{Quarterly, Dec, 2019, 97.170, RateAveraging::Compound},
{Quarterly, Mar, 2020, 97.160, RateAveraging::Compound},
{Quarterly, Jun, 2020, 97.165, RateAveraging::Compound},
{Quarterly, Sep, 2020, 97.175, RateAveraging::Compound},
{Monthly, Oct, 2018, 97.8175},
{Monthly, Nov, 2018, 97.770},
{Monthly, Dec, 2018, 97.685},
{Monthly, Jan, 2019, 97.595},
{Monthly, Feb, 2019, 97.590},
{Monthly, Mar, 2019, 97.525},
{Quarterly, Mar, 2019, 97.440},
{Quarterly, Jun, 2019, 97.295},
{Quarterly, Sep, 2019, 97.220},
{Quarterly, Dec, 2019, 97.170},
{Quarterly, Mar, 2020, 97.160},
{Quarterly, Jun, 2020, 97.165},
{Quarterly, Sep, 2020, 97.175},
};

ext::shared_ptr<OvernightIndex> index = ext::make_shared<Sofr>();
Expand Down Expand Up @@ -113,6 +112,53 @@ BOOST_AUTO_TEST_CASE(testBootstrap) {
}
}


BOOST_AUTO_TEST_CASE(testBootstrapWithJuneteen) {
BOOST_TEST_MESSAGE(
"Testing bootstrap over SOFR futures when third Wednesday falls on Juneteenth...");

Date today = Date(27, February, 2024);
Settings::instance().evaluationDate() = today;

const SofrQuotes sofrQuotes[] = {
{Quarterly, Mar, 2024, 97.295},
{Quarterly, Jun, 2024, 97.220},
{Quarterly, Sep, 2024, 97.170},
{Quarterly, Dec, 2024, 97.160},
{Quarterly, Mar, 2025, 97.165},
{Quarterly, Jun, 2025, 97.175},
};

ext::shared_ptr<OvernightIndex> index = ext::make_shared<Sofr>();

std::vector<ext::shared_ptr<RateHelper> > helpers;
for (const auto& sofrQuote : sofrQuotes) {
helpers.push_back(ext::make_shared<SofrFutureRateHelper>(
sofrQuote.price, sofrQuote.month, sofrQuote.year, sofrQuote.freq));
}

ext::shared_ptr<PiecewiseYieldCurve<Discount, Linear> > curve =
ext::make_shared<PiecewiseYieldCurve<Discount, Linear> >(today, helpers,
Actual365Fixed());

ext::shared_ptr<OvernightIndex> sofr =
ext::make_shared<Sofr>(Handle<YieldTermStructure>(curve));
OvernightIndexFuture sf(sofr, Date(20, March, 2024), Date(20, June, 2024));

Real expected_price = 97.295;
Real tolerance = 1.0e-9;

Real error = std::fabs(sf.NPV() - expected_price);
if (error > tolerance) {
BOOST_ERROR("sample futures:\n"
<< std::setprecision(8)
<< "\n estimated price: " << sf.NPV()
<< "\n expected price: " << expected_price
<< "\n error: " << error
<< "\n tolerance: " << tolerance);
}
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 1cf9e8e

Please sign in to comment.