1616 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
1717*/
1818
19- #include < ored/portfolio/builders/indexcreditdefaultswap.hpp>
2019#include < qle/pricingengines/midpointindexcdsengine.hpp>
20+ #include < qle/utilities/creditindexconstituentcurvecalibration.hpp>
2121
22- #include < ored/utilities/marketdata .hpp>
22+ #include < ored/portfolio/builders/indexcreditdefaultswap .hpp>
2323#include < ored/utilities/log.hpp>
24+ #include < ored/utilities/marketdata.hpp>
2425#include < ored/utilities/to_string.hpp>
2526
2627#include < boost/make_shared.hpp>
@@ -36,23 +37,29 @@ CreditPortfolioSensitivityDecomposition IndexCreditDefaultSwapEngineBuilder::sen
3637vector<string> IndexCreditDefaultSwapEngineBuilder::keyImpl (const Currency& ccy, const string& creditCurveId,
3738 const vector<string>& creditCurveIds,
3839 const QuantLib::ext::optional<string>& overrideCurve,
39- Real recoveryRate, const bool inCcyDiscountCurve) {
40+ const QuantLib::ext::optional<bool >& calibrateConstituentCurvesOverride,
41+ const std::vector<double >& constituentNotional, Real recoveryRate,
42+ const bool inCcyDiscountCurve) {
4043 vector<string> res{ccy.code ()};
4144 res.insert (res.end (), creditCurveIds.begin (), creditCurveIds.end ());
4245 res.push_back (creditCurveId);
4346 res.push_back (overrideCurve ? *overrideCurve : " " );
4447 if (recoveryRate != Null<Real>())
4548 res.push_back (to_string (recoveryRate));
4649 res.push_back (inCcyDiscountCurve ? " 1" : " 0" );
50+ res.push_back (
51+ calibrateConstituentCurvesOverride.has_value () ? (calibrateConstituentCurvesOverride.value () ? " 1" : " 0" ) : " " );
4752 return res;
4853}
4954
5055QuantLib::ext::shared_ptr<PricingEngine> MidPointIndexCdsEngineBuilder::engineImpl (
5156 const Currency& ccy, const string& creditCurveId, const vector<string>& creditCurveIds,
52- const QuantLib::ext::optional<string>& overrideCurve, Real recoveryRate, const bool inCcyDiscountCurve) {
57+ const QuantLib::ext::optional<string>& overrideCurve,
58+ const QuantLib::ext::optional<bool >& calibrateConstituentCurvesOverride,
59+ const std::vector<double >& constituentNotionals, Real recoveryRate, const bool inCcyDiscountCurve) {
5360
5461 std::string curve = overrideCurve ? *overrideCurve : engineParameter (" Curve" , {}, false , " Underlying" );
55-
62+
5663 if (curve == " Index" ) {
5764 auto creditCurve = indexCdsDefaultCurve (market_, creditCurveId, configuration (MarketContext::pricing));
5865 Handle<Quote> mktRecovery = market_->recoveryRate (creditCurveId, configuration (MarketContext::pricing));
@@ -70,10 +77,52 @@ QuantLib::ext::shared_ptr<PricingEngine> MidPointIndexCdsEngineBuilder::engineIm
7077 dpts.push_back (tmp->curve ());
7178 recovery.push_back (recoveryRate != Null<Real>() ? recoveryRate : tmp2->value ());
7279 }
73- return QuantLib::ext::make_shared<QuantExt::MidPointIndexCdsEngine>(
74- dpts, recovery,
75- market_->discountCurve (
76- ccy.code (), configuration (inCcyDiscountCurve ? MarketContext::irCalibration : MarketContext::pricing)));
80+ auto discountCurve = market_->discountCurve (
81+ ccy.code (), configuration (inCcyDiscountCurve ? MarketContext::irCalibration : MarketContext::pricing));
82+
83+ bool calibrateConstituentCurves =
84+ calibrateConstituentCurvesOverride
85+ ? *calibrateConstituentCurvesOverride
86+ : parseBool (engineParameter (" CalibrateUnderlyingCurves" , {}, false , " false" ));
87+ std::string runType = " " ;
88+ auto it = globalParameters_.find (" RunType" );
89+ if (it != globalParameters_.end ()) {
90+ runType = it->second ;
91+ }
92+ calibrateConstituentCurves = calibrateConstituentCurves && runType != " PortfolioAnalyser" ;
93+ if (calibrateConstituentCurves) {
94+ TLOG (" IndexCreditDefaultSwap: Calibrate constituent curves to index spread" );
95+ QL_REQUIRE (!creditCurveId.empty (),
96+ " IndexCreditDefaultSwap: cannot calibrate constituent curves to index spread "
97+ " if index credit curve ID is not set" );
98+ auto indexCreditCurve = indexCdsDefaultCurve (market_, creditCurveId, configuration (MarketContext::pricing));
99+ QL_REQUIRE (indexCreditCurve->refData ().startDate != Null<Date>(),
100+ " IndexCreditDefaultSwap: cannot calibrate constituent curves to index spread "
101+ " if index credit curve start date is not set, please check index credit curve configuration" );
102+ QL_REQUIRE (indexCreditCurve->refData ().indexTerm != 0 * Days,
103+ " IndexCreditDefaultSwap: cannot calibrate constituent curves to index spread "
104+ " if index credit curve index term is not set, please check index credit curve configuration" );
105+ QL_REQUIRE (indexCreditCurve->refData ().runningSpread != Null<Real>(),
106+ " IndexCreditDefaultSwap: cannot calibrate constituent curves to index spread "
107+ " if index credit curve running spread is not set, please check index credit curve configuration" );
108+ auto curveCalibration = ext::make_shared<QuantExt::CreditIndexConstituentCurveCalibration>(indexCreditCurve);
109+ auto res = curveCalibration->calibratedCurves (creditCurveIds, constituentNotionals, dpts, recovery);
110+ TLOG (" Calibration success: " << res.success );
111+ if (res.success ) {
112+ TLOG (" maturity,marketNPV,impliedNPV,calibrationFactor:" );
113+ for (Size i = 0 ; i < res.marketNpv .size (); ++i) {
114+ TLOG (res.cdsMaturity [i] << res.marketNpv [i] << " ," << res.impliedNpv [i] << " ,"
115+ << res.calibrationFactor [i]);
116+ }
117+ dpts = res.curves ;
118+ } else {
119+ ALOG (" IndexCreditDefaultSwap: Calibration of constituent curves to index spread failed, "
120+ " proceeding with non-calibrated "
121+ " curves. Got "
122+ << res.errorMessage << " continue with non-calibrated curves." );
123+ }
124+ }
125+ return QuantLib::ext::make_shared<QuantExt::MidPointIndexCdsEngine>(dpts, recovery, discountCurve);
77126 } else {
78127 QL_FAIL (" MidPointIndexCdsEngineBuilder: Curve Parameter value \" "
79128 << engineParameter (" Curve" ) << " \" not recognised, expected Underlying or Index" );
0 commit comments