@@ -232,79 +232,98 @@ DefaultCurve::DefaultCurve(Date asof, DefaultCurveSpec spec, const Loader& loade
232232 const QuantLib::ext::shared_ptr<DefaultCurveConfig>& configs = curveConfigs.defaultCurveConfig (spec.curveConfigID ());
233233 bool built = false ;
234234 std::string errors;
235- for (auto const & config : configs->configs ()) {
236- try {
237- recoveryRate_ = Null<Real>();
238- if (!config.second .recoveryRateQuote ().empty ()) {
239- // handle case where the recovery rate is hardcoded in the curve config
240- if (!tryParseReal (config.second .recoveryRateQuote (), recoveryRate_)) {
241- Wildcard wc (config.second .recoveryRateQuote ());
242- if (wc.hasWildcard ()) {
243- for (auto const & q : loader.get (wc, asof)) {
244- if (wc.matches (q->name ())) {
245- QL_REQUIRE (
246- recoveryRate_ == Null<Real>() ||
247- QuantLib::close_enough (recoveryRate_, q->quote ()->value ()),
248- " There is more than one recovery rate with different values matching the pattern '"
249- << wc.pattern () << " ', values: " << recoveryRate_ << " , "
250- << q->quote ()->value ());
251- recoveryRate_ = q->quote ()->value ();
235+ // Try building the curve with each config in turn, until one works
236+ // run first all configs with implyDefaultFromMarket false, only if all configurations have failed
237+ // we retry CDSSpread and Price configurations that have implyDefaultFromMarket true
238+ std::set<size_t > configsWithImplyDefaultFromMarket;
239+ for (auto const & implyDefaultFromMarket : {false , true }) {
240+ for (auto const & config : configs->configs ()) {
241+ if (implyDefaultFromMarket && ((config.second .type () != DefaultCurveConfig::Config::Type::SpreadCDS &&
242+ config.second .type () != DefaultCurveConfig::Config::Type::Price) ||
243+ !config.second .implyDefaultFromMarket ().value_or (false ))) {
244+ // For the second pass we only want SpreadCDS/Price configs that have
245+ // implyDefaultFromMarket set to true
246+ continue ;
247+ }
248+ try {
249+ recoveryRate_ = Null<Real>();
250+ if (!config.second .recoveryRateQuote ().empty ()) {
251+ // handle case where the recovery rate is hardcoded in the curve config
252+ if (!tryParseReal (config.second .recoveryRateQuote (), recoveryRate_)) {
253+ Wildcard wc (config.second .recoveryRateQuote ());
254+ if (wc.hasWildcard ()) {
255+ for (auto const & q : loader.get (wc, asof)) {
256+ if (wc.matches (q->name ())) {
257+ QL_REQUIRE (recoveryRate_ == Null<Real>() ||
258+ QuantLib::close_enough (recoveryRate_, q->quote ()->value ()),
259+ " There is more than one recovery rate with different values matching "
260+ " the pattern '"
261+ << wc.pattern () << " ', values: " << recoveryRate_ << " , "
262+ << q->quote ()->value ());
263+ recoveryRate_ = q->quote ()->value ();
264+ }
252265 }
266+ } else {
267+ QL_REQUIRE (loader.has (config.second .recoveryRateQuote (), asof),
268+ " There is no market data for the requested recovery rate "
269+ << config.second .recoveryRateQuote ());
270+ recoveryRate_ = loader.get (config.second .recoveryRateQuote (), asof)->quote ()->value ();
253271 }
254- } else {
255- QL_REQUIRE (loader.has (config.second .recoveryRateQuote (), asof),
256- " There is no market data for the requested recovery rate "
257- << config.second .recoveryRateQuote ());
258- recoveryRate_ = loader.get (config.second .recoveryRateQuote (), asof)->quote ()->value ();
259272 }
260273 }
261- }
262- // Build the default curve of the requested type
263- switch (config.second .type ()) {
264- case DefaultCurveConfig::Config::Type::SpreadCDS:
265- case DefaultCurveConfig::Config::Type::ConvSpreadCDS:
266- case DefaultCurveConfig::Config::Type::Price:
267- buildCdsCurve (configs->curveID (), config.second , asof, spec, loader, yieldCurves);
268- break ;
269- case DefaultCurveConfig::Config::Type::HazardRate:
270- buildHazardRateCurve (configs->curveID (), config.second , asof, spec, loader);
271- break ;
272- case DefaultCurveConfig::Config::Type::Benchmark:
273- buildBenchmarkCurve (configs->curveID (), config.second , asof, spec, loader, yieldCurves);
274- break ;
275- case DefaultCurveConfig::Config::Type::MultiSection:
276- buildMultiSectionCurve (configs->curveID (), config.second , asof, spec, loader, defaultCurves);
277- break ;
278- case DefaultCurveConfig::Config::Type::TransitionMatrix:
279- buildTransitionMatrixCurve (configs->curveID (), config.second , asof, spec, loader, defaultCurves);
280- break ;
281- case DefaultCurveConfig::Config::Type::Null:
282- buildNullCurve (configs->curveID (), config.second , asof, spec);
274+ // Build the default curve of the requested type
275+ switch (config.second .type ()) {
276+ case DefaultCurveConfig::Config::Type::SpreadCDS:
277+ case DefaultCurveConfig::Config::Type::ConvSpreadCDS:
278+ case DefaultCurveConfig::Config::Type::Price:
279+ buildCdsCurve (configs->curveID (), config.second , asof, spec, loader, yieldCurves,
280+ implyDefaultFromMarket);
281+ break ;
282+ case DefaultCurveConfig::Config::Type::HazardRate:
283+ buildHazardRateCurve (configs->curveID (), config.second , asof, spec, loader);
284+ break ;
285+ case DefaultCurveConfig::Config::Type::Benchmark:
286+ buildBenchmarkCurve (configs->curveID (), config.second , asof, spec, loader, yieldCurves);
287+ break ;
288+ case DefaultCurveConfig::Config::Type::MultiSection:
289+ buildMultiSectionCurve (configs->curveID (), config.second , asof, spec, loader, defaultCurves);
290+ break ;
291+ case DefaultCurveConfig::Config::Type::TransitionMatrix:
292+ buildTransitionMatrixCurve (configs->curveID (), config.second , asof, spec, loader, defaultCurves);
293+ break ;
294+ case DefaultCurveConfig::Config::Type::Null:
295+ buildNullCurve (configs->curveID (), config.second , asof, spec);
296+ break ;
297+ default :
298+ QL_FAIL (" The DefaultCurveConfig type " << static_cast <int >(config.second .type ())
299+ << " was not recognised" );
300+ }
301+ built = true ;
283302 break ;
284- default :
285- QL_FAIL (" The DefaultCurveConfig type " << static_cast <int >(config.second .type ())
286- << " was not recognised" );
303+ } catch (exception& e) {
304+ std::ostringstream message;
305+ message << " build attempt failed for " << configs->curveID () << " using config with priority "
306+ << config.first << " : " << e.what ()
307+ << " and implyDefaultFromMarket= " << to_string (implyDefaultFromMarket);
308+ DLOG (message.str ());
309+ if (!errors.empty ())
310+ errors += " , " ;
311+ errors += message.str ();
287312 }
288- built = true ;
289- break ;
290- } catch (exception& e) {
291- std::ostringstream message;
292- message << " build attempt failed for " << configs->curveID () << " using config with priority "
293- << config.first << " : " << e.what ();
294- DLOG (message.str ());
295- if (!errors.empty ())
296- errors += " , " ;
297- errors += message.str ();
298313 }
314+ if (built)
315+ break ;
299316 }
300317 QL_REQUIRE (built, " default curve building failed for " << spec.curveConfigID () << " : " << errors);
301318}
302319
303320void DefaultCurve::buildCdsCurve (const std::string& curveID, const DefaultCurveConfig::Config& config, const Date& asof,
304321 const DefaultCurveSpec& spec, const Loader& loader,
305- map<string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves) {
322+ map<string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
323+ bool implyDefaultFromMarket) {
306324
307- LOG (" Start building default curve of type SpreadCDS for curve " << curveID);
325+ LOG (" Start building default curve of type SpreadCDS for curve " << curveID << " and implyDefaultFromMarket = "
326+ << to_string (implyDefaultFromMarket));
308327
309328 QL_REQUIRE (config.type () == DefaultCurveConfig::Config::Type::SpreadCDS ||
310329 config.type () == DefaultCurveConfig::Config::Type::Price ||
@@ -345,7 +364,7 @@ void DefaultCurve::buildCdsCurve(const std::string& curveID, const DefaultCurveC
345364 refData.cashSettlementDays = cdsConv->upfrontSettlementDays ();
346365
347366 // If the configuration instructs us to imply a default from the market data, we do it here.
348- if (config. implyDefaultFromMarket () && *config. implyDefaultFromMarket () ) {
367+ if (implyDefaultFromMarket) {
349368 if (recoveryRate_ != Null<Real>() && quotes.empty ()) {
350369 // Assume entity is in default, between event determination date and auction date. Build a survival
351370 // probability curve with value 0.0 tomorrow to approximate this and allow dependent instruments to price.
0 commit comments