Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
gaussianlhplossmodel.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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#ifndef quantext_gaussian_lhp_lossmodel_hpp
20#define quantext_gaussian_lhp_lossmodel_hpp
21
22#include <ql/qldefines.hpp>
23
24#ifndef QL_PATCH_SOLARIS
25
26#include <ql/experimental/credit/recoveryratequote.hpp>
27#include <ql/math/distributions/bivariatenormaldistribution.hpp>
28#include <ql/quotes/simplequote.hpp>
29//#include <ql/experimental/credit/defaultlossmodel.hpp>
31//#include <ql/experimental/credit/basket.hpp>
32#include <ql/functional.hpp>
33#include <numeric>
34#include <ql/experimental/math/latentmodel.hpp>
35#include <qle/models/basket.hpp>
36
37/* Intended to replace GaussianLHPCDOEngine in
38 ql/experimental/credit/syntheticcdoengines.hpp
39 Moved from an engine to a loss model, CDO engines might refer to it.
40*/
41
42namespace QuantExt {
43using namespace QuantLib;
44
45/*!
46 Portfolio loss model with analytical expected tranche loss for a large
47 homogeneous pool with Gaussian one-factor copula. See for example
48 "The Normal Inverse Gaussian Distribution for Synthetic CDO pricing.",
49 Anna Kalemanova, Bernd Schmid, Ralf Werner,
50 Journal of Derivatives, Vol. 14, No. 3, (Spring 2007), pp. 80-93.
51 http://www.defaultrisk.com/pp_crdrv_91.htm
52
53 It can be used to price a credit derivative or to provide risk metrics of
54 a portfolio.
55
56 \todo It should be checking that basket exposures are deterministic (fixed
57 or programmed amortizing) otherwise the model is not fit for the basket.
58
59 \todo Bugging on tranched baskets with upper limit over maximum
60 attainable loss?
61 */
62class GaussianLHPLossModel : public DefaultLossModel, public LatentModel<GaussianCopulaPolicy> {
63public:
64 typedef GaussianCopulaPolicy copulaType;
65
66 GaussianLHPLossModel(const Handle<Quote>& correlQuote,
67 const std::vector<Handle<QuantLib::RecoveryRateQuote>>& quotes);
68
69 GaussianLHPLossModel(Real correlation, const std::vector<Real>& recoveries);
70
71 GaussianLHPLossModel(const Handle<Quote>& correlQuote, const std::vector<Real>& recoveries);
72
73 void update() override {
74 sqrt1minuscorrel_ = std::sqrt(1. - correl_->value());
75 beta_ = std::sqrt(correl_->value());
76 biphi_ = BivariateCumulativeNormalDistribution(-beta_);
77 // tell basket to notify instruments, etc, we are invalid
78 if (!basket_.empty())
79 basket_->notifyObservers();
80 }
81
82private:
83 void resetModel() override {}
84 /*! @param attachLimit as a fraction of the underlying live portfolio
85 notional
86 */
87 Real expectedTrancheLossImpl(Real remainingNot, // << at the given date 'd'
88 Real prob, // << at the given date 'd'
89 Real averageRR, // << at the given date 'd'
90 Real attachLimit, Real detachLimit) const;
91
92public:
93 // RL: additional flag
94 Real expectedTrancheLoss(const Date& d, Real recoveryRate = Null<Real>()) const override {
95 // can calls to Basket::remainingNotional(d) be cached?<<<<<<<<<<<<<
96 const Real remainingfullNot = basket_->remainingNotional(d);
97 Real averageRR = recoveryRate != Null<Real>() ? recoveryRate : averageRecovery(d);
98 Probability prob = averageProb(d);
99 Real remainingAttachAmount = basket_->remainingAttachmentAmount();
100 Real remainingDetachAmount = basket_->remainingDetachmentAmount();
101
102 // const Real attach = std::min(remainingAttachAmount
103 // / remainingfullNot, 1.);
104 // const Real detach = std::min(remainingDetachAmount
105 // / remainingfullNot, 1.);
106 const Real attach = remainingAttachAmount / remainingfullNot;
107 const Real detach = remainingDetachAmount / remainingfullNot;
108
109 return expectedTrancheLossImpl(remainingfullNot, prob, averageRR, attach, detach);
110 }
111
112 /*! The passed remainingLossFraction is in live tranche units,
113 not portfolio as a fraction of the remaining(live) tranche
114 (i.e. a_remaining=0% and det_remaining=100%)
115 */
116 Real probOverLoss(const Date& d, Real remainingLossFraction) const override;
117
118 //! Returns the ESF as an absolute amount (rather than a fraction)
119 /* The way it is implemented here is a transformation from ETL to ESF
120 is a generic algorithm, not specific to this model so it should be moved
121 to the Basket/DefaultLossModel class.
122 TO DO: Implement the inverse transformation
123 */
124 Real expectedShortfall(const Date& d, Probability perctl) const override;
125
126protected:
127 // This is wrong, it is not accounting for the current defaults ....
128 // returns the loss value in actual loss units, returns the loss value
129 // for the underlying portfolio, untranched
130 Real percentilePortfolioLossFraction(const Date& d, Real perctl) const;
131 Real expectedRecovery(const Date& d, Size iName, const DefaultProbKey& ik) const override {
132 return rrQuotes_[iName].currentLink()->value();
133 }
134
135public:
136 // same as percentilePortfolio but tranched
137 Real percentile(const Date& d, Real perctl) const override {
138 const Real remainingNot = basket_->remainingNotional(d);
139 Real remainingAttachAmount = basket_->remainingAttachmentAmount();
140 Real remainingDetachAmount = basket_->remainingDetachmentAmount();
141 const Real attach = std::min(remainingAttachAmount / remainingNot, 1.);
142 const Real detach = std::min(remainingDetachAmount / remainingNot, 1.);
143 return remainingNot *
144 std::min(std::max(percentilePortfolioLossFraction(d, perctl) - attach, 0.), detach - attach);
145 }
146
147 Probability averageProb(const Date& d) const { // not an overload of Deflossmodel ???<<<<<???
148 // weighted average by programmed exposure.
149 const std::vector<Probability> probs = basket_->remainingProbabilities(d); // use remaining basket
150 const std::vector<Real> remainingNots = basket_->remainingNotionals(d);
151 return std::inner_product(probs.begin(), probs.end(), remainingNots.begin(), 0.) /
152 basket_->remainingNotional(d);
153 }
154
155 /* One could define the average recovery without the probability
156 factor, weighting only by notional instead, but that way the expected
157 loss of the average/aggregated and the original portfolio would not
158 coincide. This introduces however a time dependence in the recovery
159 value.
160 Weighting by notional implies time dependent weighting since the basket
161 might amortize.
162 */
163 Real averageRecovery(const Date& d) const // no explicit time dependence in this model
164 {
165 const std::vector<Probability> probs = basket_->remainingProbabilities(d);
166 std::vector<Real> recoveries;
167 for (Size i = 0; i < basket_->remainingSize(); i++)
168 recoveries.push_back(rrQuotes_[i]->value());
169 std::vector<Real> notionals = basket_->remainingNotionals(d);
170 Real denominator = std::inner_product(notionals.begin(), notionals.end(), probs.begin(), 0.);
171 if (denominator == 0.)
172 return 0.;
173
174 std::transform(notionals.begin(), notionals.end(), probs.begin(), notionals.begin(), std::multiplies<Real>());
175
176 return std::inner_product(recoveries.begin(), recoveries.end(), notionals.begin(), 0.) / denominator;
177 }
178
179private:
180 // cached
181 mutable Real sqrt1minuscorrel_;
182
183 Handle<Quote> correl_;
184 std::vector<Handle<QuantLib::RecoveryRateQuote>> rrQuotes_;
185 // calculation buffers
186
187 /* The problem with defining a fixed average recovery on a portfolio
188 with uneven exposures is that it does not preserve portfolio
189 moments like the expected loss. To achieve it one should define the
190 averarage recovery with a time dependence:
191 $\hat{R}(t) = \frac{\sum_i R_i N_i P_i(t)}{\sum_i N_i P_i(t)}$
192 But the date dependence increases significantly the calculations cost.
193 Notice that this problem dissapears if the recoveries are all equal.
194 */
195
196 Real beta_;
197 BivariateCumulativeNormalDistribution biphi_;
198 static CumulativeNormalDistribution const phi_;
199};
200
201} // namespace QuantExt
202
203#endif
204
205#endif
basket of issuers and related notionals
virtual QuantLib::Real correlation() const
RelinkableHandle< QuantExt::Basket > basket_
Probability averageProb(const Date &d) const
Real percentilePortfolioLossFraction(const Date &d, Real perctl) const
Real percentile(const Date &d, Real perctl) const override
Value at Risk given a default loss percentile.
Real expectedTrancheLoss(const Date &d, Real recoveryRate=Null< Real >()) const override
std::vector< Handle< QuantLib::RecoveryRateQuote > > rrQuotes_
Real expectedTrancheLossImpl(Real remainingNot, Real prob, Real averageRR, Real attachLimit, Real detachLimit) const
BivariateCumulativeNormalDistribution biphi_
Real averageRecovery(const Date &d) const
Real probOverLoss(const Date &d, Real remainingLossFraction) const override
void resetModel() override
Concrete models do now any updates/inits they need on basket reset.
Real expectedShortfall(const Date &d, Probability perctl) const override
Returns the ESF as an absolute amount (rather than a fraction)
Real expectedRecovery(const Date &d, Size iName, const DefaultProbKey &ik) const override
static CumulativeNormalDistribution const phi_
GaussianLHPLossModel(const Handle< Quote > &correlQuote, const std::vector< Handle< QuantLib::RecoveryRateQuote > > &quotes)