@@ -278,10 +278,14 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
278278
279279 Size n = mesher_->locations ().size ();
280280 std::vector<Array> value (stochasticConversionRatios.size (), Array (n, 0.0 )), valueTmp;
281+ std::vector<Array> conversionIndicator;
282+ if (generateAdditionalResults_)
283+ conversionIndicator.resize (stochasticConversionRatios.size (), Array (n, 0.0 ));
281284
282285 // 10 add no-conversion variants for start of period coco feature
283286
284287 std::vector<Array> valueNoConversion, valueNoConversionTmp;
288+ std::vector<Array> conversionIndicatorNoConversion;
285289
286290 // 11 perform the backward PDE pricing
287291
@@ -309,12 +313,14 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
309313 Real cr = value.size () > 1 ? stochasticConversionRatios[plane] : events.getCurrentConversionRatio (i);
310314 for (Size j = 0 ; j < n; ++j) {
311315 bool cocoTriggered = true ;
312- if (events.hasContingentConversion (i) && !events.hasNoConversionPlane (i)) {
316+ if (events.hasContingentConversion (i) && !events.hasNoConversionPlane (i)) {
313317 cocoTriggered = cr * S[j] > events.getConversionData (i).cocoBarrier ;
314318 // update value from no conversion plane, if there is one and coco is not triggered
315319 if (!valueNoConversion.empty ()) {
316320 if (!cocoTriggered) {
317321 value[plane][j] = valueNoConversion[plane][j];
322+ if (!conversionIndicator.empty ())
323+ conversionIndicator[plane][j] = conversionIndicatorNoConversion[plane][j];
318324 }
319325 }
320326 }
@@ -323,6 +329,8 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
323329 if (cocoTriggered && exerciseValue > value[plane][j] + events.getBondFinalRedemption (i)) {
324330 value[plane][j] = exerciseValue;
325331 conversionExercised[plane][j] = true ;
332+ if (!conversionIndicator.empty ())
333+ conversionIndicator[plane][j] = 1.0 ;
326334 }
327335 }
328336 }
@@ -332,6 +340,7 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
332340
333341 if (events.hasContingentConversion (i) && !events.hasNoConversionPlane (i) && !valueNoConversion.empty ()) {
334342 valueNoConversion.clear ();
343+ conversionIndicatorNoConversion.clear ();
335344 }
336345
337346 // 11.4 handle cr / DP induced cr resets and resets to specific value on t_i
@@ -372,9 +381,8 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
372381 std::min (adjustedConversionRatio[j], N0 / (rd.globalFloor * referenceCP));
373382 }
374383 adjustedConversionRatio[j] =
375- std::max (cr,
376- adjustedConversionRatio[j] != QL_MAX_REAL ? adjustedConversionRatio[j]
377- : -QL_MAX_REAL);
384+ std::max (cr, adjustedConversionRatio[j] != QL_MAX_REAL ? adjustedConversionRatio[j]
385+ : -QL_MAX_REAL);
378386 }
379387 }
380388 }
@@ -447,6 +455,21 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
447455 }
448456 valueNoConversion = std::vector<Array>(1 , collapsedValue);
449457 }
458+ if (!conversionIndicator.empty ()) {
459+ for (Size j = 0 ; j < n; ++j) {
460+ collapsedValue[j] = interpolateValueFromPlanes (events.getCurrentConversionRatio (i),
461+ conversionIndicator, stochasticConversionRatios, j);
462+ }
463+ conversionIndicator = std::vector<Array>(1 , collapsedValue);
464+ }
465+ if (!conversionIndicatorNoConversion.empty ()) {
466+ for (Size j = 0 ; j < n; ++j) {
467+ collapsedValue[j] =
468+ interpolateValueFromPlanes (events.getCurrentConversionRatio (i), conversionIndicatorNoConversion,
469+ stochasticConversionRatios, j);
470+ }
471+ conversionIndicatorNoConversion = std::vector<Array>(1 , collapsedValue);
472+ }
450473 }
451474
452475 for (Size plane = 0 ; plane < value.size (); ++plane) {
@@ -469,6 +492,10 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
469492 conversionExercised[plane][j] = true ;
470493 if (!valueNoConversion.empty ())
471494 valueNoConversion[plane][j] = payoff;
495+ if (!conversionIndicator.empty ())
496+ conversionIndicator[plane][j] = 1.0 ;
497+ if (!conversionIndicatorNoConversion.empty ())
498+ conversionIndicatorNoConversion[plane][j] = 1.0 ;
472499 }
473500 }
474501
@@ -559,6 +586,10 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
559586 solver->rollback (value[plane], t_from, t_to, 1 , 0 );
560587 if (!valueNoConversion.empty ())
561588 solver->rollback (valueNoConversion[plane], t_from, t_to, 1 , 0 );
589+ if (!conversionIndicator.empty ())
590+ solver->rollback (conversionIndicator[plane], t_from, t_to, 1 , 0 );
591+ if (!conversionIndicatorNoConversion.empty ())
592+ solver->rollback (conversionIndicatorNoConversion[plane], t_from, t_to, 1 , 0 );
562593
563594 } // loop over stochastic conversion ratio planes
564595
@@ -624,7 +655,8 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
624655 << " |" << std::setw (width) << " eq_fwd"
625656 << " |" << std::setw (width) << " div_amt"
626657 << " |" << std::setw (width) << " conv_val"
627- << " |" << std::setw (width) << " conv_prc" << " |" ;
658+ << " |" << std::setw (width) << " conv_prc"
659+ << " |" ;
628660
629661 results_.additionalResults [" event_0000!" ] = header.str ();
630662
@@ -750,7 +782,7 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
750782 results_.additionalResults [" market.creditSpread(tMax)" ] =
751783 -std::log (model_->creditCurve ()->survivalProbability (tMax)) / tMax;
752784 if (!creditCurve_.empty ()) {
753- results_.additionalResults [" market.exchangeableBondSpread(tMax)" ] =
785+ results_.additionalResults [" market.exchangeableBondSpread(tMax)" ] =
754786 -std::log (creditCurve_->survivalProbability (tMax)) / tMax;
755787 }
756788 results_.additionalResults [" market.recoveryRate" ] = recoveryRate_.empty () ? 0.0 : recoveryRate_->value ();
@@ -765,6 +797,10 @@ void FdDefaultableEquityJumpDiffusionConvertibleBondEngine::calculate() const {
765797 results_.additionalResults [" model.calibrationTimes" ] = model_->stepTimes ();
766798 results_.additionalResults [" model.h0" ] = model_->h0 ();
767799 results_.additionalResults [" model.sigma" ] = model_->sigma ();
800+
801+ MonotonicCubicNaturalSpline interpolationConversionIndicator (
802+ mesher_->locations ().begin (), mesher_->locations ().end (), conversionIndicator[0 ].begin ());
803+ results_.additionalResults [" conversionIndicator" ] = interpolationConversionIndicator (logSpot);
768804}
769805
770806} // namespace QuantExt
0 commit comments