279 {
280
281
282 Real forwardBondValue = 0.0;
283 Real forwardContractPresentValue = 0.0;
284 Real forwardContractForwardValue = 0.0;
285
286 std::vector<Date> fwdBondCashflowPayDates;
287 std::vector<Real> fwdBondCashflows, fwdBondCashflowSurvivalProbabilities, fwdBondCashflowDiscountFactors;
288
289
290
291
292
293 QuantLib::ext::shared_ptr<DefaultProbabilityTermStructure> creditCurvePtr =
298
299 QuantLib::ext::shared_ptr<Bond> bd =
arguments_.underlying;
300
301
302 Date bondSettlementDate = bd->settlementDate(computeDate);
303 Real accruedAmount = dirty ? 0.0
304 : bd->accruedAmount(bondSettlementDate) * bd->notional(bondSettlementDate) / 100.0 *
306
307
308
309 if (cashSettlement) {
310 forwardBondValue = spotValue / (
incomeCurve_->discount(bondSettlementDate));
311 results_.additionalResults[
"incomeCompoundingDate"] = bondSettlementDate;
312 } else {
313 forwardBondValue = spotValue / (
incomeCurve_->discount(settlementDate));
314 results_.additionalResults[
"incomeCompoundingDate"] = settlementDate;
315 }
316
317 results_.additionalResults[
"spotForwardBondValue"] = spotValue;
318 results_.additionalResults[
"forwardForwardBondValue"] = forwardBondValue;
319 results_.additionalResults[
"incomeCompounding"] = 1.0 /
incomeCurve_->discount(bondSettlementDate);
320
321 results_.additionalResults[
"bondSettlementDate"] = bondSettlementDate;
322 results_.additionalResults[
"forwardSettlementDate"] = settlementDate;
323
324 results_.additionalResults[
"bondNotionalSettlementDate"] =
325 bd->notional(bondSettlementDate) *
arguments_.bondNotional;
326 results_.additionalResults[
"accruedAmount"] = accruedAmount;
327
328
329
330
331
332
333
334 QuantLib::ext::shared_ptr<Payoff> effectivePayoff;
336
337 forwardContractForwardValue = (*
arguments_.payoff)(forwardBondValue - accruedAmount);
339 }
else if (
arguments_.lockRate != Null<Real>()) {
340
341
342 Real price = forwardBondValue /
arguments_.bondNotional / bd->notional(bondSettlementDate) * 100.0;
343 Real yield = BondFunctions::yield(*bd, price,
arguments_.lockRateDayCounter, Compounded, Semiannual,
344 bondSettlementDate, 1E-10, 100, 0.05, Bond::Price::Dirty);
345 Real dv01, modDur = Null<Real>();
348 } else {
349 modDur = BondFunctions::duration(*bd, yield,
arguments_.lockRateDayCounter, Compounded, Semiannual,
350 Duration::Modified, bondSettlementDate);
351 dv01 = price / 100.0 * modDur;
352 }
353
354 QL_REQUIRE(
arguments_.longInForward,
"DiscountingForwardBondEngine: internal error, longInForward must be "
355 "populated if payoff is specified via lock-rate");
356 Real multiplier = (*
arguments_.longInForward) ? 1.0 : -1.0;
357 forwardContractForwardValue = multiplier * (yield -
arguments_.lockRate) * dv01 *
arguments_.bondNotional *
358 bd->notional(bondSettlementDate);
359
360 effectivePayoff = QuantLib::ext::make_shared<ForwardBondTypePayoff>(
361 (*
arguments_.longInForward) ? Position::Long : Position::Short,
363
364 results_.additionalResults[
"dv01"] = dv01;
365 results_.additionalResults[
"modifiedDuration"] = modDur;
366 results_.additionalResults[
"yield"] = yield;
367 results_.additionalResults[
"price"] = price;
369 } else {
370 QL_FAIL("DiscountingForwardBondEngine: internal error, no payoff and no lock rate given, expected exactly one "
371 "of them to be populated.");
372 }
373
374
375 forwardContractPresentValue =
376 forwardContractForwardValue * (
discountCurve_->discount(settlementDate)) *
377 creditCurvePtr->survivalProbability(computeDate) -
378 cmpPayment *
380
381
382 results_.additionalResults[
"forwardContractForwardValue"] = forwardContractForwardValue;
384 results_.additionalResults[
"forwardContractSurvivalProbability"] = creditCurvePtr->survivalProbability(computeDate);
385 results_.additionalResults[
"compensationPayment"] = cmpPayment;
386 results_.additionalResults[
"compensationPaymentDate"] = cmpPaymentDate;
388
389 fwdBondCashflows.push_back(forwardContractForwardValue);
390 fwdBondCashflowPayDates.push_back(computeDate);
391 fwdBondCashflowSurvivalProbabilities.push_back(creditCurvePtr->survivalProbability(computeDate));
392 fwdBondCashflowDiscountFactors.push_back(
discountCurve_->discount(computeDate));
393
394 fwdBondCashflows.push_back(-1 * cmpPayment);
395 fwdBondCashflowPayDates.push_back(cmpPaymentDate);
396 fwdBondCashflowSurvivalProbabilities.push_back(1);
397 fwdBondCashflowDiscountFactors.push_back(
discountCurve_->discount(cmpPaymentDate));
398
399
400 std::vector<CashFlowResults> forwardCashFlowResults;
401 forwardCashFlowResults.reserve(2);
402
403
404 CashFlowResults fwdCfResult;
405 fwdCfResult.payDate = settlementDate;
406 fwdCfResult.legNumber = 1;
407 fwdCfResult.amount = forwardContractForwardValue;
408 fwdCfResult.discountFactor =
409 discountCurve_->discount(settlementDate) * creditCurvePtr->survivalProbability(computeDate);
410 fwdCfResult.presentValue = fwdCfResult.amount * fwdCfResult.discountFactor;
411 fwdCfResult.type = "ForwardValue";
412 forwardCashFlowResults.push_back(fwdCfResult);
413
415 CashFlowResults cmpCfResult;
416 cmpCfResult.payDate = cmpPaymentDate;
417 cmpCfResult.legNumber = 2;
418 cmpCfResult.amount = -1 * cmpPayment;
419 cmpCfResult.discountFactor =
discountCurve_->discount(cmpPaymentDate);
420 cmpCfResult.presentValue = cmpCfResult.amount * cmpCfResult.discountFactor;
421 cmpCfResult.type = "Premium";
422 forwardCashFlowResults.push_back(cmpCfResult);
423 }
424
425 Real fwdBondRecovery = 0;
426
427
428 for (Size i = 0; i < bd->cashflows().size(); i++) {
429 if (bd->cashflows()[i]->hasOccurred(
431 continue;
432 if (bd->cashflows()[i]->date() >=
433 computeDate)
434 continue;
435 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(bd->cashflows()[i]);
436
437 if (coupon) {
438 Date startDate = coupon->accrualStartDate();
439 Date endDate = coupon->accrualEndDate();
440 Date effectiveStartDate = (startDate <= npvDate && npvDate <= endDate) ? npvDate : startDate;
441 Date effectiveEndDate = (startDate <= computeDate && computeDate <= endDate) ? computeDate : endDate;
442 Date defaultDate = effectiveStartDate + (effectiveEndDate - effectiveStartDate) / 2;
443 Probability
P = creditCurvePtr->defaultProbability(effectiveStartDate, effectiveEndDate);
444
445 Real couponRecovery =
446 (*effectivePayoff)(coupon->nominal() *
arguments_.bondNotional * recoveryVal - accruedAmount) *
P *
448 fwdBondRecovery += couponRecovery;
450 CashFlowResults recoveryFlow;
451 recoveryFlow.payDate = defaultDate;
452 recoveryFlow.accrualStartDate = effectiveStartDate;
453 recoveryFlow.accrualEndDate = endDate;
454 recoveryFlow.amount =
455 (*effectivePayoff)(coupon->nominal() *
arguments_.bondNotional * recoveryVal - accruedAmount);
456 recoveryFlow.discountFactor =
P * (
discountCurve_->discount(defaultDate));
457 recoveryFlow.presentValue = recoveryFlow.amount * recoveryFlow.discountFactor;
458 recoveryFlow.legNumber = 3;
459 recoveryFlow.type = "Forward_ExpectedRecovery";
460 forwardCashFlowResults.push_back(recoveryFlow);
461 }
462 }
463 }
464
465
466 QuantLib::ext::shared_ptr<Coupon> firstCoupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(bd->cashflows()[0]);
467 if (firstCoupon) {
468 Date startDate = npvDate;
469 Date stopDate = std::min(bd->cashflows()[0]->date(), computeDate);
470 Real recoveryBeforeCoupons = 0.0;
471 while (startDate < stopDate) {
473 Date endDate = (stepDate > stopDate) ? stopDate : stepDate;
474 Date defaultDate = startDate + (endDate - startDate) / 2;
475 Probability
P = creditCurvePtr->defaultProbability(startDate, endDate);
476
477 recoveryBeforeCoupons +=
478 (*effectivePayoff)(firstCoupon->nominal() *
arguments_.bondNotional * recoveryVal - accruedAmount) *
P *
480 startDate = stepDate;
481 }
482 fwdBondRecovery += recoveryBeforeCoupons;
484 CashFlowResults recoveryFlow;
485 recoveryFlow.payDate = stopDate;
486 recoveryFlow.accrualStartDate = startDate;
487 recoveryFlow.accrualEndDate = stopDate;
488 recoveryFlow.amount =
489 (*effectivePayoff)(firstCoupon->nominal() *
arguments_.bondNotional * recoveryVal - accruedAmount);
490 recoveryFlow.discountFactor = recoveryBeforeCoupons / recoveryFlow.amount;
491 recoveryFlow.presentValue = recoveryBeforeCoupons;
492 recoveryFlow.legNumber = 4;
493 recoveryFlow.type = "Forward_ExpectedRecovery";
494 forwardCashFlowResults.push_back(recoveryFlow);
495 }
496 }
497
498
499
500
501
502 if (bd->cashflows().size() == 1) {
503 QuantLib::ext::shared_ptr<Redemption> redemption = QuantLib::ext::dynamic_pointer_cast<Redemption>(bd->cashflows()[0]);
504 if (redemption) {
505 Real redemptionRecovery = 0;
506 Date startDate = npvDate;
507 while (startDate < redemption->date()) {
509 Date endDate = (stepDate > redemption->date()) ? redemption->date() : stepDate;
510 Date defaultDate = startDate + (endDate - startDate) / 2;
511 Probability
P = creditCurvePtr->defaultProbability(startDate, endDate);
512 redemptionRecovery +=
513 (*effectivePayoff)(redemption->amount() *
arguments_.bondNotional * recoveryVal - accruedAmount) *
515 startDate = stepDate;
516 }
517 fwdBondRecovery += redemptionRecovery;
519 CashFlowResults recoveryFlow;
520 recoveryFlow.payDate = redemption->date();
521 recoveryFlow.accrualStartDate = startDate;
522 recoveryFlow.accrualEndDate = redemption->date();
523 recoveryFlow.amount =
524 (*effectivePayoff)(redemption->amount() *
arguments_.bondNotional * recoveryVal - accruedAmount);
525 recoveryFlow.discountFactor = redemptionRecovery / recoveryFlow.amount;
526 recoveryFlow.presentValue = redemptionRecovery;
527 recoveryFlow.legNumber = 5;
528 recoveryFlow.type = "Forward_ExpectedRecovery";
529 forwardCashFlowResults.push_back(recoveryFlow);
530 }
531 }
532 }
533
534 results_.additionalResults[
"forwardBondRecovery"] = fwdBondRecovery;
535
536
537 if (
results_.additionalResults.find(
"cashFlowResults") !=
results_.additionalResults.end()) {
538 auto tmp =
results_.additionalResults[
"cashFlowResults"];
539 QL_REQUIRE(tmp.type() == typeid(std::vector<CashFlowResults>), "internal error: cashflowResults type not handlded");
540 std::vector<CashFlowResults> prevCfResults = boost::any_cast<std::vector<CashFlowResults>>(tmp);
541 prevCfResults.insert(prevCfResults.end(), forwardCashFlowResults.begin(), forwardCashFlowResults.end());
542 results_.additionalResults[
"cashFlowResults"] = prevCfResults;
543 } else {
544 results_.additionalResults[
"cashFlowResults"] = forwardCashFlowResults;
545 }
546
547 forwardContractPresentValue += fwdBondRecovery;
548
549 return QuantLib::ext::make_tuple(forwardContractForwardValue, forwardContractPresentValue);
550}