From 56c5bfe08511508b0417eb25e5bdb40d8c51190b Mon Sep 17 00:00:00 2001 From: francis Date: Thu, 18 Dec 2025 23:08:27 +0000 Subject: [PATCH 01/11] Remove inflation instrument with 0 tenor. Remove inflation instrument from test market that results in a helper with start date equal to maturity date. It is ignored in the zero coupon inflation term structure bootstrap but it gives an exception in the construction of the Schedule on the YoY inflation swap helper. --- OREAnalytics/test/testmarket.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index bdf8ab33b..a807ce7bb 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -436,8 +436,7 @@ TestMarket::TestMarket(Date asof, bool swapVolCube) : MarketImpl(false) { euii->addFixing(fixingDatesEUHICPXT[i], fixingRatesEUHICPXT[i], true); }; - vector datesZCII = {asof_, - asof_ + 1 * Years, + vector datesZCII = {asof_ + 1 * Years, asof_ + 2 * Years, asof_ + 3 * Years, asof_ + 4 * Years, @@ -451,7 +450,7 @@ TestMarket::TestMarket(Date asof, bool swapVolCube) : MarketImpl(false) { asof_ + 15 * Years, asof_ + 20 * Years}; - vector ratesZCII = {2.825, 2.9425, 2.975, 2.983, 3.0, 3.01, 3.008, + vector ratesZCII = {2.9425, 2.975, 2.983, 3.0, 3.01, 3.008, 3.009, 3.013, 3.0445, 3.044, 3.09, 3.109, 3.108}; zeroInflationIndices_[make_pair(Market::defaultConfiguration, "EUHICPXT")] = From a80b0d273ff2576b2649075d9c47f681fbecce22 Mon Sep 17 00:00:00 2001 From: francis Date: Sat, 20 Dec 2025 16:00:04 +0000 Subject: [PATCH 02/11] Allow no YoY inflation in TestMarket in OREA test. If year on year inflation is included in the TestMarket and the evaluation date is moved forward, we get errors in the YoY inflation swap helpers due to degenerate schedules where start date is greater than the maturity date. --- OREAnalytics/test/nettedexpsoure.cpp | 2 +- OREAnalytics/test/observationmode.cpp | 2 +- OREAnalytics/test/testmarket.cpp | 11 +++++++---- OREAnalytics/test/testmarket.hpp | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/OREAnalytics/test/nettedexpsoure.cpp b/OREAnalytics/test/nettedexpsoure.cpp index e9c27125e..4426a9227 100644 --- a/OREAnalytics/test/nettedexpsoure.cpp +++ b/OREAnalytics/test/nettedexpsoure.cpp @@ -390,7 +390,7 @@ struct TestData : ore::test::OreaTopLevelFixture { TestData(Date referenceDate, QuantLib::ext::shared_ptr dateGrid, bool withCloseOutGrid = false, bool mporStickyDate = false, Size samples=1, Size seed=5){ // Init market BOOST_TEST_MESSAGE("Setting initial market ..."); - this->initMarket_ = QuantLib::ext::make_shared(referenceDate); + this->initMarket_ = QuantLib::ext::make_shared(referenceDate, false, false); BOOST_TEST_MESSAGE("Setting initial market done!"); BOOST_TEST_MESSAGE("Building CAM ..."); diff --git a/OREAnalytics/test/observationmode.cpp b/OREAnalytics/test/observationmode.cpp index 9973bc929..ed56ad962 100644 --- a/OREAnalytics/test/observationmode.cpp +++ b/OREAnalytics/test/observationmode.cpp @@ -169,7 +169,7 @@ void simulation(string dateGridString, bool checkFixings) { ccys.push_back("JPY"); // Init market - QuantLib::ext::shared_ptr initMarket = QuantLib::ext::make_shared(today); + QuantLib::ext::shared_ptr initMarket = QuantLib::ext::make_shared(today, false, false); // build scenario sim market parameters QuantLib::ext::shared_ptr parameters(new analytics::ScenarioSimMarketParameters()); diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index a807ce7bb..195086dfa 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -207,7 +207,7 @@ parRateCurve(const Date& asof, const vector flatRateYts(Real forward); From fece2b79f45d8efaffd06a838c45b794eaaebf35 Mon Sep 17 00:00:00 2001 From: francis Date: Sat, 20 Dec 2025 16:06:15 +0000 Subject: [PATCH 03/11] Allow for excl. of inflation in sim tests. The iterative bootstrap fails on the inflation helpers on future dates duing simulation. This happens because the evaluation date moves forward and calculate() is called without a prior call to initialize(). This then leaves helpers that should not be still "alive" in the boostrap and they fail because of degenerate schedules. --- OREAnalytics/test/scenariogenerator.cpp | 101 +++++++++++++---------- OREAnalytics/test/simulationmeasures.cpp | 2 +- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/OREAnalytics/test/scenariogenerator.cpp b/OREAnalytics/test/scenariogenerator.cpp index a45a9b7f1..9f52eb148 100644 --- a/OREAnalytics/test/scenariogenerator.cpp +++ b/OREAnalytics/test/scenariogenerator.cpp @@ -95,11 +95,11 @@ void setConventions() { } struct TestData { - TestData() : referenceDate(30, July, 2015) { + TestData(bool includeInflation = true) : referenceDate(30, July, 2015), includeInflation_(includeInflation) { Settings::instance().evaluationDate() = referenceDate; // Build test market - market = QuantLib::ext::make_shared(referenceDate); + market = QuantLib::ext::make_shared(referenceDate, false, false); // Build IR configurations CalibrationType calibrationType = CalibrationType::Bootstrap; @@ -146,33 +146,11 @@ struct TestData { fxConfigs.push_back(QuantLib::ext::make_shared("GBP", "EUR", calibrationType, true, ParamType::Piecewise, sigmaTimes, sigmaValues, optionExpiries, optionStrikes)); - std::vector> eqConfigs; - // Inflation configurations - vector> infConfigs; - // Credit configs - std::vector> crLgmConfigs; + std::vector> eqConfigs; + // Credit configs + std::vector> crLgmConfigs; std::vector> crCirConfigs; - std::vector> comConfigs; - - vector> instruments = { - QuantLib::ext::make_shared(CapFloor::Cap, 5 * Years, QuantLib::ext::make_shared(0.0)) - }; - vector cbUkrpi = { CalibrationBasket(instruments) }; - - instruments = { - QuantLib::ext::make_shared(CapFloor::Floor, 5 * Years, QuantLib::ext::make_shared(0.0)) - }; - vector cbEuhicpxt = { CalibrationBasket(instruments) }; - - ReversionParameter reversion(LgmData::ReversionType::Hagan, false, - ParamType::Piecewise, { 1.0 }, { 0.5, 0.5 }); - - VolatilityParameter volatility(LgmData::VolatilityType::Hagan, true, 0.1); - - infConfigs.push_back(QuantLib::ext::make_shared(CalibrationType::Bootstrap, - cbUkrpi, "GBP", "UKRPI", reversion, volatility)); - infConfigs.push_back(QuantLib::ext::make_shared(CalibrationType::Bootstrap, - cbEuhicpxt, "EUR", "EUHICPXT", reversion, volatility)); + std::vector> comConfigs; CorrelationMatrixBuilder cmb; cmb.addCorrelation("IR:EUR", "IR:USD", Handle(QuantLib::ext::make_shared(0.6))); @@ -185,8 +163,35 @@ struct TestData { cmb.addCorrelation("IR:USD", "FX:GBPEUR", Handle(QuantLib::ext::make_shared(-0.1))); cmb.addCorrelation("IR:GBP", "FX:USDEUR", Handle(QuantLib::ext::make_shared(0.0))); cmb.addCorrelation("IR:GBP", "FX:GBPEUR", Handle(QuantLib::ext::make_shared(0.1))); - cmb.addCorrelation("INF:UKRPI", "IR:GBP", Handle(QuantLib::ext::make_shared(0.1))); - cmb.addCorrelation("INF:EUHICPXT", "IR:EUR", Handle(QuantLib::ext::make_shared(0.1))); + + // Inflation configurations + vector> infConfigs; + if (includeInflation) + { + auto infStrike = QuantLib::ext::make_shared(0.0); + + vector> instruments = { + QuantLib::ext::make_shared(CapFloor::Cap, 5 * Years, infStrike) + }; + vector cbUkrpi = {CalibrationBasket(instruments)}; + + instruments = {QuantLib::ext::make_shared(CapFloor::Floor, 5 * Years, infStrike)}; + vector cbEuhicpxt = {CalibrationBasket(instruments)}; + + ReversionParameter reversion(LgmData::ReversionType::Hagan, false, ParamType::Piecewise, {1.0}, {0.5, 0.5}); + + VolatilityParameter volatility(LgmData::VolatilityType::Hagan, true, 0.1); + + infConfigs.push_back(QuantLib::ext::make_shared( + CalibrationType::Bootstrap, cbUkrpi, "GBP", "UKRPI", reversion, volatility) + ); + infConfigs.push_back(QuantLib::ext::make_shared( + CalibrationType::Bootstrap, cbEuhicpxt, "EUR", "EUHICPXT", reversion, volatility) + ); + + cmb.addCorrelation("INF:UKRPI", "IR:GBP", Handle(QuantLib::ext::make_shared(0.1))); + cmb.addCorrelation("INF:EUHICPXT", "IR:EUR", Handle(QuantLib::ext::make_shared(0.1))); + } Real tolerance = 0.0001; QuantLib::ext::shared_ptr config( @@ -201,6 +206,7 @@ struct TestData { SavedSettings backup; Date referenceDate; + bool includeInflation_; QuantLib::ext::shared_ptr config; QuantLib::ext::shared_ptr ccLgm; QuantLib::ext::shared_ptr lgm; @@ -485,7 +491,8 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket) { BOOST_TEST_MESSAGE("Testing CrossAssetScenarioGenerator via SimMarket (Martingale tests)..."); setConventions(); - TestData d; + bool includeInflation = false; + TestData d(includeInflation); // Simulation date grid Date today = d.referenceDate; @@ -514,7 +521,8 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + if (includeInflation) + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -627,7 +635,8 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket) { BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket2) { BOOST_TEST_MESSAGE("Testing CrossAssetScenarioGenerator via SimMarket (direct test against model)..."); setConventions(); - TestData d; + bool includeInflation = false; + TestData d(includeInflation); // Simulation date grid Date today = d.referenceDate; @@ -656,7 +665,8 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket2) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + if (includeInflation) + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -768,8 +778,8 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket2) { BOOST_AUTO_TEST_CASE(testVanillaSwapExposure) { BOOST_TEST_MESSAGE("Testing EUR and USD vanilla swap exposure profiles generated with CrossAssetScenarioGenerator"); setConventions(); - - TestData d; + bool includeInflation = false; + TestData d(includeInflation); // Simulation date grid Date today = d.referenceDate; @@ -800,7 +810,8 @@ BOOST_AUTO_TEST_CASE(testVanillaSwapExposure) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + if (includeInflation) + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -909,8 +920,8 @@ BOOST_AUTO_TEST_CASE(testVanillaSwapExposure) { BOOST_AUTO_TEST_CASE(testFxForwardExposure) { BOOST_TEST_MESSAGE("Testing EUR-USD FX Forward and FX Vanilla Option exposure"); setConventions(); - - TestData d; + bool includeInflation = false; + TestData d(includeInflation); // Simulation date grid Date today = d.referenceDate; @@ -939,7 +950,8 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposure) { simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); simMarketConfig->setSimulateFXVols(false); simMarketConfig->setSimulateEquityVols(false); - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + if (includeInflation) + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -1025,8 +1037,8 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposure) { BOOST_AUTO_TEST_CASE(testFxForwardExposureZeroIrVol) { BOOST_TEST_MESSAGE("Testing EUR-USD FX Forward exposure (zero IR vol)"); setConventions(); - - TestData d; + bool includeInflation = false; + TestData d(includeInflation); // Simulation date grid Date today = d.referenceDate; @@ -1059,7 +1071,8 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposureZeroIrVol) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + if (includeInflation) + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -1139,7 +1152,7 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposureZeroIrVol) { } } -BOOST_AUTO_TEST_CASE(testCpiSwapExposure) { +BOOST_AUTO_TEST_CASE(testCpiSwapExposure, *boost::unit_test::disabled()) { BOOST_TEST_MESSAGE("Testing CPI Swap exposure"); setConventions(); diff --git a/OREAnalytics/test/simulationmeasures.cpp b/OREAnalytics/test/simulationmeasures.cpp index b87d5aa81..8b38c75dd 100644 --- a/OREAnalytics/test/simulationmeasures.cpp +++ b/OREAnalytics/test/simulationmeasures.cpp @@ -100,7 +100,7 @@ struct TestData { Settings::instance().evaluationDate() = referenceDate; // Build test market - market = QuantLib::ext::make_shared(referenceDate); + market = QuantLib::ext::make_shared(referenceDate, false, false); // Build IR configurations CalibrationType calibrationType = CalibrationType::Bootstrap; From 12adc651a8b0d3be4eb87e794aca5d85d463ff92 Mon Sep 17 00:00:00 2001 From: francis Date: Sat, 20 Dec 2025 16:10:21 +0000 Subject: [PATCH 04/11] Disable the failing obs mode defer test. The test is failing as it stands because: - the zciis_ member of the ZCIIS helper is an observer. - the value pointed to by zciis_ gets added to the deferredObservers_ - during updates, ZeroCouponInflationSwapHelper::initializeDates() gets called and zciis_ gets re-assigned i.e. the value pointed to by zciis_ just before this line, and that is still in deferredObservers_, gets deleted. - deferredObserver->update() is then called on the deleted pointer causing the test to crash. --- OREAnalytics/test/observationmode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OREAnalytics/test/observationmode.cpp b/OREAnalytics/test/observationmode.cpp index ed56ad962..7cb93ff55 100644 --- a/OREAnalytics/test/observationmode.cpp +++ b/OREAnalytics/test/observationmode.cpp @@ -415,7 +415,7 @@ BOOST_AUTO_TEST_CASE(testUnregister) { simulation("10,1Y", true); } -BOOST_AUTO_TEST_CASE(testDefer) { +BOOST_AUTO_TEST_CASE(testDefer, *boost::unit_test::disabled()) { ObservationMode::instance().setMode(ObservationMode::Mode::Defer); setConventions(); From 1081039b0a15b0c5c22c6f218e92d2baca7d335f Mon Sep 17 00:00:00 2001 From: francis Date: Sat, 20 Dec 2025 16:22:23 +0000 Subject: [PATCH 05/11] Revert the last 3 commits. Revert the last 3 commits and attempt to fix tests in a different way. --- OREAnalytics/test/nettedexpsoure.cpp | 2 +- OREAnalytics/test/observationmode.cpp | 4 +- OREAnalytics/test/scenariogenerator.cpp | 101 ++++++++++------------- OREAnalytics/test/simulationmeasures.cpp | 2 +- OREAnalytics/test/testmarket.cpp | 11 +-- OREAnalytics/test/testmarket.hpp | 2 +- 6 files changed, 53 insertions(+), 69 deletions(-) diff --git a/OREAnalytics/test/nettedexpsoure.cpp b/OREAnalytics/test/nettedexpsoure.cpp index 4426a9227..e9c27125e 100644 --- a/OREAnalytics/test/nettedexpsoure.cpp +++ b/OREAnalytics/test/nettedexpsoure.cpp @@ -390,7 +390,7 @@ struct TestData : ore::test::OreaTopLevelFixture { TestData(Date referenceDate, QuantLib::ext::shared_ptr dateGrid, bool withCloseOutGrid = false, bool mporStickyDate = false, Size samples=1, Size seed=5){ // Init market BOOST_TEST_MESSAGE("Setting initial market ..."); - this->initMarket_ = QuantLib::ext::make_shared(referenceDate, false, false); + this->initMarket_ = QuantLib::ext::make_shared(referenceDate); BOOST_TEST_MESSAGE("Setting initial market done!"); BOOST_TEST_MESSAGE("Building CAM ..."); diff --git a/OREAnalytics/test/observationmode.cpp b/OREAnalytics/test/observationmode.cpp index 7cb93ff55..9973bc929 100644 --- a/OREAnalytics/test/observationmode.cpp +++ b/OREAnalytics/test/observationmode.cpp @@ -169,7 +169,7 @@ void simulation(string dateGridString, bool checkFixings) { ccys.push_back("JPY"); // Init market - QuantLib::ext::shared_ptr initMarket = QuantLib::ext::make_shared(today, false, false); + QuantLib::ext::shared_ptr initMarket = QuantLib::ext::make_shared(today); // build scenario sim market parameters QuantLib::ext::shared_ptr parameters(new analytics::ScenarioSimMarketParameters()); @@ -415,7 +415,7 @@ BOOST_AUTO_TEST_CASE(testUnregister) { simulation("10,1Y", true); } -BOOST_AUTO_TEST_CASE(testDefer, *boost::unit_test::disabled()) { +BOOST_AUTO_TEST_CASE(testDefer) { ObservationMode::instance().setMode(ObservationMode::Mode::Defer); setConventions(); diff --git a/OREAnalytics/test/scenariogenerator.cpp b/OREAnalytics/test/scenariogenerator.cpp index 9f52eb148..a45a9b7f1 100644 --- a/OREAnalytics/test/scenariogenerator.cpp +++ b/OREAnalytics/test/scenariogenerator.cpp @@ -95,11 +95,11 @@ void setConventions() { } struct TestData { - TestData(bool includeInflation = true) : referenceDate(30, July, 2015), includeInflation_(includeInflation) { + TestData() : referenceDate(30, July, 2015) { Settings::instance().evaluationDate() = referenceDate; // Build test market - market = QuantLib::ext::make_shared(referenceDate, false, false); + market = QuantLib::ext::make_shared(referenceDate); // Build IR configurations CalibrationType calibrationType = CalibrationType::Bootstrap; @@ -146,11 +146,33 @@ struct TestData { fxConfigs.push_back(QuantLib::ext::make_shared("GBP", "EUR", calibrationType, true, ParamType::Piecewise, sigmaTimes, sigmaValues, optionExpiries, optionStrikes)); - std::vector> eqConfigs; - // Credit configs - std::vector> crLgmConfigs; + std::vector> eqConfigs; + // Inflation configurations + vector> infConfigs; + // Credit configs + std::vector> crLgmConfigs; std::vector> crCirConfigs; - std::vector> comConfigs; + std::vector> comConfigs; + + vector> instruments = { + QuantLib::ext::make_shared(CapFloor::Cap, 5 * Years, QuantLib::ext::make_shared(0.0)) + }; + vector cbUkrpi = { CalibrationBasket(instruments) }; + + instruments = { + QuantLib::ext::make_shared(CapFloor::Floor, 5 * Years, QuantLib::ext::make_shared(0.0)) + }; + vector cbEuhicpxt = { CalibrationBasket(instruments) }; + + ReversionParameter reversion(LgmData::ReversionType::Hagan, false, + ParamType::Piecewise, { 1.0 }, { 0.5, 0.5 }); + + VolatilityParameter volatility(LgmData::VolatilityType::Hagan, true, 0.1); + + infConfigs.push_back(QuantLib::ext::make_shared(CalibrationType::Bootstrap, + cbUkrpi, "GBP", "UKRPI", reversion, volatility)); + infConfigs.push_back(QuantLib::ext::make_shared(CalibrationType::Bootstrap, + cbEuhicpxt, "EUR", "EUHICPXT", reversion, volatility)); CorrelationMatrixBuilder cmb; cmb.addCorrelation("IR:EUR", "IR:USD", Handle(QuantLib::ext::make_shared(0.6))); @@ -163,35 +185,8 @@ struct TestData { cmb.addCorrelation("IR:USD", "FX:GBPEUR", Handle(QuantLib::ext::make_shared(-0.1))); cmb.addCorrelation("IR:GBP", "FX:USDEUR", Handle(QuantLib::ext::make_shared(0.0))); cmb.addCorrelation("IR:GBP", "FX:GBPEUR", Handle(QuantLib::ext::make_shared(0.1))); - - // Inflation configurations - vector> infConfigs; - if (includeInflation) - { - auto infStrike = QuantLib::ext::make_shared(0.0); - - vector> instruments = { - QuantLib::ext::make_shared(CapFloor::Cap, 5 * Years, infStrike) - }; - vector cbUkrpi = {CalibrationBasket(instruments)}; - - instruments = {QuantLib::ext::make_shared(CapFloor::Floor, 5 * Years, infStrike)}; - vector cbEuhicpxt = {CalibrationBasket(instruments)}; - - ReversionParameter reversion(LgmData::ReversionType::Hagan, false, ParamType::Piecewise, {1.0}, {0.5, 0.5}); - - VolatilityParameter volatility(LgmData::VolatilityType::Hagan, true, 0.1); - - infConfigs.push_back(QuantLib::ext::make_shared( - CalibrationType::Bootstrap, cbUkrpi, "GBP", "UKRPI", reversion, volatility) - ); - infConfigs.push_back(QuantLib::ext::make_shared( - CalibrationType::Bootstrap, cbEuhicpxt, "EUR", "EUHICPXT", reversion, volatility) - ); - - cmb.addCorrelation("INF:UKRPI", "IR:GBP", Handle(QuantLib::ext::make_shared(0.1))); - cmb.addCorrelation("INF:EUHICPXT", "IR:EUR", Handle(QuantLib::ext::make_shared(0.1))); - } + cmb.addCorrelation("INF:UKRPI", "IR:GBP", Handle(QuantLib::ext::make_shared(0.1))); + cmb.addCorrelation("INF:EUHICPXT", "IR:EUR", Handle(QuantLib::ext::make_shared(0.1))); Real tolerance = 0.0001; QuantLib::ext::shared_ptr config( @@ -206,7 +201,6 @@ struct TestData { SavedSettings backup; Date referenceDate; - bool includeInflation_; QuantLib::ext::shared_ptr config; QuantLib::ext::shared_ptr ccLgm; QuantLib::ext::shared_ptr lgm; @@ -491,8 +485,7 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket) { BOOST_TEST_MESSAGE("Testing CrossAssetScenarioGenerator via SimMarket (Martingale tests)..."); setConventions(); - bool includeInflation = false; - TestData d(includeInflation); + TestData d; // Simulation date grid Date today = d.referenceDate; @@ -521,8 +514,7 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - if (includeInflation) - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -635,8 +627,7 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket) { BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket2) { BOOST_TEST_MESSAGE("Testing CrossAssetScenarioGenerator via SimMarket (direct test against model)..."); setConventions(); - bool includeInflation = false; - TestData d(includeInflation); + TestData d; // Simulation date grid Date today = d.referenceDate; @@ -665,8 +656,7 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket2) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - if (includeInflation) - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -778,8 +768,8 @@ BOOST_AUTO_TEST_CASE(testCrossAssetSimMarket2) { BOOST_AUTO_TEST_CASE(testVanillaSwapExposure) { BOOST_TEST_MESSAGE("Testing EUR and USD vanilla swap exposure profiles generated with CrossAssetScenarioGenerator"); setConventions(); - bool includeInflation = false; - TestData d(includeInflation); + + TestData d; // Simulation date grid Date today = d.referenceDate; @@ -810,8 +800,7 @@ BOOST_AUTO_TEST_CASE(testVanillaSwapExposure) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - if (includeInflation) - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -920,8 +909,8 @@ BOOST_AUTO_TEST_CASE(testVanillaSwapExposure) { BOOST_AUTO_TEST_CASE(testFxForwardExposure) { BOOST_TEST_MESSAGE("Testing EUR-USD FX Forward and FX Vanilla Option exposure"); setConventions(); - bool includeInflation = false; - TestData d(includeInflation); + + TestData d; // Simulation date grid Date today = d.referenceDate; @@ -950,8 +939,7 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposure) { simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); simMarketConfig->setSimulateFXVols(false); simMarketConfig->setSimulateEquityVols(false); - if (includeInflation) - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -1037,8 +1025,8 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposure) { BOOST_AUTO_TEST_CASE(testFxForwardExposureZeroIrVol) { BOOST_TEST_MESSAGE("Testing EUR-USD FX Forward exposure (zero IR vol)"); setConventions(); - bool includeInflation = false; - TestData d(includeInflation); + + TestData d; // Simulation date grid Date today = d.referenceDate; @@ -1071,8 +1059,7 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposureZeroIrVol) { simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years}); simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years}); simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"}); - if (includeInflation) - simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); + simMarketConfig->setCpiIndices({"UKRPI", "EUHICPXT"}); BOOST_TEST_MESSAGE("set up scenario generator builder"); QuantLib::ext::shared_ptr sgd(new ScenarioGeneratorData); @@ -1152,7 +1139,7 @@ BOOST_AUTO_TEST_CASE(testFxForwardExposureZeroIrVol) { } } -BOOST_AUTO_TEST_CASE(testCpiSwapExposure, *boost::unit_test::disabled()) { +BOOST_AUTO_TEST_CASE(testCpiSwapExposure) { BOOST_TEST_MESSAGE("Testing CPI Swap exposure"); setConventions(); diff --git a/OREAnalytics/test/simulationmeasures.cpp b/OREAnalytics/test/simulationmeasures.cpp index 8b38c75dd..b87d5aa81 100644 --- a/OREAnalytics/test/simulationmeasures.cpp +++ b/OREAnalytics/test/simulationmeasures.cpp @@ -100,7 +100,7 @@ struct TestData { Settings::instance().evaluationDate() = referenceDate; // Build test market - market = QuantLib::ext::make_shared(referenceDate, false, false); + market = QuantLib::ext::make_shared(referenceDate); // Build IR configurations CalibrationType calibrationType = CalibrationType::Bootstrap; diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index 195086dfa..a807ce7bb 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -207,7 +207,7 @@ parRateCurve(const Date& asof, const vector flatRateYts(Real forward); From 721f3c497c4435d9b59ca87ab359be9c3b5e481a Mon Sep 17 00:00:00 2001 From: francis Date: Mon, 22 Dec 2025 12:33:51 +0000 Subject: [PATCH 06/11] Prevent ZCIIS helper observing eval date. Adding the start date to the ZeroCouponInflationSwapHelper sets updateDates_ to false in RelativeDateBootstrapHelper and means that the helper does not observe Settings evaluationDate. This brings the test in line with the way that the helper is created in InflationCurve::buildZeroInflationCurve in OREData also. There is no need to explicitly unregister the evaluationDate after creation of the helper with an explicit non-default constructed start date. Note: YearOnYearInflationSwapHelper should probably be updated in QuantLib to have the same behaviour i.e. that if an explicit start date is provided, updateDates_ is set to false in RelativeDateBootstrapHelper and the helper does not observe Settings evaluationDate. Until then, we still need the explicit unregsiter there in InflationCurve::buildYoYInflationCurve in OREData. --- OREAnalytics/test/testmarket.cpp | 6 ++---- OREData/ored/marketdata/inflationcurve.cpp | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index a807ce7bb..bdc947bfc 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -578,8 +578,7 @@ Handle TestMarket::makeZeroInflationIndex(string index, vect for (Size i = 0; i < dates.size(); i++) { Handle quote(QuantLib::ext::shared_ptr(new SimpleQuote(rates[i] / 100.0))); QuantLib::ext::shared_ptr> anInstrument(new ZeroCouponInflationSwapHelper( - quote, Period(2, Months), dates[i], TARGET(), ModifiedFollowing, ActualActual(ActualActual::ISDA), ii, CPI::AsIndex, yts)); - anInstrument->unregisterWith(Settings::instance().evaluationDate()); + quote, Period(2, Months), dates[i], TARGET(), ModifiedFollowing, ActualActual(ActualActual::ISDA), ii, CPI::AsIndex, yts, asof_)); instruments.push_back(anInstrument); }; // we can use historical or first ZCIIS for this @@ -592,7 +591,6 @@ Handle TestMarket::makeZeroInflationIndex(string index, vect pCPIts->recalculate(); cpiTS = QuantLib::ext::dynamic_pointer_cast(pCPIts); cpiTS->enableExtrapolation(true); - cpiTS->unregisterWith(Settings::instance().evaluationDate()); return Handle(parseZeroInflationIndex(index, Handle(cpiTS))); } @@ -607,7 +605,7 @@ Handle TestMarket::makeYoYInflationIndex(string index, vector Handle quote(QuantLib::ext::shared_ptr(new SimpleQuote(rates[i] / 100.0))); QL_DEPRECATED_DISABLE_WARNING QuantLib::ext::shared_ptr> anInstrument(new YearOnYearInflationSwapHelper( - quote, Period(2, Months), dates[i], TARGET(), ModifiedFollowing, ActualActual(ActualActual::ISDA), ii, yts)); + quote, Period(2, Months), dates[i], TARGET(), ModifiedFollowing, ActualActual(ActualActual::ISDA), ii, yts, asof_)); QL_DEPRECATED_ENABLE_WARNING instruments.push_back(anInstrument); }; diff --git a/OREData/ored/marketdata/inflationcurve.cpp b/OREData/ored/marketdata/inflationcurve.cpp index cdc56af10..a2d142d58 100644 --- a/OREData/ored/marketdata/inflationcurve.cpp +++ b/OREData/ored/marketdata/inflationcurve.cpp @@ -281,7 +281,6 @@ InflationCurve::CurveBuildResults zcq->quote(), convention->observationLag(), maturity, convention->fixCalendar(), convention->fixConvention(), convention->dayCounter(), index, observationInterpolation, nominalTs, swapStart); - instrument->unregisterWith(Settings::instance().evaluationDate()); helpers.push_back(instrument); } } From ac5e0349420bd81bec4313867f515579ac6ba14f Mon Sep 17 00:00:00 2001 From: francis Date: Mon, 22 Dec 2025 16:29:51 +0000 Subject: [PATCH 07/11] Temporary fix to allow scen gen tests to run. This allows all scen gen tests except testCpiSwapExposure test to run. That test still fails because the grid has only one tenor point at 5Y and the zero inflation curve is recalculated with evaluation date set to that date and fails because fixings are missing. The recalculation should not be happening - it is not intended for this inflation curve that is used in the DK inflation scenario generation to recalculate on future dates. --- OREAnalytics/test/scenariogenerator.cpp | 2 +- OREAnalytics/test/testmarket.cpp | 8 +++++++- OREAnalytics/test/testmarket.hpp | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/OREAnalytics/test/scenariogenerator.cpp b/OREAnalytics/test/scenariogenerator.cpp index a45a9b7f1..2009cbbd8 100644 --- a/OREAnalytics/test/scenariogenerator.cpp +++ b/OREAnalytics/test/scenariogenerator.cpp @@ -99,7 +99,7 @@ struct TestData { Settings::instance().evaluationDate() = referenceDate; // Build test market - market = QuantLib::ext::make_shared(referenceDate); + market = QuantLib::ext::make_shared(referenceDate, false, true); // Build IR configurations CalibrationType calibrationType = CalibrationType::Bootstrap; diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index bdc947bfc..d5de87c5f 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -207,7 +207,7 @@ parRateCurve(const Date& asof, const vector ratesZCII = {2.9425, 2.975, 2.983, 3.0, 3.01, 3.008, 3.009, 3.013, 3.0445, 3.044, 3.09, 3.109, 3.108}; + if (remove1YrInf) + { + datesZCII.erase(datesZCII.begin()); + ratesZCII.erase(ratesZCII.begin()); + } + zeroInflationIndices_[make_pair(Market::defaultConfiguration, "EUHICPXT")] = makeZeroInflationIndex("EUHICPXT", datesZCII, ratesZCII, euii, yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")]); diff --git a/OREAnalytics/test/testmarket.hpp b/OREAnalytics/test/testmarket.hpp index 4c246be16..de6d69d87 100644 --- a/OREAnalytics/test/testmarket.hpp +++ b/OREAnalytics/test/testmarket.hpp @@ -63,7 +63,7 @@ namespace testsuite { */ class TestMarket : public ore::data::MarketImpl { public: - TestMarket(Date asof, bool swapVolCube = false); + TestMarket(Date asof, bool swapVolCube = false, bool remove1YrInf = false); private: Handle flatRateYts(Real forward); From 091990810debf46c52e93547e3b9b80fa304bfdb Mon Sep 17 00:00:00 2001 From: francis Date: Mon, 22 Dec 2025 16:40:33 +0000 Subject: [PATCH 08/11] Revert "Temporary fix to allow scen gen tests to run." This reverts commit ac5e0349420bd81bec4313867f515579ac6ba14f. --- OREAnalytics/test/scenariogenerator.cpp | 2 +- OREAnalytics/test/testmarket.cpp | 8 +------- OREAnalytics/test/testmarket.hpp | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/OREAnalytics/test/scenariogenerator.cpp b/OREAnalytics/test/scenariogenerator.cpp index 2009cbbd8..a45a9b7f1 100644 --- a/OREAnalytics/test/scenariogenerator.cpp +++ b/OREAnalytics/test/scenariogenerator.cpp @@ -99,7 +99,7 @@ struct TestData { Settings::instance().evaluationDate() = referenceDate; // Build test market - market = QuantLib::ext::make_shared(referenceDate, false, true); + market = QuantLib::ext::make_shared(referenceDate); // Build IR configurations CalibrationType calibrationType = CalibrationType::Bootstrap; diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index d5de87c5f..bdc947bfc 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -207,7 +207,7 @@ parRateCurve(const Date& asof, const vector ratesZCII = {2.9425, 2.975, 2.983, 3.0, 3.01, 3.008, 3.009, 3.013, 3.0445, 3.044, 3.09, 3.109, 3.108}; - if (remove1YrInf) - { - datesZCII.erase(datesZCII.begin()); - ratesZCII.erase(ratesZCII.begin()); - } - zeroInflationIndices_[make_pair(Market::defaultConfiguration, "EUHICPXT")] = makeZeroInflationIndex("EUHICPXT", datesZCII, ratesZCII, euii, yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")]); diff --git a/OREAnalytics/test/testmarket.hpp b/OREAnalytics/test/testmarket.hpp index de6d69d87..4c246be16 100644 --- a/OREAnalytics/test/testmarket.hpp +++ b/OREAnalytics/test/testmarket.hpp @@ -63,7 +63,7 @@ namespace testsuite { */ class TestMarket : public ore::data::MarketImpl { public: - TestMarket(Date asof, bool swapVolCube = false, bool remove1YrInf = false); + TestMarket(Date asof, bool swapVolCube = false); private: Handle flatRateYts(Real forward); From b806d85c01c9dd1f538e754227da1dfe8ee8c94c Mon Sep 17 00:00:00 2001 From: francis Date: Mon, 22 Dec 2025 17:46:17 +0000 Subject: [PATCH 09/11] Fix DK inflation simulation in orea tests. Currently the DK inflation model is not using the t_0 zero inflation term structure properly when calculating future scenarios. This fix only fixes the scenario generation in the orea test suite. It is up for discussion whether similar changes should be applied at the point where the zero inflation term structure is created in ORE Data. I will open a separate issue for this. What was happening before this fix is that the zero inflation term structure, due to a series of notifications, was being forced to recalculate on the first simulation date for every sample and this recalculated curve was being used in the generation of the CPI index scenario values rather than the t_0 calculated curve. On sample 0, there is a call to `ScenarioSimMarket::updateScenario( const Date& d)` which changes the evaluationDate to the first grid date. The inflation index in the ZCIIS inside the helper is observing this evaluationDate and the inflation curve is observing the index in turn. Due to this, the inflation curve is forced to recalculate on the first grid date t_1 for sample 0. This t_1 recalculated curve is used for the generation of all scenarios for t_2,...,t_N for sample 0. Note that if relative to date t_1, some of the inflation helpers in the curve require a CPI fixing, e.g. t_1 >= fixing date of some helper, the test fails with exception from iterative bootstrap about missing fixing. On samples > 0, the `applyScenario(scenario);` in `ScenarioSimMarket::updateScenario(const Date& d)` triggers a notification chain that leads to the inflation curve being recalculated on t_1 for every sample. The chain is: - ScenarioSimMarket simData_ setValue on CPI index quote - InflationIndexObserver which in update() calls setFixing() - adds fixings in IndexManager which in turn calls `notifier(name)->notifyObservers();` - the indices in the ZCIIS swap instruments in the ZC inflation curve helpers are observing the notifier => they get notified - the ZC inflation curve is observing the indices in the previous bullet => the curve gets notified and in its `update()` call sets `calculated_` to `false` => recalculation on t_1 for next sample. --- OREAnalytics/test/testmarket.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index bdc947bfc..dfd40641f 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -579,6 +579,16 @@ Handle TestMarket::makeZeroInflationIndex(string index, vect Handle quote(QuantLib::ext::shared_ptr(new SimpleQuote(rates[i] / 100.0))); QuantLib::ext::shared_ptr> anInstrument(new ZeroCouponInflationSwapHelper( quote, Period(2, Months), dates[i], TARGET(), ModifiedFollowing, ActualActual(ActualActual::ISDA), ii, CPI::AsIndex, yts, asof_)); + + // Remove the helper's observation of the inflation index. This has the effect that the + // PiecewiseZeroInflationCurve created below will also not observe the index. It will only get recalculated + // if the initial market quotes change or if the initial nominal yield curve changes. Observation of the index + // was interfering with scneario generation. The PiecewiseZeroInflationCurve was getting recalculated on the + // first time grid date t_1 i.e. was not using the t_0 calculated curve. + anInstrument->unregisterWithAll(); + anInstrument->registerWith(yts); + anInstrument->registerWith(quote); + instruments.push_back(anInstrument); }; // we can use historical or first ZCIIS for this From ede6ce104db2be70398d5cb0dde5fa1c0933fa30 Mon Sep 17 00:00:00 2001 From: francis Date: Mon, 22 Dec 2025 18:27:44 +0000 Subject: [PATCH 10/11] Fix crashing ObservationModeTest/testDefer test. Without the changes here, YearOnYearInflationSwapHelper observes Settings evaluationDate. Because of this YearOnYearInflationSwapHelper::initializeDates() gets called when the evaluation date changes. This leads to the yyiis_ member getting reassigned to a new YOY inflation swap. However, the original pointee is still in deferredObervers_ of ObservableSettings. The testDefer test then crashes when deferredObserver->update(); is called in ObservableSettings::enableUpdates() with null deferredObserver. Note: should open a PR in QuantLib so that when swap start is provided to YearOnYearInflationSwapHelper, updateDates_ gets set to false in RelativeDateBootstrapHelper similar to ZCIIS helper. --- OREAnalytics/test/testmarket.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/OREAnalytics/test/testmarket.cpp b/OREAnalytics/test/testmarket.cpp index dfd40641f..c9681933d 100644 --- a/OREAnalytics/test/testmarket.cpp +++ b/OREAnalytics/test/testmarket.cpp @@ -583,7 +583,7 @@ Handle TestMarket::makeZeroInflationIndex(string index, vect // Remove the helper's observation of the inflation index. This has the effect that the // PiecewiseZeroInflationCurve created below will also not observe the index. It will only get recalculated // if the initial market quotes change or if the initial nominal yield curve changes. Observation of the index - // was interfering with scneario generation. The PiecewiseZeroInflationCurve was getting recalculated on the + // was interfering with scenario generation. The PiecewiseZeroInflationCurve was getting recalculated on the // first time grid date t_1 i.e. was not using the t_0 calculated curve. anInstrument->unregisterWithAll(); anInstrument->registerWith(yts); @@ -617,6 +617,22 @@ Handle TestMarket::makeYoYInflationIndex(string index, vector QuantLib::ext::shared_ptr> anInstrument(new YearOnYearInflationSwapHelper( quote, Period(2, Months), dates[i], TARGET(), ModifiedFollowing, ActualActual(ActualActual::ISDA), ii, yts, asof_)); QL_DEPRECATED_ENABLE_WARNING + + // Remove the helper's observation of the inflation index. This has the effect that the + // PiecewiseYoYInflationCurve created below will also not observe the index. It will only get recalculated + // if the initial market quotes change or if the initial nominal yield curve changes. + // Note: QuantLib needs a change so that if swap start is provided in the helper above then updateDates_ on + // the RelativeDateBootstrapHelper should be false and hence the helper does not observe Settings + // evaluationDate. Without this fix, the unregister here does this also. + // These changes are needed to allow ObservationModeTest/testDefer to pass. Without them + // YearOnYearInflationSwapHelper::initializeDates() gets called due to evaluation date changes, the yyiis_ + // member gets reassigned but the original value is still in the deferredObervers_. The testDefer test then + // crashes when deferredObserver->update(); is called in ObservableSettings::enableUpdates() with + // deferredObserver null. + anInstrument->unregisterWithAll(); + anInstrument->registerWith(yts); + anInstrument->registerWith(quote); + instruments.push_back(anInstrument); }; // we can use historical or first ZCIIS for this From 893675b1e4efc5cd2de67365d026eefae90ea66c Mon Sep 17 00:00:00 2001 From: francis Date: Tue, 23 Dec 2025 20:40:26 +0000 Subject: [PATCH 11/11] Inflation curves unregister in OREData. Make the change discussed in PR #326. The change unregisters the index from the ZC and YoY helpers and hence the inflation curve does not observe the inflation index. It was interfering with scenario generation as outlined in the PR. The YoY helpers and inflation curve are still not observing the evaluationDate. --- OREData/ored/marketdata/inflationcurve.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/OREData/ored/marketdata/inflationcurve.cpp b/OREData/ored/marketdata/inflationcurve.cpp index a2d142d58..3550a9726 100644 --- a/OREData/ored/marketdata/inflationcurve.cpp +++ b/OREData/ored/marketdata/inflationcurve.cpp @@ -281,6 +281,12 @@ InflationCurve::CurveBuildResults zcq->quote(), convention->observationLag(), maturity, convention->fixCalendar(), convention->fixConvention(), convention->dayCounter(), index, observationInterpolation, nominalTs, swapStart); + + // Unregister with inflation index. See PR #326 for details. + instrument->unregisterWithAll(); + instrument->registerWith(nominalTs); + instrument->registerWith(zcq->quote()); + helpers.push_back(instrument); } } @@ -376,8 +382,13 @@ InflationCurve::CurveBuildResults QuantLib::ext::make_shared( quote, convention->observationLag(), maturity, convention->fixCalendar(), convention->fixConvention(), convention->dayCounter(), index, nominalTs, swapStart); - instrument->unregisterWith(Settings::instance().evaluationDate()); results.pillarDates.push_back(instrument->pillarDate()); + + // Unregister with inflation index (and evaluationDate). See PR #326 for details. + instrument->unregisterWithAll(); + instrument->registerWith(nominalTs); + instrument->registerWith(quote); + helpers.push_back(instrument); } }