Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
generalisedreplicatingvarianceswapengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 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/*! \file test/varianceswap.hpp
20 \brief variance swap pricing engine test suite
21*/
22
24#include <oret/toplevelfixture.hpp>
25
26#include <boost/test/unit_test.hpp>
27
53#include <qle/models/lgm.hpp>
77
78#include <ql/pricingengines/credit/midpointcdsengine.hpp>
79#include <ql/pricingengines/forward/replicatingvarianceswapengine.hpp>
80#include <ql/processes/blackscholesprocess.hpp>
81#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
82
83#include <ql/math/array.hpp>
84#include <ql/math/comparison.hpp>
85#include <ql/quotes/simplequote.hpp>
86#include <ql/termstructures/yield/flatforward.hpp>
87#include <ql/time/calendars/nullcalendar.hpp>
88#include <ql/time/calendars/target.hpp>
89#include <ql/time/daycounters/actualactual.hpp>
90
91#include <boost/make_shared.hpp>
92
93#define LENGTH(a) (sizeof(a) / sizeof(a[0]))
94
95using namespace QuantLib;
96using namespace QuantExt;
97
98using boost::unit_test_framework::test_suite;
99
100namespace {
101
102struct ReplicatingVarianceSwapData {
103 Position::Type type;
104 Real varStrike;
105 Real nominal;
106 Real s; // spot
107 Rate q; // dividend
108 Rate r; // risk-free rate
109 Time t; // time to maturity
110 Volatility v; // volatility at t
111 Real result; // result
112 Real tol; // tolerance
113};
114
115struct Datum {
116 Option::Type type;
117 Real strike;
118 Volatility v;
119};
120
121} // namespace
122
123BOOST_FIXTURE_TEST_SUITE(OREPlusEquityFXTestSuite, ore::test::TopLevelFixture)
124
125BOOST_AUTO_TEST_SUITE(GeneralisedReplicatingVarianceSwapEngineTest)
126
127BOOST_AUTO_TEST_CASE(testT0Pricing) {
128
129 SavedSettings backup;
130 Date today = Date(3, Oct, 2019);
131 Settings::instance().evaluationDate() = today;
132 Calendar cal = TARGET();
133 DayCounter dc = Actual365Fixed();
134 Date exDate = today + Integer(0.246575 * 365 + 0.5);
135 std::vector<Date> dates(1, exDate);
136 Real volatilityStrike = 0.2;
137 Real varianceStrike = volatilityStrike * volatilityStrike;
138 Real vegaNotional = 50000.0;
139 Real varianceNotional = vegaNotional / (2.0 * 100.0 * volatilityStrike);
140
141 // add strikes in C++98 compatible way
142 Real arrStrikes[] = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, // Put Strikes
143 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0}; // Call Strikes
144 std::vector<Real> strikes(arrStrikes, arrStrikes + sizeof(arrStrikes) / sizeof(Real));
145 // add vols in C++98 compatible way
146 Real arrVols[] = {0.3, 0.29, 0.28, 0.27, 0.26, 0.25, 0.24, 0.23, 0.22, 0.21, 0.2, // Put Vols
147 0.19, 0.18, 0.17, 0.16, 0.15, 0.14, 0.13}; // Call Vols
148 std::vector<Real> volsVector(arrVols, arrVols + sizeof(arrVols) / sizeof(Real));
149 Matrix vols(18, 1, volsVector.begin(), volsVector.end());
150
151 BOOST_TEST_MESSAGE("Testing t0 pricing of the QuantExt VarSwap engine, as per Demeterfi et. al (1999).");
152 std::string equityName = "STE";
153 QuantLib::ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
154 Handle<Quote> equityPrice = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100.0));
155 Handle<YieldTermStructure> yieldTS =
156 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
157 Handle<YieldTermStructure> dividendTS =
158 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dc));
159 Handle<BlackVolTermStructure> volTS = Handle<BlackVolTermStructure>(
160 QuantLib::ext::make_shared<BlackVarianceSurface>(today, NullCalendar(), dates, strikes, vols, dc));
161 Handle<YieldTermStructure> discountingTS =
162 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
163 QuantLib::ext::shared_ptr<Index> eqIndex =
164 QuantLib::ext::make_shared<EquityIndex2>(equityName, cal, EURCurrency(), equityPrice, yieldTS, dividendTS);
165
166 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
167 new BlackScholesMertonProcess(equityPrice, dividendTS, discountingTS, volTS));
168
169 QuantLib::ext::shared_ptr<PricingEngine> engine(new GeneralisedReplicatingVarianceSwapEngine(
170 eqIndex, stochProcess, discountingTS, GeneralisedReplicatingVarianceSwapEngine::VarSwapSettings()));
171
172 QuantExt::VarianceSwap2 varianceSwap(Position::Long, varianceStrike, varianceNotional, today, exDate, cal, false);
173 varianceSwap.setPricingEngine(engine);
174
175 Real result = varianceSwap.variance();
176 Real expected = 0.040203605175062058;
177 Real tol = 1.0e-4;
178 BOOST_CHECK_CLOSE(result, expected, tol);
179 result = varianceSwap.NPV();
180 expected = 2513.8798089810457;
181 BOOST_CHECK_CLOSE(result, expected, tol);
182}
183
184BOOST_AUTO_TEST_CASE(testSeasonedSwapPricing) {
185
186 SavedSettings backup;
187 Date today = Date(30, April, 2018);
188 Settings::instance().evaluationDate() = today;
189 DayCounter dc = Actual365Fixed();
190 Date startDate = today - Integer(0.019178 * 365 + 0.5); // started 7 calendar days ago
191 Date exDate = today + Integer(0.246575 * 365 + 0.5);
192 std::string equityName = "STE";
193 Calendar cal = TARGET();
194 std::vector<Date> pastDates;
195 std::vector<Date> dates(1, exDate);
196 Real volatilityStrike = 0.2;
197 Real varianceStrike = volatilityStrike * volatilityStrike;
198 Real vegaNotional = 50000.0;
199 Real varianceNotional = vegaNotional / (2.0 * 100.0 * volatilityStrike);
200
201 for (Date day = cal.adjust(cal.advance(startDate, -1, Days)); day < today; day = cal.advance(day, 1, Days)) {
202 pastDates.push_back(day);
203 }
204
205 // add fixings in C++98 compatible way
206 Real arrFixings[] = {98.5, 98.0, 99.0, 100.2, 99.4, 98.2};
207 std::vector<Real> fixings(arrFixings, arrFixings + sizeof(arrFixings) / sizeof(Real));
208 TimeSeries<Real> fixingHistory(pastDates.begin(), pastDates.end(), fixings.begin());
209 IndexManager::instance().setHistory(equityName, fixingHistory);
210
211 // add strikes in C++98 compatible way
212 Real arrStrikes[] = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, // Put Strikes
213 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0}; // Call Strikes
214 std::vector<Real> strikes(arrStrikes, arrStrikes + sizeof(arrStrikes) / sizeof(Real));
215 // add vols in C++98 compatible way
216 Real arrVols[] = {0.3, 0.29, 0.28, 0.27, 0.26, 0.25, 0.24, 0.23, 0.22, 0.21, 0.2, // Put Vols
217 0.19, 0.18, 0.17, 0.16, 0.15, 0.14, 0.13}; // Call Vols
218 std::vector<Real> volsVector(arrVols, arrVols + sizeof(arrVols) / sizeof(Real));
219 Matrix vols(18, 1, volsVector.begin(), volsVector.end());
220
221 BOOST_TEST_MESSAGE("Testing seasoned swap pricing of the QuantExt VarSwap engine.");
222 QuantLib::ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
223 Handle<Quote> equityPrice = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100.0));
224 Handle<YieldTermStructure> yieldTS = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, cal, 0.05, dc));
225 Handle<YieldTermStructure> dividendTS =
226 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, cal, 0.0, dc));
227 Handle<BlackVolTermStructure> volTS =
228 Handle<BlackVolTermStructure>(QuantLib::ext::make_shared<BlackVarianceSurface>(today, cal, dates, strikes, vols, dc));
229 Handle<YieldTermStructure> discountingTS =
230 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, cal, 0.05, dc));
231 QuantLib::ext::shared_ptr<Index> eqIndex =
232 QuantLib::ext::make_shared<EquityIndex2>(equityName, cal, EURCurrency(), equityPrice, yieldTS, dividendTS);
233
234 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
235 new BlackScholesMertonProcess(equityPrice, dividendTS, yieldTS, volTS));
236
237 QuantLib::ext::shared_ptr<PricingEngine> engine(new GeneralisedReplicatingVarianceSwapEngine(
238 eqIndex, stochProcess, discountingTS, GeneralisedReplicatingVarianceSwapEngine::VarSwapSettings()));
239
240 QuantExt::VarianceSwap2 varianceSwap(Position::Long, varianceStrike, varianceNotional, startDate, exDate, cal,
241 false);
242 varianceSwap.setPricingEngine(engine);
243
244 Real result = varianceSwap.variance();
245 Real expected = 0.040169651620750264;
246 Real tol = 1.0e-4;
247 BOOST_CHECK_CLOSE(result, expected, tol);
248 result = varianceSwap.NPV();
249 expected = 2094.6608249765977;
250 BOOST_CHECK_CLOSE(result, expected, tol);
251
252 // A little clean up of the environment.
253 IndexManager::instance().clearHistory("EQ/" + equityName);
254}
255
256BOOST_AUTO_TEST_CASE(testForwardStartPricing) {
257
258 SavedSettings backup;
259 Date today(2, Jul, 2018);
260 Settings::instance().evaluationDate() = today;
261 Calendar cal = TARGET();
262 DayCounter dc = Actual365Fixed();
263 Date exDate = today + Integer(0.246575 * 365 + 0.5);
264 std::vector<Date> dates(1, exDate);
265 Real volatilityStrike = 0.2;
266 Real varianceStrike = volatilityStrike * volatilityStrike;
267 Real vegaNotional = 50000.0;
268 Real varianceNotional = vegaNotional / (2.0 * 100.0 * volatilityStrike);
269
270 // add strikes in C++98 compatible way
271 Real arrStrikes[] = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, // Put Strikes
272 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0}; // Call Strikes
273 std::vector<Real> strikes(arrStrikes, arrStrikes + sizeof(arrStrikes) / sizeof(Real));
274 // add vols in C++98 compatible way
275 Real arrVols[] = {0.3, 0.29, 0.28, 0.27, 0.26, 0.25, 0.24, 0.23, 0.22, 0.21, 0.2, // Put Vols
276 0.19, 0.18, 0.17, 0.16, 0.15, 0.14, 0.13}; // Call Vols
277 std::vector<Real> volsVector(arrVols, arrVols + sizeof(arrVols) / sizeof(Real));
278 Matrix vols(18, 1, volsVector.begin(), volsVector.end());
279
280 BOOST_TEST_MESSAGE(
281 "Testing future starting pricing of the QuantExt VarSwap engine, as per Demeterfi et. al (1999).");
282 std::string equityName = "STE";
283 QuantLib::ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
284 Handle<Quote> equityPrice = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100.0));
285 Handle<YieldTermStructure> yieldTS =
286 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
287 Handle<YieldTermStructure> dividendTS =
288 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dc));
289 Handle<BlackVolTermStructure> volTS = Handle<BlackVolTermStructure>(
290 QuantLib::ext::make_shared<BlackVarianceSurface>(today, NullCalendar(), dates, strikes, vols, dc));
291 Handle<YieldTermStructure> discountingTS =
292 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
293 QuantLib::ext::shared_ptr<Index> eqIndex =
294 QuantLib::ext::make_shared<EquityIndex2>(equityName, cal, EURCurrency(), equityPrice, yieldTS, dividendTS);
295
296 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
297 new BlackScholesMertonProcess(equityPrice, dividendTS, discountingTS, volTS));
298
299 QuantLib::ext::shared_ptr<PricingEngine> engine(new GeneralisedReplicatingVarianceSwapEngine(
300 eqIndex, stochProcess, discountingTS, GeneralisedReplicatingVarianceSwapEngine::VarSwapSettings()));
301
302 QuantExt::VarianceSwap2 varianceSwap(Position::Long, varianceStrike, varianceNotional, today + 7, exDate, cal,
303 false);
304 varianceSwap.setPricingEngine(engine);
305
306 Real result = varianceSwap.variance();
307 Real expected = 0.038880652347511133;
308 Real tol = 1.0e-4;
309 BOOST_CHECK_CLOSE(result, expected, tol);
310 result = varianceSwap.NPV();
311 expected = -13820.40246258254;
312 BOOST_CHECK_CLOSE(result, expected, tol);
313}
314
315BOOST_AUTO_TEST_CASE(testReplicatingVarianceSwap) {
316
317 BOOST_TEST_MESSAGE("Testing variance swap with replicating cost engine...");
318
319 ReplicatingVarianceSwapData values[] = {
320
321 // data from "A Guide to Volatility and Variance Swaps",
322 // Derman, Kamal & Zou, 1999
323 // with maturity t corrected from 0.25 to 0.246575
324 // corresponding to Jan 1, 1999 to Apr 1, 1999
325
326 // type, varStrike, nominal, s, q, r, t, v, result, tol
327 {Position::Long, 0.04, 50000, 100.0, 0.00, 0.05, 0.246575, 0.20, 0.041888574, 1.0e-4}
328
329 };
330
331 Datum replicatingOptionData[] = {
332
333 // data from "A Guide to Volatility and Variance Swaps",
334 // Derman, Kamal & Zou, 1999
335
336 // Option::Type, strike, v
337 {Option::Put, 50, 0.30}, {Option::Put, 55, 0.29}, {Option::Put, 60, 0.28}, {Option::Put, 65, 0.27},
338 {Option::Put, 70, 0.26}, {Option::Put, 75, 0.25}, {Option::Put, 80, 0.24}, {Option::Put, 85, 0.23},
339 {Option::Put, 90, 0.22}, {Option::Put, 95, 0.21}, {Option::Put, 100, 0.20}, {Option::Call, 100, 0.20},
340 {Option::Call, 105, 0.19}, {Option::Call, 110, 0.18}, {Option::Call, 115, 0.17}, {Option::Call, 120, 0.16},
341 {Option::Call, 125, 0.15}, {Option::Call, 130, 0.14}, {Option::Call, 135, 0.13}};
342
343 SavedSettings backup;
344 DayCounter dc = Actual365Fixed();
345 Date today = Date::todaysDate();
346 Settings::instance().evaluationDate() = today;
347
348 QuantLib::ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
349 QuantLib::ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
350 QuantLib::ext::shared_ptr<YieldTermStructure> qTS =
351 QuantLib::ext::shared_ptr<YieldTermStructure>(new FlatForward(today, Handle<Quote>(qRate), dc));
352 QuantLib::ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
353 QuantLib::ext::shared_ptr<YieldTermStructure> rTS =
354 QuantLib::ext::shared_ptr<YieldTermStructure>(new FlatForward(today, Handle<Quote>(rRate), dc));
355
356 for (Size i = 0; i < LENGTH(values); i++) {
357 Date exDate = today + Integer(values[i].t * 365 + 0.5);
358 std::vector<Date> dates(1);
359 dates[0] = exDate;
360
361 spot->setValue(values[i].s);
362 qRate->setValue(values[i].q);
363 rRate->setValue(values[i].r);
364
365 Size options = LENGTH(replicatingOptionData);
366 std::vector<Real> callStrikes, putStrikes, callVols, putVols;
367
368 // Assumes ascending strikes and same min call and max put strikes
369 Size j;
370 for (j = 0; j < options; j++) {
371 if (replicatingOptionData[j].type == Option::Call) {
372 callStrikes.push_back(replicatingOptionData[j].strike);
373 callVols.push_back(replicatingOptionData[j].v);
374 } else if (replicatingOptionData[j].type == Option::Put) {
375 putStrikes.push_back(replicatingOptionData[j].strike);
376 putVols.push_back(replicatingOptionData[j].v);
377 } else {
378 QL_FAIL("unknown option type");
379 }
380 }
381
382 Matrix vols(options - 1, 1);
383 std::vector<Real> strikes;
384 for (j = 0; j < putVols.size(); j++) {
385 vols[j][0] = putVols[j];
386 strikes.push_back(putStrikes[j]);
387 }
388
389 for (Size k = 1; k < callVols.size(); k++) {
390 Size j = putVols.size() - 1;
391 vols[j + k][0] = callVols[k];
392 strikes.push_back(callStrikes[k]);
393 }
394
395 QuantLib::ext::shared_ptr<BlackVolTermStructure> volTS(
396 new BlackVarianceSurface(today, NullCalendar(), dates, strikes, vols, dc));
397
398 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
399 new BlackScholesMertonProcess(Handle<Quote>(spot), Handle<YieldTermStructure>(qTS),
400 Handle<YieldTermStructure>(rTS), Handle<BlackVolTermStructure>(volTS)));
401
402 QuantLib::ext::shared_ptr<PricingEngine> engine(
403 new ReplicatingVarianceSwapEngine(stochProcess, 5.0, callStrikes, putStrikes));
404
405 QuantLib::VarianceSwap varianceSwap(values[i].type, values[i].varStrike, values[i].nominal, today, exDate);
406 varianceSwap.setPricingEngine(engine);
407
408 Real calculated = varianceSwap.variance();
409 Real expected = values[i].result;
410 Real tol = 1.0e-4;
411 BOOST_CHECK_CLOSE(calculated, expected, tol);
412 }
413}
414
415BOOST_AUTO_TEST_SUITE_END()
416
417BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(testT0Pricing)
vector< Real > strikes