[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) 2006 Cristina Duminuco
5 Copyright (C) 2006, 2008 Ferdinando Ametrano
6 Copyright (C) 2006 Katiuscia Manzoni
7
8 This file is part of QuantLib, a free-software/open-source library
9 for financial quantitative analysts and developers - http://quantlib.org/
10
11 QuantLib is free software: you can redistribute it and/or modify it
12 under the terms of the QuantLib license. You should have received a
13 copy of the license along with this program; if not, please email
14 <quantlib-dev@lists.sf.net>. The license is also available online at
15 <http://quantlib.org/license.shtml>.
16
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the license for more details.
20*/
21
22#include "swaptionvolatilitycube.hpp"
23#include "swaptionvolstructuresutilities.hpp"
24#include "utilities.hpp"
25#include <ql/indexes/swap/euriborswap.hpp>
26#include <ql/quotes/simplequote.hpp>
27#include <ql/termstructures/volatility/swaption/interpolatedswaptionvolatilitycube.hpp>
28#include <ql/termstructures/volatility/swaption/sabrswaptionvolatilitycube.hpp>
29#include <ql/termstructures/volatility/swaption/spreadedswaptionvol.hpp>
30#include <ql/termstructures/volatility/sabrsmilesection.hpp>
31#include <ql/utilities/dataformatters.hpp>
32
33using namespace QuantLib;
34using namespace boost::unit_test_framework;
35
36namespace swaption_volatility_cube_test {
37
38 struct CommonVars {
39 // global data
40 SwaptionMarketConventions conventions;
41 AtmVolatility atm;
42 RelinkableHandle<SwaptionVolatilityStructure> atmVolMatrix;
43 RelinkableHandle<SwaptionVolatilityStructure> normalVolMatrix;
44 VolatilityCube cube;
45
46 RelinkableHandle<YieldTermStructure> termStructure;
47
48 ext::shared_ptr<SwapIndex> swapIndexBase, shortSwapIndexBase;
49 bool vegaWeighedSmileFit;
50
51 // utilities
52 void makeAtmVolTest(const SwaptionVolatilityCube& volCube,
53 Real tolerance) {
54
55 for (auto& option : atm.tenors.options) {
56 for (auto& swap : atm.tenors.swaps) {
57 Rate strike = volCube.atmStrike(optionTenor: option, swapTenor: swap);
58 Volatility expVol = atmVolMatrix->volatility(optionTenor: option, swapTenor: swap, strike, extrapolate: true);
59 Volatility actVol = volCube.volatility(optionTenor: option, swapTenor: swap, strike, extrapolate: true);
60 Volatility error = std::abs(x: expVol - actVol);
61 if (error > tolerance)
62 BOOST_ERROR("\nrecovery of atm vols failed:"
63 "\nexpiry time = "
64 << option << "\nswap length = " << swap
65 << "\n atm strike = " << io::rate(strike)
66 << "\n exp. vol = " << io::volatility(expVol)
67 << "\n actual vol = " << io::volatility(actVol)
68 << "\n error = " << io::volatility(error)
69 << "\n tolerance = " << tolerance);
70 }
71 }
72 }
73
74 void makeVolSpreadsTest(const SwaptionVolatilityCube& volCube,
75 Real tolerance) {
76
77 for (Size i=0; i<cube.tenors.options.size(); i++) {
78 for (Size j=0; j<cube.tenors.swaps.size(); j++) {
79 for (Size k=0; k<cube.strikeSpreads.size(); k++) {
80 Rate atmStrike = volCube.atmStrike(optionTenor: cube.tenors.options[i],
81 swapTenor: cube.tenors.swaps[j]);
82 Volatility atmVol =
83 atmVolMatrix->volatility(optionTenor: cube.tenors.options[i],
84 swapTenor: cube.tenors.swaps[j],
85 strike: atmStrike, extrapolate: true);
86 Volatility vol =
87 volCube.volatility(optionTenor: cube.tenors.options[i],
88 swapTenor: cube.tenors.swaps[j],
89 strike: atmStrike+cube.strikeSpreads[k], extrapolate: true);
90 Volatility spread = vol-atmVol;
91 Volatility expVolSpread =
92 cube.volSpreads[i*cube.tenors.swaps.size()+j][k];
93 Volatility error = std::abs(x: expVolSpread-spread);
94 if (error>tolerance)
95 BOOST_FAIL("\nrecovery of smile vol spreads failed:"
96 "\n option tenor = " << cube.tenors.options[i] <<
97 "\n swap tenor = " << cube.tenors.swaps[j] <<
98 "\n atm strike = " << io::rate(atmStrike) <<
99 "\n strike spread = " << io::rate(cube.strikeSpreads[k]) <<
100 "\n atm vol = " << io::volatility(atmVol) <<
101 "\n smiled vol = " << io::volatility(vol) <<
102 "\n vol spread = " << io::volatility(spread) <<
103 "\n exp. vol spread = " << io::volatility(expVolSpread) <<
104 "\n error = " << io::volatility(error) <<
105 "\n tolerance = " << tolerance);
106 }
107 }
108 }
109 }
110
111 CommonVars() {
112
113 conventions.setConventions();
114
115 // ATM swaptionvolmatrix
116 atm.setMarketData();
117
118 atmVolMatrix = RelinkableHandle<SwaptionVolatilityStructure>(
119 ext::shared_ptr<SwaptionVolatilityStructure>(new
120 SwaptionVolatilityMatrix(conventions.calendar,
121 conventions.optionBdc,
122 atm.tenors.options,
123 atm.tenors.swaps,
124 atm.volsHandle,
125 conventions.dayCounter)));
126
127 normalVolMatrix = RelinkableHandle<SwaptionVolatilityStructure>(
128 ext::shared_ptr<SwaptionVolatilityStructure>(new SwaptionVolatilityMatrix(
129 conventions.calendar, conventions.optionBdc, atm.tenors.options,
130 atm.tenors.swaps, atm.volsHandle, conventions.dayCounter, false, VolatilityType::Normal)));
131
132 // Swaptionvolcube
133 cube.setMarketData();
134
135 termStructure.linkTo(h: flatRate(forward: 0.05, dc: Actual365Fixed()));
136
137 swapIndexBase = ext::shared_ptr<SwapIndex>(new
138 EuriborSwapIsdaFixA(2*Years, termStructure));
139 shortSwapIndexBase = ext::shared_ptr<SwapIndex>(new
140 EuriborSwapIsdaFixA(1*Years, termStructure));
141
142 vegaWeighedSmileFit=false;
143 }
144 };
145
146}
147
148void SwaptionVolatilityCubeTest::testSabrNormalVolatility() {
149
150 BOOST_TEST_MESSAGE("Testing sabr normal volatility...");
151
152 using namespace swaption_volatility_cube_test;
153
154 CommonVars vars;
155
156 std::vector<std::vector<Handle<Quote> > > parametersGuess(vars.cube.tenors.options.size() *
157 vars.cube.tenors.swaps.size());
158 for (Size i = 0; i < vars.cube.tenors.options.size() * vars.cube.tenors.swaps.size(); i++) {
159 parametersGuess[i] = std::vector<Handle<Quote> >(4);
160 parametersGuess[i][0] = Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.2)));
161 parametersGuess[i][1] = Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.5)));
162 parametersGuess[i][2] = Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.4)));
163 parametersGuess[i][3] = Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.0)));
164 }
165 std::vector<bool> isParameterFixed(4, false);
166
167 SabrSwaptionVolatilityCube volCube(vars.normalVolMatrix, vars.cube.tenors.options, vars.cube.tenors.swaps,
168 vars.cube.strikeSpreads, vars.cube.volSpreadsHandle,
169 vars.swapIndexBase, vars.shortSwapIndexBase, vars.vegaWeighedSmileFit,
170 parametersGuess, isParameterFixed, true);
171 Real tolerance = 7.0e-4;
172 vars.makeAtmVolTest(volCube, tolerance);
173}
174
175void SwaptionVolatilityCubeTest::testAtmVols() {
176
177 BOOST_TEST_MESSAGE("Testing swaption volatility cube (atm vols)...");
178
179 using namespace swaption_volatility_cube_test;
180
181 CommonVars vars;
182
183 InterpolatedSwaptionVolatilityCube volCube(vars.atmVolMatrix,
184 vars.cube.tenors.options,
185 vars.cube.tenors.swaps,
186 vars.cube.strikeSpreads,
187 vars.cube.volSpreadsHandle,
188 vars.swapIndexBase,
189 vars.shortSwapIndexBase,
190 vars.vegaWeighedSmileFit);
191
192 Real tolerance = 1.0e-16;
193 vars.makeAtmVolTest(volCube, tolerance);
194}
195
196void SwaptionVolatilityCubeTest::testSmile() {
197
198 BOOST_TEST_MESSAGE("Testing swaption volatility cube (smile)...");
199
200 using namespace swaption_volatility_cube_test;
201
202 CommonVars vars;
203
204 InterpolatedSwaptionVolatilityCube volCube(vars.atmVolMatrix,
205 vars.cube.tenors.options,
206 vars.cube.tenors.swaps,
207 vars.cube.strikeSpreads,
208 vars.cube.volSpreadsHandle,
209 vars.swapIndexBase,
210 vars.shortSwapIndexBase,
211 vars.vegaWeighedSmileFit);
212
213 Real tolerance = 1.0e-16;
214 vars.makeVolSpreadsTest(volCube, tolerance);
215}
216
217void SwaptionVolatilityCubeTest::testSabrVols() {
218
219 BOOST_TEST_MESSAGE("Testing swaption volatility cube (sabr interpolation)...");
220
221 using namespace swaption_volatility_cube_test;
222
223 CommonVars vars;
224
225 std::vector<std::vector<Handle<Quote> > >
226 parametersGuess(vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size());
227 for (Size i=0; i<vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size(); i++) {
228 parametersGuess[i] = std::vector<Handle<Quote> >(4);
229 parametersGuess[i][0] =
230 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.2)));
231 parametersGuess[i][1] =
232 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.5)));
233 parametersGuess[i][2] =
234 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.4)));
235 parametersGuess[i][3] =
236 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.0)));
237 }
238 std::vector<bool> isParameterFixed(4, false);
239
240 SabrSwaptionVolatilityCube volCube(vars.atmVolMatrix,
241 vars.cube.tenors.options,
242 vars.cube.tenors.swaps,
243 vars.cube.strikeSpreads,
244 vars.cube.volSpreadsHandle,
245 vars.swapIndexBase,
246 vars.shortSwapIndexBase,
247 vars.vegaWeighedSmileFit,
248 parametersGuess,
249 isParameterFixed,
250 true);
251 Real tolerance = 3.0e-4;
252 vars.makeAtmVolTest(volCube, tolerance);
253
254 tolerance = 12.0e-4;
255 vars.makeVolSpreadsTest(volCube, tolerance);
256}
257
258void SwaptionVolatilityCubeTest::testSpreadedCube() {
259
260 BOOST_TEST_MESSAGE("Testing spreaded swaption volatility cube...");
261
262 using namespace swaption_volatility_cube_test;
263
264 CommonVars vars;
265
266 std::vector<std::vector<Handle<Quote> > >
267 parametersGuess(vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size());
268 for (Size i=0; i<vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size(); i++) {
269 parametersGuess[i] = std::vector<Handle<Quote> >(4);
270 parametersGuess[i][0] =
271 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.2)));
272 parametersGuess[i][1] =
273 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.5)));
274 parametersGuess[i][2] =
275 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.4)));
276 parametersGuess[i][3] =
277 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.0)));
278 }
279 std::vector<bool> isParameterFixed(4, false);
280
281 Handle<SwaptionVolatilityStructure> volCube( ext::shared_ptr<SwaptionVolatilityStructure>(new
282 SabrSwaptionVolatilityCube(vars.atmVolMatrix,
283 vars.cube.tenors.options,
284 vars.cube.tenors.swaps,
285 vars.cube.strikeSpreads,
286 vars.cube.volSpreadsHandle,
287 vars.swapIndexBase,
288 vars.shortSwapIndexBase,
289 vars.vegaWeighedSmileFit,
290 parametersGuess,
291 isParameterFixed,
292 true)));
293
294 ext::shared_ptr<SimpleQuote> spread (new SimpleQuote(0.0001));
295 Handle<Quote> spreadHandle(spread);
296 ext::shared_ptr<SwaptionVolatilityStructure> spreadedVolCube(new
297 SpreadedSwaptionVolatility(volCube, spreadHandle));
298 std::vector<Real> strikes;
299 for (Size k=1; k<100; k++)
300 strikes.push_back(x: k*.01);
301 for (Size i=0; i<vars.cube.tenors.options.size(); i++) {
302 for (Size j=0; j<vars.cube.tenors.swaps.size(); j++) {
303 ext::shared_ptr<SmileSection> smileSectionByCube =
304 volCube->smileSection(optionTenor: vars.cube.tenors.options[i], swapTenor: vars.cube.tenors.swaps[j]);
305 ext::shared_ptr<SmileSection> smileSectionBySpreadedCube =
306 spreadedVolCube->smileSection(optionTenor: vars.cube.tenors.options[i], swapTenor: vars.cube.tenors.swaps[j]);
307 for (Real strike : strikes) {
308 Real diff = spreadedVolCube->volatility(optionTenor: vars.cube.tenors.options[i],
309 swapTenor: vars.cube.tenors.swaps[j], strike) -
310 volCube->volatility(optionTenor: vars.cube.tenors.options[i],
311 swapTenor: vars.cube.tenors.swaps[j], strike);
312 if (std::fabs(x: diff-spread->value())>1e-16)
313 BOOST_ERROR("\ndiff!=spread in volatility method:"
314 "\nexpiry time = " << vars.cube.tenors.options[i] <<
315 "\nswap length = " << vars.cube.tenors.swaps[j] <<
316 "\n atm strike = " << io::rate(strike) <<
317 "\ndiff = " << diff <<
318 "\nspread = " << spread->value());
319
320 diff = smileSectionBySpreadedCube->volatility(strike)
321 - smileSectionByCube->volatility(strike);
322 if (std::fabs(x: diff-spread->value())>1e-16)
323 BOOST_ERROR("\ndiff!=spread in smile section method:"
324 "\nexpiry time = " << vars.cube.tenors.options[i] <<
325 "\nswap length = " << vars.cube.tenors.swaps[j] <<
326 "\n atm strike = " << io::rate(strike) <<
327 "\ndiff = " << diff <<
328 "\nspread = " << spread->value());
329 }
330 }
331 }
332
333 //testing observability
334 Flag f;
335 f.registerWith(h: spreadedVolCube);
336 volCube->update();
337 if(!f.isUp())
338 BOOST_ERROR("SpreadedSwaptionVolatilityStructure "
339 << "does not propagate notifications");
340 f.lower();
341 spread->setValue(.001);
342 if(!f.isUp())
343 BOOST_ERROR("SpreadedSwaptionVolatilityStructure "
344 << "does not propagate notifications");
345}
346
347
348void SwaptionVolatilityCubeTest::testObservability() {
349 BOOST_TEST_MESSAGE("Testing volatility cube observability...");
350
351 using namespace swaption_volatility_cube_test;
352
353 CommonVars vars;
354
355 std::vector<std::vector<Handle<Quote> > >
356 parametersGuess(vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size());
357 for (Size i=0; i<vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size(); i++) {
358 parametersGuess[i] = std::vector<Handle<Quote> >(4);
359 parametersGuess[i][0] =
360 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.2)));
361 parametersGuess[i][1] =
362 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.5)));
363 parametersGuess[i][2] =
364 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.4)));
365 parametersGuess[i][3] =
366 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.0)));
367 }
368 std::vector<bool> isParameterFixed(4, false);
369
370 std::string description;
371 ext::shared_ptr<SabrSwaptionVolatilityCube> volCube1_0, volCube1_1;
372 // VolCube created before change of reference date
373 volCube1_0 = ext::make_shared<SabrSwaptionVolatilityCube>(args&: vars.atmVolMatrix,
374 args&: vars.cube.tenors.options,
375 args&: vars.cube.tenors.swaps,
376 args&: vars.cube.strikeSpreads,
377 args&: vars.cube.volSpreadsHandle,
378 args&: vars.swapIndexBase,
379 args&: vars.shortSwapIndexBase,
380 args&: vars.vegaWeighedSmileFit,
381 args&: parametersGuess,
382 args&: isParameterFixed,
383 args: true);
384
385 Date referenceDate = Settings::instance().evaluationDate();
386 Settings::instance().evaluationDate() =
387 vars.conventions.calendar.advance(date: referenceDate, period: Period(1, Days),
388 convention: vars.conventions.optionBdc);
389
390 // VolCube created after change of reference date
391 volCube1_1 = ext::make_shared<SabrSwaptionVolatilityCube>(args&: vars.atmVolMatrix,
392 args&: vars.cube.tenors.options,
393 args&: vars.cube.tenors.swaps,
394 args&: vars.cube.strikeSpreads,
395 args&: vars.cube.volSpreadsHandle,
396 args&: vars.swapIndexBase,
397 args&: vars.shortSwapIndexBase,
398 args&: vars.vegaWeighedSmileFit,
399 args&: parametersGuess,
400 args&: isParameterFixed,
401 args: true);
402 Rate dummyStrike = 0.03;
403 for (Size i=0;i<vars.cube.tenors.options.size(); i++ ) {
404 for (Size j=0; j<vars.cube.tenors.swaps.size(); j++) {
405 for (Size k=0; k<vars.cube.strikeSpreads.size(); k++) {
406
407 Volatility v0 = volCube1_0->volatility(optionTenor: vars.cube.tenors.options[i],
408 swapTenor: vars.cube.tenors.swaps[j],
409 strike: dummyStrike + vars.cube.strikeSpreads[k],
410 extrapolate: false);
411 Volatility v1 = volCube1_1->volatility(optionTenor: vars.cube.tenors.options[i],
412 swapTenor: vars.cube.tenors.swaps[j],
413 strike: dummyStrike + vars.cube.strikeSpreads[k],
414 extrapolate: false);
415 if (std::fabs(x: v0 - v1) > 1e-14)
416 BOOST_ERROR(description <<
417 " option tenor = " << vars.cube.tenors.options[i] <<
418 " swap tenor = " << vars.cube.tenors.swaps[j] <<
419 " strike = " << io::rate(dummyStrike+vars.cube.strikeSpreads[k])<<
420 " v0 = " << io::volatility(v0) <<
421 " v1 = " << io::volatility(v1) <<
422 " error = " << std::fabs(v1-v0));
423 }
424 }
425 }
426
427 Settings::instance().evaluationDate() = referenceDate;
428
429 ext::shared_ptr<InterpolatedSwaptionVolatilityCube> volCube2_0, volCube2_1;
430 // VolCube created before change of reference date
431 volCube2_0 = ext::make_shared<InterpolatedSwaptionVolatilityCube>(args&: vars.atmVolMatrix,
432 args&: vars.cube.tenors.options,
433 args&: vars.cube.tenors.swaps,
434 args&: vars.cube.strikeSpreads,
435 args&: vars.cube.volSpreadsHandle,
436 args&: vars.swapIndexBase,
437 args&: vars.shortSwapIndexBase,
438 args&: vars.vegaWeighedSmileFit);
439 Settings::instance().evaluationDate() =
440 vars.conventions.calendar.advance(date: referenceDate, period: Period(1, Days),
441 convention: vars.conventions.optionBdc);
442
443 // VolCube created after change of reference date
444 volCube2_1 = ext::make_shared<InterpolatedSwaptionVolatilityCube>(args&: vars.atmVolMatrix,
445 args&: vars.cube.tenors.options,
446 args&: vars.cube.tenors.swaps,
447 args&: vars.cube.strikeSpreads,
448 args&: vars.cube.volSpreadsHandle,
449 args&: vars.swapIndexBase,
450 args&: vars.shortSwapIndexBase,
451 args&: vars.vegaWeighedSmileFit);
452
453 for (Size i=0;i<vars.cube.tenors.options.size(); i++ ) {
454 for (Size j=0; j<vars.cube.tenors.swaps.size(); j++) {
455 for (Size k=0; k<vars.cube.strikeSpreads.size(); k++) {
456
457 Volatility v0 = volCube2_0->volatility(optionTenor: vars.cube.tenors.options[i],
458 swapTenor: vars.cube.tenors.swaps[j],
459 strike: dummyStrike + vars.cube.strikeSpreads[k],
460 extrapolate: false);
461 Volatility v1 = volCube2_1->volatility(optionTenor: vars.cube.tenors.options[i],
462 swapTenor: vars.cube.tenors.swaps[j],
463 strike: dummyStrike + vars.cube.strikeSpreads[k],
464 extrapolate: false);
465 if (std::fabs(x: v0 - v1) > 1e-14)
466 BOOST_ERROR(description <<
467 " option tenor = " << vars.cube.tenors.options[i] <<
468 " swap tenor = " << vars.cube.tenors.swaps[j] <<
469 " strike = " << io::rate(dummyStrike+vars.cube.strikeSpreads[k])<<
470 " v0 = " << io::volatility(v0) <<
471 " v1 = " << io::volatility(v1) <<
472 " error = " << std::fabs(v1-v0));
473 }
474 }
475 }
476
477 Settings::instance().evaluationDate() = referenceDate;
478}
479
480void SwaptionVolatilityCubeTest::testSabrParameters() {
481 BOOST_TEST_MESSAGE("Testing interpolation of SABR smile sections...");
482
483 using namespace swaption_volatility_cube_test;
484
485 CommonVars vars;
486
487 std::vector<std::vector<Handle<Quote> > >
488 parametersGuess(vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size());
489 for (Size i=0; i<vars.cube.tenors.options.size()*vars.cube.tenors.swaps.size(); i++) {
490 parametersGuess[i] = std::vector<Handle<Quote> >(4);
491 parametersGuess[i][0] =
492 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.2)));
493 parametersGuess[i][1] =
494 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.5)));
495 parametersGuess[i][2] =
496 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.4)));
497 parametersGuess[i][3] =
498 Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(0.0)));
499 }
500 std::vector<bool> isParameterFixed(4, false);
501
502 SabrSwaptionVolatilityCube volCube(vars.atmVolMatrix,
503 vars.cube.tenors.options,
504 vars.cube.tenors.swaps,
505 vars.cube.strikeSpreads,
506 vars.cube.volSpreadsHandle,
507 vars.swapIndexBase,
508 vars.shortSwapIndexBase,
509 vars.vegaWeighedSmileFit,
510 parametersGuess,
511 isParameterFixed,
512 true);
513
514 SwaptionVolatilityStructure* volStructure = &volCube;
515 Real tolerance = 1.0e-4;
516
517 //Interpolating between two SmileSection objects
518
519 //First section: maturity = 10Y, tenor = 2Y
520 ext::shared_ptr<SmileSection> smileSection1 = volStructure->smileSection(optionTenor: Period(10,Years), swapTenor: Period(2,Years));
521
522 //Second section: maturity = 10Y, tenor = 4Y
523 ext::shared_ptr<SmileSection> smileSection2 = volStructure->smileSection(optionTenor: Period(10,Years), swapTenor: Period(4,Years));
524
525 //Third section in the middle: maturity = 10Y, tenor = 3Y
526 ext::shared_ptr<SmileSection> smileSection3 = volStructure->smileSection(optionTenor: Period(10,Years), swapTenor: Period(3,Years));
527
528 //test alpha interpolation
529 Real alpha1 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection1)->alpha();
530 Real alpha2 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection2)->alpha();
531 Real alpha3 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection3)->alpha();
532 Real alpha12 = 0.5*(alpha1+alpha2);
533 if (std::abs(x: alpha3 - alpha12) > tolerance) {
534 BOOST_ERROR("\nChecking interpolation of alpha parameters:"
535 "\nexpected = " << alpha12 <<
536 "\nobserved = " << alpha3);
537 }
538
539 //test beta interpolation
540 Real beta1 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection1)->beta();
541 Real beta2 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection2)->beta();
542 Real beta3 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection3)->beta();
543 Real beta12 = 0.5*(beta1+beta2);
544 if (std::abs(x: beta3 - beta12) > tolerance) {
545 BOOST_ERROR("\nChecking interpolation of beta parameters:"
546 "\nexpected = " << beta12 <<
547 "\nobserved = " << beta3);
548 }
549
550 //test rho interpolation
551 Real rho1 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection1)->rho();
552 Real rho2 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection2)->rho();
553 Real rho3 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection3)->rho();
554 Real rho12 = 0.5*(rho1+rho2);
555 if (std::abs(x: rho3 - rho12) > tolerance) {
556 BOOST_ERROR("\nChecking interpolation of rho parameters:"
557 "\nexpected = " << rho12 <<
558 "\nobserved = " << rho3);
559 }
560
561 //test nu interpolation
562 Real nu1 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection1)->nu();
563 Real nu2 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection2)->nu();
564 Real nu3 = ext::dynamic_pointer_cast<SabrSmileSection>(r: smileSection3)->nu();
565 Real nu12 = 0.5*(nu1+nu2);
566 if (std::abs(x: nu3 - nu12) > tolerance) {
567 BOOST_ERROR("\nChecking interpolation of nu parameters:"
568 "\nexpected = " << nu12 <<
569 "\nobserved = " << nu3);
570 }
571
572 //test forward interpolation
573 Real forward1 = smileSection1->atmLevel();
574 Real forward2 = smileSection2->atmLevel();
575 Real forward3 = smileSection3->atmLevel();
576 Real forward12 = 0.5*(forward1+forward2);
577 if (std::abs(x: forward3 - forward12) > tolerance) {
578 BOOST_ERROR("\nChecking interpolation of forward parameters:"
579 "\nexpected = " << forward12 <<
580 "\nobserved = " << forward3);
581 }
582
583}
584
585
586test_suite* SwaptionVolatilityCubeTest::suite() {
587 auto* suite = BOOST_TEST_SUITE("Swaption Volatility Cube tests");
588
589 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testSabrNormalVolatility));
590 // SwaptionVolCubeByLinear reproduces ATM vol with machine precision
591 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testAtmVols));
592 // SwaptionVolCubeByLinear reproduces smile spreads with machine precision
593 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testSmile));
594
595 // SwaptionVolCubeBySabr reproduces ATM vol with given tolerance
596 // SwaptionVolCubeBySabr reproduces smile spreads with given tolerance
597 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testSabrVols));
598 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testSpreadedCube));
599 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testObservability));
600 suite->add(QUANTLIB_TEST_CASE(&SwaptionVolatilityCubeTest::testSabrParameters));
601
602
603 return suite;
604}
605

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