[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) 2004 Ferdinando Ametrano
5 Copyright (C) 2007 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 "jumpdiffusion.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/jumpdiffusionengine.hpp>
27#include <ql/processes/merton76process.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_1
37#define REPORT_FAILURE_1(greekName, payoff, exercise, s, q, r, today, v, \
38 intensity, meanLogJump, jumpVol, expected, \
39 calculated, error, tolerance) \
40 BOOST_FAIL(exerciseTypeToString(exercise) << " " \
41 << payoff->optionType() << " option with " \
42 << payoffTypeToString(payoff) << " payoff:\n" \
43 << " underlying value: " << s << "\n" \
44 << " strike: " << payoff->strike() <<"\n" \
45 << " dividend yield: " << io::rate(q) << "\n" \
46 << " risk-free rate: " << io::rate(r) << "\n" \
47 << " reference date: " << today << "\n" \
48 << " maturity: " << exercise->lastDate() << "\n" \
49 << " volatility: " << io::volatility(v) << "\n\n" \
50 << " intensity: " << intensity << "\n" \
51 << " mean log-jump: " << meanLogJump << "\n" \
52 << " jump volatility: " << jumpVol << "\n\n" \
53 << " expected " << greekName << ": " << expected << "\n" \
54 << " calculated " << greekName << ": " << calculated << "\n"\
55 << " error: " << error << "\n" \
56 << " tolerance: " << tolerance);
57
58#undef REPORT_FAILURE_2
59#define REPORT_FAILURE_2(greekName, payoff, exercise, s, q, r, today, v, \
60 intensity, gamma, expected, calculated, \
61 error, tolerance) \
62 BOOST_FAIL(exerciseTypeToString(exercise) << " " \
63 << payoff->optionType() << " option with " \
64 << payoffTypeToString(payoff) << " payoff:\n" \
65 << " underlying value: " << s << "\n" \
66 << " strike: " << payoff->strike() <<"\n" \
67 << " dividend yield: " << io::rate(q) << "\n" \
68 << " risk-free rate: " << io::rate(r) << "\n" \
69 << " reference date: " << today << "\n" \
70 << " maturity: " << exercise->lastDate() << "\n" \
71 << " volatility: " << io::volatility(v) << "\n" \
72 << " intensity: " << intensity << "\n" \
73 << " gamma: " << gamma << "\n\n" \
74 << " expected " << greekName << ": " << expected << "\n" \
75 << " calculated " << greekName << ": " << calculated << "\n"\
76 << " error: " << error << "\n" \
77 << " tolerance: " << tolerance);
78
79namespace {
80
81 struct HaugMertonData {
82 Option::Type type;
83 Real strike;
84 Real s; // spot
85 Rate q; // dividend
86 Rate r; // risk-free rate
87 Time t; // time to maturity
88 Volatility v; // volatility
89 Real jumpIntensity;
90 Real gamma;
91 Real result; // result
92 Real tol; // tolerance
93 };
94
95}
96
97
98void JumpDiffusionTest::testMerton76() {
99
100 BOOST_TEST_MESSAGE("Testing Merton 76 jump-diffusion model "
101 "for European options...");
102
103 /* The data below are from
104 "Option pricing formulas", E.G. Haug, McGraw-Hill 1998, pag 9
105
106 Haug use the arbitrary truncation criterium of 11 terms in the sum,
107 which doesn't guarantee convergence up to 1e-2.
108 Using Haug's criterium Haug's values have been correctly reproduced.
109 the following values have the right 1e-2 accuracy: any value different
110 from Haug has been noted.
111 */
112 HaugMertonData values[] = {
113 // type, strike, spot, q, r, t, vol, int, gamma, value, tol
114 // gamma = 0.25, strike = 80
115 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 20.67, .tol: 1e-2 },
116 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 21.74, .tol: 1e-2 },
117 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 23.63, .tol: 1e-2 },
118 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 20.65, .tol: 1e-2 },
119 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 21.70, .tol: 1e-2 },
120 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 23.61, .tol: 1e-2 },
121 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 20.64, .tol: 1e-2 },
122 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 21.70, .tol: 1e-2 },
123 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 23.61, .tol: 1e-2 }, // Haug 23.28
124 // gamma = 0.25, strike = 90
125 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 11.00, .tol: 1e-2 },
126 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 12.74, .tol: 1e-2 },
127 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 15.40, .tol: 1e-2 },
128 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 10.98, .tol: 1e-2 },
129 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 12.75, .tol: 1e-2 },
130 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 15.42, .tol: 1e-2 },
131 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 10.98, .tol: 1e-2 },
132 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 12.75, .tol: 1e-2 },
133 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 15.42, .tol: 1e-2 }, // Haug 15.20
134 // gamma = 0.25, strike = 100
135 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 3.42, .tol: 1e-2 },
136 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 5.88, .tol: 1e-2 },
137 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 8.95, .tol: 1e-2 },
138 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 3.51, .tol: 1e-2 },
139 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 5.96, .tol: 1e-2 },
140 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 9.02, .tol: 1e-2 },
141 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 3.53, .tol: 1e-2 },
142 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 5.97, .tol: 1e-2 },
143 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 9.03, .tol: 1e-2 }, // Haug 8.89
144 // gamma = 0.25, strike = 110
145 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 0.55, .tol: 1e-2 },
146 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 2.11, .tol: 1e-2 },
147 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 4.67, .tol: 1e-2 },
148 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 0.56, .tol: 1e-2 },
149 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 2.16, .tol: 1e-2 },
150 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 4.73, .tol: 1e-2 },
151 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 0.56, .tol: 1e-2 },
152 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 2.17, .tol: 1e-2 },
153 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 4.74, .tol: 1e-2 }, // Haug 4.66
154 // gamma = 0.25, strike = 120
155 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 0.10, .tol: 1e-2 },
156 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 0.64, .tol: 1e-2 },
157 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.25, .result: 2.23, .tol: 1e-2 },
158 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 0.06, .tol: 1e-2 },
159 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 0.63, .tol: 1e-2 },
160 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.25, .result: 2.25, .tol: 1e-2 },
161 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 0.05, .tol: 1e-2 },
162 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 0.62, .tol: 1e-2 },
163 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.25, .result: 2.25, .tol: 1e-2 }, // Haug 2.21
164
165 // gamma = 0.50, strike = 80
166 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 20.72, .tol: 1e-2 },
167 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 21.83, .tol: 1e-2 },
168 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 23.71, .tol: 1e-2 },
169 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 20.66, .tol: 1e-2 },
170 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 21.73, .tol: 1e-2 },
171 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 23.63, .tol: 1e-2 },
172 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 20.65, .tol: 1e-2 },
173 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 21.71, .tol: 1e-2 },
174 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 23.61, .tol: 1e-2 }, // Haug 23.28
175 // gamma = 0.50, strike = 90
176 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 11.04, .tol: 1e-2 },
177 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 12.72, .tol: 1e-2 },
178 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 15.34, .tol: 1e-2 },
179 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 11.02, .tol: 1e-2 },
180 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 12.76, .tol: 1e-2 },
181 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 15.41, .tol: 1e-2 },
182 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 11.00, .tol: 1e-2 },
183 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 12.75, .tol: 1e-2 },
184 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 15.41, .tol: 1e-2 }, // Haug 15.18
185 // gamma = 0.50, strike = 100
186 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 3.14, .tol: 1e-2 },
187 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 5.58, .tol: 1e-2 },
188 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 8.71, .tol: 1e-2 },
189 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 3.39, .tol: 1e-2 },
190 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 5.87, .tol: 1e-2 },
191 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 8.96, .tol: 1e-2 },
192 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 3.46, .tol: 1e-2 },
193 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 5.93, .tol: 1e-2 },
194 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 9.00, .tol: 1e-2 }, // Haug 8.85
195 // gamma = 0.50, strike = 110
196 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 0.53, .tol: 1e-2 },
197 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 1.93, .tol: 1e-2 },
198 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 4.42, .tol: 1e-2 },
199 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 0.58, .tol: 1e-2 },
200 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 2.11, .tol: 1e-2 },
201 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 4.67, .tol: 1e-2 },
202 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 0.57, .tol: 1e-2 },
203 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 2.14, .tol: 1e-2 },
204 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 4.71, .tol: 1e-2 }, // Haug 4.62
205 // gamma = 0.50, strike = 120
206 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 0.19, .tol: 1e-2 },
207 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 0.71, .tol: 1e-2 },
208 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.50, .result: 2.15, .tol: 1e-2 },
209 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 0.10, .tol: 1e-2 },
210 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 0.66, .tol: 1e-2 },
211 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.50, .result: 2.23, .tol: 1e-2 },
212 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 0.07, .tol: 1e-2 },
213 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 0.64, .tol: 1e-2 },
214 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.50, .result: 2.24, .tol: 1e-2 }, // Haug 2.19
215
216 // gamma = 0.75, strike = 80
217 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 20.79, .tol: 1e-2 },
218 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 21.96, .tol: 1e-2 },
219 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 23.86, .tol: 1e-2 },
220 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 20.68, .tol: 1e-2 },
221 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 21.78, .tol: 1e-2 },
222 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 23.67, .tol: 1e-2 },
223 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 20.66, .tol: 1e-2 },
224 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 21.74, .tol: 1e-2 },
225 { .type: Option::Call, .strike: 80.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 23.64, .tol: 1e-2 }, // Haug 23.30
226 // gamma = 0.75, strike = 90
227 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 11.11, .tol: 1e-2 },
228 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 12.75, .tol: 1e-2 },
229 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 15.30, .tol: 1e-2 },
230 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 11.09, .tol: 1e-2 },
231 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 12.78, .tol: 1e-2 },
232 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 15.39, .tol: 1e-2 },
233 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 11.04, .tol: 1e-2 },
234 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 12.76, .tol: 1e-2 },
235 { .type: Option::Call, .strike: 90.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 15.40, .tol: 1e-2 }, // Haug 15.17
236 // gamma = 0.75, strike = 100
237 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 2.70, .tol: 1e-2 },
238 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 5.08, .tol: 1e-2 },
239 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 8.24, .tol: 1e-2 },
240 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 3.16, .tol: 1e-2 },
241 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 5.71, .tol: 1e-2 },
242 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 8.85, .tol: 1e-2 },
243 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 3.33, .tol: 1e-2 },
244 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 5.85, .tol: 1e-2 },
245 { .type: Option::Call, .strike: 100.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 8.95, .tol: 1e-2 }, // Haug 8.79
246 // gamma = 0.75, strike = 110
247 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 0.54, .tol: 1e-2 },
248 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 1.69, .tol: 1e-2 },
249 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 3.99, .tol: 1e-2 },
250 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 0.62, .tol: 1e-2 },
251 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 2.05, .tol: 1e-2 },
252 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 4.57, .tol: 1e-2 },
253 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 0.60, .tol: 1e-2 },
254 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 2.11, .tol: 1e-2 },
255 { .type: Option::Call, .strike: 110.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 4.66, .tol: 1e-2 }, // Haug 4.56
256 // gamma = 0.75, strike = 120
257 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 0.29, .tol: 1e-2 },
258 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 0.84, .tol: 1e-2 },
259 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 1.0, .gamma: 0.75, .result: 2.09, .tol: 1e-2 },
260 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 0.15, .tol: 1e-2 },
261 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 0.71, .tol: 1e-2 },
262 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25, .jumpIntensity: 5.0, .gamma: 0.75, .result: 2.21, .tol: 1e-2 },
263 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.10, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 0.11, .tol: 1e-2 },
264 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.25, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 0.67, .tol: 1e-2 },
265 { .type: Option::Call, .strike: 120.00, .s: 100.00, .q: 0.00, .r: 0.08, .t: 0.50, .v: 0.25,.jumpIntensity: 10.0, .gamma: 0.75, .result: 2.23, .tol: 1e-2 } // Haug 2.17
266};
267
268
269
270 DayCounter dc = Actual360();
271 Date today = Date::todaysDate();
272
273 ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
274 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
275 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc);
276 ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
277 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc);
278 ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
279 ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc);
280
281 ext::shared_ptr<SimpleQuote> jumpIntensity(new SimpleQuote(0.0));
282 ext::shared_ptr<SimpleQuote> meanLogJump(new SimpleQuote(0.0));
283 ext::shared_ptr<SimpleQuote> jumpVol(new SimpleQuote(0.0));
284
285 ext::shared_ptr<Merton76Process> stochProcess(
286 new Merton76Process(Handle<Quote>(spot),
287 Handle<YieldTermStructure>(qTS),
288 Handle<YieldTermStructure>(rTS),
289 Handle<BlackVolTermStructure>(volTS),
290 Handle<Quote>(jumpIntensity),
291 Handle<Quote>(meanLogJump),
292 Handle<Quote>(jumpVol)));
293 ext::shared_ptr<PricingEngine> engine(
294 new JumpDiffusionEngine(stochProcess));
295
296 for (auto& value : values) {
297
298 ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(value.type, value.strike));
299
300 Date exDate = today + timeToDays(t: value.t);
301 ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate));
302
303 spot->setValue(value.s);
304 qRate->setValue(value.q);
305 rRate->setValue(value.r);
306
307
308 jumpIntensity->setValue(value.jumpIntensity);
309
310 // delta in Haug's notation
311 Real jVol = value.v * std::sqrt(x: value.gamma / value.jumpIntensity);
312 jumpVol->setValue(jVol);
313
314 // z in Haug's notation
315 Real diffusionVol = value.v * std::sqrt(x: 1.0 - value.gamma);
316 vol ->setValue(diffusionVol);
317
318 // Haug is assuming zero meanJump
319 Real meanJump = 0.0;
320 meanLogJump->setValue(std::log(x: 1.0+meanJump)-0.5*jVol*jVol);
321
322 Volatility totalVol =
323 std::sqrt(x: value.jumpIntensity * jVol * jVol + diffusionVol * diffusionVol);
324 Volatility volError = std::fabs(x: totalVol - value.v);
325 QL_REQUIRE(volError<1e-13,
326 volError << " mismatch");
327
328 EuropeanOption option(payoff, exercise);
329 option.setPricingEngine(engine);
330
331 Real calculated = option.NPV();
332 Real error = std::fabs(x: calculated - value.result);
333 if (error > value.tol) {
334 REPORT_FAILURE_2("value", payoff, exercise, value.s, value.q, value.r, today, value.v,
335 value.jumpIntensity, value.gamma, value.result, calculated, error,
336 value.tol);
337 }
338 }
339}
340
341void JumpDiffusionTest::testGreeks() {
342
343 BOOST_TEST_MESSAGE("Testing jump-diffusion option greeks...");
344
345 std::map<std::string,Real> calculated, expected, tolerance;
346 tolerance["delta"] = 1.0e-4;
347 tolerance["gamma"] = 1.0e-4;
348 tolerance["theta"] = 1.1e-4;
349 tolerance["rho"] = 1.0e-4;
350 tolerance["divRho"] = 1.0e-4;
351 tolerance["vega"] = 1.0e-4;
352
353 Option::Type types[] = { Option::Put, Option::Call };
354 Real strikes[] = { 50.0, 100.0, 150.0 };
355 Real underlyings[] = { 100.0 };
356 Rate qRates[] = { -0.05, 0.0, 0.05 };
357 Rate rRates[] = { 0.0, 0.01, 0.2 };
358 // The testsuite check fails if a too short maturity is chosen(i.e. 1 year).
359 // The problem is in the theta calculation. With the finite difference(fd) method
360 // we might get values too close to the jump steps, invalidating the fd methodology
361 // for calculating greeks.
362 Time residualTimes[] = { 5.0 };
363 Volatility vols[] = { 0.11 };
364 Real jInt[] = { 1.0, 5.0 };
365 Real mLJ[] = { -0.20, 0.0, 0.20 };
366 Volatility jV[] = { 0.01, 0.25 };
367
368 DayCounter dc = Actual360();
369 Date today = Date::todaysDate();
370 Settings::instance().evaluationDate() = today;
371
372 ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0));
373 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
374 Handle<YieldTermStructure> qTS(flatRate(forward: qRate, dc));
375 ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
376 Handle<YieldTermStructure> rTS(flatRate(forward: rRate, dc));
377 ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
378 Handle<BlackVolTermStructure> volTS(flatVol(volatility: vol, dc));
379
380 ext::shared_ptr<SimpleQuote> jumpIntensity(new SimpleQuote(0.0));
381 ext::shared_ptr<SimpleQuote> meanLogJump(new SimpleQuote(0.0));
382 ext::shared_ptr<SimpleQuote> jumpVol(new SimpleQuote(0.0));
383
384 ext::shared_ptr<Merton76Process> stochProcess(
385 new Merton76Process(Handle<Quote>(spot), qTS, rTS, volTS,
386 Handle<Quote>(jumpIntensity),
387 Handle<Quote>(meanLogJump),
388 Handle<Quote>(jumpVol)));
389
390 ext::shared_ptr<StrikedTypePayoff> payoff;
391
392 // The jumpdiffusionengine greeks are very sensitive to the
393 // convergence level. A tolerance of 1.0e-08 is usually
394 // sufficient to get reasonable results
395 ext::shared_ptr<PricingEngine> engine(
396 new JumpDiffusionEngine(stochProcess,1e-08));
397
398 for (auto& type : types) {
399 for (Real strike : strikes) {
400 for (Real& jj1 : jInt) {
401 jumpIntensity->setValue(jj1);
402 for (Real& jj2 : mLJ) {
403 meanLogJump->setValue(jj2);
404 for (Real& jj3 : jV) {
405 jumpVol->setValue(jj3);
406 for (Real residualTime : residualTimes) {
407 Date exDate = today + timeToDays(t: residualTime);
408 ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate));
409 for (Size kk = 0; kk < 1; kk++) {
410 // option to check
411 if (kk == 0) {
412 payoff = ext::shared_ptr<StrikedTypePayoff>(
413 new PlainVanillaPayoff(type, strike));
414 } else if (kk == 1) {
415 payoff = ext::shared_ptr<StrikedTypePayoff>(
416 new CashOrNothingPayoff(type, strike, 100.0));
417 }
418 EuropeanOption option(payoff, exercise);
419 option.setPricingEngine(engine);
420
421 for (Real u : underlyings) {
422 for (Real q : qRates) {
423 for (Real r : rRates) {
424 for (Real v : vols) {
425 spot->setValue(u);
426 qRate->setValue(q);
427 rRate->setValue(r);
428 vol->setValue(v);
429
430 Real value = option.NPV();
431 calculated["delta"] = option.delta();
432 calculated["gamma"] = option.gamma();
433 calculated["theta"] = option.theta();
434 calculated["rho"] = option.rho();
435 calculated["divRho"] = option.dividendRho();
436 calculated["vega"] = option.vega();
437
438 if (value > spot->value() * 1.0e-5) {
439 // perturb spot and get delta and gamma
440 Real du = u * 1.0e-5;
441 spot->setValue(u + du);
442 Real value_p = option.NPV(),
443 delta_p = option.delta();
444 spot->setValue(u - du);
445 Real value_m = option.NPV(),
446 delta_m = option.delta();
447 spot->setValue(u);
448 expected["delta"] =
449 (value_p - value_m) / (2 * du);
450 expected["gamma"] =
451 (delta_p - delta_m) / (2 * du);
452
453 // perturb rates and get rho and dividend rho
454 Spread dr = 1.0e-5;
455 rRate->setValue(r + dr);
456 value_p = option.NPV();
457 rRate->setValue(r - dr);
458 value_m = option.NPV();
459 rRate->setValue(r);
460 expected["rho"] =
461 (value_p - value_m) / (2 * dr);
462
463 Spread dq = 1.0e-5;
464 qRate->setValue(q + dq);
465 value_p = option.NPV();
466 qRate->setValue(q - dq);
467 value_m = option.NPV();
468 qRate->setValue(q);
469 expected["divRho"] =
470 (value_p - value_m) / (2 * dq);
471
472 // perturb volatility and get vega
473 Volatility dv = v * 1.0e-4;
474 vol->setValue(v + dv);
475 value_p = option.NPV();
476 vol->setValue(v - dv);
477 value_m = option.NPV();
478 vol->setValue(v);
479 expected["vega"] =
480 (value_p - value_m) / (2 * dv);
481
482 // get theta from time-shifted options
483 Time dT = dc.yearFraction(d1: today - 1, d2: today + 1);
484 Settings::instance().evaluationDate() =
485 today - 1;
486 value_m = option.NPV();
487 Settings::instance().evaluationDate() =
488 today + 1;
489 value_p = option.NPV();
490 Settings::instance().evaluationDate() = today;
491 expected["theta"] = (value_p - value_m) / dT;
492 // compare
493 std::map<std::string, Real>::iterator it;
494 for (it = expected.begin();
495 it != expected.end(); ++it) {
496 std::string greek = it->first;
497 Real expct = expected[greek],
498 calcl = calculated[greek],
499 tol = tolerance[greek];
500 Real error = std::fabs(x: expct - calcl);
501 if (error > tol) {
502 REPORT_FAILURE_1(
503 greek, payoff, exercise, u, q, r,
504 today, v, jj1, jj2, jj3, expct,
505 calcl, error, tol);
506 }
507 }
508 }
509 }
510 }
511 }
512 }
513 }
514 }
515 } // strike loop
516 }
517 }
518 }
519 } // type loop
520}
521
522
523test_suite* JumpDiffusionTest::suite() {
524 auto* suite = BOOST_TEST_SUITE("Jump-diffusion tests");
525 suite->add(QUANTLIB_TEST_CASE(&JumpDiffusionTest::testMerton76));
526 suite->add(QUANTLIB_TEST_CASE(&JumpDiffusionTest::testGreeks));
527 return suite;
528}
529

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