Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
basket.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 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
19#include <qle/models/basket.hpp>
20//#include <ql/experimental/credit/basket.hpp>
21#include <ql/experimental/credit/loss.hpp>
22#include <ql/time/daycounters/actualactual.hpp>
23//#include <ql/experimental/credit/defaultlossmodel.hpp>
24#include <boost/make_shared.hpp>
26
27using namespace std;
28
29namespace QuantExt {
30
31Basket::Basket(const Date& refDate, const vector<string>& names, const vector<Real>& notionals,
32 const QuantLib::ext::shared_ptr<Pool> pool, Real attachment, Real detachment,
33 const QuantLib::ext::shared_ptr<Claim>& claim)
34 : notionals_(notionals), pool_(pool), claim_(claim), attachmentRatio_(attachment), detachmentRatio_(detachment),
35 basketNotional_(0.0), attachmentAmount_(0.0), detachmentAmount_(0.0), trancheNotional_(0.0), refDate_(refDate) {
36 QL_REQUIRE(!notionals_.empty(), "notionals empty");
38 "invalid attachment/detachment ratio");
39 QL_REQUIRE(pool_, "Empty pool pointer.");
40 QL_REQUIRE(notionals_.size() == pool_->size(), "unmatched data entry sizes in basket, "
41 << notionals_.size() << " notionals, " << pool_->size()
42 << " pool size");
43
44 // registrations relevant to the loss status, not to the expected
45 // loss values; those are through models.
46 registerWith(Settings::instance().evaluationDate());
47 registerWith(claim_);
48}
49
50/*\todo Alternatively send a relinkable handle so it can be changed from
51the outside. In that case reconsider the observability chain.
52*/
53void Basket::setLossModel(const QuantLib::ext::shared_ptr<DefaultLossModel>& lossModel) {
54
55 if (lossModel_)
56 unregisterWith(lossModel_);
57 lossModel_ = lossModel;
58 if (lossModel_) {
59 // recovery quotes, defaults(once Issuer is observable)etc might
60 // trigger us:
61 registerWith(lossModel_);
62 }
63 LazyObject::update(); //<- just set calc=false
64}
65
68 for (Size i = 0; i < notionals_.size(); i++) {
72 }
74 // Calculations for status
75 computeBasket(); // or we might be called from an statistic member
76 // without being intialized yet (first called)
77 QL_REQUIRE(lossModel_, "Basket has no default loss model assigned.");
78
79 /* The model must notify us if the another basket calls it for
80 reasignment. The basket works as an argument to the deafult loss models
81 so, even if the models dont cache anything, they will be using the wrong
82 defautl TS. \todo: This has a possible optimization: the basket
83 incorporates trancheability and many models do their compuations
84 independently of that (some do but do it inefficiently when asked for
85 two tranches on the same basket; e,g, recursive model) so it might be
86 more efficient sending the pool only; however the modtionals and other
87 basket info are still used.*/
88 lossModel_->setBasket(const_cast<Basket*>(this));
89}
90
91vector<Real> Basket::probabilities(const Date& d) const {
92 calculate();
93 vector<Real> prob(size());
94 vector<DefaultProbKey> defKeys = defaultKeys();
95 for (Size j = 0; j < size(); j++)
96 prob[j] = pool_->get(pool_->names()[j]).defaultProbability(defKeys[j])->defaultProbability(d);
97 return prob;
98}
99
100Real Basket::cumulatedLoss(const Date& endDate) const {
101 calculate();
102 QL_REQUIRE(endDate >= refDate_, "Target date lies before basket inception");
103 Real loss = 0.0;
104 for (Size i = 0; i < size(); i++) {
105 QuantLib::ext::shared_ptr<DefaultEvent> credEvent =
106 pool_->get(pool_->names()[i]).defaultedBetween(refDate_, endDate, pool_->defaultKeys()[i]);
107 if (credEvent) {
108 /* \todo If the event has not settled one would need to
109 introduce some model recovery rate (independently of a loss
110 model) This remains to be done.
111 */
112 if (credEvent->hasSettled())
113 loss += claim_->amount(credEvent->date(),
114 // notionals_[i],
115 exposure(pool_->names()[i], credEvent->date()),
116 credEvent->settlement().recoveryRate(pool_->defaultKeys()[i].seniority()));
117 }
118 }
119 return loss;
120}
121
122Real Basket::settledLoss(const Date& endDate) const {
123 calculate();
124 QL_REQUIRE(endDate >= refDate_, "Target date lies before basket inception");
125
126 Real loss = 0.0;
127 for (Size i = 0; i < size(); i++) {
128 QuantLib::ext::shared_ptr<DefaultEvent> credEvent =
129 pool_->get(pool_->names()[i]).defaultedBetween(refDate_, endDate, pool_->defaultKeys()[i]);
130 if (credEvent) {
131 if (credEvent->hasSettled()) {
132 loss += claim_->amount(credEvent->date(),
133 // notionals_[i],
134 exposure(pool_->names()[i], credEvent->date()),
135 // NOtice I am requesting an exposure in the past...
136 /* also the seniority does not belong to the
137 counterparty anymore but to the position.....*/
138 credEvent->settlement().recoveryRate(pool_->defaultKeys()[i].seniority()));
139 }
140 }
141 }
142 return loss;
143}
144
146 calculate();
148}
149
150std::vector<Size> Basket::liveList(const Date& endDate) const {
151 calculate();
152 std::vector<Size> calcBufferLiveList;
153 for (Size i = 0; i < size(); i++)
154 if (!pool_->get(pool_->names()[i]).defaultedBetween(refDate_, endDate, pool_->defaultKeys()[i]))
155 calcBufferLiveList.push_back(i);
156
157 return calcBufferLiveList;
158}
159
160Real Basket::remainingNotional(const Date& endDate) const {
161 calculate();
162 Real notional = 0;
163 vector<DefaultProbKey> defKeys = defaultKeys();
164 for (Size i = 0; i < size(); i++) {
165 if (!pool_->get(pool_->names()[i]).defaultedBetween(refDate_, endDate, defKeys[i]))
166 notional += notionals_[i];
167 }
168 return notional;
169}
170
171vector<Real> Basket::remainingNotionals(const Date& endDate) const {
172 calculate();
173 QL_REQUIRE(endDate >= refDate_, "Target date lies before basket inception");
174
175 std::vector<Real> calcBufferNotionals;
176 const std::vector<Size>& alive = liveList(endDate);
177 for (Size i = 0; i < alive.size(); i++)
178 calcBufferNotionals.push_back(exposure(pool_->names()[i], endDate)); // some better way to trim it?
179 return calcBufferNotionals;
180}
181
182std::vector<Probability> Basket::remainingProbabilities(const Date& d) const {
183 calculate();
184 QL_REQUIRE(d >= refDate_, "Target date lies before basket inception");
185 vector<Real> prob;
186 const std::vector<Size>& alive = liveList();
187
188 for (Size i = 0; i < alive.size(); i++)
189 prob.push_back(
190 pool_->get(pool_->names()[i]).defaultProbability(pool_->defaultKeys()[i])->defaultProbability(d, true));
191 return prob;
192}
193
194/* It is supossed to return the addition of ALL notionals from the
195requested ctpty......*/
196Real Basket::exposure(const std::string& name, const Date& d) const {
197 calculate();
198 //'this->names_' contains duplicates, contrary to 'pool->names'
199 std::vector<std::string>::const_iterator match = std::find(pool_->names().begin(), pool_->names().end(), name);
200 QL_REQUIRE(match != pool_->names().end(), "Name not in basket.");
201 Real totalNotional = 0.;
202 do {
203 totalNotional +=
204 // NOT IMPLEMENTED YET:
205 // positions_[std::distance(names_.begin(), match)]->expectedExposure(d);
206 notionals_[std::distance(pool_->names().begin(), match)];
207 ++match;
208 match = std::find(match, pool_->names().end(), name);
209 } while (match != pool_->names().end());
210
211 return totalNotional;
212 // Size position = std::distance(poolNames.begin(),
213 // std::find(poolNames.begin(), poolNames.end(), name));
214 // QL_REQUIRE(position < pool_->size(), "Name not in pool list");
215
216 // return positions_[position]->expectedExposure(d);
217}
218
219std::vector<std::string> Basket::remainingNames(const Date& endDate) const {
220 calculate();
221 // maybe return zero directly instead?:
222 QL_REQUIRE(endDate >= refDate_, "Target date lies before basket inception");
223
224 const std::vector<Size>& alive = liveList(endDate);
225 std::vector<std::string> calcBufferNames;
226 for (Size i = 0; i < alive.size(); i++)
227 calcBufferNames.push_back(pool_->names()[alive[i]]);
228 return calcBufferNames;
229}
230
231vector<DefaultProbKey> Basket::remainingDefaultKeys(const Date& endDate) const {
232 calculate();
233 QL_REQUIRE(endDate >= refDate_, "Target date lies before basket inception");
234
235 const std::vector<Size>& alive = liveList(endDate);
236 vector<DefaultProbKey> defKeys;
237 for (Size i = 0; i < alive.size(); i++)
238 defKeys.push_back(pool_->defaultKeys()[alive[i]]);
239 return defKeys;
240}
241
243 calculate();
244 return evalDateLiveList_.size();
245}
246
247/* computed on the inception values, notice the positions might have
248amortized or changed in value and the total outstanding notional might
249differ from the inception one.*/
250Real Basket::remainingDetachmentAmount(const Date& endDate) const {
251 calculate();
252 return detachmentAmount_;
253}
254
255Real Basket::remainingAttachmentAmount(const Date& endDate) const {
256 calculate();
257 // maybe return zero directly instead?:
258 QL_REQUIRE(endDate >= refDate_, "Target date lies before basket inception");
259 Real loss = settledLoss(endDate);
260 return std::min(detachmentAmount_, attachmentAmount_ + std::max(0.0, loss - attachmentAmount_));
261}
262
263Probability Basket::probOverLoss(const Date& d, Real lossFraction) const {
264 // convert initial basket fraction to remaining basket fraction
265 calculate();
266 // if eaten up all the tranche the prob of losing any amount is 1
267 // (we have already lost it)
268 if (evalDateRemainingNot_ == 0.)
269 return 1.;
270
271 // Turn to live (remaining) tranche units to feed into the model request
272 Real xPtfl = attachmentAmount_ + (detachmentAmount_ - attachmentAmount_) * lossFraction;
274 // in live tranche fractional units
275 // if the level falls within realized losses the prob is 1.
276 if (xPtfl < 0.)
277 return 1.;
278
279 return lossModel_->probOverLoss(d, xPrim);
280}
281
282Real Basket::percentile(const Date& d, Probability prob) const {
283 calculate();
284 return lossModel_->percentile(d, prob);
285
286 // Real percLiveFract = lossModel_->percentile(d, prob);
287 // return (percLiveFract * (detachmentAmount_ - evalDateAttachAmount_) + attachmentAmount_ - evalDateAttachAmount_)
288 // /
289 // (detachmentAmount_ - attachmentAmount_);
290}
291
292// RL: additional flag
293Real Basket::expectedTrancheLoss(const Date& d, Real recoveryRate) const {
294 calculate();
295 return cumulatedLoss() + lossModel_->expectedTrancheLoss(d, recoveryRate);
296}
297
298std::vector<Real> Basket::splitVaRLevel(const Date& date, Real loss) const {
299 calculate();
300 return lossModel_->splitVaRLevel(date, loss);
301}
302
303Real Basket::expectedShortfall(const Date& d, Probability prob) const {
304 calculate();
305 return lossModel_->expectedShortfall(d, prob);
306}
307
308std::map<Real, Probability> Basket::lossDistribution(const Date& d) const {
309 calculate();
310 return lossModel_->lossDistribution(d);
311}
312
313std::vector<Probability> Basket::probsBeingNthEvent(Size n, const Date& d) const {
314 Size alreadyDefaulted = pool_->size() - remainingNames().size();
315 if (alreadyDefaulted >= n)
316 return std::vector<Probability>(remainingNames().size(), 0.);
317
318 calculate();
319 return lossModel_->probsBeingNthEvent(n - alreadyDefaulted, d);
320}
321
322Real Basket::defaultCorrelation(const Date& d, Size iName, Size jName) const {
323 calculate();
324 return lossModel_->defaultCorrelation(d, iName, jName);
325}
326
327/*! Returns the probaility of having a given or larger number of
328defaults in the basket portfolio at a given time.
329*/
330Probability Basket::probAtLeastNEvents(Size n, const Date& d) const {
331 calculate();
332 return lossModel_->probAtLeastNEvents(n, d);
333}
334
335Real Basket::recoveryRate(const Date& d, Size iName) const {
336 calculate();
337 return lossModel_->expectedRecovery(d, iName, pool_->defaultKeys()[iName]);
338}
339
341 calculate();
342 if (lossModel_)
343 return lossModel_->correlation();
344 else
345 return Null<Real>();
346}
347
348} // namespace QuantExt
basket of issuers and related notionals
std::vector< Probability > probabilities(const Date &d) const
Definition: basket.cpp:91
Real recoveryRate(const Date &d, Size iName) const
Definition: basket.cpp:335
Real evalDateAttachAmount_
Definition: basket.hpp:298
void performCalculations() const override
Definition: basket.cpp:66
Real defaultCorrelation(const Date &d, Size iName, Size jName) const
Definition: basket.cpp:322
Real detachmentAmount_
basket tranched inception detachment amount:
Definition: basket.hpp:290
Real remainingAttachmentAmount() const
Definition: basket.hpp:337
const std::vector< DefaultProbKey > & remainingDefaultKeys() const
Definition: basket.hpp:362
std::vector< Size > evalDateLiveList_
Definition: basket.hpp:299
Real attachmentAmount_
basket tranched inception attachment amount:
Definition: basket.hpp:288
QuantLib::ext::shared_ptr< Pool > pool_
Definition: basket.hpp:280
const QuantLib::ext::shared_ptr< Claim > claim_
The claim is the same for all names.
Definition: basket.hpp:282
std::vector< DefaultProbKey > defaultKeys() const
The keys each counterparty enters the basket with (sensitive to)
Definition: basket.hpp:323
QuantLib::Real correlation() const
Definition: basket.cpp:340
void computeBasket() const
Definition: basket.hpp:66
Real detachmentRatio_
Definition: basket.hpp:285
const Date refDate_
Basket inception date.
Definition: basket.hpp:304
Probability probOverLoss(const Date &d, Real lossFraction) const
Definition: basket.cpp:263
std::vector< Real > notionals_
Definition: basket.hpp:279
Size remainingSize() const
Number of counterparties alive on the requested date.
Definition: basket.cpp:242
Real exposure(const std::string &name, const Date &=Date()) const
Returns the total expected exposures for that name.
Definition: basket.cpp:196
Real trancheNotional_
basket tranched notional amount:
Definition: basket.hpp:292
Probability probAtLeastNEvents(Size n, const Date &d) const
Definition: basket.cpp:330
Real evalDateRemainingNot_
Definition: basket.hpp:298
const std::vector< Size > & liveList() const
Indexes of remaining names. Notice these are names and not positions.
Definition: basket.hpp:327
Real percentile(const Date &d, Probability prob) const
Definition: basket.cpp:282
Real attachmentRatio_
Definition: basket.hpp:284
Real expectedTrancheLoss(const Date &d, Real recoveryRate=Null< Real >()) const
Definition: basket.cpp:293
QuantLib::ext::shared_ptr< QuantExt::DefaultLossModel > lossModel_
Definition: basket.hpp:314
std::vector< Probability > probsBeingNthEvent(Size n, const Date &d) const
Definition: basket.cpp:313
Real remainingDetachmentAmount() const
Definition: basket.hpp:332
Size size() const
Basket inception number of counterparties.
Definition: basket.hpp:319
const std::vector< std::string > & remainingNames() const
Definition: basket.hpp:342
Real expectedShortfall(const Date &d, Probability prob) const
Definition: basket.cpp:303
std::vector< Real > splitVaRLevel(const Date &date, Real loss) const
Definition: basket.cpp:298
Real cumulatedLoss() const
Definition: basket.hpp:352
void setLossModel(const QuantLib::ext::shared_ptr< QuantExt::DefaultLossModel > &lossModel)
Assigns the default loss model to this basket. Resets calculations.
Definition: basket.cpp:53
const std::vector< Real > & remainingNotionals() const
Definition: basket.hpp:347
std::vector< Probability > remainingProbabilities(const Date &d) const
Definition: basket.cpp:182
Real basketNotional_
Definition: basket.hpp:286
Real remainingNotional() const
Definition: basket.cpp:145
Real settledLoss() const
Definition: basket.hpp:357
std::map< Real, Probability > lossDistribution(const Date &) const
Definition: basket.cpp:308