Calculate the npv, compoundFactorSettlement, cashflowsBeforeSettlementValue and the additional CashflowResults as of the npvDate including cashflows eligible w.r.t. the given settlement date
100 {
101
102 bool includeRefDateFlows =
104
105 Real npvValue = 0.0;
106 DiscountingRiskyBondEngine::BondNPVCalculationResults calculationResults;
107 calculationResults.cashflowsBeforeSettlementValue = 0.0;
108
109
110
111
112
113 QuantLib::ext::shared_ptr<DefaultProbabilityTermStructure> creditCurvePtr =
117
118
119 Real dfNpv = incomeCurve.empty() ?
discountCurve_->discount(npvDate) : incomeCurve->discount(npvDate);
120 Real spNpv = conditionalOnSurvival ? creditCurvePtr->survivalProbability(npvDate) : 1.0;
121
122
123 Real dfSettl =
124 incomeCurve.empty() ?
discountCurve_->discount(settlementDate) : incomeCurve->discount(settlementDate);
125 Real spSettl = creditCurvePtr->survivalProbability(settlementDate);
126 if (!conditionalOnSurvival)
127 spSettl /= creditCurvePtr->survivalProbability(npvDate);
128
129
130 calculationResults.compoundFactorSettlement = (dfNpv * spNpv) / (dfSettl * spSettl);
131
132 Size numCoupons = 0;
133 bool hasLiveCashFlow = false;
134 for (Size i = 0; i < cashflows.size(); i++) {
135 QuantLib::ext::shared_ptr<CashFlow> cf = cashflows[i];
136 if (cf->hasOccurred(npvDate, includeRefDateFlows))
137 continue;
138 hasLiveCashFlow = true;
139
141
142 Probability S = creditCurvePtr->survivalProbability(cf->date()) / spNpv;
143 Real tmp = cf->amount() * S * df;
144 if (!cf->hasOccurred(settlementDate, includeRefDateFlows))
145 npvValue += tmp;
146 else
147 calculationResults.cashflowsBeforeSettlementValue += tmp;
148
149
150
151
152
153 if (additionalResults) {
155 cfRes.discountFactor = S * df;
156 cfRes.presentValue = cfRes.amount * cfRes.discountFactor;
157 calculationResults.cashflowResults.push_back(cfRes);
158 }
159
160 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(cf);
161 if (coupon) {
162 numCoupons++;
163 Date startDate = coupon->accrualStartDate();
164 Date endDate = coupon->accrualEndDate();
165 Date effectiveStartDate = (startDate <= npvDate && npvDate <= endDate) ? npvDate : startDate;
166 Date defaultDate = effectiveStartDate + (endDate - effectiveStartDate) / 2;
167 Probability
P = creditCurvePtr->defaultProbability(effectiveStartDate, endDate) / spNpv;
168 Real expectedRecoveryAmount = coupon->nominal() * recoveryVal;
169 DiscountFactor recoveryDiscountFactor =
discountCurve_->discount(defaultDate) / dfNpv;
170 if (additionalResults && !
close_enough(expectedRecoveryAmount *
P * recoveryDiscountFactor, 0.0)) {
171
172 CashFlowResults recoveryResult;
173 recoveryResult.amount = expectedRecoveryAmount;
174 recoveryResult.payDate = defaultDate;
175 recoveryResult.currency = "";
176 recoveryResult.discountFactor =
P * recoveryDiscountFactor;
177 recoveryResult.presentValue = recoveryResult.discountFactor * recoveryResult.amount;
178 recoveryResult.type = "ExpectedRecovery";
179 calculationResults.cashflowResults.push_back(recoveryResult);
180 }
181 npvValue += expectedRecoveryAmount *
P * recoveryDiscountFactor;
182 }
183 }
184
185
186
187 if (!hasLiveCashFlow) {
188 calculationResults.npv = 0.0;
189 return calculationResults;
190 }
191
192 if (cashflows.size() > 1 && numCoupons == 0) {
193 QL_FAIL("DiscountingRiskyBondEngine does not support bonds with multiple cashflows but no coupons");
194 }
195
196
197
198
199
200 if (cashflows.size() == 1) {
201 QuantLib::ext::shared_ptr<Redemption> redemption = QuantLib::ext::dynamic_pointer_cast<Redemption>(cashflows[0]);
202 if (redemption) {
203 Date startDate = npvDate;
204 while (startDate < redemption->date()) {
206 Date endDate = (stepDate > redemption->date()) ? redemption->date() : stepDate;
207 Date defaultDate = startDate + (endDate - startDate) / 2;
208 Probability
P = creditCurvePtr->defaultProbability(startDate, endDate) / spNpv;
209 if (additionalResults) {
210 CashFlowResults recoveryResult;
211 recoveryResult.amount = redemption->amount() * recoveryVal;
212 recoveryResult.payDate = defaultDate;
213 recoveryResult.currency = "";
214 recoveryResult.discountFactor =
P *
discountCurve_->discount(defaultDate) / dfNpv;
215 recoveryResult.presentValue = recoveryResult.discountFactor * recoveryResult.amount;
216 recoveryResult.type = "ExpectedRecovery";
217 calculationResults.cashflowResults.push_back(recoveryResult);
218 }
219 npvValue += redemption->amount() * recoveryVal *
P *
discountCurve_->discount(defaultDate) / dfNpv;
220 startDate = stepDate;
221 }
222 }
223 }
224 calculationResults.npv = npvValue;
225 return calculationResults;
226}
const P2_< E1, E2 > P(const E1 &e1, const E2 &e2)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
CashFlowResults populateCashFlowResultsFromCashflow(const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &c, const QuantLib::Real multiplier, const QuantLib::Size legNo, const QuantLib::Currency ¤cy)