From 33fcd3181501119a49fca60b43ca8f8db43244f4 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Fri, 14 Jun 2024 14:40:08 +0100 Subject: [PATCH 1/3] Add a method to determine whether an IBOR coupon has fixed or not --- ql/cashflows/iborcoupon.cpp | 18 ++++++++++- ql/cashflows/iborcoupon.hpp | 1 + test-suite/cashflows.cpp | 60 +++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/ql/cashflows/iborcoupon.cpp b/ql/cashflows/iborcoupon.cpp index c07100752c7..d9953aefe6f 100644 --- a/ql/cashflows/iborcoupon.cpp +++ b/ql/cashflows/iborcoupon.cpp @@ -88,8 +88,24 @@ namespace QuantLib { return fixingDate_; } - Rate IborCoupon::indexFixing() const { + bool IborCoupon::hasFixed() const { + Date today = QuantLib::Settings::instance().evaluationDate(); + + if (fixingDate_ > today) { + return false; + } else if (fixingDate_ < today) { + return true; + } else { + // fixingDate_ == today + if (QuantLib::Settings::instance().enforcesTodaysHistoricFixings()) { + return true; + } else { + return index_->hasHistoricalFixing(fixingDate_); + } + } + } + Rate IborCoupon::indexFixing() const { initializeCachedData(); /* instead of just returning index_->fixing(fixingValueDate_) diff --git a/ql/cashflows/iborcoupon.hpp b/ql/cashflows/iborcoupon.hpp index d4f645b6937..cbc32c10af3 100644 --- a/ql/cashflows/iborcoupon.hpp +++ b/ql/cashflows/iborcoupon.hpp @@ -56,6 +56,7 @@ namespace QuantLib { //! \name Inspectors //@{ const ext::shared_ptr& iborIndex() const { return iborIndex_; } + bool hasFixed() const; //@} //! \name FloatingRateCoupon interface //@{ diff --git a/test-suite/cashflows.cpp b/test-suite/cashflows.cpp index dd9d736992d..fcba835a923 100644 --- a/test-suite/cashflows.cpp +++ b/test-suite/cashflows.cpp @@ -563,6 +563,66 @@ BOOST_AUTO_TEST_CASE(testFixedIborCouponWithoutForecastCurve) { } } +IborCoupon iborCouponForFixingDate(const ext::shared_ptr& index, Date fixingDate) { + Date startDate = index->valueDate(fixingDate); + Date endDate = index->maturityDate(fixingDate); + + IborCoupon coupon(endDate, 100.0, startDate, endDate, index->fixingDays(), index); + coupon.setPricer(ext::make_shared()); + + return coupon; +} + +BOOST_AUTO_TEST_CASE(testIborCouponKnowsWhenitHasFixed) { + BOOST_TEST_MESSAGE("Testing ibor coupon knowing when it has fixed..."); + + Date today = Settings::instance().evaluationDate(); + + auto index = ext::make_shared(); + index->clearFixings(); + auto calendar = index->fixingCalendar(); + + bool defaultEnforcesTodaysHistoricFixings = + QuantLib::Settings::instance().enforcesTodaysHistoricFixings(); + + { + IborCoupon coupon = iborCouponForFixingDate(index, calendar.advance(today, -1, Days)); + // this should not throw an exception if the fixing is missing + BOOST_CHECK_EQUAL(coupon.hasFixed(), true); + } + + { + IborCoupon coupon = iborCouponForFixingDate(index, today); + QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = false; + BOOST_CHECK_EQUAL(coupon.hasFixed(), false); + QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = + defaultEnforcesTodaysHistoricFixings; + } + + { + IborCoupon coupon = iborCouponForFixingDate(index, today); + QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = false; + index->addFixing(coupon.fixingDate(), 0.01); + BOOST_CHECK_EQUAL(coupon.hasFixed(), true); + QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = + defaultEnforcesTodaysHistoricFixings; + index->addFixing(coupon.fixingDate(), Null(), true); + } + + { + IborCoupon coupon = iborCouponForFixingDate(index, today); + QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = true; + BOOST_CHECK_EQUAL(coupon.hasFixed(), true); + QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = + defaultEnforcesTodaysHistoricFixings; + } + + { + IborCoupon coupon = iborCouponForFixingDate(index, calendar.advance(today, 1, Days)); + BOOST_CHECK_EQUAL(coupon.hasFixed(), false); + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() From f9c5f08340b2fb9011cde925cb4e7e140cd8cf3c Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Fri, 14 Jun 2024 15:32:54 +0100 Subject: [PATCH 2/3] Simplify getting the fixing for an IBOR coupon by first checking if it has fixed or not --- ql/cashflows/iborcoupon.cpp | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/ql/cashflows/iborcoupon.cpp b/ql/cashflows/iborcoupon.cpp index d9953aefe6f..3e414d263fb 100644 --- a/ql/cashflows/iborcoupon.cpp +++ b/ql/cashflows/iborcoupon.cpp @@ -114,34 +114,15 @@ namespace QuantLib { 1) allows to save date/time recalculations, and 2) takes into account par coupon needs */ - Date today = QuantLib::Settings::instance().evaluationDate(); - - if (fixingDate_>today) - return iborIndex_->forecastFixing(fixingValueDate_, - fixingEndDate_, - spanningTime_); - if (fixingDate_pastFixing(fixingDate_); QL_REQUIRE(result != Null(), "Missing " << index_->name() << " fixing for " << fixingDate_); return result; + } else { + return iborIndex_->forecastFixing(fixingValueDate_, fixingEndDate_, spanningTime_); } - - try { - Rate result = index_->pastFixing(fixingDate_); - if (result!=Null()) - return result; - else - ; // fall through and forecast - } catch (Error&) { - ; // fall through and forecast - } - return iborIndex_->forecastFixing(fixingValueDate_, - fixingEndDate_, - spanningTime_); } void IborCoupon::setPricer(const ext::shared_ptr& pricer) { From 3eac73c93092666747377b0af4042dfcdd5e39fe Mon Sep 17 00:00:00 2001 From: Luigi Ballabio Date: Mon, 24 Jun 2024 18:05:18 +0200 Subject: [PATCH 3/3] No need to restore settings; each test case does that automatically --- test-suite/cashflows.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/test-suite/cashflows.cpp b/test-suite/cashflows.cpp index fcba835a923..c66a7d5d715 100644 --- a/test-suite/cashflows.cpp +++ b/test-suite/cashflows.cpp @@ -574,29 +574,27 @@ IborCoupon iborCouponForFixingDate(const ext::shared_ptr& index, Date } BOOST_AUTO_TEST_CASE(testIborCouponKnowsWhenitHasFixed) { - BOOST_TEST_MESSAGE("Testing ibor coupon knowing when it has fixed..."); + BOOST_TEST_MESSAGE("Testing that ibor coupon knows when it has fixed..."); Date today = Settings::instance().evaluationDate(); auto index = ext::make_shared(); - index->clearFixings(); auto calendar = index->fixingCalendar(); - bool defaultEnforcesTodaysHistoricFixings = - QuantLib::Settings::instance().enforcesTodaysHistoricFixings(); - { IborCoupon coupon = iborCouponForFixingDate(index, calendar.advance(today, -1, Days)); + index->clearFixings(); // this should not throw an exception if the fixing is missing BOOST_CHECK_EQUAL(coupon.hasFixed(), true); + // but this should + BOOST_CHECK_THROW(coupon.rate(), Error); } { IborCoupon coupon = iborCouponForFixingDate(index, today); QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = false; + index->clearFixings(); BOOST_CHECK_EQUAL(coupon.hasFixed(), false); - QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = - defaultEnforcesTodaysHistoricFixings; } { @@ -604,17 +602,14 @@ BOOST_AUTO_TEST_CASE(testIborCouponKnowsWhenitHasFixed) { QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = false; index->addFixing(coupon.fixingDate(), 0.01); BOOST_CHECK_EQUAL(coupon.hasFixed(), true); - QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = - defaultEnforcesTodaysHistoricFixings; - index->addFixing(coupon.fixingDate(), Null(), true); } { IborCoupon coupon = iborCouponForFixingDate(index, today); QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = true; + index->clearFixings(); BOOST_CHECK_EQUAL(coupon.hasFixed(), true); - QuantLib::Settings::instance().enforcesTodaysHistoricFixings() = - defaultEnforcesTodaysHistoricFixings; + BOOST_CHECK_THROW(coupon.rate(), Error); } {