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/gaussianlhplossmodel.hpp>
22 
23 #ifndef QL_PATCH_SOLARIS
24 
25 
26 using std::sqrt;
27 
28 namespace QuantLib {
29 
30     CumulativeNormalDistribution const GaussianLHPLossModel::phi_ =
31         CumulativeNormalDistribution();
32 
GaussianLHPLossModel(const Handle<Quote> & correlQuote,const std::vector<Handle<RecoveryRateQuote>> & quotes)33     GaussianLHPLossModel::GaussianLHPLossModel(
34             const Handle<Quote>& correlQuote,
35             const std::vector<Handle<RecoveryRateQuote> >& quotes)
36         : LatentModel<GaussianCopulaPolicy>(sqrt(correlQuote->value()),
37             quotes.size(),
38             //g++ complains default value not seen as typename
39             GaussianCopulaPolicy::initTraits()),
40           sqrt1minuscorrel_(std::sqrt(1.-correlQuote->value())),
41           correl_(correlQuote),
42           rrQuotes_(quotes),
43           beta_(sqrt(correlQuote->value())),
44           biphi_(-sqrt(correlQuote->value()))
45         {
46             registerWith(correl_);
47             for(Size i=0; i<quotes.size(); i++)
48                 registerWith(quotes[i]);
49     }
50 
GaussianLHPLossModel(Real correlation,const std::vector<Real> & recoveries)51     GaussianLHPLossModel::GaussianLHPLossModel(
52             Real correlation,
53             const std::vector<Real>& recoveries)
54         : LatentModel<GaussianCopulaPolicy>(sqrt(correlation),
55             recoveries.size(),
56             //g++ complains default value not seen as typename
57             GaussianCopulaPolicy::initTraits()),
58           sqrt1minuscorrel_(std::sqrt(1.-correlation)),
59           correl_(Handle<Quote>(ext::make_shared<SimpleQuote>(correlation))),
60           beta_(sqrt(correlation)),
61           biphi_(-sqrt(correlation))
62         {
63             for(Size i=0; i<recoveries.size(); i++)
64                 rrQuotes_.push_back(Handle<RecoveryRateQuote>(
65                 ext::make_shared<RecoveryRateQuote>(recoveries[i])));
66         }
67 
GaussianLHPLossModel(const Handle<Quote> & correlQuote,const std::vector<Real> & recoveries)68         GaussianLHPLossModel::GaussianLHPLossModel(
69             const Handle<Quote>& correlQuote,
70             const std::vector<Real>& recoveries)
71         : LatentModel<GaussianCopulaPolicy>(sqrt(correlQuote->value()),
72             recoveries.size(),
73             //g++ complains default value not seen as typename
74             GaussianCopulaPolicy::initTraits()),
75           sqrt1minuscorrel_(std::sqrt(1.-correlQuote->value())),
76           correl_(correlQuote),
77           beta_(sqrt(correlQuote->value())),
78           biphi_(-sqrt(correlQuote->value()))
79         {
80             registerWith(correl_);
81             for(Size i=0; i<recoveries.size(); i++)
82                 rrQuotes_.push_back(Handle<RecoveryRateQuote>(
83                 ext::make_shared<RecoveryRateQuote>(recoveries[i])));
84         }
85 
86 
expectedTrancheLossImpl(Real remainingNot,Real prob,Real averageRR,Real attachLimit,Real detachLimit) const87         Real GaussianLHPLossModel::expectedTrancheLossImpl(
88             Real remainingNot, // << at the given date 'd'
89             Real prob, // << at the given date 'd'
90             Real averageRR, // << at the given date 'd'
91             // these are percentual values:
92             Real attachLimit, Real detachLimit) const
93         {
94 
95             if (attachLimit >= detachLimit) return 0.;// or is it an error?
96             // expected remaining notional:
97             if (remainingNot == 0.) return 0.;
98 
99             const Real one = 1.0 - 1.0e-12;  // FIXME DUE TO THE INV CUMUL AT 1
100             const Real k1 = std::min(one, attachLimit /(1.0 - averageRR)
101                 ) + QL_EPSILON;
102             const Real k2 = std::min(one, detachLimit /(1.0 - averageRR)
103                 ) + QL_EPSILON;
104 
105             if (prob > 0) {
106                 const Real ip = InverseCumulativeNormal::standard_value(prob);
107                 const Real invFlightK1 =
108                     (ip-sqrt1minuscorrel_ *
109                         InverseCumulativeNormal::standard_value(k1))/beta_;
110                 const Real invFlightK2 = (ip-sqrt1minuscorrel_*
111                     InverseCumulativeNormal::standard_value(k2))/beta_;
112 
113                 return remainingNot * (detachLimit * phi_(invFlightK2)
114                     - attachLimit * phi_(invFlightK1) + (1.-averageRR) *
115                     (biphi_(ip, -invFlightK2) - biphi_(ip, -invFlightK1)) );
116             }
117             else return 0.0;
118         }
119 
probOverLoss(const Date & d,Real remainingLossFraction) const120         Real GaussianLHPLossModel::probOverLoss(const Date& d,
121             Real remainingLossFraction) const {
122             // these test goes into basket<<<<<<<<<<<<<<<<<<<<<<<<<
123             QL_REQUIRE(remainingLossFraction >=0., "Incorrect loss fraction.");
124             QL_REQUIRE(remainingLossFraction <=1., "Incorrect loss fraction.");
125 
126             Real remainingAttachAmount = basket_->remainingAttachmentAmount();
127             Real remainingDetachAmount = basket_->remainingDetachmentAmount();
128             // live unerlying portfolio loss fraction (remaining portf fraction)
129 
130             const Real remainingBasktNot = basket_->remainingNotional(d);
131             const Real attach =
132                 std::min(remainingAttachAmount / remainingBasktNot, 1.);
133             const Real detach =
134                 std::min(remainingDetachAmount / remainingBasktNot, 1.);
135 
136             Real portfFract =
137                 attach + remainingLossFraction * (detach - attach);
138 
139             Real averageRR = averageRecovery(d);
140             Real maxAttLossFract = (1.-averageRR);
141             if(portfFract > maxAttLossFract) return 0.;
142 
143             // for non-equity losses add the probability jump at zero tranche
144             //   losses (since this method returns prob of losing more or
145             //   equal to)
146             if(portfFract <= QL_EPSILON) return 1.;
147 
148             Probability prob = averageProb(d);
149 
150             Real ip = InverseCumulativeNormal::standard_value(prob);
151             Real invFlightK = (ip-sqrt1minuscorrel_*
152                 InverseCumulativeNormal::standard_value(portfFract
153                     /(1.-averageRR)))/beta_;
154 
155             return  phi_(invFlightK);//probOver
156         }
157 
expectedShortfall(const Date & d,Probability perctl) const158         Real GaussianLHPLossModel::expectedShortfall(const Date& d,
159             Probability perctl) const
160         {
161             // loss as a fraction of the live portfolio
162             Real ptflLossPerc = percentilePortfolioLossFraction(d, perctl);
163             Real remainingAttachAmount = basket_->remainingAttachmentAmount();
164             Real remainingDetachAmount = basket_->remainingDetachmentAmount();
165 
166             const Real remainingNot = basket_->remainingNotional(d);
167             const Real attach =
168                 std::min(remainingAttachAmount / remainingNot, 1.);
169             const Real detach =
170                 std::min(remainingDetachAmount / remainingNot, 1.);
171 
172             if(ptflLossPerc >= detach-QL_EPSILON)
173                 return remainingNot * (detach-attach);//equivalent
174 
175             Real maxLossLevel = std::max(attach, ptflLossPerc);
176             Probability prob = averageProb(d);
177             Real averageRR = averageRecovery(d);
178 
179             Real valA = expectedTrancheLossImpl(remainingNot, prob,
180                 averageRR, maxLossLevel, detach);
181             Real valB = // probOverLoss(d, maxLossLevel);//in live tranche units
182             // from fraction of basket notional to fraction of tranche notional
183                 probOverLoss(d, std::min(std::max((maxLossLevel - attach)
184                 /(detach - attach), 0.), 1.));
185             return ( valA + (maxLossLevel - attach) * remainingNot * valB )
186                 / (1.-perctl);
187         }
188 
percentilePortfolioLossFraction(const Date & d,Real perctl) const189         Real GaussianLHPLossModel::percentilePortfolioLossFraction(
190             const Date& d, Real perctl) const
191         {
192             // this test goes into basket<<<<<<<<<<<<<<<<<<<<<<<<<
193             QL_REQUIRE(perctl >= 0. && perctl <=1.,
194                 "Percentile argument out of bounds.");
195 
196             if(perctl==0.) return 0.;// portfl == attach
197             if(perctl==1.) perctl = 1. - QL_EPSILON; // portfl == detach
198 
199             return (1.-averageRecovery(d)) *
200                 phi_( ( InverseCumulativeNormal::standard_value(averageProb(d))
201                     + beta_ * InverseCumulativeNormal::standard_value(perctl) )
202                         /sqrt1minuscorrel_);
203         }
204 
205 }
206 
207 #endif
208