[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 Neil Firth
5 Copyright (C) 2003, 2004, 2005, 2007, 2008 StatPro Italia srl
6 Copyright (C) 2004 Ferdinando Ametrano
7 Copyright (C) 2013 Yue Tian
8 Copyright (C) 2014 Thema Consulting SA
9
10 This file is part of QuantLib, a free-software/open-source library
11 for financial quantitative analysts and developers - http://quantlib.org/
12
13 QuantLib is free software: you can redistribute it and/or modify it
14 under the terms of the QuantLib license. You should have received a
15 copy of the license along with this program; if not, please email
16 <quantlib-dev@lists.sf.net>. The license is also available online at
17 <http://quantlib.org/license.shtml>.
18
19 This program is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 FOR A PARTICULAR PURPOSE. See the license for more details.
22*/
23
24#include "barrieroption.hpp"
25#include "utilities.hpp"
26#include <ql/time/calendars/nullcalendar.hpp>
27#include <ql/time/calendars/target.hpp>
28#include <ql/time/daycounters/actual360.hpp>
29#include <ql/time/daycounters/business252.hpp>
30#include <ql/math/interpolations/bicubicsplineinterpolation.hpp>
31#include <ql/instruments/barrieroption.hpp>
32#include <ql/instruments/dividendbarrieroption.hpp>
33#include <ql/instruments/europeanoption.hpp>
34#include <ql/models/equity/hestonmodel.hpp>
35#include <ql/pricingengines/barrier/analyticbarrierengine.hpp>
36#include <ql/pricingengines/barrier/binomialbarrierengine.hpp>
37#include <ql/pricingengines/barrier/fdhestonbarrierengine.hpp>
38#include <ql/pricingengines/barrier/fdblackscholesbarrierengine.hpp>
39#include <ql/pricingengines/barrier/mcbarrierengine.hpp>
40#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
41#include <ql/pricingengines/blackformula.hpp>
42#include <ql/experimental/barrieroption/perturbativebarrieroptionengine.hpp>
43#include <ql/experimental/barrieroption/vannavolgabarrierengine.hpp>
44#include <ql/termstructures/yield/zerocurve.hpp>
45#include <ql/termstructures/yield/flatforward.hpp>
46#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
47#include <ql/termstructures/volatility/equityfx/blackvariancecurve.hpp>
48#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
49#include <ql/utilities/dataformatters.hpp>
50
51using namespace QuantLib;
52using namespace boost::unit_test_framework;
53
54#undef REPORT_FAILURE
55#define REPORT_FAILURE(greekName, barrierType, barrier, rebate, payoff, \
56 exercise, s, q, r, today, v, expected, calculated, \
57 error, tolerance) \
58 BOOST_ERROR("\n" << barrierTypeToString(barrierType) << " " \
59 << exerciseTypeToString(exercise) << " " \
60 << payoff->optionType() << " option with " \
61 << payoffTypeToString(payoff) << " payoff:\n" \
62 << " underlying value: " << s << "\n" \
63 << " strike: " << payoff->strike() << "\n" \
64 << " barrier: " << barrier << "\n" \
65 << " rebate: " << rebate << "\n" \
66 << " dividend yield: " << io::rate(q) << "\n" \
67 << " risk-free rate: " << io::rate(r) << "\n" \
68 << " reference date: " << today << "\n" \
69 << " maturity: " << exercise->lastDate() << "\n" \
70 << " volatility: " << io::volatility(v) << "\n\n" \
71 << " expected " << greekName << ": " << expected << "\n" \
72 << " calculated " << greekName << ": " << calculated << "\n"\
73 << " error: " << error << "\n" \
74 << " tolerance: " << tolerance);
75
76#undef REPORT_FX_FAILURE
77#define REPORT_FX_FAILURE(greekName, barrierType, barrier, \
78 rebate, payoff, exercise, s, q, r, today, \
79 vol25Put, atmVol, vol25Call, v, \
80 expected, calculated, error, tolerance) \
81 BOOST_ERROR("\n" << barrierTypeToString(barrierType) << " " \
82 << exerciseTypeToString(exercise) << " " \
83 << payoff->optionType() << " FX option with " \
84 << payoffTypeToString(payoff) << " payoff:\n" \
85 << " underlying value: " << s << "\n" \
86 << " strike: " << payoff->strike() << "\n" \
87 << " barrier: " << barrier << "\n" \
88 << " rebate: " << rebate << "\n" \
89 << " dividend yield: " << io::rate(q) << "\n" \
90 << " risk-free rate: " << io::rate(r) << "\n" \
91 << " reference date: " << today << "\n" \
92 << " maturity: " << exercise->lastDate() << "\n" \
93 << " 25PutVol: " << io::volatility(vol25Put) << "\n" \
94 << " atmVol: " << io::volatility(atmVol) << "\n" \
95 << " 25CallVol: " << io::volatility(vol25Call) << "\n" \
96 << " volatility: " << io::volatility(v) << "\n\n" \
97 << " expected " << greekName << ": " << expected << "\n" \
98 << " calculated " << greekName << ": " << calculated << "\n"\
99 << " error: " << error << "\n" \
100 << " tolerance: " << tolerance);
101
102
103namespace barrier_option_test {
104
105 std::string barrierTypeToString(Barrier::Type type) {
106 switch(type){
107 case Barrier::DownIn:
108 return std::string("Down-and-in");
109 case Barrier::UpIn:
110 return std::string("Up-and-in");
111 case Barrier::DownOut:
112 return std::string("Down-and-out");
113 case Barrier::UpOut:
114 return std::string("Up-and-out");
115 default:
116 QL_FAIL("unknown exercise type");
117 }
118 }
119
120 struct BarrierOptionData {
121 Barrier::Type type;
122 Volatility volatility;
123 Real strike;
124 Real barrier;
125 Real callValue;
126 Real putValue;
127 };
128
129 struct NewBarrierOptionData {
130 Barrier::Type barrierType;
131 Real barrier;
132 Real rebate;
133 Option::Type type;
134 Exercise::Type exType;
135 Real strike;
136 Real s; // spot
137 Rate q; // dividend
138 Rate r; // risk-free rate
139 Time t; // time to maturity
140 Volatility v; // volatility
141 Real result; // result
142 Real tol; // tolerance
143 };
144
145 struct BarrierFxOptionData {
146 Barrier::Type barrierType;
147 Real barrier;
148 Real rebate;
149 Option::Type type;
150 Real strike;
151 Real s; // spot
152 Rate q; // dividend
153 Rate r; // risk-free rate
154 Time t; // time to maturity
155 Volatility vol25Put; // 25 delta put vol
156 Volatility volAtm; // atm vol
157 Volatility vol25Call; // 25 delta call vol
158 Volatility v; // volatility at strike
159 Real result; // result
160 Real tol; // tolerance
161 };
162
163}
164
165#define QL_ASSERT_EXCEPTION_THROWN(statement) \
166 auto exception_thrown = false; \
167 try { \
168 statement \
169 } catch (const std::exception&) { \
170 exception_thrown = true; \
171 } \
172 if (!exception_thrown) { \
173 BOOST_FAIL("exception expected"); \
174 } \
175
176void BarrierOptionTest::testParity() {
177
178 BOOST_TEST_MESSAGE("Testing that knock-in plus knock-out barrier options "
179 "replicate a European option...");
180
181 Date today = Settings::instance().evaluationDate();
182
183 DayCounter dc = Actual360();
184
185 ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>(args: 100.0);
186 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: 0.01, dc);
187 ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: 0.20, dc);
188 RelinkableHandle<BlackVolTermStructure> volHandle(volTS);
189
190 ext::shared_ptr<BlackScholesProcess> stochProcess =
191 ext::make_shared<BlackScholesProcess>(
192 args: Handle<Quote>(spot),
193 args: Handle<YieldTermStructure>(rTS),
194 args&: volHandle);
195
196 Date exerciseDate = today + 6*Months;
197
198 ext::shared_ptr<StrikedTypePayoff> payoff =
199 ext::make_shared<PlainVanillaPayoff>(args: Option::Call, args: 100.0);
200
201 ext::shared_ptr<Exercise> exercise =
202 ext::make_shared<EuropeanExercise>(args&: exerciseDate);
203
204 BarrierOption knockIn(Barrier::DownIn, 90.0, 0.0,
205 payoff, exercise);
206 BarrierOption knockOut(Barrier::DownOut, 90.0, 0.0,
207 payoff, exercise);
208 EuropeanOption european(payoff, exercise);
209
210 ext::shared_ptr<PricingEngine> barrierEngine
211 = ext::make_shared<AnalyticBarrierEngine>(args&: stochProcess);
212
213 ext::shared_ptr<PricingEngine> europeanEngine
214 = ext::make_shared<AnalyticEuropeanEngine>(args&: stochProcess);
215
216 knockIn.setPricingEngine(barrierEngine);
217 knockOut.setPricingEngine(barrierEngine);
218 european.setPricingEngine(europeanEngine);
219
220 Real replicated = knockIn.NPV() + knockOut.NPV();
221 Real expected = european.NPV();
222 Real error = std::fabs(x: replicated-expected);
223 if (error > 1e-7) {
224 BOOST_ERROR("Failed to replicate European option"
225 << "\n knock-in: " << knockIn.NPV()
226 << "\n knock-out: " << knockOut.NPV()
227 << "\n replicated: " << replicated
228 << "\n expected: " << expected
229 << std::scientific << std::setprecision(3)
230 << "\n error: " << error);
231 }
232
233 // try again with different day counters
234
235 volHandle.linkTo(h: flatVol(today, volatility: 0.20, dc: Business252(TARGET())));
236
237 replicated = knockIn.NPV() + knockOut.NPV();
238 expected = european.NPV();
239 error = std::fabs(x: replicated-expected);
240 if (error > 1e-7) {
241 BOOST_ERROR("Failed to replicate European option"
242 << "\n knock-in: " << knockIn.NPV()
243 << "\n knock-out: " << knockOut.NPV()
244 << "\n replicated: " << replicated
245 << "\n expected: " << expected
246 << std::scientific << std::setprecision(3)
247 << "\n error: " << error);
248 }
249}
250
251void BarrierOptionTest::testHaugValues() {
252
253 BOOST_TEST_MESSAGE("Testing barrier options against Haug's values...");
254
255 using namespace barrier_option_test;
256
257 Exercise::Type european = Exercise::European;
258 Exercise::Type american = Exercise::American;
259 NewBarrierOptionData values[] = {
260 /* The data below are from
261 "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag. 72
262 */
263 // barrierType, barrier, rebate, type, exercise, strk, s, q, r, t, v, result, tol
264 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 9.0246, .tol: 1.0e-4},
265 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 6.7924, .tol: 1.0e-4},
266 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 4.8759, .tol: 1.0e-4},
267 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4},
268 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4},
269 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4},
270 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.6789, .tol: 1.0e-4},
271 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.3580, .tol: 1.0e-4},
272 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.3453, .tol: 1.0e-4},
273
274 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 7.7627, .tol: 1.0e-4},
275 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 4.0109, .tol: 1.0e-4},
276 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.0576, .tol: 1.0e-4},
277 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 13.8333, .tol: 1.0e-4},
278 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 7.8494, .tol: 1.0e-4},
279 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.9795, .tol: 1.0e-4},
280 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 14.1112, .tol: 1.0e-4},
281 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 8.4482, .tol: 1.0e-4},
282 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 4.5910, .tol: 1.0e-4},
283
284 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 8.8334, .tol: 1.0e-4},
285 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 7.0285, .tol: 1.0e-4},
286 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 5.4137, .tol: 1.0e-4},
287 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.0000, .tol: 1.0e-4},
288 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.0000, .tol: 1.0e-4},
289 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.0000, .tol: 1.0e-4},
290 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.6341, .tol: 1.0e-4},
291 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.4389, .tol: 1.0e-4},
292 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.4315, .tol: 1.0e-4},
293
294 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 9.0093, .tol: 1.0e-4},
295 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 5.1370, .tol: 1.0e-4},
296 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.8517, .tol: 1.0e-4},
297 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 14.8816, .tol: 1.0e-4},
298 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 9.2045, .tol: 1.0e-4},
299 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 5.3043, .tol: 1.0e-4},
300 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 15.2098, .tol: 1.0e-4},
301 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 9.7278, .tol: 1.0e-4},
302 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 5.8350, .tol: 1.0e-4},
303
304
305 // barrierType, barrier, rebate, type, exercise, strk, s, q, r, t, v, result, tol
306 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.2798, .tol: 1.0e-4 },
307 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.2947, .tol: 1.0e-4 },
308 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.6252, .tol: 1.0e-4 },
309 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4 },
310 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4 },
311 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4 },
312 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.7760, .tol: 1.0e-4 },
313 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 5.4932, .tol: 1.0e-4 },
314 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 7.5187, .tol: 1.0e-4 },
315
316 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.9586, .tol: 1.0e-4 },
317 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 6.5677, .tol: 1.0e-4 },
318 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 11.9752, .tol: 1.0e-4 },
319 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.2845, .tol: 1.0e-4 },
320 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 5.9085, .tol: 1.0e-4 },
321 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 11.6465, .tol: 1.0e-4 },
322 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 1.4653, .tol: 1.0e-4 },
323 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.3721, .tol: 1.0e-4 },
324 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 7.0846, .tol: 1.0e-4 },
325
326 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.4170, .tol: 1.0e-4 },
327 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.4258, .tol: 1.0e-4 },
328 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.6246, .tol: 1.0e-4 },
329 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.0000, .tol: 1.0e-4 },
330 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.0000, .tol: 1.0e-4 },
331 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.0000, .tol: 1.0e-4 },
332 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 4.2293, .tol: 1.0e-4 },
333 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 5.8032, .tol: 1.0e-4 },
334 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 7.5649, .tol: 1.0e-4 },
335
336 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.8769, .tol: 1.0e-4 },
337 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 7.7989, .tol: 1.0e-4 },
338 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 13.3078, .tol: 1.0e-4 },
339 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 3.3328, .tol: 1.0e-4 },
340 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 7.2636, .tol: 1.0e-4 },
341 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 12.9713, .tol: 1.0e-4 },
342 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 2.0658, .tol: 1.0e-4 },
343 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 4.4226, .tol: 1.0e-4 },
344 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Put, .exType: european, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.30, .result: 8.3686, .tol: 1.0e-4 },
345
346 // Options with american exercise: values computed with 400 steps of Haug's VBA code (handles only out options)
347 // barrierType, barrier, rebate, type, exercise, strk, s, q, r, t, v, result, tol
348 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 0.0, .type: Option::Call, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 10.4655, .tol: 1.0e-4},
349 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 0.0, .type: Option::Call, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 4.5159, .tol: 1.0e-4},
350 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 0.0, .type: Option::Call, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.5971, .tol: 1.0e-4},
351 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4},
352 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4},
353 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4},
354 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 0.0, .type: Option::Call, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 11.8076, .tol: 1.0e-4},
355 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 0.0, .type: Option::Call, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.3993, .tol: 1.0e-4},
356 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.3457, .tol: 1.0e-4},
357
358 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 3.0, .type: Option::Put, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.2795, .tol: 1.0e-4 },
359 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 0.0, .type: Option::Put, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.3512, .tol: 1.0e-4 },
360 { .barrierType: Barrier::DownOut, .barrier: 95.0, .rebate: 0.0, .type: Option::Put, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 11.5773, .tol: 1.0e-4 },
361 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4 },
362 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4 },
363 { .barrierType: Barrier::DownOut, .barrier: 100.0, .rebate: 3.0, .type: Option::Put, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.0000, .tol: 1.0e-4 },
364 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 0.0, .type: Option::Put, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 1.4763, .tol: 1.0e-4 },
365 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 0.0, .type: Option::Put, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 3.3001, .tol: 1.0e-4 },
366 { .barrierType: Barrier::UpOut, .barrier: 105.0, .rebate: 0.0, .type: Option::Put, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 10.0000, .tol: 1.0e-4 },
367
368 // some american in-options - results (roughly) verified with other numerical methods
369 // barrierType, barrier, rebate, type, exercise, strk, s, q, r, t, v, result, tol
370 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 7.7615, .tol: 1.0e-4},
371 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 100, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 4.0118, .tol: 1.0e-4},
372 { .barrierType: Barrier::DownIn, .barrier: 95.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 2.0544, .tol: 1.0e-4},
373 { .barrierType: Barrier::DownIn, .barrier: 100.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 13.8308, .tol: 1.0e-4},
374 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 90, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 14.1150, .tol: 1.0e-4},
375 { .barrierType: Barrier::UpIn, .barrier: 105.0, .rebate: 3.0, .type: Option::Call, .exType: american, .strike: 110, .s: 100.0, .q: 0.04, .r: 0.08, .t: 0.50, .v: 0.25, .result: 4.5900, .tol: 1.0e-4},
376
377 /*
378 Data from "Going to Extreme: Correcting Simulation Bias in Exotic
379 Option Valuation"
380 D.R. Beaglehole, P.H. Dybvig and G. Zhou
381 Financial Analysts Journal; Jan / Feb 1997; 53, 1
382 */
383 // barrierType, barrier, rebate, type, strike, s, q, r, t, v, result, tol
384 // { Barrier::DownOut, 45.0, 0.0, Option::Put, 50, 50.0,-0.05, 0.10, 0.25, 0.50, 4.032, 1.0e-3 },
385 // { Barrier::DownOut, 45.0, 0.0, Option::Put, 50, 50.0,-0.05, 0.10, 1.00, 0.50, 5.477, 1.0e-3 }
386
387 };
388
389
390 DayCounter dc = Actual360();
391 Date today = Date::todaysDate();
392
393 ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>(args: 0.0);
394 ext::shared_ptr<SimpleQuote> qRate = ext::make_shared<SimpleQuote>(args: 0.0);
395 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc);
396 ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(args: 0.0);
397 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc);
398 ext::shared_ptr<SimpleQuote> vol = ext::make_shared<SimpleQuote>(args: 0.0);
399 ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc);
400
401 auto runTest = [&](const NewBarrierOptionData& value, const bool useZeroSpot, const bool useTriggeredBarrier) {
402 Date exDate = today + timeToDays(t: value.t);
403
404 spot->setValue(useZeroSpot ? 0.0 : value.s);
405 qRate->setValue(value.q);
406 rRate->setValue(value.r);
407 vol->setValue(value.v);
408
409 ext::shared_ptr<StrikedTypePayoff> payoff =
410 ext::make_shared<PlainVanillaPayoff>(args: value.type, args: value.strike);
411
412 ext::shared_ptr<BlackScholesMertonProcess> stochProcess =
413 ext::make_shared<BlackScholesMertonProcess>(
414 args: Handle<Quote>(spot),
415 args: Handle<YieldTermStructure>(qTS),
416 args: Handle<YieldTermStructure>(rTS),
417 args: Handle<BlackVolTermStructure>(volTS));
418
419 ext::shared_ptr<Exercise> exercise;
420 if (value.exType == Exercise::European)
421 exercise = ext::make_shared<EuropeanExercise>(args&: exDate);
422 else
423 exercise = ext::make_shared<AmericanExercise>(args&: exDate);
424
425 const auto barrier = useTriggeredBarrier
426 ? (value.barrierType == Barrier::Type::DownIn
427 || value.barrierType == Barrier::Type::DownOut ? value.s * 1.01 : value.s * 0.99)
428 : value.barrier;
429
430 BarrierOption barrierOption(value.barrierType, barrier, value.rebate, payoff, exercise);
431
432 ext::shared_ptr<PricingEngine> engine;
433 Real calculated;
434 Real expected;
435 Real error;
436 Real tol;
437
438 // AnalyticBarrierEngine and FdBlackScholesBarrierEngine support only european exercise type
439
440 engine = ext::make_shared<AnalyticBarrierEngine>(args&: stochProcess);
441 barrierOption.setPricingEngine(engine);
442
443 if (!useZeroSpot && !useTriggeredBarrier && value.exType == Exercise::European) {
444 Real calculated = barrierOption.NPV();
445 Real expected = value.result;
446 Real error = std::fabs(x: calculated - expected);
447 if (error > value.tol) {
448 REPORT_FAILURE("value", value.barrierType, value.barrier, value.rebate, payoff,
449 exercise, value.s, value.q, value.r, today, value.v, expected,
450 calculated, error, value.tol);
451 }
452 } else {
453 QL_ASSERT_EXCEPTION_THROWN(barrierOption.NPV();)
454 }
455
456 engine = ext::make_shared<FdBlackScholesBarrierEngine>(args&: stochProcess, args: 200, args: 400);
457 barrierOption.setPricingEngine(engine);
458
459 if (!useZeroSpot && !useTriggeredBarrier && value.exType == Exercise::European) {
460 calculated = barrierOption.NPV();
461 expected = value.result;
462 error = std::fabs(x: calculated - expected);
463 if (error > 5.0e-3) {
464 REPORT_FAILURE("fd value", value.barrierType, value.barrier, value.rebate, payoff,
465 exercise, value.s, value.q, value.r, today, value.v, expected,
466 calculated, error, value.tol);
467 }
468 } else {
469 QL_ASSERT_EXCEPTION_THROWN(barrierOption.NPV();)
470 }
471
472 engine = ext::make_shared<BinomialBarrierEngine<CoxRossRubinstein,DiscretizedBarrierOption> >(args&: stochProcess, args: 400);
473 barrierOption.setPricingEngine(engine);
474
475 if (!useZeroSpot && !useTriggeredBarrier) {
476 calculated = barrierOption.NPV();
477 expected = value.result;
478 error = std::fabs(x: calculated-expected);
479 tol = 1.1e-2;
480 if (error>tol) {
481 REPORT_FAILURE("Binomial (Boyle-lau) value", value.barrierType, value.barrier,
482 value.rebate, payoff, exercise, value.s, value.q, value.r, today,
483 value.v, expected, calculated, error, tol);
484 }
485 } else {
486 QL_ASSERT_EXCEPTION_THROWN(barrierOption.NPV();)
487 }
488
489 // Note: here, to test Derman convergence, we force maxTimeSteps to
490 // timeSteps, effectively disabling Boyle-Lau barrier adjustment.
491 // Production code should always enable Boyle-Lau. In most cases it
492 // gives very good convergence with only a modest timeStep increment.
493 engine = ext::make_shared<BinomialBarrierEngine<CoxRossRubinstein,DiscretizedDermanKaniBarrierOption> >(args&: stochProcess, args: 400);
494 barrierOption.setPricingEngine(engine);
495
496 if (!useZeroSpot && !useTriggeredBarrier) {
497 calculated = barrierOption.NPV();
498 expected = value.result;
499 error = std::fabs(x: calculated-expected);
500 tol = 4e-2;
501 if (error>tol) {
502 REPORT_FAILURE("Binomial (Derman) value", value.barrierType, value.barrier,
503 value.rebate, payoff, exercise, value.s, value.q, value.r, today,
504 value.v, expected, calculated, error, tol);
505 }
506 } else {
507 QL_ASSERT_EXCEPTION_THROWN(barrierOption.NPV();)
508 }
509 };
510
511 for (auto& value : values) {
512 runTest(value, false, false);
513 runTest(value, true, false);
514 runTest(value, false, true);
515 }
516}
517
518void BarrierOptionTest::testBabsiriValues() {
519
520 BOOST_TEST_MESSAGE("Testing barrier options against Babsiri's values...");
521
522 using namespace barrier_option_test;
523
524 /*
525 Data from
526 "Simulating Path-Dependent Options: A New Approach"
527 - M. El Babsiri and G. Noel
528 Journal of Derivatives; Winter 1998; 6, 2
529 */
530 BarrierOptionData values[] = {
531 { .type: Barrier::DownIn, .volatility: 0.10, .strike: 100, .barrier: 90, .callValue: 0.07187, .putValue: 0.0 },
532 { .type: Barrier::DownIn, .volatility: 0.15, .strike: 100, .barrier: 90, .callValue: 0.60638, .putValue: 0.0 },
533 { .type: Barrier::DownIn, .volatility: 0.20, .strike: 100, .barrier: 90, .callValue: 1.64005, .putValue: 0.0 },
534 { .type: Barrier::DownIn, .volatility: 0.25, .strike: 100, .barrier: 90, .callValue: 2.98495, .putValue: 0.0 },
535 { .type: Barrier::DownIn, .volatility: 0.30, .strike: 100, .barrier: 90, .callValue: 4.50952, .putValue: 0.0 },
536 { .type: Barrier::UpIn, .volatility: 0.10, .strike: 100, .barrier: 110, .callValue: 4.79148, .putValue: 0.0 },
537 { .type: Barrier::UpIn, .volatility: 0.15, .strike: 100, .barrier: 110, .callValue: 7.08268, .putValue: 0.0 },
538 { .type: Barrier::UpIn, .volatility: 0.20, .strike: 100, .barrier: 110, .callValue: 9.11008, .putValue: 0.0 },
539 { .type: Barrier::UpIn, .volatility: 0.25, .strike: 100, .barrier: 110, .callValue: 11.06148, .putValue: 0.0 },
540 { .type: Barrier::UpIn, .volatility: 0.30, .strike: 100, .barrier: 110, .callValue: 12.98351, .putValue: 0.0 }
541 };
542
543 Real underlyingPrice = 100.0;
544 Real rebate = 0.0;
545 Rate r = 0.05;
546 Rate q = 0.02;
547
548 DayCounter dc = Actual360();
549 Date today = Date::todaysDate();
550 ext::shared_ptr<SimpleQuote> underlying =
551 ext::make_shared<SimpleQuote>(args&: underlyingPrice);
552
553 ext::shared_ptr<SimpleQuote> qH_SME = ext::make_shared<SimpleQuote>(args&: q);
554 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qH_SME, dc);
555
556 ext::shared_ptr<SimpleQuote> rH_SME = ext::make_shared<SimpleQuote>(args&: r);
557 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rH_SME, dc);
558
559 ext::shared_ptr<SimpleQuote> volatility =
560 ext::make_shared<SimpleQuote>(args: 0.10);
561 ext::shared_ptr<BlackVolTermStructure> volTS =
562 flatVol(today, volatility, dc);
563
564 Date exDate = today+360;
565 ext::shared_ptr<Exercise> exercise =
566 ext::make_shared<EuropeanExercise>(args&: exDate);
567
568 for (auto& value : values) {
569 volatility->setValue(value.volatility);
570
571 ext::shared_ptr<StrikedTypePayoff> callPayoff =
572 ext::make_shared<PlainVanillaPayoff>(args: Option::Call, args&: value.strike);
573
574 ext::shared_ptr<BlackScholesMertonProcess> stochProcess =
575 ext::make_shared<BlackScholesMertonProcess>(
576 args: Handle<Quote>(underlying),
577 args: Handle<YieldTermStructure>(qTS),
578 args: Handle<YieldTermStructure>(rTS),
579 args: Handle<BlackVolTermStructure>(volTS));
580
581
582 ext::shared_ptr<PricingEngine> engine =
583 ext::make_shared<AnalyticBarrierEngine>(args&: stochProcess);
584
585 // analytic
586 BarrierOption barrierCallOption(value.type, value.barrier, rebate, callPayoff, exercise);
587 barrierCallOption.setPricingEngine(engine);
588 Real calculated = barrierCallOption.NPV();
589 Real expected = value.callValue;
590 Real error = std::fabs(x: calculated-expected);
591 Real maxErrorAllowed = 1.0e-5;
592 if (error>maxErrorAllowed) {
593 REPORT_FAILURE("value", value.type, value.barrier, rebate, callPayoff, exercise,
594 underlyingPrice, q, r, today, value.volatility, expected, calculated,
595 error, maxErrorAllowed);
596 }
597
598 Real maxMcRelativeErrorAllowed = 2.0e-2;
599
600 ext::shared_ptr<PricingEngine> mcEngine =
601 MakeMCBarrierEngine<LowDiscrepancy>(stochProcess)
602 .withStepsPerYear(steps: 1)
603 .withBrownianBridge()
604 .withSamples(samples: 131071) // 2^17-1
605 .withMaxSamples(samples: 1048575) // 2^20-1
606 .withSeed(seed: 5);
607
608 barrierCallOption.setPricingEngine(mcEngine);
609 calculated = barrierCallOption.NPV();
610 error = std::fabs(x: calculated-expected)/expected;
611 if (error>maxMcRelativeErrorAllowed) {
612 REPORT_FAILURE("value", value.type, value.barrier, rebate, callPayoff, exercise,
613 underlyingPrice, q, r, today, value.volatility, expected, calculated,
614 error, maxMcRelativeErrorAllowed);
615 }
616 }
617}
618
619void BarrierOptionTest::testBeagleholeValues() {
620
621 BOOST_TEST_MESSAGE("Testing barrier options against Beaglehole's values...");
622
623 using namespace barrier_option_test;
624
625 /*
626 Data from
627 "Going to Extreme: Correcting Simulation Bias in Exotic
628 Option Valuation"
629 - D.R. Beaglehole, P.H. Dybvig and G. Zhou
630 Financial Analysts Journal; Jan / Feb 1997; 53, 1
631 */
632 BarrierOptionData values[] = {
633 { .type: Barrier::DownOut, .volatility: 0.50, .strike: 50, .barrier: 45, .callValue: 5.477, .putValue: 0.0 }
634 };
635
636 Real underlyingPrice = 50.0;
637 Real rebate = 0.0;
638 Rate r = std::log(x: 1.1);
639 Rate q = 0.00;
640
641 DayCounter dc = Actual360();
642 Date today = Date::todaysDate();
643
644 ext::shared_ptr<SimpleQuote> underlying =
645 ext::make_shared<SimpleQuote>(args&: underlyingPrice);
646
647 ext::shared_ptr<SimpleQuote> qH_SME = ext::make_shared<SimpleQuote>(args&: q);
648 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qH_SME, dc);
649
650 ext::shared_ptr<SimpleQuote> rH_SME = ext::make_shared<SimpleQuote>(args&: r);
651 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rH_SME, dc);
652
653 ext::shared_ptr<SimpleQuote> volatility =
654 ext::make_shared<SimpleQuote>(args: 0.10);
655 ext::shared_ptr<BlackVolTermStructure> volTS =
656 flatVol(today, volatility, dc);
657
658
659 Date exDate = today+360;
660 ext::shared_ptr<Exercise> exercise =
661 ext::make_shared<EuropeanExercise>(args&: exDate);
662
663 for (auto& value : values) {
664 volatility->setValue(value.volatility);
665
666 ext::shared_ptr<StrikedTypePayoff> callPayoff =
667 ext::make_shared<PlainVanillaPayoff>(args: Option::Call, args&: value.strike);
668
669 ext::shared_ptr<BlackScholesMertonProcess> stochProcess =
670 ext::make_shared<BlackScholesMertonProcess>(
671 args: Handle<Quote>(underlying),
672 args: Handle<YieldTermStructure>(qTS),
673 args: Handle<YieldTermStructure>(rTS),
674 args: Handle<BlackVolTermStructure>(volTS));
675
676 ext::shared_ptr<PricingEngine> engine =
677 ext::make_shared<AnalyticBarrierEngine>(args&: stochProcess);
678
679 BarrierOption barrierCallOption(value.type, value.barrier, rebate, callPayoff, exercise);
680 barrierCallOption.setPricingEngine(engine);
681 Real calculated = barrierCallOption.NPV();
682 Real expected = value.callValue;
683 Real maxErrorAllowed = 1.0e-3;
684 Real error = std::fabs(x: calculated-expected);
685 if (error > maxErrorAllowed) {
686 REPORT_FAILURE("value", value.type, value.barrier, rebate, callPayoff, exercise,
687 underlyingPrice, q, r, today, value.volatility, expected, calculated,
688 error, maxErrorAllowed);
689 }
690
691 Real maxMcRelativeErrorAllowed = 0.01;
692 ext::shared_ptr<PricingEngine> mcEngine =
693 MakeMCBarrierEngine<LowDiscrepancy>(stochProcess)
694 .withStepsPerYear(steps: 1)
695 .withBrownianBridge()
696 .withSamples(samples: 131071) // 2^17-1
697 .withMaxSamples(samples: 1048575) // 2^20-1
698 .withSeed(seed: 10);
699
700 barrierCallOption.setPricingEngine(mcEngine);
701 calculated = barrierCallOption.NPV();
702 error = std::fabs(x: calculated-expected)/expected;
703 if (error>maxMcRelativeErrorAllowed) {
704 REPORT_FAILURE("value", value.type, value.barrier, rebate, callPayoff, exercise,
705 underlyingPrice, q, r, today, value.volatility, expected, calculated,
706 error, maxMcRelativeErrorAllowed);
707 }
708 }
709}
710
711void BarrierOptionTest::testPerturbative() {
712 BOOST_TEST_MESSAGE("Testing perturbative engine for barrier options...");
713
714 Real S = 100.0;
715 Real rebate = 0.0;
716 Rate r = 0.03;
717 Rate q = 0.02;
718
719 DayCounter dc = Actual360();
720 Date today = Date::todaysDate();
721
722 ext::shared_ptr<SimpleQuote> underlying =
723 ext::make_shared<SimpleQuote>(args&: S);
724 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: q, dc);
725 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: r, dc);
726
727 std::vector<Date> dates(2);
728 std::vector<Volatility> vols(2);
729
730 dates[0] = today + 90; vols[0] = 0.105;
731 dates[1] = today + 180; vols[1] = 0.11;
732
733 ext::shared_ptr<BlackVolTermStructure> volTS =
734 ext::make_shared<BlackVarianceCurve>(args&: today, args&: dates, args&: vols, args&: dc);
735
736 ext::shared_ptr<BlackScholesMertonProcess> stochProcess =
737 ext::make_shared<BlackScholesMertonProcess>(
738 args: Handle<Quote>(underlying),
739 args: Handle<YieldTermStructure>(qTS),
740 args: Handle<YieldTermStructure>(rTS),
741 args: Handle<BlackVolTermStructure>(volTS));
742
743 Real strike = 101.0;
744 Real barrier = 101.0;
745 Date exDate = today+180;
746
747 ext::shared_ptr<Exercise> exercise =
748 ext::make_shared<EuropeanExercise>(args&: exDate);
749 ext::shared_ptr<StrikedTypePayoff> payoff =
750 ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: strike);
751
752 BarrierOption option(Barrier::UpOut, barrier, rebate, payoff, exercise);
753
754 Natural order = 0;
755 bool zeroGamma = false;
756 ext::shared_ptr<PricingEngine> engine =
757 ext::make_shared<PerturbativeBarrierOptionEngine>(args&: stochProcess,
758 args&: order, args&: zeroGamma);
759
760 option.setPricingEngine(engine);
761
762 Real calculated = option.NPV();
763 Real expected = 0.897365;
764 Real tolerance = 1.0e-6;
765 if (std::fabs(x: calculated-expected) > tolerance) {
766 BOOST_ERROR("Failed to reproduce expected value"
767 << "\n calculated: " << std::setprecision(8) << calculated
768 << "\n expected: " << std::setprecision(8) << expected);
769 }
770
771 order = 1;
772 engine = ext::make_shared<PerturbativeBarrierOptionEngine>(args&: stochProcess,
773 args&: order,
774 args&: zeroGamma);
775
776 option.setPricingEngine(engine);
777
778 calculated = option.NPV();
779 expected = 0.894374;
780 if (std::fabs(x: calculated-expected) > tolerance) {
781 BOOST_ERROR("Failed to reproduce expected value"
782 << "\n calculated: " << std::setprecision(8) << calculated
783 << "\n expected: " << std::setprecision(8) << expected);
784 }
785
786 /* Too slow, skip
787 order = 2;
788 engine = ext::make_shared<PerturbativeBarrierOptionEngine>(stochProcess,
789 order,
790 zeroGamma);
791
792 option.setPricingEngine(engine);
793
794 calculated = option.NPV();
795 expected = 0.8943769;
796 if (std::fabs(calculated-expected) > tolerance) {
797 BOOST_ERROR("Failed to reproduce expected value"
798 << "\n calculated: " << std::setprecision(8) << calculated
799 << "\n expected: " << std::setprecision(8) << expected);
800 }
801 */
802}
803
804void BarrierOptionTest::testLocalVolAndHestonComparison() {
805 BOOST_TEST_MESSAGE("Testing local volatility and Heston FD engines "
806 "for barrier options...");
807
808 const Date settlementDate(5, July, 2002);
809 Settings::instance().evaluationDate() = settlementDate;
810
811 const DayCounter dayCounter = Actual365Fixed();
812 const Calendar calendar = TARGET();
813
814 Integer t[] = { 13, 41, 75, 165, 256, 345, 524, 703 };
815 Rate r[] = { 0.0357,0.0349,0.0341,0.0355,0.0359,0.0368,0.0386,0.0401 };
816
817 std::vector<Rate> rates(1, 0.0357);
818 std::vector<Date> dates(1, settlementDate);
819 for (Size i = 0; i < 8; ++i) {
820 dates.push_back(x: settlementDate + t[i]);
821 rates.push_back(x: r[i]);
822 }
823 const Handle<YieldTermStructure> rTS(
824 ext::make_shared<ZeroCurve>(args&: dates, args&: rates, args: dayCounter));
825 const Handle<YieldTermStructure> qTS(
826 flatRate(today: settlementDate, forward: 0.0, dc: dayCounter));
827
828 const Handle<Quote> s0(ext::make_shared<SimpleQuote>(args: 4500.00));
829
830 const std::vector<Real> strikes = { 100 ,500 ,2000,3400,3600,3800,4000,4200,4400,4500,
831 4600,4800,5000,5200,5400,5600,7500,10000,20000,30000 };
832
833 const std::vector<Volatility> v =
834 { 1.015873, 1.015873, 1.015873, 0.89729, 0.796493, 0.730914, 0.631335, 0.568895,
835 0.711309, 0.711309, 0.711309, 0.641309, 0.635593, 0.583653, 0.508045, 0.463182,
836 0.516034, 0.500534, 0.500534, 0.500534, 0.448706, 0.416661, 0.375470, 0.353442,
837 0.516034, 0.482263, 0.447713, 0.387703, 0.355064, 0.337438, 0.316966, 0.306859,
838 0.497587, 0.464373, 0.430764, 0.374052, 0.344336, 0.328607, 0.310619, 0.301865,
839 0.479511, 0.446815, 0.414194, 0.361010, 0.334204, 0.320301, 0.304664, 0.297180,
840 0.461866, 0.429645, 0.398092, 0.348638, 0.324680, 0.312512, 0.299082, 0.292785,
841 0.444801, 0.413014, 0.382634, 0.337026, 0.315788, 0.305239, 0.293855, 0.288660,
842 0.428604, 0.397219, 0.368109, 0.326282, 0.307555, 0.298483, 0.288972, 0.284791,
843 0.420971, 0.389782, 0.361317, 0.321274, 0.303697, 0.295302, 0.286655, 0.282948,
844 0.413749, 0.382754, 0.354917, 0.316532, 0.300016, 0.292251, 0.284420, 0.281164,
845 0.400889, 0.370272, 0.343525, 0.307904, 0.293204, 0.286549, 0.280189, 0.277767,
846 0.390685, 0.360399, 0.334344, 0.300507, 0.287149, 0.281380, 0.276271, 0.274588,
847 0.383477, 0.353434, 0.327580, 0.294408, 0.281867, 0.276746, 0.272655, 0.271617,
848 0.379106, 0.349214, 0.323160, 0.289618, 0.277362, 0.272641, 0.269332, 0.268846,
849 0.377073, 0.347258, 0.320776, 0.286077, 0.273617, 0.269057, 0.266293, 0.266265,
850 0.399925, 0.369232, 0.338895, 0.289042, 0.265509, 0.255589, 0.249308, 0.249665,
851 0.423432, 0.406891, 0.373720, 0.314667, 0.281009, 0.263281, 0.246451, 0.242166,
852 0.453704, 0.453704, 0.453704, 0.381255, 0.334578, 0.305527, 0.268909, 0.251367,
853 0.517748, 0.517748, 0.517748, 0.416577, 0.364770, 0.331595, 0.287423, 0.264285 };
854
855 Matrix blackVolMatrix(strikes.size(), dates.size()-1);
856 for (Size i=0; i < strikes.size(); ++i)
857 for (Size j=1; j < dates.size(); ++j) {
858 blackVolMatrix[i][j-1] = v[i*(dates.size()-1)+j-1];
859 }
860
861 const ext::shared_ptr<BlackVarianceSurface> volTS =
862 ext::make_shared<BlackVarianceSurface>(
863 args: settlementDate, args: calendar,
864 args: std::vector<Date>(dates.begin()+1,dates.end()),
865 args: strikes, args&: blackVolMatrix,
866 args: dayCounter);
867 volTS->setInterpolation<Bicubic>();
868 const ext::shared_ptr<GeneralizedBlackScholesProcess> localVolProcess =
869 ext::make_shared<BlackScholesMertonProcess>(
870 args: s0, args: qTS, args: rTS,
871 args: Handle<BlackVolTermStructure>(volTS));
872
873 const Real v0 =0.195662;
874 const Real kappa=5.6628;
875 const Real theta=0.0745911;
876 const Real sigma=1.1619;
877 const Real rho =-0.511493;
878
879 ext::shared_ptr<HestonProcess> hestonProcess =
880 ext::make_shared<HestonProcess>(args: rTS, args: qTS, args: s0, args: v0,
881 args: kappa, args: theta, args: sigma, args: rho);
882
883 ext::shared_ptr<HestonModel> hestonModel =
884 ext::make_shared<HestonModel>(args&: hestonProcess);
885
886 ext::shared_ptr<PricingEngine> fdHestonEngine =
887 ext::make_shared<FdHestonBarrierEngine>(args&: hestonModel, args: 100, args: 400, args: 50);
888
889 ext::shared_ptr<PricingEngine> fdLocalVolEngine =
890 ext::make_shared<FdBlackScholesBarrierEngine>(args: localVolProcess,
891 args: 100, args: 400, args: 0,
892 args: FdmSchemeDesc::Douglas(),
893 args: true, args: 0.35);
894
895 const Real strike = s0->value();
896 const Real barrier = 3000;
897 const Real rebate = 100;
898 const Date exDate = settlementDate + Period(20, Months);
899
900 const ext::shared_ptr<StrikedTypePayoff> payoff =
901 ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args: strike);
902
903 const ext::shared_ptr<Exercise> exercise =
904 ext::make_shared<EuropeanExercise>(args: exDate);
905
906 BarrierOption barrierOption(Barrier::DownOut,
907 barrier, rebate, payoff, exercise);
908
909 barrierOption.setPricingEngine(fdHestonEngine);
910 const Real expectedHestonNPV = 111.5;
911 const Real calculatedHestonNPV = barrierOption.NPV();
912
913 barrierOption.setPricingEngine(fdLocalVolEngine);
914 const Real expectedLocalVolNPV = 132.8;
915 const Real calculatedLocalVolNPV = barrierOption.NPV();
916
917 const Real tol = 0.01;
918
919 if (std::fabs(x: expectedHestonNPV - calculatedHestonNPV)
920 > tol*expectedHestonNPV) {
921 BOOST_FAIL("Failed to reproduce Heston barrier price for "
922 << "\n strike: " << payoff->strike()
923 << "\n barrier: " << barrier
924 << "\n maturity: " << exDate
925 << "\n calculated: " << calculatedHestonNPV
926 << "\n expected: " << expectedHestonNPV);
927 }
928 if (std::fabs(x: expectedLocalVolNPV - calculatedLocalVolNPV)
929 > tol*expectedLocalVolNPV) {
930 BOOST_FAIL("Failed to reproduce Heston barrier price for "
931 << "\n strike: " << payoff->strike()
932 << "\n barrier: " << barrier
933 << "\n maturity: " << exDate
934 << "\n calculated: " << calculatedLocalVolNPV
935 << "\n expected: " << expectedLocalVolNPV);
936 }
937}
938
939
940void BarrierOptionTest::testVannaVolgaSimpleBarrierValues() {
941 BOOST_TEST_MESSAGE("Testing barrier FX options against Vanna/Volga values...");
942
943 using namespace barrier_option_test;
944
945 BarrierFxOptionData values[] = {
946
947 //barrierType,barrier,rebate,type,strike,s,q,r,t,vol25Put,volAtm,vol25Call,vol, result, tol
948 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.148127, .tol: 1.0e-4},
949 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.075943, .tol: 1.0e-4},
950 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.0274771, .tol: 1.0e-4},
951 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.00573, .tol: 1.0e-4},
952 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00012, .tol: 1.0e-4},
953
954 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.00697606, .tol: 1.0e-4},
955 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.020078, .tol: 1.0e-4},
956 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.0489395, .tol: 1.0e-4},
957 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.0969877, .tol: 1.0e-4},
958 { .barrierType: Barrier::UpOut,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.157, .tol: 1.0e-4},
959
960 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.0322202, .tol: 1.0e-4},
961 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.0241491, .tol: 1.0e-4},
962 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.0164275, .tol: 1.0e-4},
963 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.01, .tol: 1.0e-4},
964 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Call,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00489, .tol: 1.0e-4},
965
966 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.000560713, .tol: 1.0e-4},
967 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.000546804, .tol: 1.0e-4},
968 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.000130649, .tol: 1.0e-4},
969 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.000300828, .tol: 1.0e-4},
970 { .barrierType: Barrier::UpIn,.barrier: 1.5,.rebate: 0, .type: Option::Put,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00135, .tol: 1.0e-4},
971
972 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.17746, .tol: 1.0e-4},
973 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.0994142, .tol: 1.0e-4},
974 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.0439, .tol: 1.0e-4},
975 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.01574, .tol: 1.0e-4},
976 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00501, .tol: 1.0e-4},
977
978 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.00612, .tol: 1.0e-4},
979 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.00426, .tol: 1.0e-4},
980 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.00257, .tol: 1.0e-4},
981 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.00122, .tol: 1.0e-4},
982 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00045, .tol: 1.0e-4},
983
984 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.00022, .tol: 1.0e-4},
985 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.00284, .tol: 1.0e-4},
986 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.02032, .tol: 1.0e-4},
987 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.058235, .tol: 1.0e-4},
988 { .barrierType: Barrier::DownOut,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.109432, .tol: 1.0e-4},
989
990 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0, .tol: 1.0e-4},
991 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0, .tol: 1.0e-4},
992 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0, .tol: 1.0e-4},
993 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.00017, .tol: 1.0e-4},
994 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00083, .tol: 1.0e-4},
995
996 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.00289, .tol: 1.0e-4},
997 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.00067784, .tol: 1.0e-4},
998 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0, .tol: 1.0e-4},
999 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0, .tol: 1.0e-4},
1000 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Call,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0, .tol: 1.0e-4},
1001
1002 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.17423, .tol: 1.0e-4},
1003 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.09584, .tol: 1.0e-4},
1004 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.04133, .tol: 1.0e-4},
1005 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.01452, .tol: 1.0e-4},
1006 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.00456, .tol: 1.0e-4},
1007
1008 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.00732, .tol: 1.0e-4},
1009 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.01778, .tol: 1.0e-4},
1010 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.02875, .tol: 1.0e-4},
1011 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.0390535, .tol: 1.0e-4},
1012 { .barrierType: Barrier::DownIn,.barrier: 1.1,.rebate: 0, .type: Option::Put,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.0489236, .tol: 1.0e-4},
1013
1014 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.13321,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.11638,.result: 0.00753, .tol: 1.0e-4},
1015 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.22687,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.10088,.result: 0.02062, .tol: 1.0e-4},
1016 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.31179,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08925,.result: 0.04907, .tol: 1.0e-4},
1017 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.38843,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08463,.result: 0.09711, .tol: 1.0e-4},
1018 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.46047,.s: 1.30265,.q: 0.0003541,.r: 0.0033871,.t: 1,.vol25Put: 0.10087,.volAtm: 0.08925,.vol25Call: 0.08463,.v: 0.08412,.result: 0.15752, .tol: 1.0e-4},
1019
1020 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.20493, .tol: 1.0e-4},
1021 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.105577, .tol: 1.0e-4},
1022 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.0358872, .tol: 1.0e-4},
1023 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.00634958, .tol: 1.0e-4},
1024 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0, .tol: 1.0e-4},
1025
1026 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.0108218, .tol: 1.0e-4},
1027 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.0313339, .tol: 1.0e-4},
1028 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.0751237, .tol: 1.0e-4},
1029 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.153407, .tol: 1.0e-4},
1030 { .barrierType: Barrier::UpOut,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.253767, .tol: 1.0e-4},
1031
1032 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.05402, .tol: 1.0e-4},
1033 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.0410069, .tol: 1.0e-4},
1034 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.0279562, .tol: 1.0e-4},
1035 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.0173055, .tol: 1.0e-4},
1036 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Call,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.00764, .tol: 1.0e-4},
1037
1038 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.000962737, .tol: 1.0e-4},
1039 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.00102637, .tol: 1.0e-4},
1040 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.000419834, .tol: 1.0e-4},
1041 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.00159277, .tol: 1.0e-4},
1042 { .barrierType: Barrier::UpIn,.barrier: 1.6,.rebate: 0, .type: Option::Put,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.00473629, .tol: 1.0e-4},
1043
1044 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.255098, .tol: 1.0e-4},
1045 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.145701, .tol: 1.0e-4},
1046 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.06384, .tol: 1.0e-4},
1047 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.02366, .tol: 1.0e-4},
1048 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.00764, .tol: 1.0e-4},
1049
1050 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.00592, .tol: 1.0e-4},
1051 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.00421, .tol: 1.0e-4},
1052 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.00256, .tol: 1.0e-4},
1053 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.0012, .tol: 1.0e-4},
1054 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.0004, .tol: 1.0e-4},
1055
1056 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0, .tol: 1.0e-4},
1057 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.00280549, .tol: 1.0e-4},
1058 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.0279945, .tol: 1.0e-4},
1059 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.0896352, .tol: 1.0e-4},
1060 { .barrierType: Barrier::DownOut,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.175182, .tol: 1.0e-4},
1061
1062 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511, .result: 0.00000, .tol: 1.0e-4},
1063 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089, .result: 0.00000, .tol: 1.0e-4},
1064 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444, .result: 0.00000, .tol: 1.0e-4},
1065 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.0002, .tol: 1.0e-4},
1066 { .barrierType: Barrier::DownOut,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.00096, .tol: 1.0e-4},
1067
1068 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.00384783, .tol: 1.0e-4},
1069 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.000883232, .tol: 1.0e-4},
1070 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0, .tol: 1.0e-4},
1071 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197, .result: 0.00000, .tol: 1.0e-4},
1072 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Call,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261, .result: 0.00000, .tol: 1.0e-4},
1073
1074 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.25302, .tol: 1.0e-4},
1075 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.14238, .tol: 1.0e-4},
1076 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.06128, .tol: 1.0e-4},
1077 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.02245, .tol: 1.0e-4},
1078 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Call,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.00725, .tol: 1.0e-4},
1079
1080 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.01178, .tol: 1.0e-4},
1081 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.0295548, .tol: 1.0e-4},
1082 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.047549, .tol: 1.0e-4},
1083 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.0653642, .tol: 1.0e-4},
1084 { .barrierType: Barrier::DownIn,.barrier: 1,.rebate: 0, .type: Option::Put,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.0833221, .tol: 1.0e-4},
1085
1086 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.06145,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.12511,.result: 0.01178, .tol: 1.0e-4},
1087 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.19545,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.1089,.result: 0.03236, .tol: 1.0e-4},
1088 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.32238,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09444,.result: 0.07554, .tol: 1.0e-4},
1089 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.44298,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09197,.result: 0.15479, .tol: 1.0e-4},
1090 { .barrierType: Barrier::DownIn,.barrier: 1.3,.rebate: 0, .type: Option::Put,.strike: 1.56345,.s: 1.30265,.q: 0.0009418,.r: 0.0039788,.t: 2,.vol25Put: 0.10891,.volAtm: 0.09525,.vol25Call: 0.09197,.v: 0.09261,.result: 0.25754, .tol: 1.0e-4},
1091
1092 };
1093
1094 DayCounter dc = Actual365Fixed();
1095 Date today(5, March, 2013);
1096 Settings::instance().evaluationDate() = today;
1097
1098 ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>(args: 0.0);
1099 ext::shared_ptr<SimpleQuote> qRate = ext::make_shared<SimpleQuote>(args: 0.0);
1100 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc);
1101 ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(args: 0.0);
1102 ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc);
1103 ext::shared_ptr<SimpleQuote> vol25Put = ext::make_shared<SimpleQuote>(args: 0.0);
1104 ext::shared_ptr<SimpleQuote> volAtm = ext::make_shared<SimpleQuote>(args: 0.0);
1105 ext::shared_ptr<SimpleQuote> vol25Call = ext::make_shared<SimpleQuote>(args: 0.0);
1106
1107 for (auto& value : values) {
1108
1109 spot->setValue(value.s);
1110 qRate->setValue(value.q);
1111 rRate->setValue(value.r);
1112 vol25Put->setValue(value.vol25Put);
1113 volAtm->setValue(value.volAtm);
1114 vol25Call->setValue(value.vol25Call);
1115
1116 ext::shared_ptr<StrikedTypePayoff> payoff =
1117 ext::make_shared<PlainVanillaPayoff>(args&: value.type, args&: value.strike);
1118
1119 Date exDate = today + timeToDays(t: value.t, daysPerYear: 365);
1120 ext::shared_ptr<Exercise> exercise =
1121 ext::make_shared<EuropeanExercise>(args&: exDate);
1122
1123 Handle<DeltaVolQuote> volAtmQuote = Handle<DeltaVolQuote>(ext::make_shared<DeltaVolQuote>(
1124 args: Handle<Quote>(volAtm), args: DeltaVolQuote::Fwd, args&: value.t, args: DeltaVolQuote::AtmDeltaNeutral));
1125
1126 Handle<DeltaVolQuote> vol25PutQuote(Handle<DeltaVolQuote>(ext::make_shared<DeltaVolQuote>(
1127 args: -0.25, args: Handle<Quote>(vol25Put), args&: value.t, args: DeltaVolQuote::Fwd)));
1128
1129 Handle<DeltaVolQuote> vol25CallQuote(Handle<DeltaVolQuote>(ext::make_shared<DeltaVolQuote>(
1130 args: 0.25, args: Handle<Quote>(vol25Call), args&: value.t, args: DeltaVolQuote::Fwd)));
1131
1132 BarrierOption barrierOption(value.barrierType, value.barrier, value.rebate, payoff,
1133 exercise);
1134
1135 Real bsVanillaPrice =
1136 blackFormula(optionType: value.type, strike: value.strike,
1137 forward: spot->value() * qTS->discount(t: value.t) / rTS->discount(t: value.t),
1138 stdDev: value.v * std::sqrt(x: value.t), discount: rTS->discount(t: value.t));
1139 ext::shared_ptr<PricingEngine> vannaVolgaEngine =
1140 ext::make_shared<VannaVolgaBarrierEngine>(
1141 args&: volAtmQuote,
1142 args&: vol25PutQuote,
1143 args&: vol25CallQuote,
1144 args: Handle<Quote> (spot),
1145 args: Handle<YieldTermStructure> (rTS),
1146 args: Handle<YieldTermStructure> (qTS),
1147 args: true,
1148 args&: bsVanillaPrice);
1149 barrierOption.setPricingEngine(vannaVolgaEngine);
1150
1151 Real calculated = barrierOption.NPV();
1152 Real expected = value.result;
1153 Real error = std::fabs(x: calculated-expected);
1154 if (error > value.tol) {
1155 REPORT_FX_FAILURE("value", value.barrierType, value.barrier, value.rebate, payoff,
1156 exercise, value.s, value.q, value.r, today, value.vol25Put,
1157 value.volAtm, value.vol25Call, value.v, expected, calculated, error,
1158 value.tol);
1159 }
1160 }
1161}
1162
1163void BarrierOptionTest::testOldDividendBarrierOption() {
1164 BOOST_TEST_MESSAGE("Testing old-style barrier option pricing with discrete dividends...");
1165
1166 const DayCounter dc = Actual365Fixed();
1167
1168 const Date today(11, February, 2018);
1169 const Date maturity = today + Period(1, Years);
1170 Settings::instance().evaluationDate() = today;
1171
1172 const Real spot = 100.0;
1173 const Real strike = 105.0;
1174 const Real rebate = 5.0;
1175
1176 const Real barriers[] = { 80.0, 120.0 };
1177 const Barrier::Type barrierTypes[] = { Barrier::DownOut, Barrier::UpOut };
1178
1179 const Rate r = 0.05;
1180 const Rate q = 0.0;
1181 const Volatility v = 0.02;
1182
1183 const Handle<Quote> s0(ext::make_shared<SimpleQuote>(args: spot));
1184 const Handle<YieldTermStructure> qTS(flatRate(today, forward: q, dc));
1185 const Handle<YieldTermStructure> rTS(flatRate(today, forward: r, dc));
1186 const Handle<BlackVolTermStructure> volTS(flatVol(today, volatility: v, dc));
1187
1188 const ext::shared_ptr<BlackScholesMertonProcess> bsProcess =
1189 ext::make_shared<BlackScholesMertonProcess>(args: s0, args: qTS, args: rTS, args: volTS);
1190
1191 const ext::shared_ptr<PricingEngine> douglas =
1192 ext::make_shared<FdBlackScholesBarrierEngine>(
1193 args: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::Douglas());
1194
1195 const ext::shared_ptr<PricingEngine> crankNicolson =
1196 ext::make_shared<FdBlackScholesBarrierEngine>(
1197 args: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::CrankNicolson());
1198
1199 const ext::shared_ptr<PricingEngine> craigSnyed =
1200 ext::make_shared<FdBlackScholesBarrierEngine>(
1201 args: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::CraigSneyd());
1202
1203 const ext::shared_ptr<PricingEngine> hundsdorfer =
1204 ext::make_shared<FdBlackScholesBarrierEngine>(
1205 args: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::Hundsdorfer());
1206
1207 const ext::shared_ptr<PricingEngine> mol =
1208 ext::make_shared<FdBlackScholesBarrierEngine>(
1209 args: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::MethodOfLines());
1210
1211 const ext::shared_ptr<PricingEngine> trPDF2 =
1212 ext::make_shared<FdBlackScholesBarrierEngine>(
1213 args: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::TrBDF2());
1214
1215 const ext::shared_ptr<PricingEngine> hestonEngine =
1216 ext::make_shared<FdHestonBarrierEngine>(
1217 args: ext::make_shared<HestonModel>(
1218 args: ext::make_shared<HestonProcess>(
1219 args: rTS, args: qTS, args: s0, args: v*v, args: 1.0, args: v*v, args: 0.005, args: 0.0)), args: 50, args: 101, args: 3);
1220
1221 const ext::shared_ptr<PricingEngine> engines[] = {
1222 douglas, crankNicolson,
1223 trPDF2, craigSnyed, hundsdorfer, mol, hestonEngine
1224 };
1225
1226 const ext::shared_ptr<StrikedTypePayoff> payoff =
1227 ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args: strike);
1228
1229 const ext::shared_ptr<Exercise> exercise =
1230 ext::make_shared<EuropeanExercise>(args: maturity);
1231
1232 const Real divAmount = 30;
1233 const Date divDate = today + Period(6, Months);
1234
1235 const Real expected[] = {
1236 rTS->discount(d: divDate)*rebate,
1237 (*payoff)(
1238 (spot - divAmount*rTS->discount(d: divDate))/rTS->discount(d: maturity))
1239 *rTS->discount(d: maturity)
1240 };
1241
1242 const Real relTol = 1e-4;
1243 for (Size i=0; i < LENGTH(barriers); ++i) {
1244 for (const auto& engine : engines) {
1245 const Real barrier = barriers[i];
1246 const Barrier::Type barrierType = barrierTypes[i];
1247
1248 QL_DEPRECATED_DISABLE_WARNING
1249
1250 DividendBarrierOption barrierOption(
1251 barrierType, barrier, rebate, payoff, exercise,
1252 std::vector<Date>(1, divDate),
1253 std::vector<Real>(1, divAmount));
1254
1255 QL_DEPRECATED_ENABLE_WARNING
1256
1257 barrierOption.setPricingEngine(engine);
1258
1259 const Real calculated = barrierOption.NPV();
1260
1261 const Real diff = std::fabs(x: calculated - expected[i]);
1262 if (diff > relTol*expected[i]) {
1263 BOOST_FAIL("Failed to reproduce barrier price with "
1264 "discrete dividends for "
1265 << "\n strike: " << strike
1266 << "\n barrier: " << barrier
1267 << "\n maturity: " << maturity
1268 << "\n calculated: " << calculated
1269 << "\n expected: " << expected[i]
1270 << std::scientific
1271 << "\n difference " << diff
1272 << "\n tolerance " << relTol * expected[i]);
1273 }
1274 }
1275 }
1276}
1277
1278void BarrierOptionTest::testDividendBarrierOption() {
1279 BOOST_TEST_MESSAGE("Testing barrier option pricing with discrete dividends...");
1280
1281 DayCounter dc = Actual365Fixed();
1282
1283 Date today(11, February, 2018);
1284 Date maturity = today + Period(1, Years);
1285 Settings::instance().evaluationDate() = today;
1286
1287 Real spot = 100.0;
1288 Real strike = 105.0;
1289 Real rebate = 5.0;
1290
1291 Real barriers[] = { 80.0, 120.0, 80.0, 120.0 };
1292 Barrier::Type barrierTypes[] = { Barrier::DownOut, Barrier::UpOut, Barrier::DownIn, Barrier::UpIn };
1293
1294 Rate r = 0.05;
1295 Rate q = 0.0;
1296 Volatility v = 0.02;
1297
1298 Handle<Quote> s0(ext::make_shared<SimpleQuote>(args&: spot));
1299 Handle<YieldTermStructure> qTS(flatRate(today, forward: q, dc));
1300 Handle<YieldTermStructure> rTS(flatRate(today, forward: r, dc));
1301 Handle<BlackVolTermStructure> volTS(flatVol(today, volatility: v, dc));
1302
1303 auto bsProcess = ext::make_shared<BlackScholesMertonProcess>(args&: s0, args&: qTS, args&: rTS, args&: volTS);
1304
1305 Real divAmount = 30;
1306 Date divDate = today + Period(6, Months);
1307 auto dividends = DividendVector(dividendDates: { divDate }, dividends: { divAmount });
1308
1309 auto douglas =
1310 ext::make_shared<FdBlackScholesBarrierEngine>(
1311 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::Douglas());
1312
1313 auto crankNicolson =
1314 ext::make_shared<FdBlackScholesBarrierEngine>(
1315 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::CrankNicolson());
1316
1317 auto craigSnyed =
1318 ext::make_shared<FdBlackScholesBarrierEngine>(
1319 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::CraigSneyd());
1320
1321 auto hundsdorfer =
1322 ext::make_shared<FdBlackScholesBarrierEngine>(
1323 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::Hundsdorfer());
1324
1325 auto mol =
1326 ext::make_shared<FdBlackScholesBarrierEngine>(
1327 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::MethodOfLines());
1328
1329 auto trPDF2 =
1330 ext::make_shared<FdBlackScholesBarrierEngine>(
1331 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::TrBDF2());
1332
1333 auto hestonEngine =
1334 ext::make_shared<FdHestonBarrierEngine>(
1335 args: ext::make_shared<HestonModel>(
1336 args: ext::make_shared<HestonProcess>(
1337 args&: rTS, args&: qTS, args&: s0, args: v*v, args: 1.0, args: v*v, args: 0.005, args: 0.0)),
1338 args&: dividends, args: 50, args: 101, args: 3);
1339
1340 ext::shared_ptr<PricingEngine> engines[] = {
1341 douglas, crankNicolson,
1342 trPDF2, craigSnyed, hundsdorfer, mol, hestonEngine
1343 };
1344
1345 auto payoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: strike);
1346 auto exercise = ext::make_shared<EuropeanExercise>(args&: maturity);
1347
1348 Real expected[] = {
1349 rTS->discount(d: divDate)*rebate,
1350 (*payoff)(
1351 (spot - divAmount*rTS->discount(d: divDate))/rTS->discount(d: maturity))
1352 *rTS->discount(d: maturity),
1353 29.154,
1354 4.765
1355 };
1356
1357 Real relTol = 2e-4;
1358 for (Size i=0; i < LENGTH(barriers); ++i) {
1359 for (Size j=0; j < LENGTH(engines); ++j) {
1360 Real barrier = barriers[i];
1361 Barrier::Type barrierType = barrierTypes[i];
1362
1363 BarrierOption barrierOption(barrierType, barrier, rebate, payoff, exercise);
1364 barrierOption.setPricingEngine(engines[j]);
1365
1366 Real calculated = barrierOption.NPV();
1367 Real diff = std::fabs(x: calculated - expected[i]);
1368 if (diff > relTol*expected[i]) {
1369 BOOST_ERROR("Failed to reproduce barrier price with discrete dividends:"
1370 << "\n engine: " << j
1371 << "\n strike: " << strike
1372 << "\n barrier: " << barrier
1373 << "\n maturity: " << maturity
1374 << "\n calculated: " << calculated
1375 << "\n expected: " << expected[i]
1376 << std::scientific
1377 << "\n difference " << diff
1378 << "\n tolerance " << relTol * expected[i]);
1379 }
1380 }
1381 }
1382}
1383
1384
1385void BarrierOptionTest::testDividendBarrierOptionWithDividendsPastMaturity() {
1386 BOOST_TEST_MESSAGE("Testing barrier option pricing with discrete dividends past maturity...");
1387
1388 DayCounter dc = Actual365Fixed();
1389
1390 Date today(11, February, 2018);
1391 Date maturity = today + Period(1, Years);
1392 Settings::instance().evaluationDate() = today;
1393
1394 Real spot = 100.0;
1395 Real strike = 105.0;
1396 Real rebate = 5.0;
1397
1398 Real barriers[] = { 90.0, 110.0 };
1399 Barrier::Type barrierTypes[] = { Barrier::DownOut, Barrier::UpOut };
1400
1401 Rate r = 0.05;
1402 Rate q = 0.0;
1403 Volatility v = 0.02;
1404
1405 Handle<Quote> s0(ext::make_shared<SimpleQuote>(args&: spot));
1406 Handle<YieldTermStructure> qTS(flatRate(today, forward: q, dc));
1407 Handle<YieldTermStructure> rTS(flatRate(today, forward: r, dc));
1408 Handle<BlackVolTermStructure> volTS(flatVol(today, volatility: v, dc));
1409
1410 auto bsProcess = ext::make_shared<BlackScholesMertonProcess>(args&: s0, args&: qTS, args&: rTS, args&: volTS);
1411
1412 Real divAmount = 30;
1413 Date divDate = today + Period(18, Months);
1414 auto dividends = DividendVector(dividendDates: { divDate }, dividends: { divAmount });
1415
1416 ext::shared_ptr<PricingEngine> engines[] = {
1417 ext::make_shared<FdBlackScholesBarrierEngine>(
1418 args&: bsProcess, args: 100, args: 100, args: 0, args: FdmSchemeDesc::Douglas()),
1419 ext::make_shared<FdHestonBarrierEngine>(
1420 args: ext::make_shared<HestonModel>(
1421 args: ext::make_shared<HestonProcess>(
1422 args&: rTS, args&: qTS, args&: s0, args: v*v, args: 1.0, args: v*v, args: 0.005, args: 0.0)),
1423 args: 50, args: 101, args: 3)
1424 };
1425
1426 ext::shared_ptr<PricingEngine> enginesWithDividends[] = {
1427 ext::make_shared<FdBlackScholesBarrierEngine>(
1428 args&: bsProcess, args&: dividends, args: 100, args: 100, args: 0, args: FdmSchemeDesc::Douglas()),
1429 ext::make_shared<FdHestonBarrierEngine>(
1430 args: ext::make_shared<HestonModel>(
1431 args: ext::make_shared<HestonProcess>(
1432 args&: rTS, args&: qTS, args&: s0, args: v*v, args: 1.0, args: v*v, args: 0.005, args: 0.0)),
1433 args&: dividends, args: 50, args: 101, args: 3)
1434 };
1435
1436 auto payoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: strike);
1437 auto exercise = ext::make_shared<EuropeanExercise>(args&: maturity);
1438
1439 for (Size i=0; i < LENGTH(barriers); ++i) {
1440 for (Size j=0; j < LENGTH(engines); ++j) {
1441 Real barrier = barriers[i];
1442 Barrier::Type barrierType = barrierTypes[i];
1443
1444 BarrierOption barrierOption(barrierType, barrier, rebate, payoff, exercise);
1445
1446 barrierOption.setPricingEngine(engines[j]);
1447 Real withoutDividends = barrierOption.NPV();
1448
1449 barrierOption.setPricingEngine(enginesWithDividends[j]);
1450 Real withDividends = barrierOption.NPV();
1451
1452 Real diff = std::fabs(x: withDividends - withoutDividends);
1453 Real tolerance = 1e-12;
1454 if (diff > tolerance) {
1455 BOOST_ERROR("Dividends past maturity affected option price:"
1456 << "\n engine: " << j
1457 << "\n strike: " << strike
1458 << "\n barrier: " << barrier
1459 << "\n maturity: " << maturity
1460 << "\n without dividend: " << withoutDividends
1461 << "\n with dividend: " << withDividends
1462 << std::scientific
1463 << "\n difference " << diff);
1464 }
1465 }
1466 }
1467}
1468
1469void BarrierOptionTest::testBarrierAndDividendEngine() {
1470 BOOST_TEST_MESSAGE("Testing the use of a single engine for barrier and dividend options...");
1471
1472 auto today = Date(1, January, 2023);
1473 Settings::instance().evaluationDate() = today;
1474
1475 auto u = Handle<Quote>(ext::make_shared<SimpleQuote>(args: 100.0));
1476 auto r = Handle<YieldTermStructure>(ext::make_shared<FlatForward>(args&: today, args: 0.01, args: Actual360()));
1477 auto sigma = Handle<BlackVolTermStructure>(
1478 ext::make_shared<BlackConstantVol>(args&: today, args: TARGET(), args: 0.20, args: Actual360()));
1479 auto process = ext::make_shared<BlackScholesProcess>(args&: u, args&: r, args&: sigma);
1480
1481 auto engine = ext::make_shared<FdBlackScholesBarrierEngine>(args&: process);
1482
1483 auto payoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Call, args: 90.0);
1484
1485 auto option1 = BarrierOption(Barrier::DownIn, 80.0, 0.0, payoff,
1486 ext::make_shared<EuropeanExercise>(args: Date(1, June, 2023)));
1487 QL_DEPRECATED_DISABLE_WARNING
1488 auto option2 = DividendBarrierOption(Barrier::DownIn, 80.0, 0.0, payoff,
1489 ext::make_shared<EuropeanExercise>(args: Date(1, June, 2023)),
1490 {Date(1, February, 2023)}, {1.0});
1491 QL_DEPRECATED_ENABLE_WARNING
1492
1493 option1.setPricingEngine(engine);
1494 option2.setPricingEngine(engine);
1495
1496 auto npv_before = option1.NPV();
1497 option2.NPV();
1498
1499 option1.recalculate();
1500 auto npv_after = option1.NPV();
1501
1502 if (npv_after != npv_before) {
1503 BOOST_FAIL("Failed to price barrier option correctly "
1504 "after using the engine on a dividend option: "
1505 << "\n before usage: " << npv_before << "\n after usage: " << npv_after);
1506 }
1507}
1508
1509void BarrierOptionTest::testImpliedVolatility() {
1510 BOOST_TEST_MESSAGE("Testing implied volatility for barrier options...");
1511
1512 DayCounter dc = Actual365Fixed();
1513
1514 Date today(11, February, 2018);
1515 Date maturity = today + Period(1, Years);
1516 Settings::instance().evaluationDate() = today;
1517
1518 Real spot = 100.0;
1519
1520 Rate r = 0.05;
1521 Rate q = 0.0;
1522
1523 Handle<Quote> s0(ext::make_shared<SimpleQuote>(args&: spot));
1524 Handle<YieldTermStructure> qTS(flatRate(today, forward: q, dc));
1525 Handle<YieldTermStructure> rTS(flatRate(today, forward: r, dc));
1526 Handle<BlackVolTermStructure> dummyVolTS(flatVol(today, volatility: 0.0, dc));
1527
1528 auto bsProcess = ext::make_shared<BlackScholesMertonProcess>(args&: s0, args&: qTS, args&: rTS, args&: dummyVolTS);
1529
1530 Real divAmount = 10;
1531 Date divDate = today + Period(6, Months);
1532 auto dividends = DividendVector(dividendDates: { divDate }, dividends: { divAmount });
1533
1534 Real strike = 105.0;
1535 Real rebate = 5.0;
1536
1537 auto payoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: strike);
1538 auto exercise = ext::make_shared<EuropeanExercise>(args&: maturity);
1539
1540 Real barriers[] = { 90.0, 110.0, 90.0, 110.0 };
1541 Barrier::Type barrierTypes[] = { Barrier::DownOut, Barrier::UpOut, Barrier::DownIn, Barrier::UpIn };
1542 Real targetWithoutDividends[] = { 1.0, 1.0, 5.0, 5.0 };
1543 Real targetWithDividends[] = { 8.0, 12.0, 9.0, 8.0 };
1544 Real tolerance = 1e-5;
1545
1546 for (Size i=0; i < LENGTH(barriers); ++i) {
1547 Real barrier = barriers[i];
1548 Barrier::Type barrierType = barrierTypes[i];
1549
1550 BarrierOption barrierOption(barrierType, barrier, rebate, payoff, exercise);
1551 Volatility impliedVol = barrierOption.impliedVolatility(price: targetWithoutDividends[i], process: bsProcess, accuracy: 1e-6);
1552
1553 RelinkableHandle<BlackVolTermStructure> volTS(flatVol(today, volatility: impliedVol, dc));
1554 auto process = ext::make_shared<BlackScholesMertonProcess>(args&: s0, args&: qTS, args&: rTS, args&: volTS);
1555 barrierOption.setPricingEngine(ext::make_shared<AnalyticBarrierEngine>(args&: process));
1556
1557 Real diff = std::fabs(x: barrierOption.NPV() - targetWithoutDividends[i]);
1558 if (diff > tolerance) {
1559 BOOST_ERROR("Failed to reproduce target option price:"
1560 << "\n strike: " << strike
1561 << "\n barrier: " << barrier
1562 << "\n maturity: " << maturity
1563 << "\n target value: " << targetWithoutDividends[i]
1564 << "\n calculated: " << barrierOption.NPV()
1565 << std::scientific
1566 << "\n difference " << diff);
1567 }
1568
1569 impliedVol = barrierOption.impliedVolatility(price: targetWithDividends[i], process: bsProcess, dividends, accuracy: 1e-6);
1570
1571 volTS.linkTo(h: flatVol(today, volatility: impliedVol, dc));
1572 barrierOption.setPricingEngine(ext::make_shared<FdBlackScholesBarrierEngine>(args&: process, args&: dividends));
1573
1574 diff = std::fabs(x: barrierOption.NPV() - targetWithDividends[i]);
1575 if (diff > tolerance) {
1576 BOOST_ERROR("Failed to reproduce target option price:"
1577 << "\n strike: " << strike
1578 << "\n barrier: " << barrier
1579 << "\n maturity: " << maturity
1580 << "\n target value: " << targetWithDividends[i]
1581 << "\n calculated: " << barrierOption.NPV()
1582 << std::scientific
1583 << "\n difference " << diff);
1584 }
1585 }
1586}
1587
1588void BarrierOptionTest::testLowVolatility() {
1589 BOOST_TEST_MESSAGE("Testing barrier options with low volatility value...");
1590
1591 DayCounter dc = Actual365Fixed();
1592
1593 Date today(11, February, 2018);
1594 Settings::instance().evaluationDate() = today;
1595
1596 Date maturity = today + Period(1, Years);
1597
1598 Real spot = 100.0;
1599 Volatility vol = 1e-7;
1600
1601 auto Q = ext::make_shared<SimpleQuote>();
1602 auto R = ext::make_shared<SimpleQuote>();
1603
1604 Handle<Quote> s0(ext::make_shared<SimpleQuote>(args&: spot));
1605 Handle<YieldTermStructure> qTS(flatRate(today, forward: Q, dc));
1606 Handle<YieldTermStructure> rTS(flatRate(today, forward: R, dc));
1607 Handle<BlackVolTermStructure> volTS(flatVol(today, volatility: vol, dc));
1608
1609 auto process = ext::make_shared<BlackScholesMertonProcess>(args&: s0, args&: qTS, args&: rTS, args&: volTS);
1610 auto engine = ext::make_shared<AnalyticBarrierEngine>(args&: process);
1611
1612 auto check = [&](Real strike, Option::Type optionType,
1613 Real barrier, Barrier::Type barrierType, Real rebate,
1614 Rate r, Rate q, Real expected) {
1615 R->setValue(r);
1616 Q->setValue(q);
1617 auto payoff = ext::make_shared<PlainVanillaPayoff>(args&: optionType, args&: strike);
1618 auto exercise = ext::make_shared<EuropeanExercise>(args&: maturity);
1619 BarrierOption option(barrierType, barrier, rebate, payoff, exercise);
1620 option.setPricingEngine(engine);
1621 Real value = option.NPV();
1622 Real diff = std::fabs(x: value - expected);
1623 if (std::isnan(x: value) || diff > 0.5) {
1624 BOOST_ERROR("Failed to match expected price:"
1625 << "\n strike: " << strike
1626 << "\n option type: " << optionType
1627 << "\n barrier: " << barrier
1628 << "\n barrier type: " << barrierType
1629 << "\n rebate: " << rebate
1630 << "\n risk-free rate: " << r
1631 << "\n dividend yield: " << q
1632 << "\n expected: " << expected
1633 << "\n calculated: " << value);
1634 }
1635 };
1636
1637 // strike type barrier barrier type rebate r q expected
1638
1639 check( 105.0, Option::Put, 107.0, Barrier::UpOut, 4.0, 0.03, 0.01, 3.0); // fwd = 102, put, ITM, k < H
1640 check( 109.0, Option::Put, 107.0, Barrier::UpOut, 4.0, 0.03, 0.01, 7.0); // fwd = 102, put, ITM, k > H
1641 check( 100.0, Option::Put, 107.0, Barrier::UpOut, 4.0, 0.03, 0.01, 0.0); // fwd = 102, put, OTM
1642 check( 99.0, Option::Put, 101.0, Barrier::UpOut, 4.0, 0.03, 0.01, 4.0); // fwd = 102, knocked out, k < H
1643 check( 105.0, Option::Put, 101.0, Barrier::UpOut, 4.0, 0.03, 0.01, 4.0); // fwd = 102, knocked out, k > H
1644 check( 105.0, Option::Call, 107.0, Barrier::UpOut, 4.0, 0.03, 0.01, 0.0); // fwd = 102, call, OTM, k < H
1645 check( 109.0, Option::Call, 107.0, Barrier::UpOut, 4.0, 0.03, 0.01, 0.0); // fwd = 102, call, OTM, k > H
1646 check( 100.0, Option::Call, 107.0, Barrier::UpOut, 4.0, 0.03, 0.01, 2.0); // fwd = 102, call, ITM
1647 check( 105.0, Option::Call, 101.0, Barrier::UpOut, 4.0, 0.03, 0.01, 4.0); // fwd = 102, knocked out, k < H
1648 check( 99.0, Option::Call, 101.0, Barrier::UpOut, 4.0, 0.03, 0.01, 4.0); // fwd = 102, knocked out, k > H
1649
1650 check( 105.0, Option::Put, 107.0, Barrier::UpIn, 4.0, 0.03, 0.0, 4.0); // fwd = 103, not triggered, k < H
1651 check( 109.0, Option::Put, 107.0, Barrier::UpIn, 4.0, 0.03, 0.0, 4.0); // fwd = 103, not triggered, k > H
1652 check( 105.0, Option::Put, 101.0, Barrier::UpIn, 4.0, 0.03, 0.0, 2.0); // fwd = 103, put, ITM
1653 check( 100.0, Option::Put, 101.0, Barrier::UpIn, 4.0, 0.03, 0.0, 0.0); // fwd = 103, put, OTM, k < H
1654 check( 102.0, Option::Put, 101.0, Barrier::UpIn, 4.0, 0.03, 0.0, 0.0); // fwd = 103, put, OTM, k > H
1655 check( 105.0, Option::Call, 107.0, Barrier::UpIn, 4.0, 0.03, 0.0, 4.0); // fwd = 103, not triggered, k < H
1656 check( 109.0, Option::Call, 107.0, Barrier::UpIn, 4.0, 0.03, 0.0, 4.0); // fwd = 103, not triggered, k > H
1657 check( 105.0, Option::Call, 101.0, Barrier::UpIn, 4.0, 0.03, 0.0, 0.0); // fwd = 103, call, OTM
1658 check( 100.0, Option::Call, 101.0, Barrier::UpIn, 4.0, 0.03, 0.0, 3.0); // fwd = 103, call, ITM, k < H
1659 check( 102.0, Option::Call, 101.0, Barrier::UpIn, 4.0, 0.03, 0.0, 1.0); // fwd = 103, call, ITM, k > H
1660
1661 check( 91.0, Option::Put, 93.0, Barrier::DownOut, 4.0, 0.01, 0.03, 0.0); // fwd = 98, put, OTM, k < H
1662 check( 95.0, Option::Put, 93.0, Barrier::DownOut, 4.0, 0.01, 0.03, 0.0); // fwd = 98, put, OTM, k > H
1663 check( 100.0, Option::Put, 93.0, Barrier::DownOut, 4.0, 0.01, 0.03, 2.0); // fwd = 98, put, ITM
1664 check( 97.0, Option::Put, 99.0, Barrier::DownOut, 4.0, 0.01, 0.03, 4.0); // fwd = 98, knocked out, k < H
1665 check( 101.0, Option::Put, 99.0, Barrier::DownOut, 4.0, 0.01, 0.03, 4.0); // fwd = 98, knocked out, k > H
1666 check( 91.0, Option::Call, 93.0, Barrier::DownOut, 4.0, 0.01, 0.03, 7.0); // fwd = 98, call, ITM
1667 check( 95.0, Option::Call, 93.0, Barrier::DownOut, 4.0, 0.01, 0.03, 3.0); // fwd = 98, call, ITM
1668 check( 100.0, Option::Call, 93.0, Barrier::DownOut, 4.0, 0.01, 0.03, 0.0); // fwd = 98, call, OTM
1669 check( 95.0, Option::Call, 99.0, Barrier::DownOut, 4.0, 0.01, 0.03, 4.0); // fwd = 98, knocked out, k < H
1670 check( 101.0, Option::Call, 99.0, Barrier::DownOut, 4.0, 0.01, 0.03, 4.0); // fwd = 98, knocked out, k > H
1671
1672 check( 91.0, Option::Put, 93.0, Barrier::DownIn, 4.0, 0.01, 0.04, 4.0); // fwd = 97, not triggered, k < H
1673 check( 95.0, Option::Put, 93.0, Barrier::DownIn, 4.0, 0.01, 0.04, 4.0); // fwd = 97, not triggered, k > H
1674 check( 100.0, Option::Put, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 3.0); // fwd = 97, put, ITM
1675 check( 95.0, Option::Put, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 0.0); // fwd = 97, put, OTM, k < H
1676 check( 95.0, Option::Put, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 0.0); // fwd = 97, put, OTM, k > H
1677 check( 91.0, Option::Call, 93.0, Barrier::DownIn, 4.0, 0.01, 0.04, 4.0); // fwd = 97, not triggered, k < H
1678 check( 95.0, Option::Call, 93.0, Barrier::DownIn, 4.0, 0.01, 0.04, 4.0); // fwd = 97, not triggered, k > H
1679 check( 98.0, Option::Call, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 0.0); // fwd = 97, call, OTM, k < H
1680 check( 100.0, Option::Call, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 0.0); // fwd = 97, call, OTM, k > H
1681 check( 95.0, Option::Call, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 2.0); // fwd = 97, call, ITM
1682}
1683
1684test_suite* BarrierOptionTest::suite() {
1685 auto* suite = BOOST_TEST_SUITE("Barrier option tests");
1686 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testParity));
1687 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testHaugValues));
1688 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testBabsiriValues));
1689 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testBeagleholeValues));
1690 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testLocalVolAndHestonComparison));
1691 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testOldDividendBarrierOption));
1692 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testDividendBarrierOption));
1693 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testDividendBarrierOptionWithDividendsPastMaturity));
1694 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testBarrierAndDividendEngine));
1695 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testImpliedVolatility));
1696 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testLowVolatility));
1697 return suite;
1698}
1699
1700test_suite* BarrierOptionTest::experimental() {
1701 auto* suite = BOOST_TEST_SUITE("Barrier option experimental tests");
1702 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testPerturbative));
1703 suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testVannaVolgaSimpleBarrierValues));
1704 return suite;
1705}
1706

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