From 6569bae1aac84dbb4fc6917d29acb2ab65818dd7 Mon Sep 17 00:00:00 2001 From: Zetterberg <119352036+oszette@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:50:40 +0200 Subject: [PATCH] Handle missing base discount curve and log once Add robust handling for missing base discount curves during cashflow report generation. Both cashflowutils.cpp and trade.cpp now include the logging header and use a missingBaseDiscountCurveLogged flag to emit a single DLOG entry when the base discount curve cannot be retrieved. Retrievals of the base discount curve are wrapped in try/catch blocks so cashflow processing continues with null discount factors instead of throwing. Also add a pay-date null check before attempting base curve lookup and adjust the baseDiscountCurve type/usage when constructing indexed coupon objects. --- OREData/ored/portfolio/cashflowutils.cpp | 20 ++++++++++++++++---- OREData/ored/portfolio/trade.cpp | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/OREData/ored/portfolio/cashflowutils.cpp b/OREData/ored/portfolio/cashflowutils.cpp index 963d67a0b1..61a3deac1c 100644 --- a/OREData/ored/portfolio/cashflowutils.cpp +++ b/OREData/ored/portfolio/cashflowutils.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -62,6 +63,7 @@ void populateReportDataFromAdditionalResults(std::vectordiscountCurve(ccy, configuration) : specificDiscountCurve; discountFactor = cf.payDate < asof ? 0.0 : discountCurve->discount(cf.payDate); } - if (ccy != baseCurrency) { - auto baseDiscountCurve = specificDiscountCurve.empty() ? market->discountCurve(baseCurrency, configuration) : specificDiscountCurve; - discountFactorBase = cf.payDate < asof ? 0.0 : baseDiscountCurve->discount(cf.payDate); - } + if (ccy != baseCurrency && cf.payDate != Null()) { + try { + auto baseDiscountCurve = + specificDiscountCurve.empty() ? market->discountCurve(baseCurrency, configuration) : specificDiscountCurve; + discountFactorBase = cf.payDate < asof ? 0.0 : baseDiscountCurve->discount(cf.payDate); + } catch (std::exception& e) { + if (!missingBaseDiscountCurveLogged) { + DLOG("Could not retrieve base discount curve for cashflow report (base currency " + << baseCurrency << ", configuration " << configuration + << "). DiscountFactor(Base) will be left null. Details: " << e.what()); + missingBaseDiscountCurveLogged = true; + } + } + } if (cf.presentValue != Null()) { presentValue = cf.presentValue * multiplier; } else if (effectiveAmount != Null() && discountFactor != Null()) { diff --git a/OREData/ored/portfolio/trade.cpp b/OREData/ored/portfolio/trade.cpp index 6e0a1a39bf..e195aa457f 100644 --- a/OREData/ored/portfolio/trade.cpp +++ b/OREData/ored/portfolio/trade.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -449,6 +450,7 @@ std::vector Trade::cashflows(const std::string& baseCur // add cashflows from trade legs, if no cashflows were added so far or if a leg is marked as mandatory for cashflows bool haveEngineCashflows = !result.empty(); + bool missingBaseDiscountCurveLogged = false; for (size_t i = 0; i < legs().size(); i++) { Trade::LegCashflowInclusion cashflowInclusion = Trade::LegCashflowInclusion::IfNoEngineCashflows; if (auto incl = legCashflowInclusion().find(i); incl != legCashflowInclusion().end()) { @@ -465,10 +467,20 @@ std::vector Trade::cashflows(const std::string& baseCur string ccy = legCurrencies()[i]; Handle discountCurve = specificDiscountCurve; - if (discountCurve.empty()) + if (discountCurve.empty()) discountCurve = market->discountCurve(ccy, configuration); - - Handle baseDiscountCurve = market->discountCurve(baseCurrency, configuration); + + QuantLib::ext::shared_ptr baseDiscountCurve; + try { + baseDiscountCurve = *market->discountCurve(baseCurrency, configuration); + } catch (std::exception& e) { + if (!missingBaseDiscountCurveLogged) { + DLOG("Could not retrieve base discount curve for cashflow report (base currency " + << baseCurrency << ", configuration " << configuration + << "). Base discount factor column will be left null where required. Details: " << e.what()); + missingBaseDiscountCurveLogged = true; + } + } auto fxRateCcyBase = market->fxRate(npvCurrency_ + baseCurrency, configuration)->value(); auto fxRateLocalCcy = market->fxRate(ccy + npvCurrency_, configuration)->value(); @@ -487,7 +499,7 @@ std::vector Trade::cashflows(const std::string& baseCur [&market, &configuration](const std::string qualifier) { return *market->capFloorVol(qualifier, configuration); }, - std::string(), Null(), *baseDiscountCurve)); + std::string(), Null(), baseDiscountCurve)); result.back().cashflowNo = j + 1; result.back().legNo = i + legNoOffset; }