| 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 | #include <ql/qldefines.hpp> |
| 21 | #if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC) |
| 22 | # include <ql/auto_link.hpp> |
| 23 | #endif |
| 24 | #include <ql/experimental/credit/randomdefaultlatentmodel.hpp> |
| 25 | #include <ql/termstructures/credit/flathazardrate.hpp> |
| 26 | #include <ql/currencies/europe.hpp> |
| 27 | #include <ql/time/calendars/target.hpp> |
| 28 | #include <ql/time/daycounters/actual365fixed.hpp> |
| 29 | #include <string> |
| 30 | #include <iostream> |
| 31 | #include <iomanip> |
| 32 | |
| 33 | using namespace std; |
| 34 | using namespace QuantLib; |
| 35 | |
| 36 | /* This sample code shows basic usage of a Latent variable model. |
| 37 | The data and correlation problem presented is the same as in: |
| 38 | 'Modelling Dependent Defaults: Asset Correlations Are Not Enough!' |
| 39 | Frey R., A. J. McNeil and M. A. Nyfeler RiskLab publications March 2001 |
| 40 | */ |
| 41 | int main(int, char* []) { |
| 42 | |
| 43 | try { |
| 44 | |
| 45 | std::cout << std::endl; |
| 46 | |
| 47 | Calendar calendar = TARGET(); |
| 48 | Date todaysDate(19, March, 2014); |
| 49 | // must be a business day |
| 50 | todaysDate = calendar.adjust(todaysDate); |
| 51 | |
| 52 | Settings::instance().evaluationDate() = todaysDate; |
| 53 | |
| 54 | |
| 55 | /* -------------------------------------------------------------- |
| 56 | SET UP BASKET PORTFOLIO |
| 57 | -------------------------------------------------------------- */ |
| 58 | // build curves and issuers into a basket of three names |
| 59 | std::vector<Real> hazardRates(3, -std::log(x: 1.-0.01)); |
| 60 | std::vector<std::string> names; |
| 61 | for(Size i=0; i<hazardRates.size(); i++) |
| 62 | names.push_back(x: std::string("Acme" ) + std::to_string(val: i)); |
| 63 | std::vector<Handle<DefaultProbabilityTermStructure>> defTS; |
| 64 | defTS.reserve(n: hazardRates.size()); |
| 65 | for (Real& hazardRate : hazardRates) |
| 66 | defTS.emplace_back( |
| 67 | args: ext::make_shared<FlatHazardRate>(args: 0, args: TARGET(), args&: hazardRate, args: Actual365Fixed())); |
| 68 | std::vector<Issuer> issuers; |
| 69 | for(Size i=0; i<hazardRates.size(); i++) { |
| 70 | std::vector<QuantLib::Issuer::key_curve_pair> curves(1, |
| 71 | std::make_pair(x: NorthAmericaCorpDefaultKey( |
| 72 | EURCurrency(), QuantLib::SeniorSec, |
| 73 | Period(), 1. // amount threshold |
| 74 | ), y&: defTS[i])); |
| 75 | issuers.emplace_back(args&: curves); |
| 76 | } |
| 77 | |
| 78 | auto thePool = ext::make_shared<Pool>(); |
| 79 | for(Size i=0; i<hazardRates.size(); i++) |
| 80 | thePool->add(name: names[i], issuer: issuers[i], contractTrigger: NorthAmericaCorpDefaultKey( |
| 81 | EURCurrency(), QuantLib::SeniorSec, Period(), 1.)); |
| 82 | |
| 83 | std::vector<DefaultProbKey> defaultKeys(hazardRates.size(), |
| 84 | NorthAmericaCorpDefaultKey(EURCurrency(), SeniorSec, Period(), 1.)); |
| 85 | // Recoveries are irrelevant in this example but must be given as the |
| 86 | // lib stands. |
| 87 | std::vector<ext::shared_ptr<RecoveryRateModel>> rrModels( |
| 88 | hazardRates.size(), ext::make_shared<ConstantRecoveryModel>( |
| 89 | args: ConstantRecoveryModel(0.5, SeniorSec))); |
| 90 | auto theBskt = ext::make_shared<Basket>( |
| 91 | args&: todaysDate, args&: names, args: std::vector<Real>(hazardRates.size(), 100.), |
| 92 | args&: thePool); |
| 93 | /* -------------------------------------------------------------- |
| 94 | SET UP JOINT DEFAULT EVENT LATENT MODELS |
| 95 | -------------------------------------------------------------- */ |
| 96 | // Latent model factors, corresponds to the first entry in Table1 of the |
| 97 | // publication mentioned. It is a single factor model |
| 98 | std::vector<std::vector<Real>> fctrsWeights(hazardRates.size(), |
| 99 | std::vector<Real>(1, std::sqrt(x: 0.1))); |
| 100 | // --- Default Latent models ------------------------------------- |
| 101 | #ifndef QL_PATCH_SOLARIS |
| 102 | // Gaussian integrable joint default model: |
| 103 | auto lmG = ext::make_shared<GaussianDefProbLM>(args&: fctrsWeights, |
| 104 | args: LatentModelIntegrationType::GaussianQuadrature, |
| 105 | args: GaussianCopulaPolicy::initTraits() // otherwise gcc screams |
| 106 | ); |
| 107 | #endif |
| 108 | // Define StudentT copula |
| 109 | // this is as far as we can be from the Gaussian, 2 T_3 factors: |
| 110 | std::vector<Integer> ordersT(2, 3); |
| 111 | TCopulaPolicy::initTraits iniT; |
| 112 | iniT.tOrders = ordersT; |
| 113 | // StudentT integrable joint default model: |
| 114 | auto lmT = ext::make_shared<TDefProbLM>(args&: fctrsWeights, |
| 115 | // LatentModelIntegrationType::GaussianQuadrature, |
| 116 | args: LatentModelIntegrationType::Trapezoid, args&: iniT); |
| 117 | |
| 118 | // --- Default Loss models ---------------------------------------- |
| 119 | // Gaussian random joint default model: |
| 120 | Size numSimulations = 100000; |
| 121 | // Size numCoresUsed = 4; |
| 122 | #ifndef QL_PATCH_SOLARIS |
| 123 | // Sobol, many cores |
| 124 | auto rdlmG = ext::make_shared<RandomDefaultLM<GaussianCopulaPolicy>>(args&: lmG, |
| 125 | args: std::vector<Real>(), args&: numSimulations, args: 1.e-6, args: 2863311530UL); |
| 126 | #endif |
| 127 | // StudentT random joint default model: |
| 128 | auto rdlmT = ext::make_shared<RandomDefaultLM<TCopulaPolicy>>(args&: lmT, |
| 129 | args: std::vector<Real>(), args&: numSimulations, args: 1.e-6, args: 2863311530UL); |
| 130 | |
| 131 | /* -------------------------------------------------------------- |
| 132 | DUMP SOME RESULTS |
| 133 | -------------------------------------------------------------- */ |
| 134 | /* Default correlations in a T copula should be below those of the |
| 135 | gaussian for the same factors. |
| 136 | The calculations on the MC show dispersion on both copulas (thats |
| 137 | ok) and too large values with very large dispersions on the T case. |
| 138 | Computations are ok, within the dispersion, for the gaussian; compare |
| 139 | with the direct integration in both cases. |
| 140 | However the T does converge to the gaussian value for large value of |
| 141 | the parameters. |
| 142 | */ |
| 143 | Date calcDate(TARGET().advance(date: Settings::instance().evaluationDate(), |
| 144 | period: Period(120, Months))); |
| 145 | std::vector<Probability> probEventsTLatent, probEventsTRandLoss; |
| 146 | #ifndef QL_PATCH_SOLARIS |
| 147 | std::vector<Probability> probEventsGLatent, probEventsGRandLoss; |
| 148 | #endif |
| 149 | // |
| 150 | lmT->resetBasket(basket: theBskt); |
| 151 | for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) { |
| 152 | probEventsTLatent.push_back(x: lmT->probAtLeastNEvents(n: numEvts, |
| 153 | date: calcDate)); |
| 154 | } |
| 155 | // |
| 156 | theBskt->setLossModel(rdlmT); |
| 157 | for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) { |
| 158 | probEventsTRandLoss.push_back(x: theBskt->probAtLeastNEvents(n: numEvts, |
| 159 | d: calcDate)); |
| 160 | } |
| 161 | // |
| 162 | #ifndef QL_PATCH_SOLARIS |
| 163 | lmG->resetBasket(basket: theBskt); |
| 164 | for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) { |
| 165 | probEventsGLatent.push_back(x: lmG->probAtLeastNEvents(n: numEvts, |
| 166 | date: calcDate)); |
| 167 | } |
| 168 | // |
| 169 | theBskt->setLossModel(rdlmG); |
| 170 | for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) { |
| 171 | probEventsGRandLoss.push_back(x: theBskt->probAtLeastNEvents(n: numEvts, |
| 172 | d: calcDate)); |
| 173 | } |
| 174 | #endif |
| 175 | |
| 176 | Date correlDate = TARGET().advance( |
| 177 | date: Settings::instance().evaluationDate(), period: Period(12, Months)); |
| 178 | std::vector<std::vector<Real>> correlsGlm, correlsTlm, correlsGrand, |
| 179 | correlsTrand; |
| 180 | // |
| 181 | lmT->resetBasket(basket: theBskt); |
| 182 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 183 | std::vector<Real> tmp; |
| 184 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 185 | tmp.push_back(x: lmT->defaultCorrelation(d: correlDate, |
| 186 | iNamei: iName1, iNamej: iName2)); |
| 187 | correlsTlm.push_back(x: tmp); |
| 188 | } |
| 189 | // |
| 190 | theBskt->setLossModel(rdlmT); |
| 191 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 192 | std::vector<Real> tmp; |
| 193 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 194 | tmp.push_back(x: theBskt->defaultCorrelation(d: correlDate, |
| 195 | iName: iName1, jName: iName2)); |
| 196 | correlsTrand.push_back(x: tmp); |
| 197 | } |
| 198 | #ifndef QL_PATCH_SOLARIS |
| 199 | // |
| 200 | lmG->resetBasket(basket: theBskt); |
| 201 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 202 | std::vector<Real> tmp; |
| 203 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 204 | tmp.push_back(x: lmG->defaultCorrelation(d: correlDate, |
| 205 | iNamei: iName1, iNamej: iName2)); |
| 206 | correlsGlm.push_back(x: tmp); |
| 207 | } |
| 208 | // |
| 209 | theBskt->setLossModel(rdlmG); |
| 210 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 211 | std::vector<Real> tmp; |
| 212 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 213 | tmp.push_back(x: theBskt->defaultCorrelation(d: correlDate, |
| 214 | iName: iName1, jName: iName2)); |
| 215 | correlsGrand.push_back(x: tmp); |
| 216 | } |
| 217 | #endif |
| 218 | |
| 219 | |
| 220 | std::cout << |
| 221 | " Gaussian versus T prob of extreme event (random and integrable)-" |
| 222 | << std::endl; |
| 223 | for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) { |
| 224 | std::cout << "-Prob of " << numEvts << " events... " << |
| 225 | #ifndef QL_PATCH_SOLARIS |
| 226 | probEventsGLatent[numEvts] << " ** " << |
| 227 | #else |
| 228 | "n/a" << " ** " << |
| 229 | #endif |
| 230 | probEventsTLatent[numEvts] << " ** " << |
| 231 | #ifndef QL_PATCH_SOLARIS |
| 232 | probEventsGRandLoss[numEvts]<< " ** " << |
| 233 | #else |
| 234 | "n/a" << " ** " << |
| 235 | #endif |
| 236 | probEventsTRandLoss[numEvts] |
| 237 | << std::endl; |
| 238 | } |
| 239 | |
| 240 | cout << endl; |
| 241 | cout << "-- Default correlations G,T,GRand,TRand--" << endl; |
| 242 | cout << "-----------------------------------------" << endl; |
| 243 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 244 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 245 | cout << |
| 246 | #ifndef QL_PATCH_SOLARIS |
| 247 | correlsGlm[iName1][iName2] << " , " ; |
| 248 | #else |
| 249 | "n/a" << " , " ; |
| 250 | #endif |
| 251 | cout << endl; |
| 252 | } |
| 253 | cout << endl; |
| 254 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 255 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 256 | cout << |
| 257 | correlsTlm[iName1][iName2] << " , " ; |
| 258 | ; |
| 259 | cout << endl; |
| 260 | } |
| 261 | cout << endl; |
| 262 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 263 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 264 | cout << |
| 265 | #ifndef QL_PATCH_SOLARIS |
| 266 | correlsGrand[iName1][iName2] << " , " ; |
| 267 | #else |
| 268 | "n/a" << " , " ; |
| 269 | #endif |
| 270 | cout << endl; |
| 271 | } |
| 272 | cout << endl; |
| 273 | for(Size iName1=0; iName1 <theBskt->size(); iName1++) { |
| 274 | for(Size iName2=0; iName2 <theBskt->size(); iName2++) |
| 275 | cout << |
| 276 | correlsTrand[iName1][iName2] << " , " ; |
| 277 | ; |
| 278 | cout << endl; |
| 279 | } |
| 280 | |
| 281 | return 0; |
| 282 | } catch (exception& e) { |
| 283 | cerr << e.what() << endl; |
| 284 | return 1; |
| 285 | } catch (...) { |
| 286 | cerr << "unknown error" << endl; |
| 287 | return 1; |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | |