Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
swaptionvolconstantspread.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20#include "toplevelfixture.hpp"
22#include <boost/test/unit_test.hpp>
26
27#include <ql/indexes/swap/euriborswap.hpp>
28
29#include <boost/assign/list_of.hpp>
30
31using namespace QuantExt;
32using namespace QuantLib;
33using namespace boost::unit_test_framework;
34using namespace boost::assign;
35
36namespace {
37// Variables to be used in the test
38struct CommonVars {
39 // Constructor
40 CommonVars(Size expiryIndex = 0, Size termIndex = 0, Real atmVolShift = 0) : referenceDate(5, Feb, 2016) {
41
42 // Reference date
43 Settings::instance().evaluationDate() = referenceDate;
44
45 // Link ibor index to correct forward curve
46 QuantLib::ext::shared_ptr<IborIndex> iborIndex = conventions.floatIndex->clone(yieldCurves.forward6M);
47 QuantLib::ext::shared_ptr<SwapIndex> swapIndex =
48 conventions.swapIndex->clone(yieldCurves.forward6M, yieldCurves.forward6M);
49 QuantLib::ext::shared_ptr<SwapIndex> shortSwapIndex =
50 conventions.shortSwapIndex->clone(yieldCurves.forward6M, yieldCurves.forward6M);
51
52 // Create underlying swap conventions
53 swapConventions = QuantLib::ext::make_shared<SwapConventions>(conventions.settlementDays, conventions.fixedTenor,
54 conventions.fixedCalendar, conventions.fixedConvention,
55 conventions.fixedDayCounter, iborIndex);
56
57 // Set up the various swaption matrices
58 Matrix shiftedAtmVols = atmVols.nVols;
59 QL_REQUIRE(expiryIndex < atmVols.optionTenors.size(), "expiryIndex out of range");
60 QL_REQUIRE(termIndex < atmVols.swapTenors.size(), "termIndex out of range");
61 shiftedAtmVols[expiryIndex][termIndex] += atmVolShift;
62 atmNormalVolMatrix = QuantLib::ext::make_shared<SwaptionVolatilityMatrix>(
63 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
64 atmVols.swapTenors, shiftedAtmVols, Actual365Fixed(), true, Normal);
65
66 atmLogNormalVolMatrix = QuantLib::ext::make_shared<SwaptionVolatilityMatrix>(
67 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
68 atmVols.swapTenors, shiftedAtmVols, Actual365Fixed(), true, ShiftedLognormal);
69
70 // QuantLib::ext::make_shared can only handle 9 parameters
71 atmShiftedLogNormalVolMatrix_1 = QuantLib::ext::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(
72 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
73 atmVols.swapTenors, shiftedAtmVols, Actual365Fixed(), true, ShiftedLognormal, atmVols.shifts_1));
74
75 atmShiftedLogNormalVolMatrix_2 = QuantLib::ext::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(
76 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
77 atmVols.swapTenors, atmVols.slnVols_2, Actual365Fixed(), true, ShiftedLognormal, atmVols.shifts_2));
78
79 normalVolCube = QuantLib::ext::shared_ptr<SwaptionVolCube2>(new SwaptionVolCube2(
80 Handle<QuantLib::SwaptionVolatilityStructure>(atmNormalVolMatrix), atmVols.optionTenors, atmVols.swapTenors,
81 atmVols.strikeSpreads, atmVols.nVolSpreads, swapIndex, shortSwapIndex, false, true));
82 logNormalVolCube = QuantLib::ext::shared_ptr<SwaptionVolCube2>(new SwaptionVolCube2(
83 Handle<QuantLib::SwaptionVolatilityStructure>(atmLogNormalVolMatrix), atmVols.optionTenors,
84 atmVols.swapTenors, atmVols.strikeSpreads, atmVols.lnVolSpreads, swapIndex, shortSwapIndex, false, true));
85 shiftedLogNormalVolCube = QuantLib::ext::shared_ptr<SwaptionVolCube2>(new SwaptionVolCube2(
86 Handle<QuantLib::SwaptionVolatilityStructure>(atmShiftedLogNormalVolMatrix_1), atmVols.optionTenors,
87 atmVols.swapTenors, atmVols.strikeSpreads, atmVols.slnVolSpreads, swapIndex, shortSwapIndex, false, true));
88
89 normalVolCubeConstantSpread = QuantLib::ext::shared_ptr<SwaptionVolatilityConstantSpread>(
90 new SwaptionVolatilityConstantSpread(Handle<QuantLib::SwaptionVolatilityStructure>(atmNormalVolMatrix),
91 Handle<QuantLib::SwaptionVolatilityStructure>(normalVolCube)));
92
93 logNormalVolCubeConstantSpread = QuantLib::ext::shared_ptr<SwaptionVolatilityConstantSpread>(
94 new SwaptionVolatilityConstantSpread(Handle<QuantLib::SwaptionVolatilityStructure>(atmLogNormalVolMatrix),
95 Handle<QuantLib::SwaptionVolatilityStructure>(logNormalVolCube)));
96
97 shiftedLogNormalVolCubeConstantSpread =
98 QuantLib::ext::shared_ptr<SwaptionVolatilityConstantSpread>(new SwaptionVolatilityConstantSpread(
99 Handle<QuantLib::SwaptionVolatilityStructure>(atmShiftedLogNormalVolMatrix_1),
100 Handle<QuantLib::SwaptionVolatilityStructure>(shiftedLogNormalVolCube)));
101 }
102
103 // Members
104 Date referenceDate;
105 SwaptionConventionsEUR conventions;
106 SwaptionVolatilityEUR atmVols;
107 YieldCurveEUR yieldCurves;
108 QuantLib::ext::shared_ptr<SwapConventions> swapConventions;
109 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmNormalVolMatrix;
110 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmLogNormalVolMatrix;
111 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmShiftedLogNormalVolMatrix_1;
112 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmShiftedLogNormalVolMatrix_2;
113 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> normalVolCube, normalVolCubeConstantSpread;
114 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> logNormalVolCube, logNormalVolCubeConstantSpread;
115 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> shiftedLogNormalVolCube, shiftedLogNormalVolCubeConstantSpread;
116
117 SavedSettings backup;
118};
119} // namespace
120
121BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
122
123BOOST_AUTO_TEST_SUITE(SwaptionVolConstantSpreadTest)
124BOOST_AUTO_TEST_CASE(testAtmNormalVolShiftPropagation) {
125 BOOST_TEST_MESSAGE("Testing ATM normal vol shift propagation...");
126
127 CommonVars vars1;
128 Real tolerance = 0.000001;
129 Real shift = 0.0050;
130 for (Size i = 0; i < vars1.atmVols.optionTenors.size(); ++i) {
131 for (Size j = 0; j < vars1.atmVols.swapTenors.size(); ++j) {
132 CommonVars vars2(i, j, shift);
133 Period expiry = vars1.atmVols.optionTenors[i];
134 Period term = vars1.atmVols.swapTenors[j];
135
136 Real atmVol = vars1.atmNormalVolMatrix->volatility(expiry, term, 0.0);
137 Real shiftedAtmVol = vars2.atmNormalVolMatrix->volatility(expiry, term, 0.0);
138 BOOST_CHECK_SMALL(shiftedAtmVol - atmVol - shift, tolerance);
139
140 // check that shift propagates to all strikes for this expiry/term
141 for (Real strike = 0.01; strike <= 0.08; strike += 0.005) {
142 Real otmVol = vars1.normalVolCubeConstantSpread->volatility(expiry, term, strike);
143 Real shiftedOtmVol = vars2.normalVolCubeConstantSpread->volatility(expiry, term, strike);
144 // BOOST_TEST_MESSAGE(i << "/" << j << "/" << strike << " : ATM Vol " << atmVol << " OTM Vol " << otmVol
145 // << " ATM Vol Shift " << shiftedAtmVol - atmVol << " OTM Vol Shift "
146 // << shiftedOtmVol - otmVol);
147 BOOST_CHECK_SMALL(shiftedOtmVol - otmVol - shift, tolerance);
148 }
149
150 // check that there is no shift in any other expiry/term
151 for (Size ii = 0; ii < vars1.atmVols.optionTenors.size(); ++ii) {
152 if (i == ii)
153 continue;
154 for (Size jj = 0; jj < vars1.atmVols.swapTenors.size(); ++jj) {
155 if (j == jj)
156 continue;
157 Period expiry = vars1.atmVols.optionTenors[ii];
158 Period term = vars1.atmVols.swapTenors[jj];
159 Real atmVol = vars1.atmNormalVolMatrix->volatility(expiry, term, 0.0);
160 Real shiftedAtmVol = vars2.atmNormalVolMatrix->volatility(expiry, term, 0.0);
161 BOOST_CHECK_SMALL(shiftedAtmVol - atmVol, tolerance);
162 for (Real strike = 0.01; strike <= 0.08; strike += 0.005) {
163 Real otmVol = vars1.normalVolCubeConstantSpread->volatility(expiry, term, strike);
164 Real shiftedOtmVol = vars2.normalVolCubeConstantSpread->volatility(expiry, term, strike);
165 BOOST_CHECK_SMALL(shiftedOtmVol - otmVol, tolerance);
166 }
167 }
168 }
169 }
170 }
171}
172
173BOOST_AUTO_TEST_CASE(testAtmLogNormalVolShiftPropagation) {
174 BOOST_TEST_MESSAGE("Testing ATM log-normal vol shift propagation...");
175
176 CommonVars vars1;
177 Real tolerance = 0.000001;
178 Real shift = 0.1;
179 for (Size i = 0; i < vars1.atmVols.optionTenors.size(); ++i) {
180 for (Size j = 0; j < vars1.atmVols.swapTenors.size(); ++j) {
181 CommonVars vars2(i, j, shift);
182 Period expiry = vars1.atmVols.optionTenors[i];
183 Period term = vars1.atmVols.swapTenors[j];
184
185 Real atmVol = vars1.atmLogNormalVolMatrix->volatility(expiry, term, 0.0);
186 Real shiftedAtmVol = vars2.atmLogNormalVolMatrix->volatility(expiry, term, 0.0);
187 BOOST_CHECK_SMALL(shiftedAtmVol - atmVol - shift, tolerance);
188
189 // check that shift propagates to all strikes for this expiry/term
190 for (Real strike = 0.01; strike <= 0.08; strike += 0.005) {
191 Real otmVol = vars1.normalVolCubeConstantSpread->volatility(expiry, term, strike);
192 Real shiftedOtmVol = vars2.normalVolCubeConstantSpread->volatility(expiry, term, strike);
193 // BOOST_TEST_MESSAGE(i << "/" << j << "/" << strike << " : ATM Vol " << atmVol << " OTM Vol " << otmVol
194 // << " ATM Vol Shift " << shiftedAtmVol - atmVol << " OTM Vol Shift "
195 // << shiftedOtmVol - otmVol);
196 BOOST_CHECK_SMALL(shiftedOtmVol - otmVol - shift, tolerance);
197 }
198
199 // check that there is no shift in any other expiry/term
200 for (Size ii = 0; ii < vars1.atmVols.optionTenors.size(); ++ii) {
201 if (i == ii)
202 continue;
203 for (Size jj = 0; jj < vars1.atmVols.swapTenors.size(); ++jj) {
204 if (j == jj)
205 continue;
206 Period expiry = vars1.atmVols.optionTenors[ii];
207 Period term = vars1.atmVols.swapTenors[jj];
208 Real atmVol = vars1.atmLogNormalVolMatrix->volatility(expiry, term, 0.0);
209 Real shiftedAtmVol = vars2.atmLogNormalVolMatrix->volatility(expiry, term, 0.0);
210 BOOST_CHECK_SMALL(shiftedAtmVol - atmVol, tolerance);
211 for (Real strike = 0.01; strike <= 0.08; strike += 0.005) {
212 Real otmVol = vars1.logNormalVolCubeConstantSpread->volatility(expiry, term, strike);
213 Real shiftedOtmVol = vars2.logNormalVolCubeConstantSpread->volatility(expiry, term, strike);
214 BOOST_CHECK_SMALL(shiftedOtmVol - otmVol, tolerance);
215 }
216 }
217 }
218 }
219 }
220}
221
222BOOST_AUTO_TEST_SUITE_END()
223
224BOOST_AUTO_TEST_SUITE_END()
Swaption cube that combines an ATM matrix and vol spreads from a cube.
structs containing swaption market data that can be used in tests
Convert swaption volatilities from one type to another.
swaption cube that combines an ATM matrix and vol spreads from a cube
Swaption volatility cube, fit-later-interpolate-early approach.
BOOST_AUTO_TEST_CASE(testAtmNormalVolShiftPropagation)
Fixture that can be used at top level.
structs containing yield curve market data that can be used in tests