QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
Loading...
Searching...
No Matches
basket.cpp
Go to the documentation of this file.
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
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");
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 calcBufferNotionals.reserve(alive.size());
204 for(Size i=0; i<alive.size(); i++)
205 calcBufferNotionals.push_back(
206 exposure(pool_->names()[i], endDate)
207 );// some better way to trim it?
208 return calcBufferNotionals;
209 }
210
211 std::vector<Probability> Basket::remainingProbabilities(const Date& d) const
212 {
213 QL_REQUIRE(d >= refDate_, "Target date lies before basket inception");
214 vector<Real> prob;
215 const std::vector<Size>& alive = liveList();
216
217 prob.reserve(alive.size());
218 for(Size i=0; i<alive.size(); i++)
219 prob.push_back(pool_->get(pool_->names()[i]).defaultProbability(
220 pool_->defaultKeys()[i])->defaultProbability(d, true));
221 return prob;
222 }
223
224 /* It is supossed to return the addition of ALL notionals from the
225 requested ctpty......*/
226 Real Basket::exposure(const std::string& name, const Date& d) const {
227 //'this->names_' contains duplicates, contrary to 'pool->names'
228 auto match = std::find(pool_->names().begin(), pool_->names().end(), name);
229 QL_REQUIRE(match != pool_->names().end(), "Name not in basket.");
230 Real totalNotional = 0.;
231 do{
232 totalNotional +=
233 // NOT IMPLEMENTED YET:
234 //positions_[std::distance(names_.begin(), match)]->expectedExposure(d);
235 notionals_[std::distance(pool_->names().begin(), match)];
236 ++match;
237 match = std::find(match, pool_->names().end(), name);
238 }while(match != pool_->names().end());
239
240 return totalNotional;
241 //Size position = std::distance(poolNames.begin(),
242 // std::find(poolNames.begin(), poolNames.end(), name));
243 //QL_REQUIRE(position < pool_->size(), "Name not in pool list");
244
245 //return positions_[position]->expectedExposure(d);
246 }
247
248 std::vector<std::string> Basket::remainingNames(const Date& endDate) const
249 {
250 // maybe return zero directly instead?:
251 QL_REQUIRE(endDate >= refDate_,
252 "Target date lies before basket inception");
253
254 const std::vector<Size>& alive = liveList(endDate);
255 std::vector<std::string> calcBufferNames;
256 calcBufferNames.reserve(alive.size());
257 for (unsigned long i : alive)
258 calcBufferNames.push_back(pool_->names()[i]);
259 return calcBufferNames;
260 }
261
262 vector<DefaultProbKey> Basket::remainingDefaultKeys(const Date& endDate) const
263 {
264 QL_REQUIRE(endDate >= refDate_,
265 "Target date lies before basket inception");
266
267 const std::vector<Size>& alive = liveList(endDate);
268 vector<DefaultProbKey> defKeys;
269 defKeys.reserve(alive.size());
270 for (unsigned long i : alive)
271 defKeys.push_back(pool_->defaultKeys()[i]);
272 return defKeys;
273 }
274
276 return evalDateLiveList_.size();
277 }
278
280 return remainingDefaultKeys(d).size();
281 }
282
283 /* computed on the inception values, notice the positions might have
284 amortized or changed in value and the total outstanding notional might
285 differ from the inception one.*/
287 QL_REQUIRE(endDate >= refDate_,
288 "Target date lies before basket inception");
289 return detachmentAmount_;
290 }
291
293 // maybe return zero directly instead?:
294 QL_REQUIRE(endDate >= refDate_,
295 "Target date lies before basket inception");
296 Real loss = settledLoss(endDate);
297 return std::min(detachmentAmount_, attachmentAmount_ +
298 std::max(0.0, loss - attachmentAmount_));
299 }
300
301 Probability Basket::probOverLoss(const Date& d, Real lossFraction) const {
302 // convert initial basket fraction to remaining basket fraction
303 calculate();
304 // if eaten up all the tranche the prob of losing any amount is 1
305 // (we have already lost it)
306 if(evalDateRemainingNot_ == 0.) return 1.;
307
308 // Turn to live (remaining) tranche units to feed into the model request
309 Real xPtfl = attachmentAmount_ +
311 Real xPrim = (xPtfl- evalDateAttachAmount_)/
313 // in live tranche fractional units
314 // if the level falls within realized losses the prob is 1.
315 if(xPtfl < 0.) return 1.;
316
317 return lossModel_->probOverLoss(d, xPrim);
318 }
319
321 calculate();
322 return lossModel_->percentile(d, prob);
323 }
324
326 calculate();
327 return cumulatedLoss() + lossModel_->expectedTrancheLoss(d);
328 }
329
330 std::vector<Real> Basket::splitVaRLevel(const Date& date, Real loss) const {
331 calculate();
332 return lossModel_->splitVaRLevel(date, loss);
333 }
334
336 calculate();
337 return lossModel_->expectedShortfall(d, prob);
338 }
339
340 std::map<Real, Probability> Basket::lossDistribution(const Date& d) const {
341 calculate();
342 return lossModel_->lossDistribution(d);
343 }
344
345 std::vector<Probability>
347
348 Size alreadyDefaulted = pool_->size() - remainingNames().size();
349 if(alreadyDefaulted >=n)
350 return std::vector<Probability>(remainingNames().size(), 0.);
351
352 calculate();
353 return lossModel_->probsBeingNthEvent(n-alreadyDefaulted, d);
354 }
355
356 Real Basket::defaultCorrelation(const Date& d, Size iName, Size jName) const{
357 calculate();
358 return lossModel_->defaultCorrelation(d, iName, jName);
359
360 }
361
362 /*! Returns the probaility of having a given or larger number of
363 defaults in the basket portfolio at a given time.
364 */
366 calculate();
367 return lossModel_->probAtLeastNEvents(n, d);
368
369 }
370
371 Real Basket::recoveryRate(const Date& d, Size iName) const {
372 calculate();
373 return
374 lossModel_->expectedRecovery(d, iName, pool_->defaultKeys()[iName]);
375 }
376
377}
act/act day counters
basket of issuers and related notionals
std::vector< Probability > probabilities(const Date &d) const
Definition: basket.cpp:110
Real recoveryRate(const Date &d, Size iName) const
Definition: basket.cpp:371
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:356
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:301
std::vector< Real > notionals_
Definition: basket.hpp:275
Size remainingSize() const
Number of counterparties alive on the requested date.
Definition: basket.cpp:275
Real exposure(const std::string &name, const Date &=Date()) const
Returns the total expected exposures for that name.
Definition: basket.cpp:226
Real trancheNotional_
basket tranched notional amount:
Definition: basket.hpp:288
Probability probAtLeastNEvents(Size n, const Date &d) const
Definition: basket.cpp:365
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:320
ext::shared_ptr< DefaultLossModel > lossModel_
Definition: basket.hpp:312
Real expectedTrancheLoss(const Date &d) const
Definition: basket.cpp:325
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:346
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:335
std::vector< Real > splitVaRLevel(const Date &date, Real loss) const
Definition: basket.cpp:330
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:211
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:340
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:243
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:226
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
Date d
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
Pair of loss time and amount, sortable by loss time.
Definition: any.hpp:37
STL namespace.