[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2008 Roland Lichters
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 "cdo.hpp"
21#include "utilities.hpp"
22#include <ql/experimental/credit/cdo.hpp>
23#include <ql/experimental/credit/pool.hpp>
24#include <ql/experimental/credit/integralcdoengine.hpp>
25#include <ql/experimental/credit/midpointcdoengine.hpp>
26#include <ql/experimental/credit/randomdefaultlatentmodel.hpp>
27#include <ql/experimental/credit/inhomogeneouspooldef.hpp>
28#include <ql/experimental/credit/homogeneouspooldef.hpp>
29#include <ql/experimental/credit/gaussianlhplossmodel.hpp>
30#include <ql/termstructures/yield/flatforward.hpp>
31#include <ql/termstructures/credit/flathazardrate.hpp>
32#include <ql/time/calendars/target.hpp>
33#include <ql/time/daycounters/actual360.hpp>
34#include <ql/time/daycounters/actualactual.hpp>
35#include <ql/quotes/simplequote.hpp>
36#include <ql/currencies/europe.hpp>
37#include <iomanip>
38#include <iostream>
39
40using namespace QuantLib;
41using namespace boost::unit_test_framework;
42
43#ifndef QL_PATCH_SOLARIS
44
45namespace cdo_test {
46
47 Real hwAttachment[] = { 0.00, 0.03, 0.06, 0.10 };
48 Real hwDetachment[] = { 0.03, 0.06, 0.10, 1.00 };
49
50 struct hwDatum {
51 Real correlation;
52 Integer nm;
53 Integer nz;
54 Real trancheSpread[4];
55 };
56
57 // HW Table 7
58 // corr, Nm, Nz, 0-3, 3-6, 6-10, 10-100
59 hwDatum hwData7[] = {
60 { .correlation: 0.1, .nm: -1, .nz: -1, .trancheSpread: { 2279, 450, 89, 1 } },
61 { .correlation: 0.3, .nm: -1, .nz: -1, .trancheSpread: { 1487, 472, 203, 7 } },
62 // Opening the T, T&G tests too. The convolution is analytical
63 // now so it runs it a time comparable to the gaussian tests and
64 // has enough precission to pass the tests.
65 // Below the T models are integrated with a quadrature, even if this
66 // is incorrect the test pass good enough, the quadrature gets to
67 // be worst as the kernel deviates from a normal, this is low
68 // orders of the T; here 5 is enough, 3 would not be.
69 { .correlation: 0.3, .nm: -1, .nz: 5, .trancheSpread: { 1766, 420, 161, 6 } },
70 { .correlation: 0.3, .nm: 5, .nz: -1, .trancheSpread: { 1444, 408, 171, 10 } },
71 { .correlation: 0.3, .nm: 5, .nz: 5, .trancheSpread: { 1713, 359, 136, 9 } }
72 };
73
74 void check(int i, int j, const std::string& desc, Real found, Real expected,
75 Real bpTolerance, Real relativeTolerance)
76 {
77 /* Uncomment to display the full show if your debugging:
78 std::cout<< "Case: "<< i << " " << j << " " << found << " :: "
79 << expected << " ("<< desc << ") " << std::endl;
80 */
81 Real absDiff = found - expected;
82 Real relDiff = absDiff / expected;
83 BOOST_CHECK_MESSAGE (fabs(relDiff) < relativeTolerance ||
84 fabs(absDiff) < bpTolerance,
85 "case " << i << " " << j << " ("<< desc << "): "
86 << found << " vs. " << expected);
87 }
88
89}
90
91#endif
92
93
94void CdoTest::testHW(unsigned dataSet) {
95 #ifndef QL_PATCH_SOLARIS
96
97 BOOST_TEST_MESSAGE ("Testing CDO premiums against Hull-White values"
98 " for data set " << dataSet << "...");
99
100 using namespace cdo_test;
101
102 Size poolSize = 100;
103 Real lambda = 0.01;
104
105 // nBuckets and period determine the computation time
106 Size nBuckets = 200;
107 // Period period = 1*Months;
108 // for MC engines
109 Size numSims = 5000;
110
111 Real rate = 0.05;
112 DayCounter daycount = Actual360();
113 Compounding cmp = Continuous; // Simple;
114
115 Real recovery = 0.4;
116 std::vector<Real> nominals(poolSize, 100.0);
117 Real premium = 0.02;
118 Period maxTerm (5, Years);
119 Schedule schedule = MakeSchedule().from(effectiveDate: Date (1, September, 2006))
120 .to(terminationDate: Date (1, September, 2011))
121 .withTenor(Period (3, Months))
122 .withCalendar(TARGET());
123
124 Date asofDate = Date(31, August, 2006);
125
126 Settings::instance().evaluationDate() = asofDate;
127
128 ext::shared_ptr<YieldTermStructure> yieldPtr(
129 new FlatForward (asofDate, rate,
130 daycount, cmp));
131 Handle<YieldTermStructure> yieldHandle (yieldPtr);
132
133 Handle<Quote> hazardRate(ext::shared_ptr<Quote>(new SimpleQuote(lambda)));
134 std::vector<Handle<DefaultProbabilityTermStructure> > basket;
135 ext::shared_ptr<DefaultProbabilityTermStructure> ptr (
136 new FlatHazardRate (asofDate,
137 hazardRate,
138 ActualActual(ActualActual::ISDA)));
139 ext::shared_ptr<Pool> pool (new Pool());
140 std::vector<std::string> names;
141 // probability key items
142 std::vector<Issuer> issuers;
143 std::vector<std::pair<DefaultProbKey,
144 Handle<DefaultProbabilityTermStructure> > > probabilities;
145 probabilities.emplace_back(
146 args: NorthAmericaCorpDefaultKey(EURCurrency(), SeniorSec, Period(0, Weeks), 10.),
147 args: Handle<DefaultProbabilityTermStructure>(ptr));
148
149 for (Size i=0; i<poolSize; ++i) {
150 std::ostringstream o;
151 o << "issuer-" << i;
152 names.push_back(x: o.str());
153 basket.emplace_back(args&: ptr);
154 issuers.emplace_back(args&: probabilities);
155 pool->add(name: names.back(), issuer: issuers.back(), contractTrigger: NorthAmericaCorpDefaultKey(
156 EURCurrency(), QuantLib::SeniorSec, Period(), 1.));
157 }
158
159 ext::shared_ptr<SimpleQuote> correlation (new SimpleQuote(0.0));
160 Handle<Quote> hCorrelation (correlation);
161 QL_REQUIRE (LENGTH(hwAttachment) == LENGTH(hwDetachment),
162 "data length does not match");
163
164 ext::shared_ptr<PricingEngine> midPCDOEngine( new MidPointCDOEngine(
165 yieldHandle));
166 ext::shared_ptr<PricingEngine> integralCDOEngine( new IntegralCDOEngine(
167 yieldHandle));
168
169 const Size i = dataSet;
170 correlation->setValue (hwData7[i].correlation);
171 QL_REQUIRE (LENGTH(hwAttachment) == LENGTH(hwData7[i].trancheSpread),
172 "data length does not match");
173 std::vector<ext::shared_ptr<DefaultLossModel> > basketModels;
174 std::vector<std::string> modelNames;
175 std::vector<Real> relativeToleranceMidp, relativeTolerancePeriod,
176 absoluteTolerance;
177
178 if (hwData7[i].nm == -1 && hwData7[i].nz == -1){
179 ext::shared_ptr<GaussianConstantLossLM> gaussKtLossLM(new
180 GaussianConstantLossLM(hCorrelation,
181 std::vector<Real>(poolSize, recovery),
182 LatentModelIntegrationType::GaussianQuadrature, poolSize,
183 GaussianCopulaPolicy::initTraits()));
184
185 // 1.-Inhomogeneous gaussian
186 modelNames.emplace_back(args: "Inhomogeneous gaussian");
187 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
188 IHGaussPoolLossModel(gaussKtLossLM, nBuckets, 5., -5, 15)));
189 absoluteTolerance.push_back(x: 1.);
190 relativeToleranceMidp.push_back(x: 0.04);
191 relativeTolerancePeriod.push_back(x: 0.04);
192 // 2.-homogeneous gaussian
193 modelNames.emplace_back(args: "Homogeneous gaussian");
194 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
195 HomogGaussPoolLossModel(gaussKtLossLM, nBuckets, 5., -5, 15)));
196 absoluteTolerance.push_back(x: 1.);
197 relativeToleranceMidp.push_back(x: 0.04);
198 relativeTolerancePeriod.push_back(x: 0.04);
199 // 3.-random default gaussian
200 modelNames.emplace_back(args: "Random default gaussian");
201 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>(new
202 RandomDefaultLM<GaussianCopulaPolicy>(gaussKtLossLM, numSims)));
203 absoluteTolerance.push_back(x: 1.);
204 relativeToleranceMidp.push_back(x: 0.07);
205 relativeTolerancePeriod.push_back(x: 0.07);
206 // SECOND MC
207 // gaussian LHP
208 modelNames.emplace_back(args: "Gaussian LHP");
209 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>(new
210 GaussianLHPLossModel(hCorrelation,
211 std::vector<Real>(poolSize, recovery))));
212 absoluteTolerance.push_back(x: 10.);
213 relativeToleranceMidp.push_back(x: 0.5);
214 relativeTolerancePeriod.push_back(x: 0.5);
215 // Binomial...
216 // Saddle point...
217 // Recursive ...
218 }
219 else if (hwData7[i].nm > 0 && hwData7[i].nz > 0) {
220 TCopulaPolicy::initTraits initTG;
221 initTG.tOrders.push_back(x: hwData7[i].nm);
222 initTG.tOrders.push_back(x: hwData7[i].nz);
223 ext::shared_ptr<TConstantLossLM> TKtLossLM(new TConstantLossLM(
224 hCorrelation, std::vector<Real>(poolSize, recovery),
225 LatentModelIntegrationType::GaussianQuadrature,
226 poolSize,
227 initTG));
228 // 1.-inhomogeneous studentT
229 modelNames.emplace_back(args: "Inhomogeneous student");
230 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
231 IHStudentPoolLossModel(TKtLossLM, nBuckets, 5., -5., 15)));
232 absoluteTolerance.push_back(x: 1.);
233 relativeToleranceMidp.push_back(x: 0.04);
234 relativeTolerancePeriod.push_back(x: 0.04);
235 // 2.-homogeneous student T
236 modelNames.emplace_back(args: "Homogeneous student");
237 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
238 HomogTPoolLossModel(TKtLossLM, nBuckets, 5., -5., 15)));
239 absoluteTolerance.push_back(x: 1.);
240 relativeToleranceMidp.push_back(x: 0.04);
241 relativeTolerancePeriod.push_back(x: 0.04);
242 // 3.-random default student T
243 modelNames.emplace_back(args: "Random default studentT");
244 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>(new
245 RandomDefaultLM<TCopulaPolicy>(TKtLossLM, numSims)));
246 absoluteTolerance.push_back(x: 1.);
247 relativeToleranceMidp.push_back(x: 0.07);
248 relativeTolerancePeriod.push_back(x: 0.07);
249 // SECOND MC
250 // Binomial...
251 // Saddle point...
252 // Recursive ...
253 }
254 else if (hwData7[i].nm > 0 && hwData7[i].nz == -1) {
255 TCopulaPolicy::initTraits initTG;
256 initTG.tOrders.push_back(x: hwData7[i].nm);
257 initTG.tOrders.push_back(x: 45);
258 /* T_{55} is pretty close to a gaussian. Probably theres no need to
259 be this conservative as the polynomial convolution gets shorter and
260 faster as the order decreases.
261 */
262 ext::shared_ptr<TConstantLossLM> TKtLossLM(new TConstantLossLM(
263 hCorrelation,
264 std::vector<Real>(poolSize, recovery),
265 LatentModelIntegrationType::GaussianQuadrature,
266 poolSize,
267 initTG));
268 // 1.-inhomogeneous
269 modelNames.emplace_back(args: "Inhomogeneous student-gaussian");
270 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
271 IHStudentPoolLossModel(TKtLossLM, nBuckets, 5., -5., 15)));
272 absoluteTolerance.push_back(x: 1.);
273 relativeToleranceMidp.push_back(x: 0.04);
274 relativeTolerancePeriod.push_back(x: 0.04);
275 // 2.-homogeneous
276 modelNames.emplace_back(args: "Homogeneous student-gaussian");
277 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
278 HomogTPoolLossModel(TKtLossLM, nBuckets, 5., -5., 15)));
279 absoluteTolerance.push_back(x: 1.);
280 relativeToleranceMidp.push_back(x: 0.04);
281 relativeTolerancePeriod.push_back(x: 0.04);
282 // 3.-random default
283 modelNames.emplace_back(args: "Random default student-gaussian");
284 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>(new
285 RandomDefaultLM<TCopulaPolicy>(TKtLossLM, numSims)));
286 absoluteTolerance.push_back(x: 1.);
287 relativeToleranceMidp.push_back(x: 0.07);
288 relativeTolerancePeriod.push_back(x: 0.07);
289 // SECOND MC
290 // Binomial...
291 // Saddle point...
292 // Recursive ...
293 }
294 else if (hwData7[i].nm == -1 && hwData7[i].nz > 0) {
295 TCopulaPolicy::initTraits initTG;
296 initTG.tOrders.push_back(x: 45);// pretty close to gaussian
297 initTG.tOrders.push_back(x: hwData7[i].nz);
298 ext::shared_ptr<TConstantLossLM> TKtLossLM(new TConstantLossLM(
299 hCorrelation,
300 std::vector<Real>(poolSize, recovery),
301 LatentModelIntegrationType::GaussianQuadrature,
302 poolSize,
303 initTG));
304 // 1.-inhomogeneous gaussian
305 modelNames.emplace_back(args: "Inhomogeneous gaussian-student");
306 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
307 IHStudentPoolLossModel(TKtLossLM, nBuckets, 5., -5., 15)));
308 absoluteTolerance.push_back(x: 1.);
309 relativeToleranceMidp.push_back(x: 0.04);
310 relativeTolerancePeriod.push_back(x: 0.04);
311 // 2.-homogeneous gaussian
312 modelNames.emplace_back(args: "Homogeneous gaussian-student");
313 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>( new
314 HomogTPoolLossModel(TKtLossLM, nBuckets, 5., -5., 15)));
315 absoluteTolerance.push_back(x: 1.);
316 relativeToleranceMidp.push_back(x: 0.04);
317 relativeTolerancePeriod.push_back(x: 0.04);
318 // 3.-random default gaussian
319 modelNames.emplace_back(args: "Random default gaussian-student");
320 basketModels.push_back(x: ext::shared_ptr<DefaultLossModel>(new
321 RandomDefaultLM<TCopulaPolicy>(TKtLossLM, numSims)));
322 absoluteTolerance.push_back(x: 1.);
323 relativeToleranceMidp.push_back(x: 0.07);
324 relativeTolerancePeriod.push_back(x: 0.07);
325 // SECOND MC
326 // Binomial...
327 // Saddle point...
328 // Recursive ...
329 }
330 else {
331 return;
332 }
333
334 for (Size j = 0; j < LENGTH(hwAttachment); j ++) {
335 ext::shared_ptr<Basket> basketPtr (
336 new Basket(asofDate, names, nominals, pool,
337 hwAttachment[j], hwDetachment[j]));
338 std::ostringstream trancheId;
339 trancheId << "[" << hwAttachment[j] << " , " << hwDetachment[j]
340 << "]";
341 SyntheticCDO cdoe(basketPtr, Protection::Seller,
342 schedule, 0.0, premium, daycount, Following);
343
344 for(Size im=0; im<basketModels.size(); im++) {
345
346 basketPtr->setLossModel(basketModels[im]);
347
348 cdoe.setPricingEngine(midPCDOEngine);
349 check(i, j, desc: modelNames[im]
350 +std::string(" with midp integration on ")+trancheId.str(),
351 found: cdoe.fairPremium() * 1e4, expected: hwData7[i].trancheSpread[j],
352 bpTolerance: absoluteTolerance[im], relativeTolerance: relativeToleranceMidp[im]);
353
354 cdoe.setPricingEngine(integralCDOEngine);
355 check(i, j, desc: modelNames[im]
356 +std::string(" with step integration on ")+trancheId.str(),
357 found: cdoe.fairPremium() * 1e4, expected: hwData7[i].trancheSpread[j],
358 bpTolerance: absoluteTolerance[im], relativeTolerance: relativeTolerancePeriod[im]);
359 }
360 }
361 #endif
362}
363
364
365test_suite* CdoTest::suite(SpeedLevel speed) {
366 auto* suite = BOOST_TEST_SUITE("CDO tests");
367
368 #ifndef QL_PATCH_SOLARIS
369 if (speed == Slow) {
370 // unrolled to get different test names
371 suite->add(QUANTLIB_TEST_CASE([=](){ CdoTest::testHW(0); }));
372 suite->add(QUANTLIB_TEST_CASE([=](){ CdoTest::testHW(1); }));
373 suite->add(QUANTLIB_TEST_CASE([=](){ CdoTest::testHW(2); }));
374 suite->add(QUANTLIB_TEST_CASE([=](){ CdoTest::testHW(3); }));
375 suite->add(QUANTLIB_TEST_CASE([=](){ CdoTest::testHW(4); }));
376 }
377 #endif
378 return suite;
379}
380

source code of quantlib/test-suite/cdo.cpp