QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
Loading...
Searching...
No Matches
creditriskplus.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) 2013 Peter Caspers
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
21#include <map>
22#include <utility>
23
24using std::sqrt;
25
26namespace QuantLib {
27
29
30 CreditRiskPlus::CreditRiskPlus(std::vector<Real> exposure,
31 std::vector<Real> defaultProbability,
32 std::vector<Size> sector,
33 std::vector<Real> relativeDefaultVariance,
34 Matrix correlation,
35 const Real unit)
36 : exposure_(std::move(exposure)), pd_(std::move(defaultProbability)),
37 sector_(std::move(sector)), relativeDefaultVariance_(std::move(relativeDefaultVariance)),
38 correlation_(std::move(correlation)), unit_(unit) {
39
40 m_ = exposure_.size();
41
42 QL_REQUIRE(m_ > 0, "no exposures given");
43 QL_REQUIRE(m_ == pd_.size(), "number of exposures ("
44 << m_
45 << ") must be equal to number of pds ("
46 << pd_.size() << ")");
47 QL_REQUIRE(m_ == sector_.size(),
48 "number of exposures ("
49 << m_
50 << ") must be equal to number of exposure sectors ("
51 << sector_.size() << ")");
52
55 "correlation matrix (" << n_ << "," << correlation_.columns()
56 << ") must be a square matrix");
57
59 "number of relative default variances ("
60 << relativeDefaultVariance_.size() << ")"
61 << " must be equal to number of sectors (" << n_ << ")");
62
63 exposureSum_ = 0.0;
64 el_ = 0.0;
65 el2_ = 0.0;
66 for (Size i = 0; i < m_; ++i) {
67 QL_REQUIRE(exposure_[i] >= 0.0, "exposure #"
68 << i << " is negative ("
69 << exposure_[i] << ")");
70 QL_REQUIRE(pd_[i] > 0.0, "pd #" << i << " is negative (" << pd_[i]
71 << ")");
72 QL_REQUIRE(sector_[i] < n_, "sector #" << i << " (" << sector_[i]
73 << ") is out of range 0..."
74 << (n_ - 1));
76 el_ += pd_[i] * exposure_[i];
77 el2_ += pd_[i] * exposure_[i]*exposure_[i];
78 }
79
80 QL_REQUIRE(unit_ > 0.0, "loss unit (" << unit_ << ") must be positive");
81
82 compute();
83 }
84
86
87 Size i = 0;
88 Real sum = loss_[0];
89 while(i < upperIndex_-1 && sum < p) {
90 ++i;
91 sum += loss_[i];
92 }
93
94 if(loss_[0] >= p)
95 return 0.0;
96
97 Real p1 = sum - loss_[i];
98 Real p2 = sum >= p ? sum : 1.0;
99 Real l1 = (i - 1) * unit_;
100 Real l2 = i * unit_;
101
102 return l1 + (p - p1) / (p2 - p1) * (l2 - l1);
103 }
104
106
107 std::vector<Real> sectorPdSum_, sectorSpecTerms_;
108
109 sectorPdSum_ = std::vector<Real>(n_, 0.0);
110 sectorExposure_ = std::vector<Real>(n_, 0.0);
111 sectorEl_ = std::vector<Real>(n_, 0.0);
112 sectorSpecTerms_ = std::vector<Real>(n_, 0.0);
113 sectorUl_ = std::vector<Real>(n_, 0.0);
114 marginalLoss_ = std::vector<Real>(m_, 0.0);
115
116 std::vector<Real> pdAdj(m_, 0.0);
117
118 // compute exposure bands
119
120 unsigned long maxNu_ = 0;
121 upperIndex_ = 0;
122
123 // map of nuC_ to expected loss
124 std::map<unsigned long, Real, std::less<> > epsNuC_;
125
126 std::map<unsigned long, Real, std::less<> >::iterator iter;
127
128 for (Size k = 0; k < m_; ++k) {
129 auto exUnit = (unsigned long)(std::floor(0.5 + exposure_[k] / unit_)); // round
130 if (exposure_[k] > 0 && exUnit == 0)
131 exUnit = 1; // but avoid zero exposure
132 if (exUnit > maxNu_)
133 maxNu_ = exUnit;
134 pdAdj[k] = exposure_[k] > 0.0
135 ? exposure_[k] * pd_[k] / (exUnit * unit_)
136 : Real(0.0); // adjusted pd
137 Real el = exUnit * pdAdj[k];
138 if (exUnit > 0) {
139 iter = epsNuC_.find(exUnit);
140 if (iter == epsNuC_.end()) {
141 epsNuC_.insert(std::pair<unsigned long, Real>(exUnit, el));
142 } else {
143 (*iter).second += el;
144 }
145 upperIndex_ += exUnit;
146 }
147 }
148
149 // compute per sector figures
150
151 Real pdSum_ = 0;
152 for (Size k = 0; k < m_; ++k) {
153 pdSum_ += pdAdj[k];
154 sectorPdSum_[sector_[k]] += pd_[k];
156 sectorEl_[sector_[k]] += exposure_[k] * pd_[k];
157 }
158
159 for (Size i = 0; i < n_; ++i) {
160
161 // precompute sector specific terms (formula 15 in [1])
162
163 sectorSpecTerms_[i] += relativeDefaultVariance_[i] * sectorEl_[i];
164 for (Size j = 0; j < n_; ++j) {
165 if (j != i) {
166 sectorSpecTerms_[i] +=
167 correlation_[i][j] *
168 std::sqrt(relativeDefaultVariance_[i] *
170 sectorEl_[j];
171 }
172 }
173 }
174
175 // compute synthetic standard deviation (formula 12 in [1])
176
177 ul_ = 0.0;
178 for (Size i = 0; i < n_; ++i) {
179 sectorUl_[i] =
181 ul_ += sectorUl_[i];
182 for (Size j = 0; j < n_; ++j) {
183 if (j != i) {
184 ul_ += correlation_[i][j] *
185 std::sqrt(relativeDefaultVariance_[i] *
187 sectorEl_[i] * sectorEl_[j];
188 }
189 }
190 }
191
192 Real matchUl_ = ul_; // formula 13 in [1], rhs
193 for (Size k = 0; k < m_; ++k) {
194 Real tmp = pd_[k] * exposure_[k] * exposure_[k];
195 sectorUl_[sector_[k]] += tmp;
196 ul_ += tmp;
197 }
198 ul_ = std::sqrt(ul_);
199 for (Size i = 0; i < n_; ++i)
200 sectorUl_[i] = std::sqrt(sectorUl_[i]);
201
202 // compute risk contributions (formula 15 in [1])
203
204 for (Size k = 0; k < m_; ++k) {
205 marginalLoss_[k] = pd_[k] * exposure_[k] / ul_ *
206 (sectorSpecTerms_[sector_[k]] + exposure_[k]);
207 }
208
209 // compute sigmaC_ and deduced figures
210
211 Real sigmaC_ = pdSum_ * sqrt(matchUl_ / (el_ * el_));
212 Real alphaC_ = pdSum_ * pdSum_ / (sigmaC_ * sigmaC_);
213 Real betaC_ = sigmaC_ * sigmaC_ / pdSum_;
214 Real pC_ = betaC_ / (1.0 + betaC_);
215
216 // compute loss distribution
217
218 loss_.clear();
219 loss_.push_back(std::pow(1.0 - pC_, alphaC_)); // A(0)
220
221 Real res;
222 for (unsigned long n = 0; n < upperIndex_ - 1; ++n) { // compute A(n+1)
223 // recursively
224 res = 0.0;
225 for (unsigned long j = 0;
226 j <= std::min<unsigned long>(maxNu_ - 1, n); ++j) {
227 iter = epsNuC_.find(j + 1);
228 if (iter != epsNuC_.end()) {
229 res += (*iter).second * loss_[n - j] * alphaC_;
230 if (j <= n - 1)
231 res += (*iter).second / ((Real)(j + 1)) *
232 ((Real)(n - j)) * loss_[n - j];
233 }
234 }
235 loss_.push_back(res * pC_ / (pdSum_ * ((Real)(n + 1))));
236 }
237 }
238
240}
const Real correlation_
std::vector< Real > loss_
const std::vector< Real > exposure_
std::vector< Real > sectorEl_
CreditRiskPlus(std::vector< Real > exposure, std::vector< Real > defaultProbability, std::vector< Size > sector, std::vector< Real > relativeDefaultVariance, Matrix correlation, Real unit)
std::vector< Real > sectorExposure_
const std::vector< Real > relativeDefaultVariance_
const std::vector< Real > pd_
const std::vector< Size > sector_
std::vector< Real > marginalLoss_
std::vector< Real > sectorUl_
Matrix used in linear algebra.
Definition: matrix.hpp:41
Size rows() const
Definition: matrix.hpp:504
Size columns() const
Definition: matrix.hpp:508
Extended CreditRisk+ Model.
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
QL_REAL Real
real number
Definition: types.hpp:50
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:37
STL namespace.
#define QL_DEPRECATED_DISABLE_WARNING
Definition: qldefines.hpp:216
#define QL_DEPRECATED_ENABLE_WARNING
Definition: qldefines.hpp:217
Real pd_