QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
basket.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2008 Roland Lichters
5 Copyright (C) 2009, 2014 Jose Aparicio
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
21#include <ql/experimental/credit/basket.hpp>
22#include <ql/experimental/credit/defaultlossmodel.hpp>
23#include <ql/experimental/credit/loss.hpp>
24#include <ql/time/daycounters/actualactual.hpp>
25#include <algorithm>
26#include <numeric>
27#include <utility>
28
29using namespace std;
30
31namespace QuantLib {
32
33 Basket::Basket(const Date& refDate,
34 const vector<string>& names,
35 vector<Real> notionals,
36 ext::shared_ptr<Pool> pool,
37 Real attachment,
38 Real detachment,
39 ext::shared_ptr<Claim> claim)
40 : notionals_(std::move(notionals)), pool_(std::move(pool)), claim_(std::move(claim)),
41 attachmentRatio_(attachment), detachmentRatio_(detachment), basketNotional_(0.0),
42 attachmentAmount_(0.0), detachmentAmount_(0.0), trancheNotional_(0.0), refDate_(refDate) {
43 QL_REQUIRE(!notionals_.empty(), "notionals empty");
44 QL_REQUIRE (attachmentRatio_ >= 0 &&
47 "invalid attachment/detachment ratio");
48 QL_REQUIRE(pool_, "Empty pool pointer.");
49 QL_REQUIRE(notionals_.size() == pool_->size(),
50 "unmatched data entry sizes in basket");
51
52 // registrations relevant to the loss status, not to the expected
53 // loss values; those are through models.
54 registerWith(Settings::instance().evaluationDate());
56
58
59 // At this point Issuers in the pool might or might not have
60 // probability term structures for the defultKeys(eventType+
61 // currency+seniority) entering in this basket. This is not
62 // necessarily a problem.
63 for (Real notional : notionals_) {
67 }
69 }
70
71 /*\todo Alternatively send a relinkable handle so it can be changed from
72 the outside. In that case reconsider the observability chain.
73 */
75 const ext::shared_ptr<DefaultLossModel>& lossModel) {
76
77 if (lossModel_ != nullptr)
79 lossModel_ = lossModel;
80 if (lossModel_ != nullptr) {
81 //recovery quotes, defaults(once Issuer is observable)etc might
82 // trigger us:
84 }
85 LazyObject::update(); //<- just set calc=false
86 }
87
89 // Calculations for status
90 computeBasket();// or we might be called from an statistic member
91 // without being initialized yet (first called)
92 QL_REQUIRE(lossModel_, "Basket has no default loss model assigned.");
93
94 /* The model must notify us if the another basket calls it for
95 reasignment. The basket works as an argument to the deafult loss models
96 so, even if the models dont cache anything, they will be using the wrong
97 default TS. \todo: This has a possible optimization: the basket
98 incorporates trancheability and many models do their compuations
99 independently of that (some do but do it inefficiently when asked for
100 two tranches on the same basket; e,g, recursive model) so it might be
101 more efficient sending the pool only; however the modtionals and other
102 basket info are still used.*/
103 lossModel_->setBasket(const_cast<Basket*>(this));
104 }
105
107 return std::accumulate(notionals_.begin(), notionals_.end(), Real(0.0));
108 }
109
110 vector<Real> Basket::probabilities(const Date& d) const {
111 vector<Real> prob(size());
112 vector<DefaultProbKey> defKeys = defaultKeys();
113 for (Size j = 0; j < size(); j++)
114 prob[j] = pool_->get(pool_->names()[j]).defaultProbability(
115 defKeys[j])->defaultProbability(d);
116 return prob;
117 }
118
119 Real Basket::cumulatedLoss(const Date& endDate) const {
120 QL_REQUIRE(endDate >= refDate_,
121 "Target date lies before basket inception");
122 Real loss = 0.0;
123 for (Size i = 0; i < size(); i++) {
124 ext::shared_ptr<DefaultEvent> credEvent =
125 pool_->get(pool_->names()[i]).defaultedBetween(refDate_,
126 endDate, pool_->defaultKeys()[i]);
127 if (credEvent != nullptr) {
128 /* \todo If the event has not settled one would need to
129 introduce some model recovery rate (independently of a loss
130 model) This remains to be done.
131 */
132 if(credEvent->hasSettled())
133 loss += claim_->amount(credEvent->date(),
134 // notionals_[i],
135 exposure(pool_->names()[i], credEvent->date()),
136 credEvent->settlement().recoveryRate(
137 pool_->defaultKeys()[i].seniority()));
138 }
139 }
140 return loss;
141 }
142
143 Real Basket::settledLoss(const Date& endDate) const {
144 QL_REQUIRE(endDate >= refDate_,
145 "Target date lies before basket inception");
146
147 Real loss = 0.0;
148 for (Size i = 0; i < size(); i++) {
149 ext::shared_ptr<DefaultEvent> credEvent =
150 pool_->get(pool_->names()[i]).defaultedBetween(refDate_,
151 endDate, pool_->defaultKeys()[i]);
152 if (credEvent != nullptr) {
153 if(credEvent->hasSettled()) {
154 loss += claim_->amount(credEvent->date(),
155 //notionals_[i],
156 exposure(pool_->names()[i], credEvent->date()),
157 //NOtice I am requesting an exposure in the past...
158 /* also the seniority does not belong to the
159 counterparty anymore but to the position.....*/
160 credEvent->settlement().recoveryRate(
161 pool_->defaultKeys()[i].seniority()));
162 }
163 }
164 }
165 return loss;
166 }
167
170 }
171
172 std::vector<Size> Basket::liveList(const Date& endDate) const {
173 std::vector<Size> calcBufferLiveList;
174 for (Size i = 0; i < size(); i++)
175 if (!pool_->get(pool_->names()[i]).defaultedBetween(
176 refDate_,
177 endDate,
178 pool_->defaultKeys()[i]))
179 calcBufferLiveList.push_back(i);
180
181 return calcBufferLiveList;
182 }
183
184 Real Basket::remainingNotional(const Date& endDate) const {
185 Real notional = 0;
186 vector<DefaultProbKey> defKeys = defaultKeys();
187 for (Size i = 0; i < size(); i++) {
188 if (!pool_->get(pool_->names()[i]).defaultedBetween(refDate_,
189 endDate,
190 defKeys[i]))
191 notional += notionals_[i];
192 }
193 return notional;
194 }
195
196 vector<Real> Basket::remainingNotionals(const Date& endDate) const
197 {
198 QL_REQUIRE(endDate >= refDate_,
199 "Target date lies before basket inception");
200
201 std::vector<Real> calcBufferNotionals;
202 const std::vector<Size>& alive = liveList(endDate);
203 for(Size i=0; i<alive.size(); i++)
204 calcBufferNotionals.push_back(
205 exposure(pool_->names()[i], endDate)
206 );// some better way to trim it?
207 return calcBufferNotionals;
208 }
209
210 std::vector<Probability> Basket::remainingProbabilities(const Date& d) const
211 {
212 QL_REQUIRE(d >= refDate_, "Target date lies before basket inception");
213 vector<Real> prob;
214 const std::vector<Size>& alive = liveList();
215
216 for(Size i=0; i<alive.size(); i++)
217 prob.push_back(pool_->get(pool_->names()[i]).defaultProbability(
218 pool_->defaultKeys()[i])->defaultProbability(d, true));
219 return prob;
220 }
221
222 /* It is supossed to return the addition of ALL notionals from the
223 requested ctpty......*/
224 Real Basket::exposure(const std::string& name, const Date& d) const {
225 //'this->names_' contains duplicates, contrary to 'pool->names'
226 auto match = std::find(pool_->names().begin(), pool_->names().end(), name);
227 QL_REQUIRE(match != pool_->names().end(), "Name not in basket.");
228 Real totalNotional = 0.;
229 do{
230 totalNotional +=
231 // NOT IMPLEMENTED YET:
232 //positions_[std::distance(names_.begin(), match)]->expectedExposure(d);
233 notionals_[std::distance(pool_->names().begin(), match)];
234 ++match;
235 match = std::find(match, pool_->names().end(), name);
236 }while(match != pool_->names().end());
237
238 return totalNotional;
239 //Size position = std::distance(poolNames.begin(),
240 // std::find(poolNames.begin(), poolNames.end(), name));
241 //QL_REQUIRE(position < pool_->size(), "Name not in pool list");
242
243 //return positions_[position]->expectedExposure(d);
244 }
245
246 std::vector<std::string> Basket::remainingNames(const Date& endDate) const
247 {
248 // maybe return zero directly instead?:
249 QL_REQUIRE(endDate >= refDate_,
250 "Target date lies before basket inception");
251
252 const std::vector<Size>& alive = liveList(endDate);
253 std::vector<std::string> calcBufferNames;
254 calcBufferNames.reserve(alive.size());
255 for (unsigned long i : alive)
256 calcBufferNames.push_back(pool_->names()[i]);
257 return calcBufferNames;
258 }
259
260 vector<DefaultProbKey> Basket::remainingDefaultKeys(const Date& endDate) const
261 {
262 QL_REQUIRE(endDate >= refDate_,
263 "Target date lies before basket inception");
264
265 const std::vector<Size>& alive = liveList(endDate);
266 vector<DefaultProbKey> defKeys;
267 defKeys.reserve(alive.size());
268 for (unsigned long i : alive)
269 defKeys.push_back(pool_->defaultKeys()[i]);
270 return defKeys;
271 }
272
274 return evalDateLiveList_.size();
275 }
276
278 return remainingDefaultKeys(d).size();
279 }
280
281 /* computed on the inception values, notice the positions might have
282 amortized or changed in value and the total outstanding notional might
283 differ from the inception one.*/
285 QL_REQUIRE(endDate >= refDate_,
286 "Target date lies before basket inception");
287 return detachmentAmount_;
288 }
289
291 // maybe return zero directly instead?:
292 QL_REQUIRE(endDate >= refDate_,
293 "Target date lies before basket inception");
294 Real loss = settledLoss(endDate);
295 return std::min(detachmentAmount_, attachmentAmount_ +
296 std::max(0.0, loss - attachmentAmount_));
297 }
298
299 Probability Basket::probOverLoss(const Date& d, Real lossFraction) const {
300 // convert initial basket fraction to remaining basket fraction
301 calculate();
302 // if eaten up all the tranche the prob of losing any amount is 1
303 // (we have already lost it)
304 if(evalDateRemainingNot_ == 0.) return 1.;
305
306 // Turn to live (remaining) tranche units to feed into the model request
307 Real xPtfl = attachmentAmount_ +
309 Real xPrim = (xPtfl- evalDateAttachAmount_)/
311 // in live tranche fractional units
312 // if the level falls within realized losses the prob is 1.
313 if(xPtfl < 0.) return 1.;
314
315 return lossModel_->probOverLoss(d, xPrim);
316 }
317
318 Real Basket::percentile(const Date& d, Probability prob) const {
319 calculate();
320 return lossModel_->percentile(d, prob);
321 }
322
324 calculate();
325 return cumulatedLoss() + lossModel_->expectedTrancheLoss(d);
326 }
327
328 std::vector<Real> Basket::splitVaRLevel(const Date& date, Real loss) const {
329 calculate();
330 return lossModel_->splitVaRLevel(date, loss);
331 }
332
334 calculate();
335 return lossModel_->expectedShortfall(d, prob);
336 }
337
338 std::map<Real, Probability> Basket::lossDistribution(const Date& d) const {
339 calculate();
340 return lossModel_->lossDistribution(d);
341 }
342
343 std::vector<Probability>
345
346 Size alreadyDefaulted = pool_->size() - remainingNames().size();
347 if(alreadyDefaulted >=n)
348 return std::vector<Probability>(remainingNames().size(), 0.);
349
350 calculate();
351 return lossModel_->probsBeingNthEvent(n-alreadyDefaulted, d);
352 }
353
354 Real Basket::defaultCorrelation(const Date& d, Size iName, Size jName) const{
355 calculate();
356 return lossModel_->defaultCorrelation(d, iName, jName);
357
358 }
359
364 calculate();
365 return lossModel_->probAtLeastNEvents(n, d);
366
367 }
368
369 Real Basket::recoveryRate(const Date& d, Size iName) const {
370 calculate();
371 return
372 lossModel_->expectedRecovery(d, iName, pool_->defaultKeys()[iName]);
373 }
374
375}
std::vector< Probability > probabilities(const Date &d) const
Definition: basket.cpp:110
Real recoveryRate(const Date &d, Size iName) const
Definition: basket.cpp:369
Real evalDateAttachAmount_
Definition: basket.hpp:296
void performCalculations() const override
Definition: basket.cpp:88
const ext::shared_ptr< Claim > claim_
The claim is the same for all names.
Definition: basket.hpp:278
Real defaultCorrelation(const Date &d, Size iName, Size jName) const
Definition: basket.cpp:354
Real detachmentAmount_
basket tranched inception detachment amount:
Definition: basket.hpp:286
Real remainingAttachmentAmount() const
Definition: basket.hpp:341
const std::vector< DefaultProbKey > & remainingDefaultKeys() const
Definition: basket.hpp:362
ext::shared_ptr< Pool > pool_
Definition: basket.hpp:276
std::vector< Size > evalDateLiveList_
Definition: basket.hpp:298
Real attachmentAmount_
basket tranched inception attachment amount:
Definition: basket.hpp:284
std::vector< DefaultProbKey > defaultKeys() const
The keys each counterparty enters the basket with (sensitive to)
Definition: basket.hpp:325
Basket()=default
void computeBasket() const
Definition: basket.hpp:74
Real detachmentRatio_
Definition: basket.hpp:281
const Date refDate_
Basket inception date.
Definition: basket.hpp:303
Probability probOverLoss(const Date &d, Real lossFraction) const
Definition: basket.cpp:299
std::vector< Real > notionals_
Definition: basket.hpp:275
Size remainingSize() const
Number of counterparties alive on the requested date.
Definition: basket.cpp:273
Real exposure(const std::string &name, const Date &=Date()) const
Returns the total expected exposures for that name.
Definition: basket.cpp:224
Real trancheNotional_
basket tranched notional amount:
Definition: basket.hpp:288
Probability probAtLeastNEvents(Size n, const Date &d) const
Definition: basket.cpp:363
Real evalDateRemainingNot_
Definition: basket.hpp:295
const std::vector< Size > & liveList() const
Indexes of remaining names. Notice these are names and not positions.
Definition: basket.hpp:333
Real percentile(const Date &d, Probability prob) const
Definition: basket.cpp:318
ext::shared_ptr< DefaultLossModel > lossModel_
Definition: basket.hpp:312
Real expectedTrancheLoss(const Date &d) const
Definition: basket.cpp:323
void setLossModel(const ext::shared_ptr< DefaultLossModel > &lossModel)
Assigns the default loss model to this basket. Resets calculations.
Definition: basket.cpp:74
Real notional() const
Basket total notional at inception.
Definition: basket.cpp:106
Real attachmentRatio_
Definition: basket.hpp:280
std::vector< Probability > probsBeingNthEvent(Size n, const Date &d) const
Definition: basket.cpp:344
Real remainingDetachmentAmount() const
Definition: basket.hpp:337
Size size() const
Basket inception number of counterparties.
Definition: basket.hpp:317
const std::vector< std::string > & remainingNames() const
Definition: basket.hpp:345
Real expectedShortfall(const Date &d, Probability prob) const
Definition: basket.cpp:333
std::vector< Real > splitVaRLevel(const Date &date, Real loss) const
Definition: basket.cpp:328
Real cumulatedLoss() const
Definition: basket.hpp:353
const std::vector< Real > & remainingNotionals() const
Definition: basket.hpp:349
std::vector< Probability > remainingProbabilities(const Date &d) const
Definition: basket.cpp:210
Real basketNotional_
Definition: basket.hpp:282
Real remainingNotional() const
Definition: basket.cpp:168
Real settledLoss() const
Definition: basket.hpp:357
std::map< Real, Probability > lossDistribution(const Date &) const
Definition: basket.cpp:338
Concrete date class.
Definition: date.hpp:125
virtual void calculate() const
Definition: lazyobject.hpp:253
void update() override
Definition: lazyobject.hpp:188
Size unregisterWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:245
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
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
STL namespace.