[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) 2003, 2007 Ferdinando Ametrano
5 Copyright (C) 2003, 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 "extendedtrees.hpp"
22#include "utilities.hpp"
23#include <ql/time/daycounters/actual360.hpp>
24#include <ql/instruments/europeanoption.hpp>
25#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
26#include <ql/pricingengines/vanilla/binomialengine.hpp>
27#include <ql/experimental/lattices/extendedbinomialtree.hpp>
28#include <ql/termstructures/yield/flatforward.hpp>
29#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
30#include <ql/utilities/dataformatters.hpp>
31#include <map>
32
33using namespace QuantLib;
34using namespace boost::unit_test_framework;
35
36#undef REPORT_FAILURE
37#define REPORT_FAILURE(greekName, payoff, exercise, s, q, r, today, \
38 v, expected, calculated, error, tolerance) \
39 BOOST_ERROR(exerciseTypeToString(exercise) << " " \
40 << payoff->optionType() << " option with " \
41 << payoffTypeToString(payoff) << " payoff:\n" \
42 << " spot value: " << s << "\n" \
43 << " strike: " << payoff->strike() << "\n" \
44 << " dividend yield: " << io::rate(q) << "\n" \
45 << " risk-free rate: " << io::rate(r) << "\n" \
46 << " reference date: " << today << "\n" \
47 << " maturity: " << exercise->lastDate() << "\n" \
48 << " volatility: " << io::volatility(v) << "\n\n" \
49 << " expected " << greekName << ": " << expected << "\n" \
50 << " calculated " << greekName << ": " << calculated << "\n"\
51 << " error: " << error << "\n" \
52 << " tolerance: " << tolerance);
53
54namespace extended_trees_test {
55
56 // utilities
57
58 enum EngineType { Analytic,
59 JR, CRR, EQP, TGEO, TIAN, LR, JOSHI };
60
61 ext::shared_ptr<GeneralizedBlackScholesProcess>
62 makeProcess(const ext::shared_ptr<Quote>& u,
63 const ext::shared_ptr<YieldTermStructure>& q,
64 const ext::shared_ptr<YieldTermStructure>& r,
65 const ext::shared_ptr<BlackVolTermStructure>& vol) {
66 return ext::make_shared<BlackScholesMertonProcess>(
67 args: Handle<Quote>(u),
68 args: Handle<YieldTermStructure>(q),
69 args: Handle<YieldTermStructure>(r),
70 args: Handle<BlackVolTermStructure>(vol));
71 }
72
73 ext::shared_ptr<VanillaOption>
74 makeOption(const ext::shared_ptr<StrikedTypePayoff>& payoff,
75 const ext::shared_ptr<Exercise>& exercise,
76 const ext::shared_ptr<Quote>& u,
77 const ext::shared_ptr<YieldTermStructure>& q,
78 const ext::shared_ptr<YieldTermStructure>& r,
79 const ext::shared_ptr<BlackVolTermStructure>& vol,
80 EngineType engineType,
81 Size binomialSteps) {
82
83 ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess =
84 makeProcess(u,q,r,vol);
85
86 ext::shared_ptr<PricingEngine> engine;
87 switch (engineType) {
88 case Analytic:
89 engine = ext::shared_ptr<PricingEngine>(
90 new AnalyticEuropeanEngine(stochProcess));
91 break;
92 case JR:
93 engine = ext::shared_ptr<PricingEngine>(
94 new BinomialVanillaEngine<ExtendedJarrowRudd>(stochProcess,
95 binomialSteps));
96 break;
97 case CRR:
98 engine = ext::shared_ptr<PricingEngine>(
99 new BinomialVanillaEngine<ExtendedCoxRossRubinstein>(
100 stochProcess,
101 binomialSteps));
102 break;
103 case EQP:
104 engine = ext::shared_ptr<PricingEngine>(
105 new BinomialVanillaEngine<ExtendedAdditiveEQPBinomialTree>(
106 stochProcess,
107 binomialSteps));
108 break;
109 case TGEO:
110 engine = ext::shared_ptr<PricingEngine>(
111 new BinomialVanillaEngine<ExtendedTrigeorgis>(stochProcess,
112 binomialSteps));
113 break;
114 case TIAN:
115 engine = ext::shared_ptr<PricingEngine>(
116 new BinomialVanillaEngine<ExtendedTian>(stochProcess,
117 binomialSteps));
118 break;
119 case LR:
120 engine = ext::shared_ptr<PricingEngine>(
121 new BinomialVanillaEngine<ExtendedLeisenReimer>(
122 stochProcess,
123 binomialSteps));
124 break;
125 case JOSHI:
126 engine = ext::shared_ptr<PricingEngine>(
127 new BinomialVanillaEngine<ExtendedJoshi4>(stochProcess,
128 binomialSteps));
129 break;
130 default:
131 QL_FAIL("unknown engine type");
132 }
133
134 ext::shared_ptr<VanillaOption> option(
135 new EuropeanOption(payoff, exercise));
136 option->setPricingEngine(engine);
137 return option;
138 }
139
140}
141
142namespace {
143
144 void testEngineConsistency(extended_trees_test::EngineType engine,
145 Size binomialSteps,
146 std::map<std::string,Real> tolerance) {
147
148 using namespace extended_trees_test;
149
150 std::map<std::string,Real> calculated, expected;
151
152 // test options
153 Option::Type types[] = { Option::Call, Option::Put };
154 Real strikes[] = { 75.0, 100.0, 125.0 };
155 Integer lengths[] = { 1 };
156
157 // test data
158 Real underlyings[] = { 100.0 };
159 Rate qRates[] = { 0.00, 0.05 };
160 Rate rRates[] = { 0.01, 0.05, 0.15 };
161 Volatility vols[] = { 0.11, 0.50, 1.20 };
162
163 DayCounter dc = Actual360();
164 Date today = Date::todaysDate();
165
166 ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
167 ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
168 ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today,volatility: vol,dc);
169 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
170 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today,forward: qRate,dc);
171 ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
172 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today,forward: rRate,dc);
173
174 for (auto& type : types) {
175 for (Real strike : strikes) {
176 for (int length : lengths) {
177 Date exDate = today + length * 360;
178 ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate));
179 ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, strike));
180 // reference option
181 ext::shared_ptr<VanillaOption> refOption =
182 makeOption(payoff, exercise, u: spot, q: qTS, r: rTS, vol: volTS, engineType: Analytic, binomialSteps: Null<Size>());
183 // option to check
184 ext::shared_ptr<VanillaOption> option =
185 makeOption(payoff, exercise, u: spot, q: qTS, r: rTS, vol: volTS, engineType: engine, binomialSteps);
186
187 for (Real u : underlyings) {
188 for (Real m : qRates) {
189 for (Real n : rRates) {
190 for (Real v : vols) {
191 Rate q = m, r = n;
192 spot->setValue(u);
193 qRate->setValue(q);
194 rRate->setValue(r);
195 vol->setValue(v);
196
197 expected.clear();
198 calculated.clear();
199
200 expected["value"] = refOption->NPV();
201 calculated["value"] = option->NPV();
202
203 if (option->NPV() > spot->value() * 1.0e-5) {
204 expected["delta"] = refOption->delta();
205 expected["gamma"] = refOption->gamma();
206 expected["theta"] = refOption->theta();
207 calculated["delta"] = option->delta();
208 calculated["gamma"] = option->gamma();
209 calculated["theta"] = option->theta();
210 }
211 std::map<std::string, Real>::iterator it;
212 for (it = calculated.begin(); it != calculated.end(); ++it) {
213 std::string greek = it->first;
214 Real expct = expected[greek], calcl = calculated[greek],
215 tol = tolerance[greek];
216 Real error = relativeError(x1: expct, x2: calcl, reference: u);
217 if (error > tol) {
218 REPORT_FAILURE(greek, payoff, exercise, u, q, r, today,
219 v, expct, calcl, error, tol);
220 }
221 }
222 }
223 }
224 }
225 }
226 }
227 }
228 }
229 }
230
231}
232
233
234void ExtendedTreesTest::testJRBinomialEngines() {
235
236 BOOST_TEST_MESSAGE("Testing time-dependent JR binomial European engines "
237 "against analytic results...");
238
239 using namespace extended_trees_test;
240
241 EngineType engine = JR;
242 Size steps = 251;
243 std::map<std::string,Real> relativeTol;
244 relativeTol["value"] = 0.002;
245 relativeTol["delta"] = 1.0e-3;
246 relativeTol["gamma"] = 1.0e-4;
247 relativeTol["theta"] = 0.03;
248 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
249}
250
251void ExtendedTreesTest::testCRRBinomialEngines() {
252
253 BOOST_TEST_MESSAGE("Testing time-dependent CRR binomial European engines "
254 "against analytic results...");
255
256 using namespace extended_trees_test;
257
258 EngineType engine = CRR;
259 Size steps = 501;
260 std::map<std::string,Real> relativeTol;
261 relativeTol["value"] = 0.02;
262 relativeTol["delta"] = 1.0e-3;
263 relativeTol["gamma"] = 1.0e-4;
264 relativeTol["theta"] = 0.03;
265 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
266}
267
268void ExtendedTreesTest::testEQPBinomialEngines() {
269
270 BOOST_TEST_MESSAGE("Testing time-dependent EQP binomial European engines "
271 "against analytic results...");
272
273 using namespace extended_trees_test;
274
275 EngineType engine = EQP;
276 Size steps = 501;
277 std::map<std::string,Real> relativeTol;
278 relativeTol["value"] = 0.02;
279 relativeTol["delta"] = 1.0e-3;
280 relativeTol["gamma"] = 1.0e-4;
281 relativeTol["theta"] = 0.03;
282 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
283}
284
285void ExtendedTreesTest::testTGEOBinomialEngines() {
286
287 BOOST_TEST_MESSAGE("Testing time-dependent TGEO binomial European engines "
288 "against analytic results...");
289
290 using namespace extended_trees_test;
291
292 EngineType engine = TGEO;
293 Size steps = 251;
294 std::map<std::string,Real> relativeTol;
295 relativeTol["value"] = 0.002;
296 relativeTol["delta"] = 1.0e-3;
297 relativeTol["gamma"] = 1.0e-4;
298 relativeTol["theta"] = 0.03;
299 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
300}
301
302void ExtendedTreesTest::testTIANBinomialEngines() {
303
304 BOOST_TEST_MESSAGE("Testing time-dependent TIAN binomial European engines "
305 "against analytic results...");
306
307 using namespace extended_trees_test;
308
309 EngineType engine = TIAN;
310 Size steps = 251;
311 std::map<std::string,Real> relativeTol;
312 relativeTol["value"] = 0.002;
313 relativeTol["delta"] = 1.0e-3;
314 relativeTol["gamma"] = 1.0e-4;
315 relativeTol["theta"] = 0.03;
316 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
317}
318
319void ExtendedTreesTest::testLRBinomialEngines() {
320
321 BOOST_TEST_MESSAGE("Testing time-dependent LR binomial European engines "
322 "against analytic results...");
323
324 using namespace extended_trees_test;
325
326 EngineType engine = LR;
327 Size steps = 251;
328 std::map<std::string,Real> relativeTol;
329 relativeTol["value"] = 1.0e-6;
330 relativeTol["delta"] = 1.0e-3;
331 relativeTol["gamma"] = 1.0e-4;
332 relativeTol["theta"] = 0.03;
333 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
334}
335
336void ExtendedTreesTest::testJOSHIBinomialEngines() {
337
338 BOOST_TEST_MESSAGE("Testing time-dependent Joshi binomial European engines "
339 "against analytic results...");
340
341 using namespace extended_trees_test;
342
343 EngineType engine = JOSHI;
344 Size steps = 251;
345 std::map<std::string,Real> relativeTol;
346 relativeTol["value"] = 1.0e-7;
347 relativeTol["delta"] = 1.0e-3;
348 relativeTol["gamma"] = 1.0e-4;
349 relativeTol["theta"] = 0.03;
350 testEngineConsistency(engine, binomialSteps: steps, tolerance: relativeTol);
351}
352
353test_suite* ExtendedTreesTest::suite() {
354 auto* suite = BOOST_TEST_SUITE("European option extended trees tests");
355
356 suite->add(QUANTLIB_TEST_CASE(&ExtendedTreesTest::testJRBinomialEngines));
357 suite->add(QUANTLIB_TEST_CASE(&ExtendedTreesTest::testCRRBinomialEngines));
358 suite->add(QUANTLIB_TEST_CASE(&ExtendedTreesTest::testEQPBinomialEngines));
359 suite->add(QUANTLIB_TEST_CASE(&ExtendedTreesTest::testTGEOBinomialEngines));
360 suite->add(QUANTLIB_TEST_CASE(&ExtendedTreesTest::testTIANBinomialEngines));
361 suite->add(QUANTLIB_TEST_CASE(&ExtendedTreesTest::testLRBinomialEngines));
362 suite->add(QUANTLIB_TEST_CASE(
363 &ExtendedTreesTest::testJOSHIBinomialEngines));
364
365 return suite;
366}
367

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