[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 Warren Chou
5 Copyright (C) 2007, 2008 StatPro Italia srl
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
21#include "varianceswaps.hpp"
22#include "utilities.hpp"
23#include <ql/quotes/simplequote.hpp>
24#include <ql/time/daycounters/actual365fixed.hpp>
25#include <ql/time/calendars/nullcalendar.hpp>
26#include <ql/instruments/varianceswap.hpp>
27#include <ql/pricingengines/forward/replicatingvarianceswapengine.hpp>
28#include <ql/pricingengines/forward/mcvarianceswapengine.hpp>
29#include <ql/math/randomnumbers/rngtraits.hpp>
30#include <ql/termstructures/yield/flatforward.hpp>
31#include <ql/termstructures/volatility/equityfx/blackvariancecurve.hpp>
32#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
33#include <ql/utilities/dataformatters.hpp>
34#include <ql/processes/blackscholesprocess.hpp>
35
36using namespace QuantLib;
37using namespace boost::unit_test_framework;
38
39#undef REPORT_FAILURE
40#define REPORT_FAILURE(greekName, isLong, varStrike, nominal, s, q, r, today, \
41 exDate, v, expected, calculated, error, tolerance) \
42 BOOST_ERROR( \
43 " variance swap with " \
44 << " underlying value: " << s << "\n" \
45 << " strike: " << varStrike << "\n" \
46 << " nominal: " << nominal << "\n" \
47 << " dividend yield: " << io::rate(q) << "\n" \
48 << " risk-free rate: " << io::rate(r) << "\n" \
49 << " reference date: " << today << "\n" \
50 << " maturity: " << exDate << "\n" \
51 << " volatility: " << io::volatility(v) << "\n\n" \
52 << " expected " << greekName << ": " << expected << "\n" \
53 << " calculated " << greekName << ": " << calculated << "\n"\
54 << " error: " << error << "\n" \
55 << " tolerance: " << tolerance);
56
57
58namespace {
59
60 struct MCVarianceSwapData {
61 Position::Type type;
62 Real varStrike;
63 Real nominal;
64 Real s; // spot
65 Rate q; // dividend
66 Rate r; // risk-free rate
67 Time t1; // intermediate time
68 Time t; // time to maturity
69 Volatility v1; // volatility at t1
70 Volatility v; // volatility at t
71 Real result; // result
72 Real tol; // tolerance
73 };
74
75 struct ReplicatingVarianceSwapData {
76 Position::Type type;
77 Real varStrike;
78 Real nominal;
79 Real s; // spot
80 Rate q; // dividend
81 Rate r; // risk-free rate
82 Time t; // time to maturity
83 Volatility v; // volatility at t
84 Real result; // result
85 Real tol; // tolerance
86 };
87
88 struct Datum {
89 Option::Type type;
90 Real strike;
91 Volatility v;
92 };
93
94}
95
96
97void VarianceSwapTest::testReplicatingVarianceSwap() {
98
99 BOOST_TEST_MESSAGE("Testing variance swap with replicating cost engine...");
100
101 ReplicatingVarianceSwapData values[] = {
102
103 // data from "A Guide to Volatility and Variance Swaps",
104 // Derman, Kamal & Zou, 1999
105 // with maturity t corrected from 0.25 to 0.246575
106 // corresponding to Jan 1, 1999 to Apr 1, 1999
107
108 //type, varStrike, nominal, s, q, r, t, v, result, tol
109 { .type: Position::Long, .varStrike: 0.04, .nominal: 50000, .s: 100.0, .q: 0.00, .r: 0.05, .t: 0.246575, .v: 0.20, .result: 0.04189, .tol: 1.0e-4}
110
111 };
112
113 Datum replicatingOptionData[] = {
114
115 // data from "A Guide to Volatility and Variance Swaps",
116 // Derman, Kamal & Zou, 1999
117
118 //Option::Type, strike, v
119 { .type: Option::Put, .strike: 50, .v: 0.30 },
120 { .type: Option::Put, .strike: 55, .v: 0.29 },
121 { .type: Option::Put, .strike: 60, .v: 0.28 },
122 { .type: Option::Put, .strike: 65, .v: 0.27 },
123 { .type: Option::Put, .strike: 70, .v: 0.26 },
124 { .type: Option::Put, .strike: 75, .v: 0.25 },
125 { .type: Option::Put, .strike: 80, .v: 0.24 },
126 { .type: Option::Put, .strike: 85, .v: 0.23 },
127 { .type: Option::Put, .strike: 90, .v: 0.22 },
128 { .type: Option::Put, .strike: 95, .v: 0.21 },
129 { .type: Option::Put, .strike: 100, .v: 0.20 },
130 { .type: Option::Call, .strike: 100, .v: 0.20 },
131 { .type: Option::Call, .strike: 105, .v: 0.19 },
132 { .type: Option::Call, .strike: 110, .v: 0.18 },
133 { .type: Option::Call, .strike: 115, .v: 0.17 },
134 { .type: Option::Call, .strike: 120, .v: 0.16 },
135 { .type: Option::Call, .strike: 125, .v: 0.15 },
136 { .type: Option::Call, .strike: 130, .v: 0.14 },
137 { .type: Option::Call, .strike: 135, .v: 0.13 }
138 };
139
140 DayCounter dc = Actual365Fixed();
141 Date today = Date::todaysDate();
142
143 ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
144 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
145 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc);
146 ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
147 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc);
148
149 for (auto& value : values) {
150 Date exDate = today + timeToDays(t: value.t, daysPerYear: 365);
151 std::vector<Date> dates(1);
152 dates[0] = exDate;
153
154 spot->setValue(value.s);
155 qRate->setValue(value.q);
156 rRate->setValue(value.r);
157
158 Size options = LENGTH(replicatingOptionData);
159 std::vector<Real> callStrikes, putStrikes, callVols, putVols;
160
161 // Assumes ascending strikes and same min call and max put strikes
162 Size j;
163 for (j=0; j<options; j++) {
164 if (replicatingOptionData[j].type == Option::Call) {
165 callStrikes.push_back(x: replicatingOptionData[j].strike);
166 callVols.push_back(x: replicatingOptionData[j].v);
167 } else if (replicatingOptionData[j].type == Option::Put) {
168 putStrikes.push_back(x: replicatingOptionData[j].strike);
169 putVols.push_back(x: replicatingOptionData[j].v);
170 } else {
171 QL_FAIL("unknown option type");
172 }
173 }
174
175 Matrix vols(options-1, 1);
176 std::vector<Real> strikes;
177 for (j=0; j<putVols.size(); j++) {
178 vols[j][0] = putVols[j];
179 strikes.push_back(x: putStrikes[j]);
180 }
181
182 for (Size k=1; k<callVols.size(); k++) {
183 Size j = putVols.size()-1;
184 vols[j+k][0] = callVols[k];
185 strikes.push_back(x: callStrikes[k]);
186 }
187
188 ext::shared_ptr<BlackVolTermStructure> volTS(new
189 BlackVarianceSurface(today, NullCalendar(),
190 dates, strikes, vols, dc));
191
192 ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
193 new BlackScholesMertonProcess(
194 Handle<Quote>(spot),
195 Handle<YieldTermStructure>(qTS),
196 Handle<YieldTermStructure>(rTS),
197 Handle<BlackVolTermStructure>(volTS)));
198
199
200 ext::shared_ptr<PricingEngine> engine(
201 new ReplicatingVarianceSwapEngine(stochProcess, 5.0,
202 callStrikes,
203 putStrikes));
204
205 VarianceSwap varianceSwap(value.type, value.varStrike, value.nominal, today, exDate);
206 varianceSwap.setPricingEngine(engine);
207
208 Real calculated = varianceSwap.variance();
209 Real expected = value.result;
210 Real error = std::fabs(x: calculated-expected);
211 if (error > value.tol)
212 REPORT_FAILURE("value", values[i].type, value.varStrike, value.nominal, value.s,
213 value.q, value.r, today, exDate, value.v, expected, calculated, error,
214 value.tol);
215 }
216}
217
218
219void VarianceSwapTest::testMCVarianceSwap() {
220
221 BOOST_TEST_MESSAGE("Testing variance swap with Monte Carlo engine...");
222
223 MCVarianceSwapData values[] = {
224
225 // data from "A Guide to Volatility and Variance Swaps",
226 // Derman, Kamal & Zou, 1999
227 // with maturity t corrected from 0.25 to 0.246575
228 // corresponding to Jan 1, 1999 to Apr 1, 1999
229
230 // exercising code using BlackVarianceCurve because BlackVarianceSurface is unreliable
231 // Result should be v*v for arbitrary t1 and v1 (as long as 0<=t1<t and 0<=v1<v)
232
233 //type, varStrike, nominal, s, q, r, t1, t, v1, v, result, tol
234 { .type: Position::Long, .varStrike: 0.04, .nominal: 50000, .s: 100.0, .q: 0.00, .r: 0.05, .t1: 0.1, .t: 0.246575, .v1: 0.1, .v: 0.20, .result: 0.04, .tol: 3.0e-4}
235
236 };
237
238
239 DayCounter dc = Actual365Fixed();
240 Date today = Date::todaysDate();
241
242 ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
243 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
244 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc);
245 ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
246 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc);
247 std::vector<Volatility> vols(2);
248 std::vector<Date> dates(2);
249
250 for (auto& value : values) {
251 Date exDate = today + timeToDays(t: value.t, daysPerYear: 365);
252 Date intermDate = today + timeToDays(t: value.t1, daysPerYear: 365);
253 ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate));
254 dates[0] = intermDate;
255 dates[1] = exDate;
256
257 spot->setValue(value.s);
258 qRate->setValue(value.q);
259 rRate->setValue(value.r);
260 vols[0] = value.v1;
261 vols[1] = value.v;
262
263 ext::shared_ptr<BlackVolTermStructure> volTS(
264 new BlackVarianceCurve(today, dates, vols, dc, true));
265
266 ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
267 new BlackScholesMertonProcess(
268 Handle<Quote>(spot),
269 Handle<YieldTermStructure>(qTS),
270 Handle<YieldTermStructure>(rTS),
271 Handle<BlackVolTermStructure>(volTS)));
272
273 ext::shared_ptr<PricingEngine> engine;
274 engine =
275 MakeMCVarianceSwapEngine<PseudoRandom>(stochProcess)
276 .withStepsPerYear(steps: 250)
277 .withSamples(samples: 1023)
278 .withSeed(seed: 42);
279
280 VarianceSwap varianceSwap(value.type, value.varStrike, value.nominal, today, exDate);
281 varianceSwap.setPricingEngine(engine);
282
283 Real calculated = varianceSwap.variance();
284 Real expected = value.result;
285 Real error = std::fabs(x: calculated-expected);
286 if (error > value.tol)
287 REPORT_FAILURE("value", values[i].type, value.varStrike, value.nominal, value.s,
288 value.q, value.r, today, exDate, value.v, expected, calculated, error,
289 value.tol);
290 }
291}
292
293test_suite* VarianceSwapTest::suite() {
294 auto* suite = BOOST_TEST_SUITE("Variance swap tests");
295
296 suite->add(QUANTLIB_TEST_CASE(
297 &VarianceSwapTest::testReplicatingVarianceSwap));
298 suite->add(QUANTLIB_TEST_CASE(&VarianceSwapTest::testMCVarianceSwap));
299 return suite;
300}
301
302

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