225 {
226
227 Date today = Settings::instance().evaluationDate();
230
231
233 vector<Date> lossDistributionDatesVector;
234 map<string, QuantLib::ext::shared_ptr<BucketedDistribution> > lossDistributionMap;
235 if (!lossDistributionDates.empty()) {
236
237
238 Real maxLoss = 0.0;
239 for (
auto& bonds :
arguments_.basket->bonds()) {
240 string name = bonds.first;
241 maxLoss += bonds.second->notional(today) *
arguments_.basket->multiplier(name) * (1.0 -
arguments_.basket->recoveryRate(name));
242 }
243
244
245 Size numBuckets = 100;
246 for (map<Date, string>::iterator it = lossDistributionDates.begin(); it != lossDistributionDates.end(); it++) {
247 QuantLib::ext::shared_ptr<BucketedDistribution> bucketedDistribution =
248 QuantLib::ext::make_shared<BucketedDistribution> (0, maxLoss, numBuckets);
249
250 for (Size i = 0; i < numBuckets; i++)
251 bucketedDistribution->probabilities()[i] = 0.0;
252 lossDistributionMap[it->second] = bucketedDistribution;
253
254 lossDistributionDatesVector.push_back(it->first);
255 }
256 }
257
258
259 vector<Date> dates =
arguments_.schedule.dates();
260
261
262 std::vector<Date>::iterator it;
263 for (it=dates.begin(); it<dates.end(); ++it)
264 if(*it > today) break;
265
266 dates.erase(dates.begin(), it);
267 dates.insert(dates.begin(), today);
268
269 Real tmax = 1.0 + ActualActual(ActualActual::ISDA).yearFraction(today, dates.back());
270
272
274
275
276 DayCounter feeDayCount =
arguments_.feeDayCounter;
278
279
280 map<Currency, vector<Cash> > cf;
281 map<Currency, vector<Cash> > iFlows;
282 map<Currency, vector<Cash> > pFlows;
283 map<Currency, vector<Real> > basketNotional;
284
285
286 vector<Real> basketValue(
samples_, 0.0);
287 vector<vector<Real> > trancheValue;
288 vector<Tranche> tranches =
arguments_.tranches;
289 for (Size i = 0 ; i < tranches.size() ; i ++){
290 vector<Real> tempTrancheValue(
samples_, 0.0);
291 trancheValue.push_back(tempTrancheValue);
292 }
293
294 vector <map<Currency, vector<vector<Real> > > > trancheBalance;
295 trancheBalance.resize(tranches.size());
296 for (Size i = 0 ; i < tranches.size() ; i ++){
297 trancheBalance[i][ccy].resize(
298 dates.size(), vector<Real>(
samples_, 0.0));
299 }
300
301 vector<Real> feeValue(
samples_, 0.0);
302 vector<Real> subfeeValue(
samples_, 0.0);
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320 for (Size i = 0; i <
samples_; i++) {
321 rdm_->nextSequence(tmax);
322
323 for( Size k = 0 ; k < tranches.size() ; k++){
324 trancheBalance[k][ccy][0][i] =
arguments_.tranches[k].faceAmount;
325 }
326
327 vector< map<Currency, Real> >trancheInterest;
328 trancheInterest.resize(tranches.size());
329 for( Size k = 0 ; k < tranches.size() ; k++){
330 trancheInterest[k][ccy] = 0.0;
331 }
332
333
334 cf.clear();
335 iFlows.clear();
336 pFlows.clear();
337 basketNotional.clear();
338
339 map<Currency, vector<Cash> > cf_full =
arguments_.basket->scenarioCashflow(dates);
340 map<Currency, vector<Cash> > iFlows_full =
arguments_.basket->scenarioInterestflow(dates);
341 map<Currency, vector<Cash> > pFlows_full =
arguments_.basket->scenarioPrincipalflow(dates);
342 map<Currency, vector<Real> > basketNotional_full =
arguments_.basket->scenarioRemainingNotional(dates);
343
344 set<Currency> basketCurrency =
arguments_.basket->unique_currencies();
345
346 if(basketCurrency.size() > 1){
347
348 vector<Cash> cf_single;
349 vector<Cash> iFlows_single;
350 vector<Cash> pFlows_single;
351 vector<Real> basketNotional_single;
352
353 for(size_t d = 0; d < dates.size(); d++){
354
355 double cf1 = 0.0;
356 double cf2 = 0.0;
357 double if1 = 0.0;
358 double if2 = 0.0;
359 double pf1 = 0.0;
360 double pf2 = 0.0;
361
362 double bn = 0.0;
363
364 for(auto& basketCcy : basketCurrency){
365 cf1 +=
arguments_.basket->convert(cf_full[basketCcy][d].flow_, basketCcy, dates[d]);
366 cf2 +=
arguments_.basket->convert(cf_full[basketCcy][d].discountedFlow_, basketCcy, dates[d]);
367
368 if1 +=
arguments_.basket->convert(iFlows_full[basketCcy][d].flow_, basketCcy, dates[d]);
369 if2 +=
arguments_.basket->convert(iFlows_full[basketCcy][d].discountedFlow_, basketCcy, dates[d]);
370
371 pf1 +=
arguments_.basket->convert(pFlows_full[basketCcy][d].flow_, basketCcy, dates[d]);
372 pf2 +=
arguments_.basket->convert(pFlows_full[basketCcy][d].discountedFlow_, basketCcy, dates[d]);
373
374 bn +=
arguments_.basket->convert(basketNotional_full[basketCcy][d], basketCcy, dates[d]);
375 }
376 cf_single.push_back(Cash(cf1, cf2));
377 iFlows_single.push_back(Cash(if1,if2));
378 pFlows_single.push_back(Cash(pf1,pf2));
379 basketNotional_single.push_back(bn);
380 }
381 cf[ccy] = cf_single;
382 iFlows[ccy] = iFlows_single;
383 pFlows[ccy] = pFlows_single;
384 basketNotional[ccy] = basketNotional_single;
385
386 }
387 else{
388 cf = cf_full;
389 iFlows = iFlows_full;
390 pFlows = pFlows_full;
391 basketNotional = basketNotional_full;
392 }
393
394 for (Size j = 1; j < dates.size(); j++) {
395
396
397
398 Real intCcyDis = (iFlows[ccy][j].flow_ > 0 ?
399 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_ :
400 0.0);
401
402
403
404
405 Real tiny = 1.0e-6;
406 Real flowsCheck = fabs(cf[ccy][j].flow_
407 -iFlows[ccy][j].flow_
408 -pFlows[ccy][j].flow_);
409 QL_REQUIRE( flowsCheck < tiny,
410 "Interest and Principal Flows don't sum to Total: "
411 << flowsCheck);
412
413 Real dfFlowsCheck = fabs(cf[ccy][j].discountedFlow_
414 -iFlows[ccy][j].discountedFlow_
415 -pFlows[ccy][j].discountedFlow_);
416
417 QL_REQUIRE( dfFlowsCheck < tiny,
418 "discounted Interest and Principal Flows don't sum to Total: "
419 << dfFlowsCheck);
420
421
422
423
424 vector<map<Currency, Real> >trancheIntAcc;
425 trancheIntAcc.resize(tranches.size());
426 vector<Real> trancheInterestRates;
427
428 for (Size k = 0 ; k< tranches.size(); k++){
429 Real trancheInterestRate =
430 tranches[k].leg[j-1]->amount()/
431 tranches[k].faceAmount;
432 trancheInterestRates.push_back(trancheInterestRate);
433
434 trancheIntAcc[k][ccy] = trancheBalance[k][ccy][j-1][i]
435 * (trancheInterestRate);
436 trancheInterest[k][ccy] += trancheIntAcc[k][ccy];
437 trancheBalance[k][ccy][j][i]
438 = trancheBalance[k][ccy][j-1][i];
439 }
440
441
442
443 Real ccyDFlow = cf[ccy][j].discountedFlow_;
444 Real basketInterest = iFlows[ccy][j].flow_;
445
446
447
448
449 Real ccyFeeClaim = basketNotional[ccy][j] *
arguments_.seniorFee
450 * feeDayCount.yearFraction(dates[j-1], dates[j]);
451
452 Real ccyFeeFlow = std::min(ccyFeeClaim, iFlows[ccy][j].flow_);
453
454 iFlows[ccy][j].flow_ -= ccyFeeFlow;
455 iFlows[ccy][j].discountedFlow_ -= ccyFeeFlow * intCcyDis;
456
457 cf[ccy][j].flow_ -= ccyFeeFlow;
458 cf[ccy][j].discountedFlow_ -= ccyFeeFlow * intCcyDis;
459
460 feeValue[i] += ccyFeeFlow * intCcyDis;
461
462 QL_REQUIRE(cf[ccy][j].flow_ >= 0.0, "ccy flows < 0");
463
464
465
466
467
468 vector<map<Currency, Real> > trancheAmortization;
469 vector<map<Currency, Cash> > tranche;
470 trancheAmortization.resize(tranches.size());
471 tranche.resize(tranches.size());
472
473
474 for (Size k = 0 ; k < tranches.size() ; k++){
475
476 tranche[k][ccy].flow_ = 0.;
477 tranche[k][ccy].discountedFlow_ = 0.;
478
479 Real icRatio =
arguments_.tranches[k].icRatio;
480 Real ocRatio =
arguments_.tranches[k].ocRatio;
481
482
484 basketNotional[ccy][j],
485 basketInterest,
486 trancheBalance,
487 trancheInterestRates,
488 icRatio,
489 ocRatio);
490
492 tranche[k], trancheBalance[k],
493 trancheInterest[k],
494 trancheIntAcc[k], cureAmount);
495
497 tranche, trancheBalance,
498 cureAmount);
499
500 }
501
502
503 for (Size k = 0 ; k < tranches.size() ; k++){
504
506 tranche[k], trancheBalance[k],
507 trancheInterest[k]);
508
509 cf[ccy][j].flow_ -= tranche[k][ccy].flow_;
510 cf[ccy][j].discountedFlow_ -= tranche[k][ccy].discountedFlow_;
511
512 }
513
514
515
516
517
518 Real ccysubFeeClaim = basketNotional[ccy][j] *
arguments_.subordinatedFee
519 * feeDayCount.yearFraction(dates[j-1], dates[j]);
520
521 Real ccysubFeeFlow = std::min(ccysubFeeClaim, iFlows[ccy][j].flow_);
522
523 iFlows[ccy][j].flow_ -= ccysubFeeFlow;
524 iFlows[ccy][j].discountedFlow_ -= ccysubFeeFlow * intCcyDis;
525
526 cf[ccy][j].flow_ -= ccysubFeeFlow;
527 cf[ccy][j].discountedFlow_ -= ccysubFeeFlow * intCcyDis;
528
529 subfeeValue[i] += ccysubFeeFlow * intCcyDis;
530 QL_REQUIRE(cf[ccy][j].flow_ >= -1.0E-5, "ccy flows < 0");
531
532
533
534
535
537
538 Cash residual(0.0, 0.0);
539 residual.discountedFlow_ = pFlows[ccy][j].discountedFlow_ + iFlows[ccy][j].discountedFlow_;
540 residual.flow_ = pFlows[ccy][j].flow_ + iFlows[ccy][j].flow_;
541
542 tranche.back()[ccy].flow_ += residual.flow_ * (1 - x);
543 tranche.back()[ccy].discountedFlow_ += residual.discountedFlow_ * (1 - x);
544
545 feeValue[i] += residual.discountedFlow_ * x;
546
547 cf[ccy][j].flow_ -= residual.flow_;
548 cf[ccy][j].discountedFlow_ -= residual.discountedFlow_;
549
550
551
552
553
554 QL_REQUIRE(cf[ccy][j].flow_ >= -1.e-5, "residual ccy flow < 0: "<<
555 cf[ccy][j].flow_);
556
557 QL_REQUIRE(ccyFeeFlow >= -1.e-5, "ccy fee flow < 0");
558
559 for(Size k = 0; k < tranches.size() ; k++){
560 QL_REQUIRE(tranche[k][ccy].flow_ >= -1.e-5, "ccy "<<
561 tranches[k].name <<" flow < 0: "
562 <<tranche[k][ccy].flow_);
563 }
564
565
566 basketValue[i] += ccyDFlow;
567 for(Size k = 0 ; k < tranches.size() ; k ++){
568 trancheValue[k][i] += tranche[k][ccy].discountedFlow_;
569 }
570 Real tranchenpvError(0.);
571 for(Size k = 0 ; k < tranches.size() ; k ++){
572 tranchenpvError +=trancheValue[k][i];
573 }
574 Real npvError(0.);
575 if(basketValue[i] > 0.){
576 npvError = (feeValue[i] + subfeeValue[i] + tranchenpvError) / basketValue[i] - 1.0;
578
579 QL_FAIL("NPVs do not add up, rel. error " << npvError);
580 }
581
582 QL_REQUIRE(basketValue[i] >= 0.0,
583 "negative basket value " << basketValue[i]);
584
585 }
586
587
588
589
590 if (!lossDistributionDates.empty()) {
591
592 map<Currency, vector<Cash> >
593 lossDist =
arguments_.basket->scenarioLossflow(lossDistributionDatesVector);
594
595
596
597 for (Size k = 0; k < lossDistributionDatesVector.size(); k++) {
598 Real loss = lossDist[ccy][k].flow_;
599 Date d = lossDistributionDatesVector[k];
600 string dateString = lossDistributionDates[d];
601
602 QuantLib::ext::shared_ptr<BucketedDistribution> bd = lossDistributionMap[dateString];
603 Size index = bd->bucket(loss);
604 bd->probabilities()[index] += 1.0 /
samples_;
605 }
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624 }
625
626
627 Stats basketStats(basketValue);
628 vector<Stats> trancheStats;
629
630 for(Size i = 0 ; i < tranches.size(); i++){
631 Stats trancheStat(trancheValue[i]);
632 trancheStats.push_back(trancheStat);
633 }
634 Stats feeStats(feeValue);
635 Stats subfeeStats(subfeeValue);
636
637 results_.basketValue = basketStats.mean();
638 for(Size i = 0 ;i < tranches.size(); i ++){
639 results_.trancheValue.push_back(trancheStats[i].mean());
640 }
641 results_.feeValue = feeStats.mean();
642 results_.subfeeValue = subfeeStats.mean();
643
644 results_.basketValueStd = basketStats.std();
645 for(Size i = 0 ;i < tranches.size(); i ++){
646 results_.trancheValueStd.push_back( trancheStats[i].std());
647 }
648 results_.feeValueStd = feeStats.std();
649 results_.subfeeValueStd = subfeeStats.std();
650
651
652 Distribution basketDist = basketStats.histogram(
bins_);
653 results_.additionalResults[
"BasketDistribution"] = basketDist;
654 Distribution feeDist = feeStats.histogram(
bins_);
655 results_.additionalResults[
"SeniorFeeDistribution"] = feeDist;
656 Distribution subfeeDist = subfeeStats.histogram(
bins_);
657 results_.additionalResults[
"SubFeeDistribution"] = subfeeDist;
658 std::vector<Distribution> trancheDistResults;
659 for(Size i = 0 ;i < tranches.size(); i ++){
660 Distribution trancheDist = trancheStats[i].histogram(
bins_);
661 trancheDistResults.push_back(trancheDist);
662 }
663 results_.additionalResults[
"TrancheValueDistribution"] = trancheDistResults;
664
665 if (!lossDistributionDates.empty())
666 results_.additionalResults[
"LossDistribution"] = lossDistributionMap;
667
668
669 for (Size i = 0; i < tranches.size(); i++) {
670 if(tranches[i].name ==
arguments_.investedTrancheName){
671 results_.value = trancheStats[i].mean();
672 results_.errorEstimate = trancheStats[i].std();
673 }
674 }
675 QL_REQUIRE(
results_.value != Null<Real>(),
"CBO Investment " <<
arguments_.investedTrancheName <<
" could not be assigned : no NPV");
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 }
const Instrument::results * results_
virtual void initialize() const
std::map< QuantLib::Date, std::string > getLossDistributionDates(const QuantLib::Date &valuationDate) const
Return dates on the CBO schedule that are closest to the requested lossDistributionPeriods.
void interestWaterfall(Size sampleIndex, Size dateIndex, const vector< Date > &dates, map< Currency, vector< Cash > > &basketFlow, map< Currency, Cash > &trancheFlow, map< Currency, vector< vector< Real > > > &balance, map< Currency, Real > &interest, map< Currency, Real > &interestAcc, Real cureAmount) const
interest waterfall
void principalWaterfall(Size sampleIndex, Size dateIndex, const vector< Date > &dates, map< Currency, vector< Cash > > &basketFlow, map< Currency, Cash > &trancheFlow, map< Currency, vector< vector< Real > > > &balance, map< Currency, Real > &interest) const
pricipal waterfall
Real icocCureAmount(Size sampleIndex, Size dateIndex, Size trancheNo, Real basketNotional, Real basketInterest, vector< map< Currency, vector< vector< Real > > > > &trancheBalances, vector< Real > trancheInterestRates, Real icRatios, Real ocRatios) const
icoc cure amount
void icocInterestWaterfall(Size i, Size j, Size k, const vector< Date > &dates, map< Currency, vector< Cash > > &iFlows, vector< map< Currency, Cash > > &tranches, vector< map< Currency, vector< vector< Real > > > > &balances, Real cureAmount) const
icoc interest waterfall
Swap::arguments * arguments_