From 0eff87734e487f4bb672a97bf634c7e4d0004caa Mon Sep 17 00:00:00 2001 From: Peter Caspers Date: Sat, 1 Nov 2025 14:02:31 +0100 Subject: [PATCH 1/5] QPR-13654 move built flag to trade level --- OREData/ored/portfolio/portfolio.cpp | 10 +++++++--- OREData/ored/portfolio/portfolio.hpp | 3 +-- OREData/ored/portfolio/trade.cpp | 2 ++ OREData/ored/portfolio/trade.hpp | 7 +++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/OREData/ored/portfolio/portfolio.cpp b/OREData/ored/portfolio/portfolio.cpp index 22317fbd2f..44529070c5 100644 --- a/OREData/ored/portfolio/portfolio.cpp +++ b/OREData/ored/portfolio/portfolio.cpp @@ -42,7 +42,6 @@ void Portfolio::clear() { } void Portfolio::reset() { - isBuilt_ = false; LOG("Reset portfolio of size " << trades_.size()); for (auto [id, t] : trades_) t->reset(); @@ -175,9 +174,13 @@ void Portfolio::build(const QuantLib::ext::shared_ptr& engineFact .log(); } QL_REQUIRE(trades_.size() > 0, "Portfolio does not contain any built trades, context is '" + context + "'"); - isBuilt_ = true; } +bool Portfolio::isBuilt() const { + return std::all_of(trades_.begin(), trades_.end(), [](const auto& s) { return s.second->isBuilt(); }); +} + + Date Portfolio::maturity() const { QL_REQUIRE(trades_.size() > 0, "Cannot get maturity of an empty portfolio"); Date mat = Date::minDate(); @@ -221,7 +224,6 @@ void Portfolio::add(const QuantLib::ext::shared_ptr& trade) { QL_REQUIRE(!has(trade->id()), "Attempted to add a trade to the portfolio with an id, which already exists."); underlyingIndicesCache_.clear(); trades_[trade->id()] = trade; - isBuilt_ = false; } bool Portfolio::has(const string& id) { return trades_.find(id) != trades_.end(); } @@ -308,6 +310,7 @@ std::pair, bool> buildTrade(QuantLib::ext::shar try { trade->reset(); trade->build(engineFactory); + trade->setBuilt(); TLOG("Required Fixings for trade " << trade->id() << ":"); TLOGGERSTREAM(trade->requiredFixings()); return std::make_pair(nullptr, true); @@ -326,6 +329,7 @@ std::pair, bool> buildTrade(QuantLib::ext::shar failed->setEnvelope(trade->envelope()); failed->build(engineFactory); failed->resetPricingStats(trade->getNumberOfPricings(), trade->getCumulativePricingTime()); + failed->setBuilt(); LOG("Built failed trade with id " << failed->id()); return std::make_pair(failed, false); } else { diff --git a/OREData/ored/portfolio/portfolio.hpp b/OREData/ored/portfolio/portfolio.hpp index e966b13a4d..8630634f5e 100644 --- a/OREData/ored/portfolio/portfolio.hpp +++ b/OREData/ored/portfolio/portfolio.hpp @@ -87,7 +87,7 @@ class Portfolio : public XMLSerializable { const bool emitStructuredError = true); //! if the portfolio has been built - bool isBuilt() const { return isBuilt_; } + bool isBuilt() const; //! Calculates the maturity of the portfolio QuantLib::Date maturity() const; @@ -138,7 +138,6 @@ class Portfolio : public XMLSerializable { bool buildFailedTrades_, ignoreTradeBuildFail_; std::map> trades_; std::map> underlyingIndicesCache_; - bool isBuilt_ = false; }; std::pair, bool> buildTrade( diff --git a/OREData/ored/portfolio/trade.cpp b/OREData/ored/portfolio/trade.cpp index bbb5bf74bf..12c98fb86d 100644 --- a/OREData/ored/portfolio/trade.cpp +++ b/OREData/ored/portfolio/trade.cpp @@ -300,6 +300,8 @@ void Trade::reset() { savedNumberOfPricings_ += instrument_->getNumberOfPricings(); savedCumulativePricingTime_ += instrument_->getCumulativePricingTime(); } + // reset build status + setBuilt(false); // reset members instrument_ = QuantLib::ext::shared_ptr(); legs_.clear(); diff --git a/OREData/ored/portfolio/trade.hpp b/OREData/ored/portfolio/trade.hpp index 429ed3bdf6..06ccce7de4 100644 --- a/OREData/ored/portfolio/trade.hpp +++ b/OREData/ored/portfolio/trade.hpp @@ -246,6 +246,12 @@ class Trade : public XMLSerializable { const std::string& configuration, const bool includePastCashflows) const; + /* set build status, this flag is maintained in buildTrade() and Trade::reset(), i.e. _not_ in Trade::build() */ + void setBuilt(const bool b = true) const { isBuilt_ = b; } + + /* get build status */ + bool isBuilt() const { return isBuilt_; } + protected: string tradeType_; // class name of the derived class QuantLib::ext::shared_ptr instrument_; @@ -294,6 +300,7 @@ class Trade : public XMLSerializable { string id_; Envelope envelope_; TradeActions tradeActions_; + mutable bool isBuilt_ = false; }; template From 1c7fb035ee78e89f7f192a6141f11deffd414e25 Mon Sep 17 00:00:00 2001 From: Peter Caspers Date: Tue, 4 Nov 2025 14:59:22 +0100 Subject: [PATCH 2/5] align submodule --- ORE-SWIG/QuantLib-SWIG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ORE-SWIG/QuantLib-SWIG b/ORE-SWIG/QuantLib-SWIG index 9b1d648856..18203bf0af 160000 --- a/ORE-SWIG/QuantLib-SWIG +++ b/ORE-SWIG/QuantLib-SWIG @@ -1 +1 @@ -Subproject commit 9b1d6488567f1cc84ab55d4c08cfef7ea7d1550b +Subproject commit 18203bf0af4639a51a08c705e7b07bc864d2116b From 585a2c3a8b672d16fbc4993cdfbbbffc485a8e4f Mon Sep 17 00:00:00 2001 From: farahkhashman Date: Wed, 21 Jan 2026 11:33:25 +0000 Subject: [PATCH 3/5] merge conflict fix v1.8.14.1 --- QuantLib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QuantLib b/QuantLib index ee9cf1dde5..a6ef3224df 160000 --- a/QuantLib +++ b/QuantLib @@ -1 +1 @@ -Subproject commit ee9cf1dde56965141be6fa87ddfce9fa42034580 +Subproject commit a6ef3224dfe0024debde0a14ca6e0af6d8a00ee1 From 9b3150d9b7c57efc26fda1b834afcad4f1832172 Mon Sep 17 00:00:00 2001 From: farahkhashman Date: Wed, 21 Jan 2026 13:30:57 +0000 Subject: [PATCH 4/5] align submodules --- ORE-SWIG/QuantLib-SWIG | 2 +- QuantLib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ORE-SWIG/QuantLib-SWIG b/ORE-SWIG/QuantLib-SWIG index 18203bf0af..b1184e42d4 160000 --- a/ORE-SWIG/QuantLib-SWIG +++ b/ORE-SWIG/QuantLib-SWIG @@ -1 +1 @@ -Subproject commit 18203bf0af4639a51a08c705e7b07bc864d2116b +Subproject commit b1184e42d4a544bd46f260b825d4fe7e31442450 diff --git a/QuantLib b/QuantLib index a6ef3224df..2937e4bc8f 160000 --- a/QuantLib +++ b/QuantLib @@ -1 +1 @@ -Subproject commit a6ef3224dfe0024debde0a14ca6e0af6d8a00ee1 +Subproject commit 2937e4bc8f3e44c16ea1149cfcbe12a8eed1ea29 From d0bd49bf82b540235ee0841bc3457d26b998f125 Mon Sep 17 00:00:00 2001 From: Matt Menefee Date: Thu, 9 Apr 2026 01:21:00 -0600 Subject: [PATCH 5/5] Enable extrapolation on SABR swaption vol cube The SABR-calibrated swaption volatility cube was not marked as extrapolation-enabled, causing QuantLib to throw when pricing instruments with tenors or strikes outside the interpolated grid. Call enableExtrapolation() on the cube immediately after SABR calibration so that vol queries beyond the grid boundaries use the calibrated model's natural extrapolation rather than failing. --- OREData/ored/marketdata/genericyieldvolcurve.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/OREData/ored/marketdata/genericyieldvolcurve.cpp b/OREData/ored/marketdata/genericyieldvolcurve.cpp index 15cfe839fc..2d0daf2619 100644 --- a/OREData/ored/marketdata/genericyieldvolcurve.cpp +++ b/OREData/ored/marketdata/genericyieldvolcurve.cpp @@ -508,6 +508,7 @@ GenericYieldVolCurve::GenericYieldVolCurve( : QuantLib::ShiftedLognormal, initialModelParameters, config->outputShift(), config->modelShift(), maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError); + cube->enableExtrapolation(); } // Wrap it in a SwaptionVolCubeWithATM, disable short-cut for atm vol retrieval