24#include <ql/cashflows/coupon.hpp>
25#include <ql/math/distributions/normaldistribution.hpp>
26#include <ql/math/interpolations/cubicinterpolation.hpp>
27#include <ql/methods/finitedifferences/meshers/fdmmeshercomposite.hpp>
28#include <ql/methods/finitedifferences/meshers/uniform1dmesher.hpp>
29#include <ql/methods/finitedifferences/solvers/fdmbackwardsolver.hpp>
30#include <ql/timegrid.hpp>
37 Real price = cd.
price * notional;
47 const std::vector<Real>& stochasticConversionRatios,
const Size j) {
48 if (value.size() == 1)
49 return value.front()[j];
51 Size idx = std::distance(
52 stochasticConversionRatios.begin(),
53 std::upper_bound(stochasticConversionRatios.begin(), stochasticConversionRatios.end(), conversionRatio));
55 return value.front()[j];
56 else if (idx == stochasticConversionRatios.size())
57 return value.back()[j];
59 Real x0 = stochasticConversionRatios[idx - 1];
60 Real x1 = stochasticConversionRatios[idx];
61 Real y0 = value[idx - 1][j];
62 Real y1 = value[idx][j];
63 Real alpha = (x1 - conversionRatio) / (x1 - x0);
64 return alpha * y0 + (1.0 - alpha) * y1;
69 const Handle<DefaultableEquityJumpDiffusionModel>& model,
70 const Handle<QuantLib::YieldTermStructure>& discountingCurve,
const Handle<QuantLib::Quote>& discountingSpread,
71 const Handle<QuantLib::DefaultProbabilityTermStructure>& creditCurve,
const Handle<QuantLib::Quote>& recoveryRate,
72 const Handle<FxIndex>& fxConversion,
const bool staticMesher,
const Size timeStepsPerYear,
73 const Size stateGridPoints,
const Real mesherEpsilon,
const Real mesherScaling,
74 const std::vector<Real> conversionRatioDiscretisationGrid,
const bool generateAdditionalResults)
75 : model_(model), discountingCurve_(discountingCurve), discountingSpread_(discountingSpread),
76 creditCurve_(creditCurve), recoveryRate_(recoveryRate), fxConversion_(fxConversion), staticMesher_(staticMesher),
77 timeStepsPerYear_(timeStepsPerYear), stateGridPoints_(stateGridPoints), mesherEpsilon_(mesherEpsilon),
78 mesherScaling_(mesherScaling), conversionRatioDiscretisationGrid_(conversionRatioDiscretisationGrid),
79 generateAdditionalResults_(generateAdditionalResults) {
97 Date today = Settings::instance().evaluationDate();
103 for (Size i = 0; i <
arguments_.cashflows.size(); ++i) {
104 if (
arguments_.cashflows[i]->date() > today) {
119 for (
auto const& c :
arguments_.conversionRatioData) {
125 for (
auto const& c :
arguments_.conversionData) {
131 for (
auto const& c :
arguments_.mandatoryConversionData) {
137 for (
auto const& c :
arguments_.conversionResetData) {
143 for (
auto const& c :
arguments_.dividendProtectionData) {
153 QL_REQUIRE(!events.
times().empty(),
154 "FdDefaultableEquityJumpDiffusionConvertibleEngine: internal error, times are empty");
160 Real spot =
model_->equity()->equitySpot()->value();
161 Real logSpot = std::log(spot);
167 for (Size i = 1; i < grid.size(); ++i) {
168 forward = spot *
model_->equity()->equityDividendCurve()->discount(grid[i]) /
169 model_->equity()->equityForecastCurve()->discount(grid[i]);
170 mi = std::min(mi, forward);
171 ma = std::max(ma, forward);
173 Real sigmaSqrtT = std::max(1E-2 * std::sqrt(grid.back()), std::sqrt(
model_->totalBlackVariance()));
174 Real normInvEps = InverseCumulativeNormal()(1.0 -
mesherEpsilon_);
175 Real xMin = std::log(mi) - sigmaSqrtT * normInvEps *
mesherScaling_;
176 Real xMax = std::log(ma) + sigmaSqrtT * normInvEps *
mesherScaling_;
184 std::vector<Real> notionalTimes = {0.0};
185 std::vector<Real> notionals = {N0};
186 std::vector<Real> couponAmounts, couponAccrualStartTimes, couponAccrualEndTimes, couponPayTimes;
188 if (c->date() <= today)
190 if (
auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c)) {
192 notionalTimes.push_back(
model_->timeFromReference(c->date()));
193 notionals.push_back(cpn->nominal());
195 couponAmounts.push_back(cpn->amount());
196 couponAccrualStartTimes.push_back(
model_->timeFromReference(cpn->accrualStartDate()));
197 couponAccrualEndTimes.push_back(
model_->timeFromReference(cpn->accrualEndDate()));
198 couponPayTimes.push_back(
model_->timeFromReference(cpn->date()));
202 auto notional = [¬ionalTimes, ¬ionals](
const Real t) {
203 auto cn = std::upper_bound(notionalTimes.begin(), notionalTimes.end(), t,
204 [](Real s, Real t) { return s < t && !close_enough(s, t); });
205 return notionals[std::max<Size>(std::distance(notionalTimes.begin(), cn), 1) - 1];
208 auto recovery = [
this, ¬ional, N0](
const Real t,
const Real S,
const Real conversionRatio) {
209 Real currentBondNotional = notional(t);
211 Real conversionValue = 0.0;
212 if (conversionRatio != Null<Real>())
213 conversionValue = currentBondNotional / N0 * conversionRatio * S * (1.0 - this->
model_->eta());
214 if (!this->
arguments_.exchangeableData.isExchangeable) {
216 return std::max(rr * currentBondNotional, conversionValue);
219 return currentBondNotional;
223 std::function<Real(Real, Real, Real)> addRecovery;
224 if (
arguments_.exchangeableData.isExchangeable) {
225 addRecovery = [
this, ¬ional, N0](
const Real t,
const Real S,
const Real conversionRatio) {
226 Real currentBondNotional = notional(t);
228 Real conversionValue = 0.0;
229 if (conversionRatio != Null<Real>())
230 conversionValue = currentBondNotional / N0 * conversionRatio * S * (1.0 - this->
model_->eta());
231 if (!this->
arguments_.exchangeableData.isSecured) {
233 return rr * currentBondNotional;
236 return conversionValue + rr * std::max(currentBondNotional - conversionValue, 0.0);
241 auto accrual = [&couponAmounts, &couponPayTimes, &couponAccrualStartTimes, &couponAccrualEndTimes](
const Real t) {
243 for (Size i = 0; i < couponAmounts.size(); ++i) {
244 if (couponPayTimes[i] > t && t > couponAccrualStartTimes[i]) {
245 accruals += (t - couponAccrualStartTimes[i]) / (couponAccrualEndTimes[i] - couponAccrualStartTimes[i]) *
254 auto fdmOp = QuantLib::ext::make_shared<FdmDefaultableEquityJumpDiffusionOp>(
260 auto solver = QuantLib::ext::make_shared<FdmBackwardSolver>(
261 fdmOp, std::vector<QuantLib::ext::shared_ptr<BoundaryCondition<FdmLinearOp>>>(),
nullptr, FdmSchemeDesc::Douglas());
269 std::vector<Real> stochasticConversionRatios(1, Null<Real>());
279 Size n =
mesher_->locations().size();
280 std::vector<Array> value(stochasticConversionRatios.size(), Array(n, 0.0)), valueTmp;
281 std::vector<Array> conversionIndicator;
283 conversionIndicator.resize(stochasticConversionRatios.size(), Array(n, 0.0));
287 std::vector<Array> valueNoConversion, valueNoConversionTmp;
288 std::vector<Array> conversionIndicatorNoConversion;
292 Array S(
mesher_->locations().begin(),
mesher_->locations().end());
295 for (Size i = grid.size() - 1; i > 0; --i) {
299 Real t_from = grid[i];
300 Real t_to = grid[i - 1];
305 valueNoConversion = value;
306 conversionIndicatorNoConversion = conversionIndicator;
311 std::vector<std::vector<bool>> conversionExercised(value.size(), std::vector<bool>(n,
false));
312 for (Size plane = 0; plane < value.size(); ++plane) {
315 for (Size j = 0; j < n; ++j) {
316 bool cocoTriggered =
true;
320 if (!valueNoConversion.empty()) {
321 if (!cocoTriggered) {
322 value[plane][j] = valueNoConversion[plane][j];
323 if (!conversionIndicator.empty())
324 conversionIndicator[plane][j] = conversionIndicatorNoConversion[plane][j];
328 Real exerciseValue = S[j] * cr * notional(grid[i]) / N0 + accrual(grid[i]);
331 value[plane][j] = exerciseValue;
332 conversionExercised[plane][j] =
true;
333 if (!conversionIndicator.empty())
334 conversionIndicator[plane][j] = 1.0;
343 valueNoConversion.clear();
344 conversionIndicatorNoConversion.clear();
350 if (!valueNoConversion.empty())
351 valueNoConversionTmp = valueNoConversion;
353 for (Size plane = 0; plane < value.size(); ++plane) {
357 Array adjustedConversionRatio(n, Null<Real>());
366 std::fill(adjustedConversionRatio.begin(), adjustedConversionRatio.end(), 0.0);
368 Real referenceCP = N0 / cr;
369 for (Size j = 0; j < n; ++j) {
371 adjustedConversionRatio[j] = QL_MAX_REAL;
373 adjustedConversionRatio[j] =
374 std::min(adjustedConversionRatio[j], N0 / (rd.
gearing * S[j]));
377 adjustedConversionRatio[j] =
378 std::min(adjustedConversionRatio[j], N0 / (rd.
floor * referenceCP));
381 adjustedConversionRatio[j] =
382 std::min(adjustedConversionRatio[j], N0 / (rd.
globalFloor * referenceCP));
384 adjustedConversionRatio[j] =
385 std::max(cr, adjustedConversionRatio[j] != QL_MAX_REAL ? adjustedConversionRatio[j]
396 for (Size j = 0; j < n; ++j) {
398 if (adjustedConversionRatio[j] == Null<Real>()) {
399 adjustedConversionRatio[j] = value.size() > 1 ? stochasticConversionRatios[plane]
403 (std::exp(
model_->dividendYield(s, t) * (t - s)) - 1.0) * S[j];
406 Real d = absolute ? D : D / S[j];
409 ? std::max(d - H, 0.0)
411 adjustedConversionRatio[j] *= (absolute ? S[j] / std::max(S[j] - C, 1E-4) : (1.0 + C));
413 Real f = std::max(S[j] - H, 0.0) / std::max(S[j] - D, 1E-4);
416 f = std::max(f, 1.0);
417 adjustedConversionRatio[j] *= f;
421 for (Size j = 0; j < n; ++j) {
422 Real lookupValue = adjustedConversionRatio[j];
424 lookupValue = rd.
newCr;
426 if (lookupValue != Null<Real>()) {
430 if (!valueNoConversion.empty()) {
432 stochasticConversionRatios, j);
439 value.swap(valueTmp);
440 if (!valueNoConversion.empty())
441 valueNoConversion.swap(valueNoConversionTmp);
446 Array collapsedValue(n);
447 for (Size j = 0; j < n; ++j) {
449 stochasticConversionRatios, j);
451 value = std::vector<Array>(1, collapsedValue);
452 if (!valueNoConversion.empty()) {
453 for (Size j = 0; j < n; ++j) {
455 valueNoConversion, stochasticConversionRatios, j);
457 valueNoConversion = std::vector<Array>(1, collapsedValue);
459 if (!conversionIndicator.empty()) {
460 for (Size j = 0; j < n; ++j) {
462 conversionIndicator, stochasticConversionRatios, j);
464 conversionIndicator = std::vector<Array>(1, collapsedValue);
466 if (!conversionIndicatorNoConversion.empty()) {
467 for (Size j = 0; j < n; ++j) {
470 stochasticConversionRatios, j);
472 conversionIndicatorNoConversion = std::vector<Array>(1, collapsedValue);
476 for (Size plane = 0; plane < value.size(); ++plane) {
481 for (Size j = 0; j < n; ++j) {
485 if (S[j] < d.pepsLowerBarrier) {
487 }
else if (S[j] > d.pepsUpperBarrier) {
488 payoff = d.pepsUpperConversionRatio * S[j] * notional(grid[i]) / N0 + accrual(grid[i]);
490 payoff = notional(grid[i]) + accrual(grid[i]);
492 value[plane][j] = payoff;
493 conversionExercised[plane][j] =
true;
494 if (!valueNoConversion.empty())
495 valueNoConversion[plane][j] = payoff;
496 if (!conversionIndicator.empty())
497 conversionIndicator[plane][j] = 1.0;
498 if (!conversionIndicatorNoConversion.empty())
499 conversionIndicatorNoConversion[plane][j] = 1.0;
508 for (Size j = 0; j < n; ++j) {
509 if (!conversionExercised[plane][j]) {
518 Real forcedConversionValue = S[j] * cr * notional(grid[i]) / N0 + accrual(grid[i]);
519 if (forcedConversionValue > c && forcedConversionValue < value[plane][j]) {
521 if (!conversionIndicator.empty())
522 conversionIndicator[plane][j] = 0.0;
523 conversionExercised[plane][j] =
false;
525 value[plane][j] = std::min(std::max(forcedConversionValue, c), value[plane][j]);
526 if (!valueNoConversion.empty()) {
527 valueNoConversion[plane][j] =
528 std::min(std::max(forcedConversionValue, c), valueNoConversion[plane][j]);
537 for (Size j = 0; j < n; ++j) {
538 if (c > value[plane][j]) {
541 if (!conversionIndicator.empty())
542 conversionIndicator[plane][j] = 0.0;
543 conversionExercised[plane][j] =
false;
546 if (!valueNoConversion.empty()) {
547 for (Size j = 0; j < n; ++j) {
548 valueNoConversion[plane][j] = std::max(c, valueNoConversion[plane][j]);
561 for (Size j = 0; j < n; ++j) {
566 ? std::max(D - H, 0.0)
568 value[plane][j] += a * cr;
569 if (!valueNoConversion.empty())
570 valueNoConversion[plane][j] += a * cr;
578 if (!valueNoConversion.empty())
582 for (Size j = 0; j < n; ++j) {
583 if (!conversionExercised[plane][j] && !
arguments_.perpetual) {
585 if (!valueNoConversion.empty())
594 fdmOp->setConversionRatio([cr](
const Real S) {
return cr; });
598 solver->rollback(value[plane], t_from, t_to, 1, 0);
599 if (!valueNoConversion.empty())
600 solver->rollback(valueNoConversion[plane], t_from, t_to, 1, 0);
601 if (!conversionIndicator.empty())
602 solver->rollback(conversionIndicator[plane], t_from, t_to, 1, 0);
603 if (!conversionIndicatorNoConversion.empty())
604 solver->rollback(conversionIndicatorNoConversion[plane], t_from, t_to, 1, 0);
612 fdmOp->setConversionRatio([](
const Real S) {
return Null<Real>(); });
614 Array valueBondFloor(n, 0.0);
615 for (Size i = grid.size() - 1; i > 0; --i) {
616 Real t_from = grid[i];
617 Real t_to = grid[i - 1];
621 solver->rollback(valueBondFloor, t_from, t_to, 1, 0);
628 "FdDefaultableEquityJumpDiffusionConvertibleEngine: internal error, have "
630 <<
" pde planes after complete rollback, the planes should have been collapsed to one during the rollback");
632 MonotonicCubicNaturalSpline interpolationValue(
mesher_->locations().begin(),
mesher_->locations().end(),
634 MonotonicCubicNaturalSpline interpolationBondFloor(
mesher_->locations().begin(),
mesher_->locations().end(),
635 valueBondFloor.begin());
636 interpolationValue.enableExtrapolation();
637 interpolationBondFloor.enableExtrapolation();
638 Real npv = interpolationValue(logSpot);
639 Real npvBondFloor = interpolationBondFloor(logSpot);
640 results_.additionalResults[
"BondFloor"] = npvBondFloor;
653 constexpr Size width = 12;
654 std::ostringstream header;
655 header << std::left <<
"|" << std::setw(width) <<
"time"
656 <<
"|" << std::setw(width) <<
"date"
657 <<
"|" << std::setw(width) <<
"notional"
658 <<
"|" << std::setw(width) <<
"accrual"
659 <<
"|" << std::setw(width) <<
"flow"
660 <<
"|" << std::setw(width) <<
"call"
661 <<
"|" << std::setw(width) <<
"put"
662 <<
"|" << std::setw(2 * width) <<
"conversion"
663 <<
"|" << std::setw(2 * width) <<
"CR_reset"
664 <<
"|" << std::setw(width) <<
"div_passth"
665 <<
"|" << std::setw(width) <<
"curr_cr"
666 <<
"|" << std::setw(width) <<
"fxConv"
667 <<
"|" << std::setw(width) <<
"eq_fwd"
668 <<
"|" << std::setw(width) <<
"div_amt"
669 <<
"|" << std::setw(width) <<
"conv_val"
670 <<
"|" << std::setw(width) <<
"conv_prc"
673 results_.additionalResults[
"event_0000!"] = header.str();
676 for (Size i = 0; i < grid.size(); ++i) {
678 std::ostringstream dateStr, bondFlowStr, callStr, putStr, convStr, convResetStr, divStr, currentConvStr,
679 fxConvStr, eqFwdStr, divAmtStr, convValStr, convPrcStr;
681 Real eqFwd =
model_->equity()->equitySpot()->value() /
682 model_->equity()->equityForecastCurve()->discount(grid[i]) *
683 model_->equity()->equityDividendCurve()->discount(grid[i]);
694 callStr <<
"@" << cd.
price;
696 callStr <<
" s@" << cd.softTriggerRatio;
700 putStr <<
"@" << cd.
price;
718 if (cd.resetToSpecificValue) {
719 convResetStr <<
"->" << cd.
newCr <<
" ";
721 if (cd.resetActive) {
722 convResetStr << cd.gearing <<
"@" << cd.threshold;
724 convResetStr <<
"/CPt ";
726 convResetStr <<
"/CP0 ";
728 if (cd.divProtActive) {
729 convResetStr <<
"DP(" << cd.lastDividendProtectionTimeIndex <<
"/"
730 <<
model_->dividendYield(grid[cd.lastDividendProtectionTimeIndex], grid[i]) <<
")@"
732 Real s = grid[cd.lastDividendProtectionTimeIndex + 1];
735 cd.accruedHistoricalDividends + (std::exp(
model_->dividendYield(s, t) * (t - s)) - 1.0) * eqFwd;
741 Real s = grid[cd.lastDividendProtectionTimeIndex + 1];
744 cd.accruedHistoricalDividends + (std::exp(
model_->dividendYield(s, t) * (t - s)) - 1.0) * eqFwd;
747 currentConvStr <<
"NA";
752 currentConvStr <<
"s";
761 std::ostringstream eventDescription;
762 eventDescription << std::left <<
"|" << std::setw(width) << grid[i] <<
"|" << std::setw(width)
763 << dateStr.str() <<
"|" << std::setw(width) << notional(grid[i]) <<
"|" << std::setw(width)
764 << accrual(grid[i]) <<
"|" << std::setw(width) << bondFlowStr.str() <<
"|"
765 << std::setw(width) << callStr.str() <<
"|" << std::setw(width) << putStr.str() <<
"|"
766 << std::setw(2 * width) << convStr.str() <<
"|" << std::setw(2 * width)
767 << convResetStr.str() <<
"|" << std::setw(width) << divStr.str() <<
"|" << std::setw(width)
768 << currentConvStr.str() <<
"|" << std::setw(width) << fxConvStr.str() <<
"|"
769 << std::setw(width) << eqFwdStr.str() <<
"|" << std::setw(width) << divAmtStr.str() <<
"|"
770 << std::setw(width) << convValStr.str() <<
"|" << std::setw(width) << convPrcStr.str()
772 std::string label =
"0000" + std::to_string(counter++);
773 results_.additionalResults[
"event_" + label.substr(label.size() - 5)] = eventDescription.str();
776 if (counter >= 100000)
784 Real tMax = grid[grid.size() - 1];
785 results_.additionalResults[
"trade.tMax"] = tMax;
787 Real eqFwd =
model_->equity()->equitySpot()->value() /
model_->equity()->equityForecastCurve()->discount(tMax) *
788 model_->equity()->equityDividendCurve()->discount(tMax);
789 results_.additionalResults[
"market.discountRate(tMax)"] =
792 results_.additionalResults[
"market.discountingSpread"] =
794 results_.additionalResults[
"market.creditSpread(tMax)"] =
795 -std::log(
model_->creditCurve()->survivalProbability(tMax)) / tMax;
797 results_.additionalResults[
"market.exchangeableBondSpread(tMax)"] =
798 -std::log(
creditCurve_->survivalProbability(tMax)) / tMax;
801 results_.additionalResults[
"market.equitySpot"] =
model_->equity()->equitySpot()->value();
802 results_.additionalResults[
"market.equityForward(tMax)"] = eqFwd;
803 results_.additionalResults[
"market.equityVolaility(tMax)"] =
804 std::sqrt(
model_->totalBlackVariance() /
model_->stepTimes().back());
806 results_.additionalResults[
"model.fdGridSize"] = grid.size();
809 results_.additionalResults[
"model.calibrationTimes"] =
model_->stepTimes();
813 if (!conversionIndicator.empty()) {
814 MonotonicCubicNaturalSpline interpolationConversionIndicator(
815 mesher_->locations().begin(),
mesher_->locations().end(), conversionIndicator[0].begin());
816 results_.additionalResults[
"conversionIndicator"] = interpolationConversionIndicator(logSpot);
const Instrument::results * results_
bool hasDividendPassThrough(const Size i) const
bool hasCall(const Size i) const
bool hasMandatoryConversion(const Size i) const
bool hasContingentConversion(const Size i) const
bool hasConversion(const Size i) const
const std::set< Real > & times() const
void registerMandatoryConversion(const ConvertibleBond2::MandatoryConversionData &c)
void registerBondCashflow(const QuantLib::ext::shared_ptr< CashFlow > &c)
void registerConversionReset(const ConvertibleBond2::ConversionResetData &c)
const ConversionResetData & getConversionResetData(const Size i) const
void registerPut(const ConvertibleBond2::CallabilityData &c)
Real getCurrentFxConversion(const Size i) const
void registerConversion(const ConvertibleBond2::ConversionData &c)
const MandatoryConversionData & getMandatoryConversionData(const Size i) const
void registerConversionRatio(const ConvertibleBond2::ConversionRatioData &c)
bool hasPut(const Size i) const
bool hasNoConversionPlane(const Size i) const
void finalise(const TimeGrid &grid)
bool hasConversionReset(const Size i) const
Real getInitialConversionRatio() const
void registerCall(const ConvertibleBond2::CallabilityData &c)
Date getAssociatedDate(const Size i) const
Real getCurrentConversionRatio(const Size i) const
const ConversionData & getConversionData(const Size i) const
const std::map< std::string, boost::any > & additionalResults() const
const DividendPassThroughData & getDividendPassThroughData(const Size i) const
bool hasBondCashflow(const Size i) const
const CallData & getCallData(const Size i) const
Real getBondCashflow(const Size i) const
Real getBondFinalRedemption(const Size i) const
const CallData & getPutData(const Size i) const
bool hasStochasticConversionRatio(const Size i) const
void registerDividendProtection(const ConvertibleBond2::DividendProtectionData &c)
void registerMakeWhole(const ConvertibleBond2::MakeWholeData &c)
Handle< DefaultableEquityJumpDiffusionModel > model_
Handle< QuantLib::YieldTermStructure > discountingCurve_
Handle< QuantLib::DefaultProbabilityTermStructure > creditCurve_
FdDefaultableEquityJumpDiffusionConvertibleBondEngine(const Handle< DefaultableEquityJumpDiffusionModel > &model, const Handle< QuantLib::YieldTermStructure > &discountingCurve=Handle< QuantLib::YieldTermStructure >(), const Handle< QuantLib::Quote > &discountingSpread=Handle< QuantLib::Quote >(), const Handle< QuantLib::DefaultProbabilityTermStructure > &creditCurve=Handle< QuantLib::DefaultProbabilityTermStructure >(), const Handle< QuantLib::Quote > &recoveryRate=Handle< QuantLib::Quote >(), const Handle< QuantExt::FxIndex > &fxConversion=Handle< QuantExt::FxIndex >(), const bool staticMesher=false, const Size timeStepsPerYear=24, const Size stateGridPoints=100, const Real mesherEpsilon=1E-4, const Real mesherScaling=1.5, const std::vector< Real > conversionRatioDiscretisationGrid={0.1, 0.5, 0.7, 0.9, 1.0, 1.1, 1.3, 1.5, 2.0, 5.0, 10.0}, const bool generateAdditionalResults=true)
void calculate() const override
Handle< QuantExt::FxIndex > fxConversion_
Handle< QuantLib::Quote > discountingSpread_
std::vector< Real > conversionRatioDiscretisationGrid_
QuantLib::ext::shared_ptr< Fdm1dMesher > mesher_
Handle< QuantLib::Quote > recoveryRate_
bool generateAdditionalResults_
Real interpolateValueFromPlanes(const Real conversionRatio, const std::vector< Array > &value, const std::vector< Real > &stochasticConversionRatios, const Size j)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real getCallPriceAmount(const FdConvertibleBondEvents::CallData &cd, Real notional, Real accruals)
std::function< Real(Real, Real)> mwCr
ConvertibleBond2::CallabilityData::PriceType priceType
Real accruedHistoricalDividends
bool resetToSpecificValue
ConvertibleBond2::DividendProtectionData::AdjustmentStyle adjustmentStyle
Size lastDividendProtectionTimeIndex
ConvertibleBond2::DividendProtectionData::DividendType dividendType
ConvertibleBond2::ConversionResetData::ReferenceType reference
Real accruedHistoricalDividends
ConvertibleBond2::DividendProtectionData::AdjustmentStyle adjustmentStyle
Size lastDividendProtectionTimeIndex
Real pepsLowerConversionRatio
Swap::arguments * arguments_
std::vector< Size > steps