Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
capfloortermvolcurve.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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
19#include <boost/assign.hpp>
20// clang-format off
21#include <boost/test/unit_test.hpp>
22#include <boost/test/data/test_case.hpp>
23// clang-format on
24#include <boost/variant.hpp>
25
28
29#include <ql/math/interpolations/backwardflatinterpolation.hpp>
30#include <ql/quotes/simplequote.hpp>
33
34using namespace QuantExt;
35using namespace QuantLib;
36using namespace boost::unit_test_framework;
37
39using boost::assign::list_of;
40using boost::assign::map_list_of;
41using std::boolalpha;
42using std::fixed;
43using std::map;
44using std::ostream;
45using std::pair;
46using std::setprecision;
47using std::string;
48using std::vector;
49
50namespace {
51
52// Variables to be used in the test
53struct CommonVars : public qle::test::TopLevelFixture {
54
55 // Constructor
56 CommonVars()
57 : referenceDate(5, Feb, 2016), settlementDays(0), calendar(TARGET()), bdc(Following),
58 dayCounter(Actual365Fixed()), tolerance(1.0e-12) {
59
60 // Reference date
61 Settings::instance().evaluationDate() = referenceDate;
62
63 // Test cap floor data
64 CapFloorVolatilityEUR testData;
65 tenors = testData.atmTenors;
66 volQuotes.resize(tenors.size());
67 volHandles.resize(tenors.size());
68 for (Size i = 0; i < tenors.size(); ++i) {
69 volQuotes[i] = QuantLib::ext::make_shared<SimpleQuote>(testData.nAtmVols[i]);
70 volHandles[i] = Handle<Quote>(volQuotes[i]);
71 }
72 }
73
74 // Valuation date for the test
75 Date referenceDate;
76
77 // Variables used in the optionlet volatility structure creation
78 Natural settlementDays;
79 Calendar calendar;
80 BusinessDayConvention bdc;
81 DayCounter dayCounter;
82
83 // Test tolerance for comparing the NPVs
84 Real tolerance;
85
86 // Cap floor test data
87 vector<Period> tenors;
88 vector<QuantLib::ext::shared_ptr<SimpleQuote> > volQuotes;
89 vector<Handle<Quote> > volHandles;
90};
91
92// Interpolation types for the data driven test case
93typedef boost::variant<Linear, BackwardFlat, QuantExt::LinearFlat, Cubic, QuantExt::CubicFlat> InterpolationType;
94
95vector<InterpolationType> interpolationTypes = list_of(InterpolationType(Linear()))(InterpolationType(BackwardFlat()))(
96 InterpolationType(QuantExt::LinearFlat()))(InterpolationType(Cubic()))(InterpolationType(QuantExt::CubicFlat()));
97
98// If the cap floor term volatility curve in the test has a floating or fixed reference date
99vector<bool> isMovingValues = list_of(true)(false);
100
101// If the cap floor term volatility curve has a flat first period or not
102vector<bool> flatFirstPeriodValues = list_of(true)(false);
103
104// So that I can reuse below
105string to_string(const InterpolationType& interpolationType) {
106 string result;
107 switch (interpolationType.which()) {
108 case 0:
109 result = "Linear";
110 break;
111 case 1:
112 result = "BackwardFlat";
113 break;
114 case 2:
115 result = "LinearFlat";
116 break;
117 case 3:
118 result = "Cubic";
119 break;
120 case 4:
121 result = "CubicFlat";
122 break;
123 default:
124 BOOST_FAIL("Unexpected interpolation type");
125 }
126 return result;
127}
128
129} // namespace
130
131// Needed for BOOST_DATA_TEST_CASE below
132// https://stackoverflow.com/a/33965517/1771882
133namespace boost {
134namespace test_tools {
135namespace tt_detail {
136template <> struct print_log_value<InterpolationType> {
137 void operator()(ostream& os, const InterpolationType& interpolationType) { os << to_string(interpolationType); }
138};
139} // namespace tt_detail
140} // namespace test_tools
141} // namespace boost
142
143BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
144
145BOOST_AUTO_TEST_SUITE(CapFloorTermVolCurveTests)
146
147BOOST_DATA_TEST_CASE_F(CommonVars, testCapFloorTermVolCurveInterpolation,
148 bdata::make(interpolationTypes) * bdata::make(isMovingValues) *
149 bdata::make(flatFirstPeriodValues),
150 interpolationType, isMoving, flatFirstPeriod) {
151
152 BOOST_TEST_MESSAGE("Testing cap floor term volatility curve with different interpolation methods");
153
154 BOOST_TEST_MESSAGE("Test inputs are:");
155 BOOST_TEST_MESSAGE(" Interpolation type: " << to_string(interpolationType));
156 BOOST_TEST_MESSAGE(" Floating reference date: " << boolalpha << isMoving);
157 BOOST_TEST_MESSAGE(" Flat first period: " << boolalpha << flatFirstPeriod);
158
159 // Create the CapFloorTermVolatilityStructure using the appropriate interpolation and reference date/settlement days
160 QuantLib::ext::shared_ptr<CapFloorTermVolatilityStructure> cftvs;
161 switch (interpolationType.which()) {
162 case 0:
163 if (isMoving) {
164 BOOST_TEST_MESSAGE("Using Linear interpolation with a moving reference date");
165 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
166 settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
167 } else {
168 BOOST_TEST_MESSAGE("Using Linear interpolation with a fixed reference date");
169 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
170 referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
171 }
172 break;
173 case 1:
174 if (isMoving) {
175 BOOST_TEST_MESSAGE("Using BackwardFlat interpolation with a moving reference date");
176 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<BackwardFlat> >(
177 settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
178 } else {
179 BOOST_TEST_MESSAGE("Using BackwardFlat interpolation with a fixed reference date");
180 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<BackwardFlat> >(
181 referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
182 }
183 break;
184 case 2:
185 if (isMoving) {
186 BOOST_TEST_MESSAGE("Using LinearFlat interpolation with a moving reference date");
187 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<LinearFlat> >(
188 settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
189 } else {
190 BOOST_TEST_MESSAGE("Using LinearFlat interpolation with a fixed reference date");
191 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<LinearFlat> >(
192 referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
193 }
194 break;
195 case 3:
196 if (isMoving) {
197 BOOST_TEST_MESSAGE("Using Cubic interpolation with a moving reference date");
198 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
199 settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
200 } else {
201 BOOST_TEST_MESSAGE("Using Cubic interpolation with a fixed reference date");
202 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
203 referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
204 }
205 break;
206 case 4:
207 if (isMoving) {
208 BOOST_TEST_MESSAGE("Using CubicFlat interpolation with a moving reference date");
209 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<CubicFlat> >(
210 settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
211 } else {
212 BOOST_TEST_MESSAGE("Using CubicFlat interpolation with a fixed reference date");
213 BOOST_REQUIRE_NO_THROW(cftvs = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<CubicFlat> >(
214 referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
215 }
216 break;
217 default:
218 BOOST_FAIL("Unexpected interpolation type");
219 }
220
221 BOOST_TEST_MESSAGE("Test the initial curve dates");
222 for (Size i = 0; i < tenors.size(); ++i) {
223 Date curveDate = cftvs->optionDateFromTenor(tenors[i]);
224 Date manualDate = calendar.advance(referenceDate, tenors[i], bdc);
225 BOOST_CHECK_EQUAL(curveDate, manualDate);
226 }
227
228 BOOST_TEST_MESSAGE("Test that curve returns input values on pillars");
229 for (Size i = 0; i < tenors.size(); ++i) {
230 Volatility vol = cftvs->volatility(tenors[i], 0.01);
231 BOOST_CHECK_SMALL(volQuotes[i]->value() - vol, tolerance);
232 }
233
234 // Bump the 5Y ATM volatility quote (3rd element in quote vector)
235 Size idx = 2;
236 Volatility bump = 0.0005;
237 Volatility baseValue = volQuotes[idx]->value();
238 volQuotes[idx]->setValue(baseValue + bump);
239
240 BOOST_TEST_MESSAGE("Test that curve returns input values on pillars after bump");
241 for (Size i = 0; i < tenors.size(); ++i) {
242 Volatility vol = cftvs->volatility(tenors[i], 0.01);
243 BOOST_CHECK_SMALL(volQuotes[i]->value() - vol, tolerance);
244 // Also check the point was bumped
245 if (i == idx) {
246 BOOST_CHECK_SMALL(vol - baseValue - bump, tolerance);
247 }
248 }
249
250 BOOST_TEST_MESSAGE("Test the curve dates after moving the evaluation date");
251 Date manualDate;
252 Date newDate = calendar.advance(referenceDate, 1 * Months);
253 Settings::instance().evaluationDate() = newDate;
254 for (Size i = 0; i < tenors.size(); ++i) {
255 Date curveDate = cftvs->optionDateFromTenor(tenors[i]);
256 manualDate =
257 isMoving ? calendar.advance(newDate, tenors[i], bdc) : calendar.advance(referenceDate, tenors[i], bdc);
258 BOOST_CHECK_EQUAL(curveDate, manualDate);
259 }
260
261 BOOST_TEST_MESSAGE("Test that curve returns input values after moving the evaluation date");
262 for (Size i = 0; i < tenors.size(); ++i) {
263 Volatility vol = cftvs->volatility(tenors[i], 0.01);
264 BOOST_CHECK_SMALL(volQuotes[i]->value() - vol, tolerance);
265 }
266
267 // Reset the evaluation date
268 Settings::instance().evaluationDate() = referenceDate;
269
270 BOOST_TEST_MESSAGE("Test extrapolation settings with out of range date");
271 Date oorDate = cftvs->maxDate() + 1 * Months;
272 BOOST_CHECK_NO_THROW(cftvs->volatility(oorDate, 0.01, true));
273 BOOST_CHECK_THROW(cftvs->volatility(oorDate, 0.01), Error);
274 cftvs->enableExtrapolation();
275 BOOST_CHECK_NO_THROW(cftvs->volatility(oorDate, 0.01));
276}
277
278BOOST_AUTO_TEST_SUITE_END()
279
280BOOST_AUTO_TEST_SUITE_END()
structs containing capfloor market data that can be used in tests
BOOST_DATA_TEST_CASE_F(CommonVars, testCapFloorTermVolCurveInterpolation, bdata::make(interpolationTypes) *bdata::make(isMovingValues) *bdata::make(flatFirstPeriodValues), interpolationType, isMoving, flatFirstPeriod)
Cap floor at-the-money term volatility curve.
Cubic interpolation and flat extrapolation factory and traits.
Interpolated cap floor term volatility curve.
Linear-interpolation and flat extrapolation factory and traits
flat interpolation decorator
Fixture that can be used at top level.