Skip to content

Commit 87bcaea

Browse files
pcaspersjenkins
authored andcommitted
QPR-12123 add conversion indicator pv as additional results
1 parent 28d5713 commit 87bcaea

1 file changed

Lines changed: 42 additions & 6 deletions

File tree

QuantExt/qle/pricingengines/fddefaultableequityjumpdiffusionconvertiblebondengine.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)