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