21#include <ql/experimental/credit/loss.hpp>
22#include <ql/time/daycounters/actualactual.hpp>
32 const vector<Date>& dates,
33 map<Currency,vector<Cash> >& iFlows,
34 map<Currency,Cash>& tranche,
35 map<Currency,vector<vector<Real> > >& balance,
36 map<Currency,Real>& interest,
37 map<Currency,Real>& interestAcc,
38 Real cureAmount)
const {
42 if (balance[ccy][j][i] < tiny) {
43 tranche[ccy].flow_ = 0.0;
44 tranche[ccy].discountedFlow_ = 0.0;
48 Real ccyDis = (iFlows[ccy][j].flow_ > 0 ?
49 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_:
54 Real amount = std::min(iFlows[ccy][j].flow_, interestAcc[ccy]);
56 tranche[ccy].flow_ += amount;
57 tranche[ccy].discountedFlow_ += amount * ccyDis;
59 iFlows[ccy][j].flow_ -= amount;
60 iFlows[ccy][j].discountedFlow_ -= amount * ccyDis;
62 interest[ccy] -= amount;
65 balance[ccy][j][i] = std::max(balance[ccy][j][i], 0.0);
66 iFlows[ccy][j].flow_ = std::max(iFlows[ccy][j].flow_, 0.0);
67 iFlows[ccy][j].discountedFlow_ = std::max(iFlows[ccy][j].discountedFlow_, 0.0);
68 tranche[ccy].discountedFlow_ = std::max(tranche[ccy].discountedFlow_, 0.0);
75 const vector<Date>& dates,
76 map<Currency,vector<Cash> >& iFlows,
77 vector<map<Currency,Cash> >& tranches,
78 vector<map<Currency,vector<vector<Real> > > >& balances,
79 Real cureAmount)
const {
82 Real ccyDis = (iFlows[ccy][j].flow_ > 0 ?
83 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_:
88 Real cureAvailable =
min(iFlows[ccy][j].flow_, cureAmount);
90 for(Size k = 0; k <= l; k++){
92 Real amount = std::min(balances[k][ccy][j][i], cureAvailable);
94 tranches[k][ccy].flow_ += amount;
95 tranches[k][ccy].discountedFlow_ += amount * ccyDis;
97 iFlows[ccy][j].flow_ -= amount;
98 iFlows[ccy][j].discountedFlow_ -= amount * ccyDis;
100 balances[k][ccy][j][i] -= amount;
102 cureAvailable -= amount;
105 balances[k][ccy][j][i] = std::max(balances[k][ccy][j][i], 0.0);
106 iFlows[ccy][j].flow_ = std::max(iFlows[ccy][j].flow_, 0.0);
107 iFlows[ccy][j].discountedFlow_ = std::max(iFlows[ccy][j].discountedFlow_, 0.0);
108 tranches[k][ccy].discountedFlow_ = std::max(tranches[k][ccy].discountedFlow_, 0.0);
115 const vector<Date>& dates,
116 map<Currency,vector<Cash> >& pFlows,
117 map<Currency,Cash>& tranche,
118 map<Currency,vector<vector<Real> > >& balance,
119 map<Currency,Real>& interest)
const {
122 Real ccyDis = (pFlows[ccy][j].flow_ > 0 ?
123 pFlows[ccy][j].discountedFlow_ / pFlows[ccy][j].flow_:
128 Real amount = std::min(pFlows[ccy][j].flow_, balance[ccy][j][i]);
130 tranche[ccy].flow_ += amount;
131 tranche[ccy].discountedFlow_ += amount * ccyDis;
133 pFlows[ccy][j].flow_ -= amount;
134 pFlows[ccy][j].discountedFlow_ -= amount * ccyDis;
136 balance[ccy][j][i] -= amount;
139 balance[ccy][j][i] = std::max(balance[ccy][j][i], 0.0);
140 pFlows[ccy][j].flow_ = std::max(pFlows[ccy][j].flow_, 0.0);
141 pFlows[ccy][j].discountedFlow_ = std::max(pFlows[ccy][j].discountedFlow_, 0.0);
142 tranche[ccy].discountedFlow_ = std::max(tranche[ccy].discountedFlow_, 0.0);
144 interest[ccy] -= std::min(interest[ccy], amount);
153 vector<map<Currency ,vector<vector<Real> > > >& trancheBalances,
154 vector<Real> trancheInterestRates,
156 Real ocRatio)
const {
161 if((icRatio < 0.) && (ocRatio < 0.)){
168 for(Size l = 0; l < k; l++){
169 poC-= trancheBalances[l][ccy][j][i];
170 piC-= trancheBalances[l][ccy][j][i]*trancheInterestRates[l];
173 poC+= basketNotional/ocRatio;
174 if(trancheInterestRates[k] <= 0.){
178 piC+= basketInterest/icRatio;
179 piC/= trancheInterestRates[k];
181 piC = std::max(piC, 0.);
182 poC = std::max(poC, 0.);
183 Real pTarget = std::min(poC, piC);
185 cureAmount =
max(trancheBalances[k][ccy][j][i] - pTarget, 0.) ;
194 return map<Date, string>();
197 map<Date, string> lossDistributionDates;
198 const vector<Date>& cboDates =
arguments_.schedule.dates();
200 vector<Date>::const_iterator it;
205 it = lower_bound(cboDates.begin(), cboDates.end(), date);
206 if (it == cboDates.begin()) {
208 }
else if (it == cboDates.end()) {
209 lossDistributionDates[cboDates.back()] = oss.str();
212 lossDistributionDates[*it] = oss.str();
219 lossDistributionDates[cboDates.back()] =
"Maturity";
221 return lossDistributionDates;
227 Date today = Settings::instance().evaluationDate();
233 vector<Date> lossDistributionDatesVector;
234 map<string, QuantLib::ext::shared_ptr<BucketedDistribution> > lossDistributionMap;
235 if (!lossDistributionDates.empty()) {
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));
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);
250 for (Size i = 0; i < numBuckets; i++)
251 bucketedDistribution->probabilities()[i] = 0.0;
252 lossDistributionMap[it->second] = bucketedDistribution;
254 lossDistributionDatesVector.push_back(it->first);
259 vector<Date> dates =
arguments_.schedule.dates();
262 std::vector<Date>::iterator it;
263 for (it=dates.begin(); it<dates.end(); ++it)
264 if(*it > today)
break;
266 dates.erase(dates.begin(), it);
267 dates.insert(dates.begin(), today);
269 Real tmax = 1.0 + ActualActual(ActualActual::ISDA).yearFraction(today, dates.back());
276 DayCounter feeDayCount =
arguments_.feeDayCounter;
280 map<Currency, vector<Cash> > cf;
281 map<Currency, vector<Cash> > iFlows;
282 map<Currency, vector<Cash> > pFlows;
283 map<Currency, vector<Real> > basketNotional;
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);
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));
301 vector<Real> feeValue(
samples_, 0.0);
302 vector<Real> subfeeValue(
samples_, 0.0);
320 for (Size i = 0; i <
samples_; i++) {
321 rdm_->nextSequence(tmax);
323 for( Size k = 0 ; k < tranches.size() ; k++){
324 trancheBalance[k][ccy][0][i] =
arguments_.tranches[k].faceAmount;
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;
337 basketNotional.clear();
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);
344 set<Currency> basketCurrency =
arguments_.basket->unique_currencies();
346 if(basketCurrency.size() > 1){
348 vector<Cash> cf_single;
349 vector<Cash> iFlows_single;
350 vector<Cash> pFlows_single;
351 vector<Real> basketNotional_single;
353 for(
size_t d = 0; d < dates.size(); d++){
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]);
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]);
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]);
374 bn +=
arguments_.basket->convert(basketNotional_full[basketCcy][d], basketCcy, dates[d]);
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);
382 iFlows[ccy] = iFlows_single;
383 pFlows[ccy] = pFlows_single;
384 basketNotional[ccy] = basketNotional_single;
389 iFlows = iFlows_full;
390 pFlows = pFlows_full;
391 basketNotional = basketNotional_full;
394 for (Size j = 1; j < dates.size(); j++) {
398 Real intCcyDis = (iFlows[ccy][j].flow_ > 0 ?
399 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_ :
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: "
413 Real dfFlowsCheck = fabs(cf[ccy][j].discountedFlow_
414 -iFlows[ccy][j].discountedFlow_
415 -pFlows[ccy][j].discountedFlow_);
417 QL_REQUIRE( dfFlowsCheck < tiny,
418 "discounted Interest and Principal Flows don't sum to Total: "
424 vector<map<Currency, Real> >trancheIntAcc;
425 trancheIntAcc.resize(tranches.size());
426 vector<Real> trancheInterestRates;
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);
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];
443 Real ccyDFlow = cf[ccy][j].discountedFlow_;
444 Real basketInterest = iFlows[ccy][j].flow_;
449 Real ccyFeeClaim = basketNotional[ccy][j] *
arguments_.seniorFee
450 * feeDayCount.yearFraction(dates[j-1], dates[j]);
452 Real ccyFeeFlow = std::min(ccyFeeClaim, iFlows[ccy][j].flow_);
454 iFlows[ccy][j].flow_ -= ccyFeeFlow;
455 iFlows[ccy][j].discountedFlow_ -= ccyFeeFlow * intCcyDis;
457 cf[ccy][j].flow_ -= ccyFeeFlow;
458 cf[ccy][j].discountedFlow_ -= ccyFeeFlow * intCcyDis;
460 feeValue[i] += ccyFeeFlow * intCcyDis;
462 QL_REQUIRE(cf[ccy][j].flow_ >= 0.0,
"ccy flows < 0");
468 vector<map<Currency, Real> > trancheAmortization;
469 vector<map<Currency, Cash> > tranche;
470 trancheAmortization.resize(tranches.size());
471 tranche.resize(tranches.size());
474 for (Size k = 0 ; k < tranches.size() ; k++){
476 tranche[k][ccy].flow_ = 0.;
477 tranche[k][ccy].discountedFlow_ = 0.;
479 Real icRatio =
arguments_.tranches[k].icRatio;
480 Real ocRatio =
arguments_.tranches[k].ocRatio;
484 basketNotional[ccy][j],
487 trancheInterestRates,
492 tranche[k], trancheBalance[k],
494 trancheIntAcc[k], cureAmount);
497 tranche, trancheBalance,
503 for (Size k = 0 ; k < tranches.size() ; k++){
506 tranche[k], trancheBalance[k],
509 cf[ccy][j].flow_ -= tranche[k][ccy].flow_;
510 cf[ccy][j].discountedFlow_ -= tranche[k][ccy].discountedFlow_;
518 Real ccysubFeeClaim = basketNotional[ccy][j] *
arguments_.subordinatedFee
519 * feeDayCount.yearFraction(dates[j-1], dates[j]);
521 Real ccysubFeeFlow = std::min(ccysubFeeClaim, iFlows[ccy][j].flow_);
523 iFlows[ccy][j].flow_ -= ccysubFeeFlow;
524 iFlows[ccy][j].discountedFlow_ -= ccysubFeeFlow * intCcyDis;
526 cf[ccy][j].flow_ -= ccysubFeeFlow;
527 cf[ccy][j].discountedFlow_ -= ccysubFeeFlow * intCcyDis;
529 subfeeValue[i] += ccysubFeeFlow * intCcyDis;
530 QL_REQUIRE(cf[ccy][j].flow_ >= -1.0E-5,
"ccy flows < 0");
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_;
542 tranche.back()[ccy].flow_ += residual.
flow_ * (1 - x);
543 tranche.back()[ccy].discountedFlow_ += residual.
discountedFlow_ * (1 - x);
547 cf[ccy][j].flow_ -= residual.
flow_;
554 QL_REQUIRE(cf[ccy][j].flow_ >= -1.e-5,
"residual ccy flow < 0: "<<
557 QL_REQUIRE(ccyFeeFlow >= -1.e-5,
"ccy fee flow < 0");
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_);
566 basketValue[i] += ccyDFlow;
567 for(Size k = 0 ; k < tranches.size() ; k ++){
568 trancheValue[k][i] += tranche[k][ccy].discountedFlow_;
570 Real tranchenpvError(0.);
571 for(Size k = 0 ; k < tranches.size() ; k ++){
572 tranchenpvError +=trancheValue[k][i];
575 if(basketValue[i] > 0.){
576 npvError = (feeValue[i] + subfeeValue[i] + tranchenpvError) / basketValue[i] - 1.0;
579 QL_FAIL(
"NPVs do not add up, rel. error " << npvError);
582 QL_REQUIRE(basketValue[i] >= 0.0,
583 "negative basket value " << basketValue[i]);
590 if (!lossDistributionDates.empty()) {
592 map<Currency, vector<Cash> >
593 lossDist =
arguments_.basket->scenarioLossflow(lossDistributionDatesVector);
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];
602 QuantLib::ext::shared_ptr<BucketedDistribution> bd = lossDistributionMap[dateString];
603 Size index = bd->bucket(loss);
604 bd->probabilities()[index] += 1.0 /
samples_;
627 Stats basketStats(basketValue);
628 vector<Stats> trancheStats;
630 for(Size i = 0 ; i < tranches.size(); i++){
631 Stats trancheStat(trancheValue[i]);
632 trancheStats.push_back(trancheStat);
634 Stats feeStats(feeValue);
635 Stats subfeeStats(subfeeValue);
638 for(Size i = 0 ;i < tranches.size(); i ++){
639 results_.trancheValue.push_back(trancheStats[i].mean());
645 for(Size i = 0 ;i < tranches.size(); i ++){
646 results_.trancheValueStd.push_back( trancheStats[i].std());
653 results_.additionalResults[
"BasketDistribution"] = basketDist;
655 results_.additionalResults[
"SeniorFeeDistribution"] = feeDist;
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);
663 results_.additionalResults[
"TrancheValueDistribution"] = trancheDistResults;
665 if (!lossDistributionDates.empty())
666 results_.additionalResults[
"LossDistribution"] = lossDistributionMap;
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();
675 QL_REQUIRE(
results_.value != Null<Real>(),
"CBO Investment " <<
arguments_.investedTrancheName <<
" could not be assigned : no NPV");
Deals with a bucketed distribution.
Monte Carlo pricing engine for the cashflow CDO instrument.
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 calculate() const override
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
QuantLib::ext::shared_ptr< RandomDefaultModel > rdm_
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
std::vector< QuantLib::Period > lossDistributionPeriods_
Periods from valuation date for which to return loss distributions.
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
helper class for the MonteCarloCBOEngine
Distribution histogram(Size bins, Real xmin=-QL_MAX_REAL, Real xmax=QL_MAX_REAL)
CompiledFormula min(CompiledFormula x, const CompiledFormula &y)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
Swap::arguments * arguments_