QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
recursivelossmodel.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2009, 2014 Jose Aparicio
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#ifndef quantlib_recursive_loss_model_hpp
21#define quantlib_recursive_loss_model_hpp
22
23#include <ql/experimental/credit/constantlosslatentmodel.hpp>
24#include <ql/experimental/credit/defaultlossmodel.hpp>
25#include <map>
26#include <algorithm>
27
28namespace QuantLib {
29
46 template<class copulaPolicy>
48 public:
50 const ext::shared_ptr<ConstantLossLatentmodel<copulaPolicy> >& m,
51 // nope! use max common divisor. See O'Kane. Or give both options at least.
52 Size nbuckets = 1)
53 : copula_(m), nBuckets_(nbuckets) {}
54
55 private:
62 std::map<Real, Probability> conditionalLossDistrib(const std::vector<Probability>& pDefDate,
63 const std::vector<Real>& mktFactor) const;
64 Real expectedConditionalLoss(const std::vector<Probability>& pDefDate, //<< never used!!
65 const std::vector<Real>& mktFactor) const;
66 std::vector<Real> conditionalLossProb(const std::vector<Probability>& pDefDate,
67 // const Date& date,
68 const std::vector<Real>& mktFactor) const;
69 // versions using the P-inverse, deprecate the former
70 std::map<Real, Probability> conditionalLossDistribInvP(const std::vector<Real>& pDefDate,
71 // const Date& date,
72 const std::vector<Real>& mktFactor) const;
73 Real expectedConditionalLossInvP(const std::vector<Real>& pDefDate,
74 // const Date& date,
75 const std::vector<Real>& mktFactor) const;
76 protected:
77 void resetModel() override;
78
79 public:
80 /* Expected tranche Loss calculation.
81 This is computed from the first equation on page 70 (not numbered)
82 Notice that while we want to compute:
83 \f[
84 EL(t) = \sum_{l_k}l_k P(l;t) =
85 \sum_{l_k}l_k \int P(l_k;t|\omega) d\omega q(\omega)
86 \f]
87 One can invert the sumation and the integral order to:
88 \f[
89 EL(t) = \int\,q(\omega)\,d\omega\,\sum_{l_k}\,l_k\,P(l_k;t|\omega) =
90 \int\,q(\omega)\,d\omega\,EL(t|\omega)
91 \f]
92 and this is the way it is integrated here. The recursion formula
93 makes it easier this way.
94 */
95 Real expectedTrancheLoss(const Date& date) const override;
96 std::vector<Real> lossProbability(const Date& date) const;
97 // REMEBER THIS HAS TO BE MOVED TO A DISTRIBUTION OBJECT.............
98 std::map<Real, Probability> lossDistribution(const Date& d) const override;
99 // INTEGRATE THEN SEARCH RATHER THAN SEARCH AND THEN INTEGRATE:
100 // Here I am not using a search because the point might not be attainable
101 // (loss distrib is not continuous)
102 Real percentile(const Date& d, Real percentile) const override;
103 Real expectedShortfall(const Date& d, Real perctl) const override;
104
105 protected:
106 const ext::shared_ptr<ConstantLossLatentmodel<copulaPolicy> > copula_;
107 private:
108 // loss model descriptor members
110 mutable std::vector<Real> wk_;
113 // correl = beta * beta
114 // When constructing through a single correlation number the factor is
115 // taken to be the positive swuare root of this number in the copula.
117 // cached remaining basket magnitudes:
122 mutable std::vector<Real> notionals_;
123 };
124
125
127
128 // Inlines ------------------------------------------------
129
130 template<class CP>
132 const Date& date) const
133 {
134/*
135 std::map<Real, Probability> dist = lossDistribution(date);
136
137 Real expLoss = 0.;
138 std::map<Real, Probability>::iterator distIt = dist.begin();
139
140 while(distIt != dist.end()) {
141 Real loss = distIt->first * lossUnit_;
142 loss = std::max(std::min(loss, detachAmount_)-attachAmount_, 0.);
143 // MIN MAX BUGS ....??
144 expLoss += loss * distIt->second;
145 distIt++;
146 }
147 return expLoss ;
148
149
150
151
153
154 // calculate inverted unconditional Ps first so we save the inversion:
155 // TO DO : turn to STL algorithm code
156 std::vector<Probability> uncDefProb =
157 basket_->remainingProbabilities(date);
158
159 return copula_->integratedExpectedValue(
160 [&](const std::vector<Real>& v1) {
161 return expectedConditionalLoss(uncDefProb, v1);
162 });
163 */
164
165 std::vector<Probability> uncDefProb =
166 basket_->remainingProbabilities(date);
167 std::vector<Real> invProb;
168 for(Size i=0; i<uncDefProb.size(); ++i)
169 invProb.push_back(copula_->inverseCumulativeY(uncDefProb[i], i));
171 return copula_->integratedExpectedValue(
172 [&](const std::vector<Real>& v1) {
173 return expectedConditionalLossInvP(invProb, v1);
174 });
175 }
176
177 template<class CP>
178 inline std::vector<Real> RecursiveLossModel<CP>::lossProbability(const Date& date) const {
179
180 std::vector<Probability> uncDefProb =
181 basket_->remainingProbabilities(date);
182 return copula_->integratedExpectedValueV(
183 [&](const std::vector<Real>& v1) {
184 return conditionalLossProb(uncDefProb, v1);
185 });
186 }
187
188 // -------------------------------------------------------------------
189
190 template<class CP>
192 // basket update:
193 notionals_ = basket_->remainingNotionals();
194 notional_ = basket_->remainingNotional();
195 attachAmount_ = basket_->remainingAttachmentAmount();
196 detachAmount_ = basket_->remainingDetachmentAmount();
197 // model parameters:
198 remainingBsktSize_ = notionals_.size();
199
200 copula_->resetBasket(basket_.currentLink());
201
202 std::vector<Real> lgdsTmp, lgds;
203 for(Size i=0; i<remainingBsktSize_; ++i)
204 lgds.push_back(notionals_[i]*(1.-copula_->recoveries()[i]));
205 lgdsTmp = lgds;
207 lgds.erase(std::remove(lgds.begin(), lgds.end(), 0.), lgds.end());
208 lossUnit_ = *(std::min_element(lgds.begin(), lgds.end()))
209 / nBuckets_;
210 for(Size i=0; i<remainingBsktSize_; ++i)
211 wk_.push_back(std::floor(lgdsTmp[i]/lossUnit_ + .5));
212 }
213
214 // make it return a distribution object?
215 template<class CP>
216 std::map<Real, Probability> RecursiveLossModel<CP>::lossDistribution(const Date& d) const
217 {
218 std::map<Real, Probability> distrib;
219 std::vector<Real> values = lossProbability(d);
220 Real sum = 0.;
221 for(Size i=0; i<values.size(); ++i) {
222 distrib.insert(std::make_pair<Real, Probability>(i * lossUnit_,
223 sum + values[i]));
224 sum += values[i];
225 }
226 return distrib;
227 }
228
229 // Integrate then search rather than search and then integrate?
230 // Here I am not using a search because the point might be not attainable
231 // (loss distrib is not continuous)
232 template<class CP>
234 Real percentile) const
235 {
236 std::map<Real, Probability> dist = lossDistribution(d);
237
238 if(dist.begin()->second >=1.) return dist.begin()->first;
239
240 // deterministic case (e.g. date requested is todays date)
241 if(dist.size() == 1) return dist.begin()->first;
242
243 if(percentile == 1.) return dist.rbegin()->second;
244 if(percentile == 0.) return dist.begin()->second;
245 std::map<Real, Probability>::const_iterator itdist = dist.begin();
246 while (itdist->second <= percentile) ++itdist;
247 Real valPlus = itdist->second;
248 Real xPlus = itdist->first;
249 --itdist; //we're never 1st or last, because of tests above
250 Real valMin = itdist->second;
251 Real xMin = itdist->first;
252
253 // return xPlus-(xPlus-xMin)*(valPlus-percentile)/(valPlus-valMin);
254 Real portfLoss = xPlus-(xPlus-xMin)*(valPlus-percentile)
255 /(valPlus-valMin);
256 return //remainingNotional_ *
257 std::min(std::max(portfLoss - attachAmount_, 0.),
258 detachAmount_ - attachAmount_);
259 }
260
261 template<class CP>
263 Real perctl) const
264 {
265 if(d == Settings::instance().evaluationDate()) return 0.;
266 std::map<Real, Probability> distrib = lossDistribution(d);
267
268 std::map<Real, Probability>::iterator itNxt, itDist =
269 distrib.begin();
270 for(; itDist != distrib.end(); ++itDist)
271 if(itDist->second >= perctl) break;
272 itNxt = itDist;
273 --itDist; // what if we are on the first one?!!!
274
275 // One could linearly triangulate the exact point and get extra
276 // precission on the first(broken) period.
277 if(itNxt != distrib.end()) {
278 Real lossNxt = std::min(std::max(itNxt->first - attachAmount_,
279 0.), detachAmount_ - attachAmount_);
280 Real lossHere = std::min(std::max(itDist->first - attachAmount_,
281 0.), detachAmount_ - attachAmount_);
282
283 Real val = lossNxt - (itNxt->second - perctl) *
284 (lossNxt - lossHere) / (itNxt->second - itDist->second);
285 Real suma = (itNxt->second - perctl) * (lossNxt + val) * .5;
286 ++itDist; ++itNxt;
287 do{
288 lossNxt = std::min(std::max(itNxt->first - attachAmount_,
289 0.), detachAmount_ - attachAmount_);
290 lossHere = std::min(std::max(itDist->first - attachAmount_,
291 0.), detachAmount_ - attachAmount_);
292 suma += .5 * (lossHere + lossNxt) * (itNxt->second -
293 itDist->second);
294 ++itDist; ++itNxt;
295 }while(itNxt != distrib.end());
296 return suma / (1.-perctl);
297 }
298 return 0.;// well, we are in error.... fix: FAIL
299 }
300
301 template<class CP>
303 const std::vector<Probability>& pDefDate,
304 //const Date& date,
305 const std::vector<Real>& mktFactor) const
306 {
307 //eq. 10 p.68
308 //attainable losses distribution, recursive algorithm
309 const std::vector<Probability>& uncDefProb = pDefDate;// alias, remove
310
311 std::map<Real, Probability> pIndepDistrib;
313 pIndepDistrib.insert(std::make_pair(0., 1.));
314 for(Size iName=0; iName<remainingBsktSize_; ++iName) {
315 Probability pDef =
316 copula_->conditionalDefaultProbability(uncDefProb[iName], iName,
317 mktFactor);
319 std::map<Real, Probability> pDistTemp;
320 auto distIt = pIndepDistrib.begin();
321 while(distIt != pIndepDistrib.end()) {
323 auto matchIt = pDistTemp.find(distIt->first);
324 if (matchIt != pDistTemp.end()) {
325 matchIt->second += distIt->second * (1. - pDef);
326 }else{
327 pDistTemp.insert(std::make_pair(distIt->first,
328 distIt->second * (1.-pDef)));
329 }
331 matchIt = pDistTemp.find(distIt->first + wk_[iName]);
332 if(matchIt != pDistTemp.end()) {
333 matchIt->second += distIt->second * pDef;
334 }else{
335 pDistTemp.insert(std::make_pair(
336 distIt->first+wk_[iName], distIt->second * pDef));
337 }
338 ++distIt;
339 }
341 pIndepDistrib = pDistTemp;
342 }
343 /* Apply tranche limits now .... mind you this could be done outside*/
345 return pIndepDistrib;
346 }
347
348 // twice?! rewrite one in terms of the other, this is a duplicate!
349 template<class CP>
351 const std::vector<Real>& invpDefDate,
352 //const Date& date,
353 const std::vector<Real>& mktFactor) const
354 {
355 // eq. 10 p.68
356 // attainable losses distribution, recursive algorithm
357
358 std::map<Real, Probability> pIndepDistrib;
359 // K=0
360 pIndepDistrib.insert(std::make_pair(0., 1.));
361 for(Size iName=0; iName<remainingBsktSize_; ++iName) {
362 Probability pDef =
363 copula_->conditionalDefaultProbabilityInvP(invpDefDate[iName],
364 iName, mktFactor);
365
366 // iterate on all possible losses in the distribution:
367 std::map<Real, Probability> pDistTemp;
368 auto distIt = pIndepDistrib.begin();
369 while(distIt != pIndepDistrib.end()) {
370 // update prob if this name does not default
371 auto matchIt = pDistTemp.find(distIt->first);
372 if(matchIt != pDistTemp.end()) {
373 matchIt->second += distIt->second * (1.-pDef);
374 }else{
375 pDistTemp.insert(std::make_pair(distIt->first,
376 distIt->second * (1.-pDef)));
377 }
378 // and if it does
379 matchIt = pDistTemp.find(distIt->first + wk_[iName]);
380 if(matchIt != pDistTemp.end()) {
381 matchIt->second += distIt->second * pDef;
382 }else{
383 pDistTemp.insert(std::make_pair(
384 distIt->first+wk_[iName], distIt->second * pDef));
385 }
386 ++distIt;
387 }
388 // copy back
389 pIndepDistrib = pDistTemp;
390 }
391 /* Apply tranche limits now .... mind you this could be done outside*/
392 return pIndepDistrib;
393 }
394
395
396
397
398 /*
399 Bugs here???. The max min on the tranche looks
400 wrong. It is better to have a tranche function since that way we can avoid
401 adding up losses over all the posible losses rather than just over the
402 tranche limits.
403 */
405 template<class CP>
407 const std::vector<Probability>& pDefDate,
408 //const Date& date,
409 const std::vector<Real>& mktFactor) const
410 {
411 std::map<Real, Probability> pIndepDistrib =
412 conditionalLossDistrib(pDefDate, mktFactor);
413
414 // get the expected value subject to the value of the market
415 // factor.
416 Real expLoss = 0.;
417 //---------------------------------------------------------------
418 /* This is the original (easy to read) loop which I have partially
419 unroll below to take profit of the fact that once we go over
420 the tranche top the loss amount is fixed:
421 */
422 auto distIt = pIndepDistrib.begin();
423
424 while(distIt != pIndepDistrib.end()) {
425 Real loss = distIt->first * lossUnit_;
426 // loss = std::max(std::min(loss, detachAmount_)-attachAmount_, 0.);
427 loss = std::min(std::max(loss - attachAmount_, 0.),
428 detachAmount_ - attachAmount_);
429 // MIN MAX BUGS ....??
430 expLoss += loss * distIt->second;
431 ++distIt;
432 }
433 return expLoss ;
434 }
435
436 template<class CP>
437 // again, I am duplicating code.
439 const std::vector<Real>& invPDefDate,
440 //const Date& date,
441 const std::vector<Real>& mktFactor) const
442 {
443 std::map<Real, Probability> pIndepDistrib =
444 conditionalLossDistribInvP(invPDefDate, mktFactor);
445
446 // get the expected value subject to the value of the market
447 // factor.
448 Real expLoss = 0.;
449 //---------------------------------------------------------------
450 /* This is the original (easy to read) loop which I have partially
451 unroll below to take profit of the fact that once we go over
452 the tranche top the loss amount is fixed:
453 */
454 auto distIt = pIndepDistrib.begin();
455
456 while(distIt != pIndepDistrib.end()) {
457 Real loss = distIt->first * lossUnit_;
458 // loss = std::max(std::min(loss, detachAmount_)-attachAmount_, 0.);
459 loss = std::min(std::max(loss - attachAmount_, 0.),
460 detachAmount_ - attachAmount_);
461 // MIN MAX BUGS ....???
462 expLoss += loss * distIt->second;
463 ++distIt;
464 }
465 return expLoss ;
466 }
467
468 template<class CP>
470 const std::vector<Probability>& pDefDate,
471 //const Date& date,
472 const std::vector<Real>& mktFactor) const
473 {
474 std::map<Real, Probability> pIndepDistrib =
475 conditionalLossDistrib(pDefDate, mktFactor);
476
477 std::vector<Real> results;
478 auto distIt = pIndepDistrib.begin();
479 while(distIt != pIndepDistrib.end()) {
480 //Real loss = distIt->first * loss_unit_
481 // ;
482 //loss = std::max(std::min(loss,
483 // results_.xMax)-results_.xMin, 0.);
484 //expLoss += loss * distIt->second;
485
486 results.push_back(distIt->second);
487 ++distIt;
488 }
489 return results;
490 }
491
492}
493
494#endif
Concrete date class.
Definition: date.hpp:125
std::vector< Real > conditionalLossProb(const std::vector< Probability > &pDefDate, const std::vector< Real > &mktFactor) const
std::map< Real, Probability > conditionalLossDistrib(const std::vector< Probability > &pDefDate, const std::vector< Real > &mktFactor) const
Real expectedConditionalLoss(const std::vector< Probability > &pDefDate, const std::vector< Real > &mktFactor) const
Portfolio loss conditional to the market factor value.
Real attachAmount_
name to name factor. In the single factor copula:
Real percentile(const Date &d, Real percentile) const override
Value at Risk given a default loss percentile.
std::map< Real, Probability > conditionalLossDistribInvP(const std::vector< Real > &pDefDate, const std::vector< Real > &mktFactor) const
Real expectedConditionalLossInvP(const std::vector< Real > &pDefDate, const std::vector< Real > &mktFactor) const
Real expectedTrancheLoss(const Date &date) const override
const ext::shared_ptr< ConstantLossLatentmodel< copulaPolicy > > copula_
RecursiveLossModel(const ext::shared_ptr< ConstantLossLatentmodel< copulaPolicy > > &m, Size nbuckets=1)
std::vector< Real > lossProbability(const Date &date) const
void resetModel() override
Concrete models do now any updates/inits they need on basket reset.
Real expectedShortfall(const Date &d, Real perctl) const override
Expected shortfall given a default loss percentile.
std::map< Real, Probability > lossDistribution(const Date &d) const override
Full loss distribution.
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
QL_REAL Real
real number
Definition: types.hpp:50
Real Probability
probability
Definition: types.hpp:82
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
RecursiveLossModel< GaussianCopulaPolicy > RecursiveGaussLossModel