Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
cbomcengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd.
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
21#include <ql/experimental/credit/loss.hpp>
22#include <ql/time/daycounters/actualactual.hpp>
23
24using namespace std;
25using namespace QuantLib;
26
27namespace QuantExt {
28
29 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
30 void MonteCarloCBOEngine::interestWaterfall(Size i, // sample index
31 Size j, // date index
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 {
39 Currency ccy = arguments_.ccy;
40
41 Real tiny = 1e-9;
42 if (balance[ccy][j][i] < tiny) {
43 tranche[ccy].flow_ = 0.0;
44 tranche[ccy].discountedFlow_ = 0.0;
45 return;
46 }
47
48 Real ccyDis = (iFlows[ccy][j].flow_ > 0 ?
49 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_:
50 0.0);
51
52 // Accrued Interest
53
54 Real amount = std::min(iFlows[ccy][j].flow_, interestAcc[ccy]);
55
56 tranche[ccy].flow_ += amount;
57 tranche[ccy].discountedFlow_ += amount * ccyDis;
58
59 iFlows[ccy][j].flow_ -= amount;
60 iFlows[ccy][j].discountedFlow_ -= amount * ccyDis;
61
62 interest[ccy] -= amount;
63
64 // Truncate rounding errors
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);
69 }
70
71 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
72 void MonteCarloCBOEngine::icocInterestWaterfall(Size i, // sample index
73 Size j, // date index
74 Size l, //tranche
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 {
80 Currency ccy = arguments_.ccy;
81
82 Real ccyDis = (iFlows[ccy][j].flow_ > 0 ?
83 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_:
84 0.0);
85
86 //IC and OC
87
88 Real cureAvailable = min(iFlows[ccy][j].flow_, cureAmount);
89
90 for(Size k = 0; k <= l; k++){
91
92 Real amount = std::min(balances[k][ccy][j][i], cureAvailable);
93
94 tranches[k][ccy].flow_ += amount;
95 tranches[k][ccy].discountedFlow_ += amount * ccyDis;
96
97 iFlows[ccy][j].flow_ -= amount;
98 iFlows[ccy][j].discountedFlow_ -= amount * ccyDis;
99
100 balances[k][ccy][j][i] -= amount;
101
102 cureAvailable -= amount;
103
104 // truncate rounding errors
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);
109 }
110 }
111
112 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
113 void MonteCarloCBOEngine::principalWaterfall(Size i, // sample index
114 Size j, // date index
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 {
120 Currency ccy = arguments_.ccy;
121
122 Real ccyDis = (pFlows[ccy][j].flow_ > 0 ?
123 pFlows[ccy][j].discountedFlow_ / pFlows[ccy][j].flow_:
124 0.0);
125
126 //Principal Waterfall
127
128 Real amount = std::min(pFlows[ccy][j].flow_, balance[ccy][j][i]);
129
130 tranche[ccy].flow_ += amount;
131 tranche[ccy].discountedFlow_ += amount * ccyDis;
132
133 pFlows[ccy][j].flow_ -= amount;
134 pFlows[ccy][j].discountedFlow_ -= amount * ccyDis;
135
136 balance[ccy][j][i] -= amount;
137
138 // truncate rounding errors
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);
143
144 interest[ccy] -= std::min(interest[ccy], amount);
145 }
146
147 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
149 Size j,
150 Size k,
151 Real basketNotional,
152 Real basketInterest,
153 vector<map<Currency ,vector<vector<Real> > > >& trancheBalances,
154 vector<Real> trancheInterestRates,
155 Real icRatio,
156 Real ocRatio) const {
157 Currency ccy = arguments_.ccy;
158
159 Real cureAmount;
160
161 if((icRatio < 0.) && (ocRatio < 0.)){
162 cureAmount = 0.;
163 }
164 else{
165 Real poC = 0.;
166 Real piC = 0.;
167
168 for(Size l = 0; l < k; l++){
169 poC-= trancheBalances[l][ccy][j][i];
170 piC-= trancheBalances[l][ccy][j][i]*trancheInterestRates[l];
171 }
172
173 poC+= basketNotional/ocRatio;
174 if(trancheInterestRates[k] <= 0.){
175 piC=poC;
176 }
177 else {
178 piC+= basketInterest/icRatio;
179 piC/= trancheInterestRates[k];
180 }
181 piC = std::max(piC, 0.);
182 poC = std::max(poC, 0.);
183 Real pTarget = std::min(poC, piC);
184
185 cureAmount = max(trancheBalances[k][ccy][j][i] - pTarget, 0.) ;
186 }
187 return cureAmount;
188 }
189
190 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191 // copied from AmortizingCboLbEngine
192 map<Date, string> MonteCarloCBOEngine::getLossDistributionDates(const Date& valuationDate) const {
193 if (lossDistributionPeriods_.size() == 0)
194 return map<Date, string>();
195
196 // For each period, return first date in CBO schedule greater than or equal to it
197 map<Date, string> lossDistributionDates;
198 const vector<Date>& cboDates = arguments_.schedule.dates();
199 Date date;
200 vector<Date>::const_iterator it;
201 ostringstream oss;
202 for (Size i = 0; i < lossDistributionPeriods_.size(); i++) {
203 date = valuationDate + lossDistributionPeriods_[i];
204 oss << io::short_period(lossDistributionPeriods_[i]);
205 it = lower_bound(cboDates.begin(), cboDates.end(), date);
206 if (it == cboDates.begin()) {
207 continue;
208 } else if (it == cboDates.end()) {
209 lossDistributionDates[cboDates.back()] = oss.str();
210 break;
211 } else {
212 lossDistributionDates[*it] = oss.str();
213 }
214 oss.str("");
215 }
216
217 // Add the maturity date of the note also with a special string
218 // This overwrites any entry for the last date above
219 lossDistributionDates[cboDates.back()] = "Maturity";
220
221 return lossDistributionDates;
222 }
223
224 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
226
227 Date today = Settings::instance().evaluationDate();
228 initialize(); //set the underlying Basket
229 rdm_->reset();
230
231 // Prepare additional results for loss distributions if they have been requested
232 map<Date, string> lossDistributionDates = getLossDistributionDates(today);
233 vector<Date> lossDistributionDatesVector;
234 map<string, QuantLib::ext::shared_ptr<BucketedDistribution> > lossDistributionMap;
235 if (!lossDistributionDates.empty()) {
236
237 // calculate max loss from the pool
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 // initialise the Loss BucketedDistribtions with the above maxLoss
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 // Set all probabilities = 0.0;
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 //get date grid, dependending on tranche (to be valued)
259 vector<Date> dates = arguments_.schedule.dates();
260
261 //adjust the date grid, such that it starts with today
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
271 arguments_.basket->fillFlowMaps();
272
273 arguments_.basket->setGrid(dates);
274
275 //other requirements
276 DayCounter feeDayCount = arguments_.feeDayCounter;
277 Currency ccy = arguments_.ccy;
278
279 //asset flows
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 //liability flows
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 //prepare for convergence analysis
306 bool convergenceFlag = false;
307 std::vector<double> runningMean(tranches.size(),0.0);
308 std::vector<double> runningStdev(tranches.size(),0.0);
309 if(ore::data::Log::instance().filter(ORE_DATA)){
310 TLOG("convergence analysis - check first two moments");
311 std::ostringstream labels;
312 labels << "sample";
313 for(Size k = 0 ; k < tranches.size(); k++)
314 labels << "," << tranches[k].name << "-mean,"<< tranches[k].name << "-stdev";
315 TLOG(labels.str());
316 convergenceFlag = true;
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 //Get Collection from bondbasket and exchange into base currency...
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 //Cash pf(0.0, 0.0);
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 //Back out discountfactors
397
398 Real intCcyDis = (iFlows[ccy][j].flow_ > 0 ?
399 iFlows[ccy][j].discountedFlow_ / iFlows[ccy][j].flow_ :
400 0.0);
401
402 /**************************************************************
403 * check flows add up
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 * tranche interest claim
423 */
424 vector<map<Currency, Real> >trancheIntAcc;
425 trancheIntAcc.resize(tranches.size());
426 vector<Real> trancheInterestRates;
427 // ccy
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 //ccy
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 * Collections
442 */
443 Real ccyDFlow = cf[ccy][j].discountedFlow_;
444 Real basketInterest = iFlows[ccy][j].flow_; //for cure amount calc
445
446 /**************************************************************
447 * Senior fees
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 * tranche waterfall
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 //Interest Waterfall incl. ICOC
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 //IC and OC Target Balances
483 Real cureAmount = icocCureAmount(i,j,k,
484 basketNotional[ccy][j],
485 basketInterest,
486 trancheBalance,
487 trancheInterestRates,
488 icRatio,
489 ocRatio);
490
491 interestWaterfall(i, j, dates, iFlows,
492 tranche[k], trancheBalance[k],
493 trancheInterest[k],
494 trancheIntAcc[k], cureAmount);
495
496 icocInterestWaterfall(i, j, k, dates, iFlows,
497 tranche, trancheBalance,
498 cureAmount);
499
500 }
501
502 //Principal Waterfall
503 for (Size k = 0 ; k < tranches.size() ; k++){
504
505 principalWaterfall(i, j, dates, pFlows,
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 * Subordinated Fee
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 * Kicker:
534 * Split excess flows between equity tranche (1-x) and senior fee (x)
535 */
536 Real x = arguments_.equityKicker;
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 * Consistency checks
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;
577 if(fabs(npvError) > errorTolerance_)
578 //ALOG("NPVs do not add up, rel. error " << npvError);
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 } // end dates
586
587 /**************************************************************
588 * Loss Distribution
589 */
590 if (!lossDistributionDates.empty()) {
591 // get the losses on this sample for each date
592 map<Currency, vector<Cash> >
593 lossDist = arguments_.basket->scenarioLossflow(lossDistributionDatesVector);
594
595 // foreach date, we see what bucket our loss falls into and we increase the probability for
596 // that bucket by 1/samples
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); // find the bucket we need to update
604 bd->probabilities()[index] += 1.0 / samples_;
605 }
606 }
607
608 /*
609 //convergence
610 if(convergenceFlag){
611 std::ostringstream meanStev;
612 meanStev << i << fixed << setprecision(2);
613 for(Size k = 0 ; k < tranches.size(); k++){
614 runningMean[k] += trancheValue[k][i];
615 double mean = runningMean[k] / (i + 1);
616 meanStev << "," << mean;
617 runningStdev[k] += trancheValue[k][i]*trancheValue[k][i];
618 double stdev = sqrt(runningStdev[k] / (i + 1) - mean * mean);
619 meanStev << "," << stdev;
620 }
621 TLOG(meanStev.str());
622 }
623 */
624 } // end samples
625
626 //handle results...
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 //distribution output
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 //define which tranche is in the npv-file and scale by investment amount
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 //results of all components in logFile
679 LOG ("-----------------------------------");
680 LOG ("CBO valuation results:");
681 double liab = 0.0;
682 for(Size i = 0 ; i < tranches.size(); i++){
683 LOG( tranches[i].name << " - value " << fixed << setprecision(2) << trancheStats[i].mean() << " - stdev: " << trancheStats[i].std() << " - face amount " << tranches[i].faceAmount );
684 liab += trancheStats[i].mean();
685 }
686 LOG ( "senior fee - value " << fixed << setprecision(2) << feeStats.mean() << " - stdev " << feeStats.std());
687 LOG ( "sub fee - value " << fixed << setprecision(2) << subfeeStats.mean() << " - stdev " << subfeeStats.std());
688 LOG ( "portfolio - value " << fixed << setprecision(2) << basketStats.mean() << " - stdev " << basketStats.std());
689 liab += feeStats.mean() + subfeeStats.mean();
690 LOG ( "total: assets " << fixed << setprecision(2) << basketStats.mean() << " vs. liabilities " << liab );
691 LOG ( "check total :" << fixed << setprecision(10) << basketStats.mean() - liab);
692 LOG ("-----------------------------------");
693 */
694
695 }// calculate
696
697}
Deals with a bucketed distribution.
Monte Carlo pricing engine for the cashflow CDO instrument.
const Instrument::results * results_
Definition: cdsoption.cpp:81
virtual void initialize() const
Definition: cboengine.hpp:39
Real discountedFlow_
Definition: bondbasket.hpp:48
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
Definition: cbomcengine.cpp:30
QuantLib::ext::shared_ptr< RandomDefaultModel > rdm_
Definition: cbomcengine.hpp:94
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
Definition: cbomcengine.cpp:72
helper class for the MonteCarloCBOEngine
Definition: cboengine.hpp:45
Distribution histogram(Size bins, Real xmin=-QL_MAX_REAL, Real xmax=QL_MAX_REAL)
Definition: cboengine.cpp:43
CompiledFormula min(CompiledFormula x, const CompiledFormula &y)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
Swap::arguments * arguments_