22#include <boost/test/unit_test.hpp>
27#include <ql/indexes/swap/euriborswap.hpp>
28#include <ql/pricingengines/blackformula.hpp>
30#include <boost/assign/list_of.hpp>
34using namespace boost::unit_test_framework;
35using namespace boost::assign;
41 CommonVars() : referenceDate(5, Feb, 2016) {
43 Settings::instance().evaluationDate() = referenceDate;
46 QuantLib::ext::shared_ptr<IborIndex> iborIndex = conventions.floatIndex->clone(yieldCurves.forward6M);
49 swapConventions = QuantLib::ext::make_shared<SwapConventions>(conventions.settlementDays, conventions.fixedTenor,
50 conventions.fixedCalendar, conventions.fixedConvention,
51 conventions.fixedDayCounter, iborIndex);
54 atmNormalVolMatrix = QuantLib::ext::make_shared<SwaptionVolatilityMatrix>(
55 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
56 atmVols.swapTenors, atmVols.nVols, Actual365Fixed(),
true, Normal);
58 atmLogNormalVolMatrix = QuantLib::ext::make_shared<SwaptionVolatilityMatrix>(
59 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
60 atmVols.swapTenors, atmVols.lnVols, Actual365Fixed(),
true, ShiftedLognormal);
63 atmShiftedLogNormalVolMatrix_1 = QuantLib::ext::shared_ptr<SwaptionVolatilityMatrix>(
new SwaptionVolatilityMatrix(
64 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
65 atmVols.swapTenors, atmVols.slnVols_1, Actual365Fixed(),
true, ShiftedLognormal, atmVols.shifts_1));
67 atmShiftedLogNormalVolMatrix_2 = QuantLib::ext::shared_ptr<SwaptionVolatilityMatrix>(
new SwaptionVolatilityMatrix(
68 referenceDate, conventions.fixedCalendar, conventions.fixedConvention, atmVols.optionTenors,
69 atmVols.swapTenors, atmVols.slnVols_2, Actual365Fixed(),
true, ShiftedLognormal, atmVols.shifts_2));
77 QuantLib::ext::shared_ptr<SwapConventions> swapConventions;
78 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmNormalVolMatrix;
79 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmLogNormalVolMatrix;
80 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmShiftedLogNormalVolMatrix_1;
81 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> atmShiftedLogNormalVolMatrix_2;
88BOOST_AUTO_TEST_SUITE(SwaptionVolatilityConverterTest)
91 BOOST_TEST_MESSAGE(
"Testing conversion of swaption vols from normal to lognormal...");
96 Real tolerance = 0.00001;
100 vars.yieldCurves.discountEonia, vars.swapConventions, vars.swapConventions,
101 1 * Years, 30 * Years, ShiftedLognormal);
104 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
105 Volatility targetVol = 0.0;
106 Volatility outVol = 0.0;
107 Real dummyStrike = 0.0;
108 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
109 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
110 Period optionTenor = vars.atmVols.optionTenors[i];
111 Period swapTenor = vars.atmVols.swapTenors[j];
112 targetVol = vars.atmVols.lnVols[i][j];
113 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
114 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
120 BOOST_TEST_MESSAGE(
"Testing conversion of swaption vols from lognormal to normal...");
125 Real tolerance = 0.00001;
129 vars.yieldCurves.discountEonia, vars.yieldCurves.discountEonia,
130 vars.swapConventions, vars.swapConventions, 1 * Years, 30 * Years, Normal);
133 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
134 Volatility targetVol = 0.0;
135 Volatility outVol = 0.0;
136 Real dummyStrike = 0.0;
137 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
138 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
139 Period optionTenor = vars.atmVols.optionTenors[i];
140 Period swapTenor = vars.atmVols.swapTenors[j];
141 targetVol = vars.atmVols.nVols[i][j];
142 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
143 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
149 BOOST_TEST_MESSAGE(
"Testing conversion of swaption vols from normal to shifted lognormal...");
154 Real tolerance = 0.00001;
158 vars.yieldCurves.discountEonia, vars.swapConventions, vars.swapConventions,
159 1 * Years, 30 * Years, ShiftedLognormal, vars.atmVols.shifts_1);
162 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
163 Volatility targetVol = 0.0;
164 Volatility outVol = 0.0;
165 Real dummyStrike = 0.0;
166 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
167 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
168 Period optionTenor = vars.atmVols.optionTenors[i];
169 Period swapTenor = vars.atmVols.swapTenors[j];
170 targetVol = vars.atmVols.slnVols_1[i][j];
171 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
172 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
178 BOOST_TEST_MESSAGE(
"Testing conversion of swaption vols from shifted lognormal to shifted lognormal...");
183 Real tolerance = 0.00001;
187 vars.yieldCurves.discountEonia, vars.yieldCurves.discountEonia,
188 vars.swapConventions, vars.swapConventions, 1 * Years, 30 * Years,
189 ShiftedLognormal, vars.atmVols.shifts_2);
192 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
193 Volatility targetVol = 0.0;
194 Volatility outVol = 0.0;
195 Real dummyStrike = 0.0;
196 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
197 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
198 Period optionTenor = vars.atmVols.optionTenors[i];
199 Period swapTenor = vars.atmVols.swapTenors[j];
200 targetVol = vars.atmVols.slnVols_2[i][j];
201 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
202 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
208 BOOST_TEST_MESSAGE(
"Testing conversion of swaption vols from shifted lognormal to normal...");
213 Real tolerance = 0.00001;
217 vars.yieldCurves.discountEonia, vars.yieldCurves.discountEonia,
218 vars.swapConventions, vars.swapConventions, 1 * Years, 30 * Years, Normal);
221 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
222 Volatility targetVol = 0.0;
223 Volatility outVol = 0.0;
224 Real dummyStrike = 0.0;
225 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
226 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
227 Period optionTenor = vars.atmVols.optionTenors[i];
228 Period swapTenor = vars.atmVols.swapTenors[j];
229 targetVol = vars.atmVols.nVols[i][j];
230 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
231 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
237 BOOST_TEST_MESSAGE(
"Testing failure to imply lognormal vol from normal vol...");
242 vector<Period> optionTenors = list_of(Period(3, Months))(Period(1, Years));
243 vector<Period> swapTenors = list_of(Period(1, Years))(Period(5, Years));
244 Matrix normalVols(2, 2);
245 normalVols[0][0] = 0.003340;
246 normalVols[0][1] = 0.004973;
247 normalVols[1][0] = 0.003543;
248 normalVols[1][1] = 0.005270;
250 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> volMatrix = QuantLib::ext::make_shared<SwaptionVolatilityMatrix>(
251 vars.referenceDate, vars.conventions.fixedCalendar, vars.conventions.fixedConvention, optionTenors, swapTenors,
252 normalVols, Actual365Fixed(),
true, Normal);
256 vars.yieldCurves.discountEonia, vars.swapConventions, vars.swapConventions,
257 1 * Years, 30 * Years, ShiftedLognormal);
260 BOOST_CHECK_THROW(converter.
convert(), QuantLib::Error);
264 BOOST_TEST_MESSAGE(
"Testing shifts supplied to normal converter ignored...");
269 Real tolerance = 0.00001;
274 vars.referenceDate, vars.atmLogNormalVolMatrix, vars.yieldCurves.discountEonia, vars.yieldCurves.discountEonia,
275 vars.swapConventions, vars.swapConventions, 1 * Years, 30 * Years, Normal, vars.atmVols.shifts_1);
278 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
279 Volatility targetVol = 0.0;
280 Volatility outVol = 0.0;
281 Real dummyStrike = 0.0;
282 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
283 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
284 Period optionTenor = vars.atmVols.optionTenors[i];
285 Period swapTenor = vars.atmVols.swapTenors[j];
286 targetVol = vars.atmVols.nVols[i][j];
287 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
288 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
294 BOOST_TEST_MESSAGE(
"Testing construction of SwaptionVolatilityConverter from SwapIndex...");
299 Real tolerance = 0.00001;
302 QuantLib::ext::shared_ptr<SwapIndex> swapIndex =
303 QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(2 * Years, vars.yieldCurves.forward6M, vars.yieldCurves.discountEonia);
310 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
311 Volatility targetVol = 0.0;
312 Volatility outVol = 0.0;
313 Real dummyStrike = 0.0;
314 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
315 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
316 Period optionTenor = vars.atmVols.optionTenors[i];
317 Period swapTenor = vars.atmVols.swapTenors[j];
318 targetVol = vars.atmVols.nVols[i][j];
319 outVol = convertedsvs->volatility(optionTenor, swapTenor, dummyStrike);
320 BOOST_CHECK_SMALL(outVol - targetVol, tolerance);
326 BOOST_TEST_MESSAGE(
"Testing construction from SwapIndex with no exogenous discount curve...");
331 QuantLib::ext::shared_ptr<SwapIndex> swapIndex =
332 QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(2 * Years, vars.yieldCurves.forward6M);
339 BOOST_CHECK_NO_THROW(converter.
convert());
343 BOOST_TEST_MESSAGE(
"Testing lognormal to normal conversion for cube...");
348 QuantLib::ext::shared_ptr<SwapIndex> shortSwapIndex =
349 QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(1 * Years, vars.yieldCurves.forward3M, vars.yieldCurves.discountEonia);
350 QuantLib::ext::shared_ptr<SwapIndex> swapIndex =
351 QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(30 * Years, vars.yieldCurves.forward6M, vars.yieldCurves.discountEonia);
354 QuantLib::ext::shared_ptr<SwaptionVolatilityCube> cube = QuantLib::ext::make_shared<QuantExt::SwaptionVolCube2>(
355 Handle<SwaptionVolatilityStructure>(vars.atmLogNormalVolMatrix), vars.atmVols.optionTenors,
356 vars.atmVols.swapTenors, vars.atmVols.strikeSpreads, vars.atmVols.lnVolSpreads, swapIndex, shortSwapIndex,
361 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> convertedsvs = converter.
convert();
364 for (Size i = 0; i < vars.atmVols.optionTenors.size(); ++i) {
365 for (Size j = 0; j < vars.atmVols.swapTenors.size(); ++j) {
366 for (Size k = 0; k < vars.atmVols.strikeSpreads.size(); ++k) {
367 Period optionTenor = vars.atmVols.optionTenors[i];
368 Period swapTenor = vars.atmVols.swapTenors[j];
369 Real atmStrike = cube->atmStrike(optionTenor, swapTenor);
370 Real strikeSpread = vars.atmVols.strikeSpreads[k];
371 Real strike = atmStrike + strikeSpread;
373 Real inVol = cube->volatility(optionTenor, swapTenor, atmStrike + strikeSpread);
374 Real outVol = convertedsvs->volatility(optionTenor, swapTenor, atmStrike + strikeSpread);
375 Option::Type type = strikeSpread < 0.0 ? Option::Put : Option::Call;
376 Real tte = cube->optionTimes()[i];
377 Real inPrem = blackFormula(type, strike, atmStrike, inVol * std::sqrt(tte));
378 Real outPrem = bachelierBlackFormula(type, strike, atmStrike, outVol * std::sqrt(tte));
379 BOOST_CHECK_CLOSE(inPrem, outPrem, 0.01);
386BOOST_AUTO_TEST_SUITE_END()
388BOOST_AUTO_TEST_SUITE_END()
Class that converts a supplied SwaptionVolatilityStructure to one of another type with possibly diffe...
QuantLib::ext::shared_ptr< SwaptionVolatilityStructure > convert() const
Method that returns the converted SwaptionVolatilityStructure
structs containing swaption market data that can be used in tests
Convert swaption volatilities from one type to another.
Swaption volatility cube, fit-later-interpolate-early approach.
BOOST_AUTO_TEST_CASE(testNormalToLognormal)
Fixture that can be used at top level.
structs containing yield curve market data that can be used in tests