| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2014 Jose Aparicio |
| 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 | |
| 20 | #ifndef quantlib_base_correl_lossmodel_hpp |
| 21 | #define quantlib_base_correl_lossmodel_hpp |
| 22 | |
| 23 | |
| 24 | #include <ql/quote.hpp> |
| 25 | #include <ql/quotes/simplequote.hpp> |
| 26 | |
| 27 | #include <ql/experimental/credit/basket.hpp> |
| 28 | #include <ql/experimental/credit/defaultlossmodel.hpp> |
| 29 | #include <ql/experimental/credit/basecorrelationstructure.hpp> |
| 30 | |
| 31 | // move these to the CPP (and the template spezs) |
| 32 | #include <ql/experimental/credit/binomiallossmodel.hpp> |
| 33 | #include <ql/experimental/credit/gaussianlhplossmodel.hpp> |
| 34 | #include <ql/experimental/credit/inhomogeneouspooldef.hpp> |
| 35 | #include <utility> |
| 36 | |
| 37 | namespace QuantLib { |
| 38 | |
| 39 | /*! Base Correlation loss model; interpolation is performed by portfolio |
| 40 | (live) amount percentage.\par |
| 41 | Though the literature on this model is inmense, see for a more than |
| 42 | introductory level (precrisis) chapters 19, 20 and 21 of <b>Modelling single |
| 43 | name and multi-name credit derivatives.</b> Dominic O'Kane, Wiley Finance, |
| 44 | 2008\par |
| 45 | For freely available documentation see:\par |
| 46 | Credit Correlation: A Guide; JP Morgan Credit Derivatives Strategy; |
| 47 | 12 March 2004 \par |
| 48 | Introducing Base Correlations; JP Morgan Credit Derivatives Strategy; |
| 49 | 22 March 2004 \par |
| 50 | A Relative Value Framework for Credit Correlation; JP Morgan Credit |
| 51 | Derivatives Strategy; 27 April 2004 \par |
| 52 | Valuing and Hedging Synthetic CDO Tranches Using Base Correlations; Bear |
| 53 | Stearns; May 17, 2004 \par |
| 54 | Correlation Primer; Nomura Fixed Income Research, August 6, 2004 \par |
| 55 | Base Correlation Explained; Lehman Brothers Fixed Income Quantitative |
| 56 | Credit Research; 15 November 2004 \par |
| 57 | 'Pricing CDOs with a smile' in Societe Generale Credit Research; |
| 58 | February 2005 \par |
| 59 | For bespoke base correlation see: \par |
| 60 | Base Correlation Mapping in Lehman Brothers' Quantitative Credit Research |
| 61 | Quarterly; Volume 2007-Q1 \par |
| 62 | You can explore typical postcrisis data by perusing some of the JPMorgan |
| 63 | Global Correlation Daily Analytics \par |
| 64 | Here the crisis model problems of ability to price stressed portfolios |
| 65 | or tranches over the maximum loss are the responsibility of the base models. |
| 66 | Users should select their models according to this; choosing the copula or |
| 67 | a random loss given default base model (or more exotic ones). \par |
| 68 | Notice this is different to a bespoke base correlation loss (bespoke here |
| 69 | refering to basket composition, not just attachment levels) ; where |
| 70 | loss interpolation is on the expected loss value to match the two baskets. |
| 71 | Therefore the correlation surface should refer to the same basket intended |
| 72 | to be priced. But this is left to the user and is not implemented in the |
| 73 | correlation surface (yet...) |
| 74 | |
| 75 | \todo Bespoke portfolios BC models are yet to be implemented. |
| 76 | |
| 77 | BaseModel_T must have a constructor with a single quote value |
| 78 | */ |
| 79 | /* Criticism: |
| 80 | This model is not as generic as it could be. In principle a default loss |
| 81 | model dependent on a single factor correlation parameter is the only |
| 82 | restriction on the base loss model(s) type. This class however is tied to a |
| 83 | LatentModel single factor. But there is no need for the |
| 84 | underlying model to be of a latent type. This link is due to the copula |
| 85 | initialization traits which have to be present for non trivial copula |
| 86 | policies initialization (e.g. Student-T base correl models) |
| 87 | |
| 88 | Maybe a possibility is to pass copiable instances of the model and relinking |
| 89 | to the correlation in two internal copies. |
| 90 | */ |
| 91 | template <class BaseModel_T, class Corr2DInt_T> |
| 92 | class BaseCorrelationLossModel : public DefaultLossModel, |
| 93 | public virtual Observer { |
| 94 | private: |
| 95 | typedef typename BaseModel_T::copulaType::initTraits initTraits; |
| 96 | public: |
| 97 | BaseCorrelationLossModel(const Handle<BaseCorrelationTermStructure<Corr2DInt_T> >& correlTS, |
| 98 | std::vector<Real> recoveries, |
| 99 | const initTraits& traits = initTraits()) |
| 100 | : localCorrelationAttach_(ext::make_shared<SimpleQuote>(args: 0.)), |
| 101 | localCorrelationDetach_(ext::make_shared<SimpleQuote>(args: 0.)), |
| 102 | recoveries_(std::move(recoveries)), correlTS_(correlTS), copulaTraits_(traits) { |
| 103 | registerWith(h: correlTS); |
| 104 | registerWith(h: Settings::instance().evaluationDate()); |
| 105 | } |
| 106 | |
| 107 | private: |
| 108 | // react to base correl surface notifications (quotes or reference date) |
| 109 | void update() override { |
| 110 | setupModels(); |
| 111 | // tell basket to notify instruments, etc, we are invalid |
| 112 | if (!basket_.empty()) |
| 113 | basket_->notifyObservers(); |
| 114 | } |
| 115 | |
| 116 | /* Update model caches after basket assignement. */ |
| 117 | void resetModel() override { |
| 118 | remainingNotional_ = basket_->remainingNotional(); |
| 119 | attachRatio_ = basket_->remainingAttachmentAmount() / remainingNotional_; |
| 120 | detachRatio_ = basket_->remainingDetachmentAmount() / remainingNotional_; |
| 121 | |
| 122 | basketAttach_ = ext::make_shared<Basket>(args: basket_->refDate(), args: basket_->remainingNames(), |
| 123 | args: basket_->remainingNotionals(), args: basket_->pool(), |
| 124 | args: 0.0, args&: attachRatio_, args: basket_->claim()); |
| 125 | basketDetach_ = ext::make_shared<Basket>(args: basket_->refDate(), args: basket_->remainingNames(), |
| 126 | args: basket_->remainingNotionals(), args: basket_->pool(), |
| 127 | args: 0.0, args&: detachRatio_, args: basket_->claim()); |
| 128 | setupModels(); |
| 129 | } |
| 130 | /* Most of the statistics are not implemented, not impossible but |
| 131 | the model is intended for pricing rather than ptfolio risk management. |
| 132 | */ |
| 133 | Real expectedTrancheLoss(const Date& d) const override; |
| 134 | |
| 135 | protected: |
| 136 | /*! Sets up attach/detach models. Gets called on basket update. |
| 137 | To be specialized on the spacific model type. |
| 138 | */ |
| 139 | void setupModels() const; |
| 140 | private: |
| 141 | mutable Real attachRatio_, detachRatio_; |
| 142 | mutable Real remainingNotional_; |
| 143 | |
| 144 | //! Correlation buffer to pick up values from the surface and |
| 145 | // trigger calculation. |
| 146 | ext::shared_ptr<SimpleQuote> localCorrelationAttach_, |
| 147 | localCorrelationDetach_; |
| 148 | mutable ext::shared_ptr<Basket> basketAttach_, |
| 149 | basketDetach_; |
| 150 | // just cached for the update method |
| 151 | mutable std::vector<Real> recoveries_; |
| 152 | Handle<BaseCorrelationTermStructure<Corr2DInt_T> > correlTS_; |
| 153 | // Initialization parameters for models copula |
| 154 | mutable typename BaseModel_T::copulaType::initTraits copulaTraits_; |
| 155 | // Models of equity baskets. |
| 156 | mutable ext::shared_ptr<BaseModel_T> scalarCorrelModelAttach_; |
| 157 | mutable ext::shared_ptr<BaseModel_T> scalarCorrelModelDetach_; |
| 158 | }; |
| 159 | |
| 160 | |
| 161 | // Remember ETL returns the EL on the live part of the basket. |
| 162 | template<class LM, class I> |
| 163 | Real BaseCorrelationLossModel<LM, I>::expectedTrancheLoss( |
| 164 | const Date& d) const |
| 165 | { |
| 166 | Real correlK1 = correlTS_->correlation(d, attachRatio_); |
| 167 | Real correlK2 = correlTS_->correlation(d, detachRatio_); |
| 168 | |
| 169 | /* reset correl and call base models which have the different baskets |
| 170 | associated.*/ |
| 171 | localCorrelationAttach_->setValue(correlK1); |
| 172 | Real expLossK1 = |
| 173 | basketAttach_->expectedTrancheLoss(d); |
| 174 | localCorrelationDetach_->setValue(correlK2); |
| 175 | Real expLossK2 = |
| 176 | basketDetach_->expectedTrancheLoss(d); |
| 177 | return expLossK2 - expLossK1; |
| 178 | } |
| 179 | |
| 180 | |
| 181 | // ---------------------------------------------------------------------- |
| 182 | |
| 183 | |
| 184 | /* Concrete specializations submodels construction. With the dummy template |
| 185 | parameter trick partial specializations leaving the interpolation open |
| 186 | would be possible. |
| 187 | */ |
| 188 | |
| 189 | #ifndef QL_PATCH_SOLARIS |
| 190 | |
| 191 | template<> |
| 192 | inline void BaseCorrelationLossModel<GaussianLHPLossModel, |
| 193 | BilinearInterpolation>::setupModels() const |
| 194 | { |
| 195 | // on this assignment any previous registration with the attach and |
| 196 | // detach baskets should be removed |
| 197 | scalarCorrelModelAttach_ = ext::make_shared<GaussianLHPLossModel>( |
| 198 | args: Handle<Quote>(localCorrelationAttach_), args&: recoveries_); |
| 199 | scalarCorrelModelDetach_ = ext::make_shared<GaussianLHPLossModel>( |
| 200 | args: Handle<Quote>(localCorrelationDetach_), args&: recoveries_); |
| 201 | |
| 202 | basketAttach_->setLossModel(scalarCorrelModelAttach_); |
| 203 | basketDetach_->setLossModel(scalarCorrelModelDetach_); |
| 204 | } |
| 205 | |
| 206 | template<> |
| 207 | inline void BaseCorrelationLossModel<GaussianBinomialLossModel, |
| 208 | BilinearInterpolation>::setupModels() const |
| 209 | { |
| 210 | ext::shared_ptr<GaussianConstantLossLM> lmA = |
| 211 | ext::make_shared<GaussianConstantLossLM>( |
| 212 | args: Handle<Quote>(localCorrelationAttach_), args&: recoveries_, |
| 213 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 214 | args: recoveries_.size(), args&: copulaTraits_); |
| 215 | ext::shared_ptr<GaussianConstantLossLM> lmD = |
| 216 | ext::make_shared<GaussianConstantLossLM>( |
| 217 | args: Handle<Quote>(localCorrelationDetach_), args&: recoveries_, |
| 218 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 219 | args: recoveries_.size(), args&: copulaTraits_); |
| 220 | scalarCorrelModelAttach_ = |
| 221 | ext::make_shared<GaussianBinomialLossModel>(args&: lmA); |
| 222 | scalarCorrelModelDetach_ = |
| 223 | ext::make_shared<GaussianBinomialLossModel>(args&: lmD); |
| 224 | |
| 225 | basketAttach_->setLossModel(scalarCorrelModelAttach_); |
| 226 | basketDetach_->setLossModel(scalarCorrelModelDetach_); |
| 227 | |
| 228 | } |
| 229 | |
| 230 | template<> |
| 231 | inline void BaseCorrelationLossModel<TBinomialLossModel, |
| 232 | BilinearInterpolation>::setupModels() const |
| 233 | { |
| 234 | ext::shared_ptr<TConstantLossLM> lmA = |
| 235 | ext::make_shared<TConstantLossLM>( |
| 236 | args: Handle<Quote>(localCorrelationAttach_), args&: recoveries_, |
| 237 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 238 | args: recoveries_.size(), args&: copulaTraits_); |
| 239 | ext::shared_ptr<TConstantLossLM> lmD = |
| 240 | ext::make_shared<TConstantLossLM>( |
| 241 | args: Handle<Quote>(localCorrelationDetach_), args&: recoveries_, |
| 242 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 243 | args: recoveries_.size(), args&: copulaTraits_); |
| 244 | |
| 245 | scalarCorrelModelAttach_ = |
| 246 | ext::make_shared<TBinomialLossModel>(args&: lmA); |
| 247 | scalarCorrelModelDetach_ = |
| 248 | ext::make_shared<TBinomialLossModel>(args&: lmD); |
| 249 | |
| 250 | basketAttach_->setLossModel(scalarCorrelModelAttach_); |
| 251 | basketDetach_->setLossModel(scalarCorrelModelDetach_); |
| 252 | } |
| 253 | |
| 254 | /* \todo Fix this model, is failing for equity tranches at least, the |
| 255 | base model works all right, its the link here. |
| 256 | */ |
| 257 | template<> |
| 258 | inline void BaseCorrelationLossModel<IHGaussPoolLossModel, |
| 259 | BilinearInterpolation>::setupModels() const |
| 260 | { |
| 261 | ext::shared_ptr<GaussianConstantLossLM> lmA = |
| 262 | ext::make_shared<GaussianConstantLossLM>( |
| 263 | args: Handle<Quote>(localCorrelationAttach_), args&: recoveries_, |
| 264 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 265 | args: recoveries_.size(), args&: copulaTraits_); |
| 266 | ext::shared_ptr<GaussianConstantLossLM> lmD = |
| 267 | ext::make_shared<GaussianConstantLossLM>( |
| 268 | args: Handle<Quote>(localCorrelationDetach_), args&: recoveries_, |
| 269 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 270 | args: recoveries_.size(), args&: copulaTraits_); |
| 271 | |
| 272 | // \todo Allow the sending specific model params, as the number of |
| 273 | // buckets here. |
| 274 | scalarCorrelModelAttach_ = |
| 275 | ext::make_shared<IHGaussPoolLossModel>(args&: lmA, args: 500); |
| 276 | scalarCorrelModelDetach_ = |
| 277 | ext::make_shared<IHGaussPoolLossModel>(args&: lmD, args: 500); |
| 278 | |
| 279 | basketAttach_->setLossModel(scalarCorrelModelAttach_); |
| 280 | basketDetach_->setLossModel(scalarCorrelModelDetach_); |
| 281 | } |
| 282 | |
| 283 | #endif |
| 284 | |
| 285 | |
| 286 | // Vanilla BC model |
| 287 | #ifndef QL_PATCH_SOLARIS |
| 288 | typedef BaseCorrelationLossModel<GaussianLHPLossModel, |
| 289 | BilinearInterpolation> GaussianLHPFlatBCLM; |
| 290 | #endif |
| 291 | |
| 292 | } |
| 293 | |
| 294 | #endif |
| 295 | |