1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | |
3 | /* |
4 | Copyright (C) 2003, 2004 Ferdinando Ametrano |
5 | Copyright (C) 2005, 2007 StatPro Italia srl |
6 | Copyright (C) 2006 Warren Chou |
7 | |
8 | This file is part of QuantLib, a free-software/open-source library |
9 | for financial quantitative analysts and developers - http://quantlib.org/ |
10 | |
11 | QuantLib is free software: you can redistribute it and/or modify it |
12 | under the terms of the QuantLib license. You should have received a |
13 | copy of the license along with this program; if not, please email |
14 | <quantlib-dev@lists.sf.net>. The license is also available online at |
15 | <http://quantlib.org/license.shtml>. |
16 | |
17 | This program is distributed in the hope that it will be useful, but WITHOUT |
18 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
19 | FOR A PARTICULAR PURPOSE. See the license for more details. |
20 | */ |
21 | |
22 | #include "lookbackoptions.hpp" |
23 | #include "utilities.hpp" |
24 | #include <ql/time/daycounters/actual360.hpp> |
25 | #include <ql/instruments/lookbackoption.hpp> |
26 | #include <ql/pricingengines/lookback/analyticcontinuousfloatinglookback.hpp> |
27 | #include <ql/pricingengines/lookback/analyticcontinuousfixedlookback.hpp> |
28 | #include <ql/pricingengines/lookback/analyticcontinuouspartialfloatinglookback.hpp> |
29 | #include <ql/pricingengines/lookback/analyticcontinuouspartialfixedlookback.hpp> |
30 | #include <ql/pricingengines/lookback/mclookbackengine.hpp> |
31 | #include <ql/termstructures/yield/flatforward.hpp> |
32 | #include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp> |
33 | #include <ql/utilities/dataformatters.hpp> |
34 | #include <ql/processes/blackscholesprocess.hpp> |
35 | |
36 | using namespace QuantLib; |
37 | using namespace boost::unit_test_framework; |
38 | |
39 | #undef REPORT_FAILURE_FLOATING |
40 | #define REPORT_FAILURE_FLOATING(greekName, minmax, payoff, exercise, \ |
41 | s, q, r, today, v, \ |
42 | expected, calculated, error, tolerance) \ |
43 | BOOST_ERROR( \ |
44 | exerciseTypeToString(exercise) \ |
45 | << payoff->optionType() << " lookback option with " \ |
46 | << payoffTypeToString(payoff) << " payoff:\n" \ |
47 | << " underlying value: " << s << "\n" \ |
48 | << " dividend yield: " << io::rate(q) << "\n" \ |
49 | << " risk-free rate: " << io::rate(r) << "\n" \ |
50 | << " reference date: " << today << "\n" \ |
51 | << " maturity: " << exercise->lastDate() << "\n" \ |
52 | << " volatility: " << io::volatility(v) << "\n\n" \ |
53 | << " expected " << greekName << ": " << expected << "\n" \ |
54 | << " calculated " << greekName << ": " << calculated << "\n"\ |
55 | << " error: " << error << "\n" \ |
56 | << " tolerance: " << tolerance); |
57 | |
58 | #undef REPORT_FAILURE_FIXED |
59 | #define REPORT_FAILURE_FIXED(greekName, minmax, payoff, exercise, \ |
60 | s, q, r, today, v, \ |
61 | expected, calculated, error, tolerance) \ |
62 | BOOST_ERROR( \ |
63 | exerciseTypeToString(exercise) \ |
64 | << payoff->optionType() << " lookback option with " \ |
65 | << payoffTypeToString(payoff) << " payoff:\n" \ |
66 | << " underlying value: " << s << "\n" \ |
67 | << " strike: " << payoff->strike() << "\n" \ |
68 | << " dividend yield: " << io::rate(q) << "\n" \ |
69 | << " risk-free rate: " << io::rate(r) << "\n" \ |
70 | << " reference date: " << today << "\n" \ |
71 | << " maturity: " << exercise->lastDate() << "\n" \ |
72 | << " volatility: " << io::volatility(v) << "\n\n" \ |
73 | << " expected " << greekName << ": " << expected << "\n" \ |
74 | << " calculated " << greekName << ": " << calculated << "\n"\ |
75 | << " error: " << error << "\n" \ |
76 | << " tolerance: " << tolerance); |
77 | |
78 | #undef REPORT_FAILURE_MC |
79 | #define REPORT_FAILURE_MC(lookbackType, optionType, analytical, monteCarlo, tolerance) \ |
80 | BOOST_ERROR( \ |
81 | "Analytical and MC " << lookbackType << " " << optionType << " values differed by more than tolerance" << "\n" \ |
82 | << " Analytical: " << analytical << "\n" \ |
83 | << " Monte Carlo: " << monteCarlo << "\n" \ |
84 | << " tolerance: " << tolerance << "\n" \ |
85 | << " difference: " << std::abs(analytical - monteCarlo)); |
86 | |
87 | namespace { |
88 | |
89 | struct LookbackOptionData { |
90 | Option::Type type; |
91 | Real strike; |
92 | Real minmax; |
93 | Real s; // spot |
94 | Rate q; // dividend |
95 | Rate r; // risk-free rate |
96 | Time t; // time to maturity |
97 | Volatility v; // volatility |
98 | |
99 | //Partial-time lookback options: |
100 | Real l; // level above/below actual extremum |
101 | Real t1; // time to start of lookback period |
102 | |
103 | Real result; // result |
104 | Real tol; // tolerance |
105 | }; |
106 | |
107 | } |
108 | |
109 | |
110 | void LookbackOptionTest::testAnalyticContinuousFloatingLookback() { |
111 | |
112 | BOOST_TEST_MESSAGE( |
113 | "Testing analytic continuous floating-strike lookback options..." ); |
114 | |
115 | |
116 | LookbackOptionData values[] = { |
117 | |
118 | // data from "Option Pricing Formulas", Haug, 1998, pg.61-62 |
119 | |
120 | // type, strike, minmax, s, q, r, t, v, l, t1, result, tol |
121 | { .type: Option::Call, .strike: 0, .minmax: 100, .s: 120.0, .q: 0.06, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 25.3533, .tol: 1.0e-4}, |
122 | |
123 | // data from "Connecting discrete and continuous path-dependent options", |
124 | // Broadie, Glasserman & Kou, 1999, pg.70-74 |
125 | |
126 | // type, strike, minmax, s, q, r, t, v, l, t1, result, tol |
127 | { .type: Option::Call, .strike: 0, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.05, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 23.7884, .tol: 1.0e-4}, |
128 | { .type: Option::Call, .strike: 0, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.05, .t: 0.20, .v: 0.30, .l: 0, .t1: 0, .result: 10.7190, .tol: 1.0e-4}, |
129 | { .type: Option::Call, .strike: 0, .minmax: 100, .s: 110.0, .q: 0.00, .r: 0.05, .t: 0.20, .v: 0.30, .l: 0, .t1: 0, .result: 14.4597, .tol: 1.0e-4}, |
130 | { .type: Option::Put, .strike: 0, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 15.3526, .tol: 1.0e-4}, |
131 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 16.8468, .tol: 1.0e-4}, |
132 | { .type: Option::Put, .strike: 0, .minmax: 120, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 21.0645, .tol: 1.0e-4}, |
133 | }; |
134 | |
135 | DayCounter dc = Actual360(); |
136 | Date today = Date::todaysDate(); |
137 | |
138 | ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0)); |
139 | ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0)); |
140 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
141 | ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0)); |
142 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
143 | ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0)); |
144 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
145 | |
146 | for (auto& value : values) { |
147 | Date exDate = today + timeToDays(t: value.t); |
148 | ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate)); |
149 | |
150 | spot->setValue(value.s); |
151 | qRate->setValue(value.q); |
152 | rRate->setValue(value.r); |
153 | vol->setValue(value.v); |
154 | |
155 | ext::shared_ptr<FloatingTypePayoff> payoff(new FloatingTypePayoff(value.type)); |
156 | |
157 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess( |
158 | new BlackScholesMertonProcess( |
159 | Handle<Quote>(spot), |
160 | Handle<YieldTermStructure>(qTS), |
161 | Handle<YieldTermStructure>(rTS), |
162 | Handle<BlackVolTermStructure>(volTS))); |
163 | |
164 | ext::shared_ptr<PricingEngine> engine( |
165 | new AnalyticContinuousFloatingLookbackEngine(stochProcess)); |
166 | |
167 | ContinuousFloatingLookbackOption option(value.minmax, payoff, exercise); |
168 | option.setPricingEngine(engine); |
169 | |
170 | Real calculated = option.NPV(); |
171 | Real expected = value.result; |
172 | Real error = std::fabs(x: calculated-expected); |
173 | if (error > value.tol) { |
174 | REPORT_FAILURE_FLOATING("value" , values[i].minmax, payoff, exercise, value.s, value.q, |
175 | value.r, today, value.v, expected, calculated, error, |
176 | value.tol); |
177 | } |
178 | } |
179 | } |
180 | |
181 | |
182 | void LookbackOptionTest::testAnalyticContinuousFixedLookback() { |
183 | |
184 | BOOST_TEST_MESSAGE( |
185 | "Testing analytic continuous fixed-strike lookback options..." ); |
186 | |
187 | LookbackOptionData values[] = { |
188 | // data from "Option Pricing Formulas", Haug, 1998, pg.63-64 |
189 | //type, strike, minmax, s, q, r, t, v, l, t1, result, tol |
190 | { .type: Option::Call, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.10, .l: 0, .t1: 0, .result: 13.2687, .tol: 1.0e-4}, |
191 | { .type: Option::Call, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.20, .l: 0, .t1: 0, .result: 18.9263, .tol: 1.0e-4}, |
192 | { .type: Option::Call, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 24.9857, .tol: 1.0e-4}, |
193 | { .type: Option::Call, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.10, .l: 0, .t1: 0, .result: 8.5126, .tol: 1.0e-4}, |
194 | { .type: Option::Call, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.20, .l: 0, .t1: 0, .result: 14.1702, .tol: 1.0e-4}, |
195 | { .type: Option::Call, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 20.2296, .tol: 1.0e-4}, |
196 | { .type: Option::Call, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.10, .l: 0, .t1: 0, .result: 4.3908, .tol: 1.0e-4}, |
197 | { .type: Option::Call, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.20, .l: 0, .t1: 0, .result: 9.8905, .tol: 1.0e-4}, |
198 | { .type: Option::Call, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 15.8512, .tol: 1.0e-4}, |
199 | { .type: Option::Call, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.10, .l: 0, .t1: 0, .result: 18.3241, .tol: 1.0e-4}, |
200 | { .type: Option::Call, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.20, .l: 0, .t1: 0, .result: 26.0731, .tol: 1.0e-4}, |
201 | { .type: Option::Call, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 34.7116, .tol: 1.0e-4}, |
202 | { .type: Option::Call, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.10, .l: 0, .t1: 0, .result: 13.8000, .tol: 1.0e-4}, |
203 | { .type: Option::Call, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.20, .l: 0, .t1: 0, .result: 21.5489, .tol: 1.0e-4}, |
204 | { .type: Option::Call, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 30.1874, .tol: 1.0e-4}, |
205 | { .type: Option::Call, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.10, .l: 0, .t1: 0, .result: 9.5445, .tol: 1.0e-4}, |
206 | { .type: Option::Call, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.20, .l: 0, .t1: 0, .result: 17.2965, .tol: 1.0e-4}, |
207 | { .type: Option::Call, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 25.9002, .tol: 1.0e-4}, |
208 | |
209 | { .type: Option::Put, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.10, .l: 0, .t1: 0, .result: 0.6899, .tol: 1.0e-4}, |
210 | { .type: Option::Put, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.20, .l: 0, .t1: 0, .result: 4.4448, .tol: 1.0e-4}, |
211 | { .type: Option::Put, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 8.9213, .tol: 1.0e-4}, |
212 | { .type: Option::Put, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.10, .l: 0, .t1: 0, .result: 3.3917, .tol: 1.0e-4}, |
213 | { .type: Option::Put, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.20, .l: 0, .t1: 0, .result: 8.3177, .tol: 1.0e-4}, |
214 | { .type: Option::Put, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 13.1579, .tol: 1.0e-4}, |
215 | { .type: Option::Put, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.10, .l: 0, .t1: 0, .result: 8.1478, .tol: 1.0e-4}, |
216 | { .type: Option::Put, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.20, .l: 0, .t1: 0, .result: 13.0739, .tol: 1.0e-4}, |
217 | { .type: Option::Put, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 0.50, .v: 0.30, .l: 0, .t1: 0, .result: 17.9140, .tol: 1.0e-4}, |
218 | { .type: Option::Put, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.10, .l: 0, .t1: 0, .result: 1.0534, .tol: 1.0e-4}, |
219 | { .type: Option::Put, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.20, .l: 0, .t1: 0, .result: 6.2813, .tol: 1.0e-4}, |
220 | { .type: Option::Put, .strike: 95, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 12.2376, .tol: 1.0e-4}, |
221 | { .type: Option::Put, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.10, .l: 0, .t1: 0, .result: 3.8079, .tol: 1.0e-4}, |
222 | { .type: Option::Put, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.20, .l: 0, .t1: 0, .result: 10.1294, .tol: 1.0e-4}, |
223 | { .type: Option::Put, .strike: 100, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 16.3889, .tol: 1.0e-4}, |
224 | { .type: Option::Put, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.10, .l: 0, .t1: 0, .result: 8.3321, .tol: 1.0e-4}, |
225 | { .type: Option::Put, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.20, .l: 0, .t1: 0, .result: 14.6536, .tol: 1.0e-4}, |
226 | { .type: Option::Put, .strike: 105, .minmax: 100, .s: 100.0, .q: 0.00, .r: 0.10, .t: 1.00, .v: 0.30, .l: 0, .t1: 0, .result: 20.9130, .tol: 1.0e-4} |
227 | |
228 | }; |
229 | |
230 | DayCounter dc = Actual360(); |
231 | Date today = Date::todaysDate(); |
232 | |
233 | ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0)); |
234 | ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0)); |
235 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
236 | ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0)); |
237 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
238 | ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0)); |
239 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
240 | |
241 | for (auto& value : values) { |
242 | Date exDate = today + timeToDays(t: value.t); |
243 | ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate)); |
244 | |
245 | spot->setValue(value.s); |
246 | qRate->setValue(value.q); |
247 | rRate->setValue(value.r); |
248 | vol->setValue(value.v); |
249 | |
250 | ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(value.type, value.strike)); |
251 | |
252 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess( |
253 | new BlackScholesMertonProcess( |
254 | Handle<Quote>(spot), |
255 | Handle<YieldTermStructure>(qTS), |
256 | Handle<YieldTermStructure>(rTS), |
257 | Handle<BlackVolTermStructure>(volTS))); |
258 | |
259 | ext::shared_ptr<PricingEngine> engine( |
260 | new AnalyticContinuousFixedLookbackEngine(stochProcess)); |
261 | |
262 | ContinuousFixedLookbackOption option(value.minmax, payoff, exercise); |
263 | option.setPricingEngine(engine); |
264 | |
265 | Real calculated = option.NPV(); |
266 | Real expected = value.result; |
267 | Real error = std::fabs(x: calculated-expected); |
268 | if (error > value.tol) { |
269 | REPORT_FAILURE_FIXED("value" , values[i].minmax, payoff, exercise, value.s, value.q, |
270 | value.r, today, value.v, expected, calculated, error, value.tol); |
271 | } |
272 | } |
273 | } |
274 | |
275 | void LookbackOptionTest::testAnalyticContinuousPartialFloatingLookback() { |
276 | |
277 | BOOST_TEST_MESSAGE( |
278 | "Testing analytic continuous partial floating-strike lookback options..." ); |
279 | |
280 | |
281 | LookbackOptionData values[] = { |
282 | |
283 | // data from "Option Pricing Formulas, Second Edition", Haug, 2006, pg.146 |
284 | |
285 | //type, strike, minmax, s, q, r, t, v, l, t1, result, tol |
286 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.25, .result: 8.6524, .tol: 1.0e-4}, |
287 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.5, .result: 9.2128, .tol: 1.0e-4}, |
288 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.75, .result: 9.5567, .tol: 1.0e-4}, |
289 | |
290 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.25, .result: 10.5751, .tol: 1.0e-4}, |
291 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.5, .result: 11.2601, .tol: 1.0e-4}, |
292 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.75, .result: 11.6804, .tol: 1.0e-4}, |
293 | |
294 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.25, .result: 13.3402, .tol: 1.0e-4}, |
295 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.5, .result: 14.5121, .tol: 1.0e-4}, |
296 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.75, .result: 15.314, .tol: 1.0e-4}, |
297 | |
298 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.25, .result: 16.3047, .tol: 1.0e-4}, |
299 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.5, .result: 17.737, .tol: 1.0e-4}, |
300 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.75, .result: 18.7171, .tol: 1.0e-4}, |
301 | |
302 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.25, .result: 17.9831, .tol: 1.0e-4}, |
303 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.5, .result: 19.6618, .tol: 1.0e-4}, |
304 | { .type: Option::Call, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.75, .result: 20.8493, .tol: 1.0e-4}, |
305 | |
306 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.25, .result: 21.9793, .tol: 1.0e-4}, |
307 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.5, .result: 24.0311, .tol: 1.0e-4}, |
308 | { .type: Option::Call, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.75, .result: 25.4825, .tol: 1.0e-4}, |
309 | |
310 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.25, .result: 2.7189, .tol: 1.0e-4}, |
311 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.5, .result: 3.4639, .tol: 1.0e-4}, |
312 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.75, .result: 4.1912, .tol: 1.0e-4}, |
313 | |
314 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.25, .result: 3.3231, .tol: 1.0e-4}, |
315 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.5, .result: 4.2336, .tol: 1.0e-4}, |
316 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 1, .t1: 0.75, .result: 5.1226, .tol: 1.0e-4}, |
317 | |
318 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.25, .result: 7.9153, .tol: 1.0e-4}, |
319 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.5, .result: 9.5825, .tol: 1.0e-4}, |
320 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.75, .result: 11.0362, .tol: 1.0e-4}, |
321 | |
322 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.25, .result: 9.6743, .tol: 1.0e-4}, |
323 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.5, .result: 11.7119, .tol: 1.0e-4}, |
324 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 1, .t1: 0.75, .result: 13.4887, .tol: 1.0e-4}, |
325 | |
326 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.25, .result: 13.4719, .tol: 1.0e-4}, |
327 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.5, .result: 16.1495, .tol: 1.0e-4}, |
328 | { .type: Option::Put, .strike: 0, .minmax: 90, .s: 90, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.75, .result: 18.4071, .tol: 1.0e-4}, |
329 | |
330 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.25, .result: 16.4657, .tol: 1.0e-4}, |
331 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.5, .result: 19.7383, .tol: 1.0e-4}, |
332 | { .type: Option::Put, .strike: 0, .minmax: 110, .s: 110, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 1, .t1: 0.75, .result: 22.4976, .tol: 1.0e-4} |
333 | }; |
334 | |
335 | DayCounter dc = Actual360(); |
336 | Date today = Date::todaysDate(); |
337 | |
338 | ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0)); |
339 | ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0)); |
340 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
341 | ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0)); |
342 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
343 | ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0)); |
344 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
345 | |
346 | for (auto& value : values) { |
347 | Date exDate = today + timeToDays(t: value.t); |
348 | ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate)); |
349 | |
350 | spot->setValue(value.s); |
351 | qRate->setValue(value.q); |
352 | rRate->setValue(value.r); |
353 | vol->setValue(value.v); |
354 | |
355 | ext::shared_ptr<FloatingTypePayoff> payoff(new FloatingTypePayoff(value.type)); |
356 | |
357 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess( |
358 | new BlackScholesMertonProcess( |
359 | Handle<Quote>(spot), |
360 | Handle<YieldTermStructure>(qTS), |
361 | Handle<YieldTermStructure>(rTS), |
362 | Handle<BlackVolTermStructure>(volTS))); |
363 | |
364 | ext::shared_ptr<PricingEngine> engine( |
365 | new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess)); |
366 | |
367 | Date lookbackEnd = today + timeToDays(t: value.t1); |
368 | ContinuousPartialFloatingLookbackOption option(value.minmax, value.l, lookbackEnd, payoff, |
369 | exercise); |
370 | option.setPricingEngine(engine); |
371 | |
372 | Real calculated = option.NPV(); |
373 | Real expected = value.result; |
374 | Real error = std::fabs(x: calculated-expected); |
375 | if (error > value.tol) { |
376 | REPORT_FAILURE_FLOATING("value" , values[i].minmax, payoff, exercise, value.s, value.q, |
377 | value.r, today, value.v, expected, calculated, error, |
378 | value.tol); |
379 | } |
380 | } |
381 | } |
382 | |
383 | void LookbackOptionTest::testAnalyticContinuousPartialFixedLookback() { |
384 | |
385 | BOOST_TEST_MESSAGE( |
386 | "Testing analytic continuous fixed-strike lookback options..." ); |
387 | |
388 | LookbackOptionData values[] = { |
389 | // data from "Option Pricing Formulas, Second Edition", Haug, 2006, pg.148 |
390 | //type, strike, minmax, s, q, r, t, v, l, t1, result, tol |
391 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.25, .result: 20.2845, .tol: 1.0e-4}, |
392 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.5, .result: 19.6239, .tol: 1.0e-4}, |
393 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.75, .result: 18.6244, .tol: 1.0e-4}, |
394 | |
395 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.25, .result: 4.0432, .tol: 1.0e-4}, |
396 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.5, .result: 3.958, .tol: 1.0e-4}, |
397 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.75, .result: 3.7015, .tol: 1.0e-4}, |
398 | |
399 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.25, .result: 27.5385, .tol: 1.0e-4}, |
400 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.5, .result: 25.8126, .tol: 1.0e-4}, |
401 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.75, .result: 23.4957, .tol: 1.0e-4}, |
402 | |
403 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.25, .result: 11.4895, .tol: 1.0e-4}, |
404 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.5, .result: 10.8995, .tol: 1.0e-4}, |
405 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.75, .result: 9.8244, .tol: 1.0e-4}, |
406 | |
407 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.25, .result: 35.4578, .tol: 1.0e-4}, |
408 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.5, .result: 32.7172, .tol: 1.0e-4}, |
409 | { .type: Option::Call, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.75, .result: 29.1473, .tol: 1.0e-4}, |
410 | |
411 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.25, .result: 19.725, .tol: 1.0e-4}, |
412 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.5, .result: 18.4025, .tol: 1.0e-4}, |
413 | { .type: Option::Call, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.75, .result: 16.2976, .tol: 1.0e-4}, |
414 | |
415 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.25, .result: 0.4973, .tol: 1.0e-4}, |
416 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.5, .result: 0.4632, .tol: 1.0e-4}, |
417 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.75, .result: 0.3863, .tol: 1.0e-4}, |
418 | |
419 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.25, .result: 12.6978, .tol: 1.0e-4}, |
420 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.5, .result: 10.9492, .tol: 1.0e-4}, |
421 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.1, .l: 0, .t1: 0.75, .result: 9.1555, .tol: 1.0e-4}, |
422 | |
423 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.25, .result: 4.5863, .tol: 1.0e-4}, |
424 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.5, .result: 4.1925, .tol: 1.0e-4}, |
425 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.75, .result: 3.5831, .tol: 1.0e-4}, |
426 | |
427 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.25, .result: 19.0255, .tol: 1.0e-4}, |
428 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.5, .result: 16.9433, .tol: 1.0e-4}, |
429 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.2, .l: 0, .t1: 0.75, .result: 14.6505, .tol: 1.0e-4}, |
430 | |
431 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.25, .result: 9.9348, .tol: 1.0e-4}, |
432 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.5, .result: 9.1111, .tol: 1.0e-4}, |
433 | { .type: Option::Put, .strike: 90, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.75, .result: 7.9267, .tol: 1.0e-4}, |
434 | |
435 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.25, .result: 25.2112, .tol: 1.0e-4}, |
436 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.5, .result: 22.8217, .tol: 1.0e-4}, |
437 | { .type: Option::Put, .strike: 110, .minmax: 0, .s: 100, .q: 0, .r: 0.06, .t: 1, .v: 0.3, .l: 0, .t1: 0.75, .result: 20.0566, .tol: 1.0e-4} |
438 | }; |
439 | |
440 | DayCounter dc = Actual360(); |
441 | Date today = Date::todaysDate(); |
442 | |
443 | ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0)); |
444 | ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0)); |
445 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
446 | ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0)); |
447 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
448 | ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0)); |
449 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
450 | |
451 | for (auto& value : values) { |
452 | Date exDate = today + timeToDays(t: value.t); |
453 | ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate)); |
454 | |
455 | spot->setValue(value.s); |
456 | qRate->setValue(value.q); |
457 | rRate->setValue(value.r); |
458 | vol->setValue(value.v); |
459 | |
460 | ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(value.type, value.strike)); |
461 | |
462 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess( |
463 | new BlackScholesMertonProcess( |
464 | Handle<Quote>(spot), |
465 | Handle<YieldTermStructure>(qTS), |
466 | Handle<YieldTermStructure>(rTS), |
467 | Handle<BlackVolTermStructure>(volTS))); |
468 | |
469 | ext::shared_ptr<PricingEngine> engine( |
470 | new AnalyticContinuousPartialFixedLookbackEngine(stochProcess)); |
471 | |
472 | Date lookbackStart = today + timeToDays(t: value.t1); |
473 | ContinuousPartialFixedLookbackOption option(lookbackStart, |
474 | payoff, |
475 | exercise); |
476 | option.setPricingEngine(engine); |
477 | |
478 | Real calculated = option.NPV(); |
479 | Real expected = value.result; |
480 | Real error = std::fabs(x: calculated-expected); |
481 | if (error > value.tol) { |
482 | REPORT_FAILURE_FIXED("value" , values[i].minmax, payoff, exercise, value.s, value.q, |
483 | value.r, today, value.v, expected, calculated, error, value.tol); |
484 | } |
485 | } |
486 | } |
487 | |
488 | void LookbackOptionTest::testMonteCarloLookback() { |
489 | BOOST_TEST_MESSAGE("Testing Monte Carlo engines for lookback options..." ); |
490 | |
491 | Real tolerance = 0.1; |
492 | |
493 | DayCounter dc = Actual360(); |
494 | Date today = Date::todaysDate(); |
495 | |
496 | Real strike = 90; |
497 | Real t = 1; |
498 | Real t1= 0.25; |
499 | |
500 | Date exDate = today + timeToDays(t); |
501 | ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate)); |
502 | |
503 | ext::shared_ptr<SimpleQuote> spot(new SimpleQuote(0.0)); |
504 | ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0)); |
505 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
506 | ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0)); |
507 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
508 | ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0)); |
509 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
510 | |
511 | spot ->setValue(100); |
512 | qRate->setValue(0); |
513 | rRate->setValue(0.06); |
514 | vol ->setValue(0.1); |
515 | |
516 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess( |
517 | new BlackScholesMertonProcess( |
518 | Handle<Quote>(spot), |
519 | Handle<YieldTermStructure>(qTS), |
520 | Handle<YieldTermStructure>(rTS), |
521 | Handle<BlackVolTermStructure>(volTS))); |
522 | |
523 | Option::Type types[] = { Option::Call, Option::Put }; |
524 | |
525 | for (auto type : types) { |
526 | ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, strike)); |
527 | |
528 | /** |
529 | * Partial Fixed |
530 | * **/ |
531 | |
532 | Date lookbackStart = today + timeToDays(t: t1); |
533 | ContinuousPartialFixedLookbackOption partialFixedLookback(lookbackStart, |
534 | payoff, |
535 | exercise); |
536 | ext::shared_ptr<PricingEngine> engine( |
537 | new AnalyticContinuousPartialFixedLookbackEngine(stochProcess)); |
538 | partialFixedLookback.setPricingEngine(engine); |
539 | |
540 | Real analytical = partialFixedLookback.NPV(); |
541 | |
542 | ext::shared_ptr<PricingEngine> mcpartialfixedengine = |
543 | MakeMCLookbackEngine<ContinuousPartialFixedLookbackOption, PseudoRandom> |
544 | (stochProcess) |
545 | .withSteps(steps: 2000) |
546 | .withAntitheticVariate() |
547 | .withSeed(seed: 1) |
548 | .withAbsoluteTolerance(tolerance); |
549 | |
550 | partialFixedLookback.setPricingEngine(mcpartialfixedengine); |
551 | Real monteCarlo = partialFixedLookback.NPV(); |
552 | |
553 | Real diff = std::abs(x: analytical - monteCarlo); |
554 | |
555 | if (diff > tolerance){ |
556 | REPORT_FAILURE_MC("Partial Fixed" , type, analytical, monteCarlo, tolerance); |
557 | } |
558 | |
559 | /** |
560 | * Fixed |
561 | * **/ |
562 | |
563 | Real minMax = 100; |
564 | |
565 | ContinuousFixedLookbackOption fixedLookback(minMax, |
566 | payoff, |
567 | exercise); |
568 | ext::shared_ptr<PricingEngine> analyticalfixedengine( |
569 | new AnalyticContinuousFixedLookbackEngine(stochProcess)); |
570 | fixedLookback.setPricingEngine(analyticalfixedengine); |
571 | |
572 | analytical = fixedLookback.NPV(); |
573 | |
574 | ext::shared_ptr<PricingEngine> mcfixedengine = |
575 | MakeMCLookbackEngine<ContinuousFixedLookbackOption, PseudoRandom> |
576 | (stochProcess) |
577 | .withSteps(steps: 2000) |
578 | .withAntitheticVariate() |
579 | .withSeed(seed: 1) |
580 | .withAbsoluteTolerance(tolerance); |
581 | |
582 | fixedLookback.setPricingEngine(mcfixedengine); |
583 | monteCarlo = fixedLookback.NPV(); |
584 | |
585 | diff = std::abs(x: analytical - monteCarlo); |
586 | |
587 | if (diff > tolerance){ |
588 | REPORT_FAILURE_MC("Fixed" , type, analytical, monteCarlo, tolerance); |
589 | } |
590 | |
591 | /** |
592 | * Partial Floating |
593 | * **/ |
594 | |
595 | Real lambda = 1; |
596 | Date lookbackEnd = today + timeToDays(t: t1); |
597 | |
598 | ext::shared_ptr<FloatingTypePayoff> floatingPayoff(new FloatingTypePayoff(type)); |
599 | |
600 | ContinuousPartialFloatingLookbackOption partialFloating(minMax, |
601 | lambda, |
602 | lookbackEnd, |
603 | floatingPayoff, |
604 | exercise); |
605 | ext::shared_ptr<PricingEngine> analyticalpartialFloatingengine( |
606 | new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess)); |
607 | partialFloating.setPricingEngine(analyticalpartialFloatingengine); |
608 | |
609 | analytical = partialFloating.NPV(); |
610 | |
611 | ext::shared_ptr<PricingEngine> mcpartialfloatingengine = |
612 | MakeMCLookbackEngine<ContinuousPartialFloatingLookbackOption, PseudoRandom> |
613 | (stochProcess) |
614 | .withSteps(steps: 2000) |
615 | .withAntitheticVariate() |
616 | .withSeed(seed: 1) |
617 | .withAbsoluteTolerance(tolerance); |
618 | |
619 | partialFloating.setPricingEngine(mcpartialfloatingengine); |
620 | monteCarlo = partialFloating.NPV(); |
621 | |
622 | diff = std::abs(x: analytical - monteCarlo); |
623 | |
624 | if (diff > tolerance){ |
625 | REPORT_FAILURE_MC("Partial Floating" , type, analytical, monteCarlo, tolerance); |
626 | } |
627 | |
628 | /** |
629 | * Floating |
630 | * **/ |
631 | |
632 | ContinuousFloatingLookbackOption floating(minMax, |
633 | floatingPayoff, |
634 | exercise); |
635 | ext::shared_ptr<PricingEngine> analyticalFloatingengine( |
636 | new AnalyticContinuousFloatingLookbackEngine(stochProcess)); |
637 | floating.setPricingEngine(analyticalFloatingengine); |
638 | |
639 | analytical = floating.NPV(); |
640 | |
641 | ext::shared_ptr<PricingEngine> mcfloatingengine = |
642 | MakeMCLookbackEngine<ContinuousFloatingLookbackOption, PseudoRandom> |
643 | (stochProcess) |
644 | .withSteps(steps: 2000) |
645 | .withAntitheticVariate() |
646 | .withSeed(seed: 1) |
647 | .withAbsoluteTolerance(tolerance); |
648 | |
649 | floating.setPricingEngine(mcfloatingengine); |
650 | monteCarlo = floating.NPV(); |
651 | |
652 | diff = std::abs(x: analytical - monteCarlo); |
653 | |
654 | if (diff > tolerance){ |
655 | REPORT_FAILURE_MC("Floating" , type, analytical, monteCarlo, tolerance); |
656 | } |
657 | } |
658 | } |
659 | |
660 | |
661 | test_suite* LookbackOptionTest::suite(SpeedLevel speed) { |
662 | auto* suite = BOOST_TEST_SUITE("Lookback option tests" ); |
663 | |
664 | suite->add(QUANTLIB_TEST_CASE(&LookbackOptionTest::testAnalyticContinuousFloatingLookback)); |
665 | suite->add(QUANTLIB_TEST_CASE(&LookbackOptionTest::testAnalyticContinuousFixedLookback)); |
666 | suite->add(QUANTLIB_TEST_CASE(&LookbackOptionTest::testAnalyticContinuousPartialFloatingLookback)); |
667 | suite->add(QUANTLIB_TEST_CASE(&LookbackOptionTest::testAnalyticContinuousPartialFixedLookback)); |
668 | |
669 | if (speed == Slow) { |
670 | suite->add(QUANTLIB_TEST_CASE(&LookbackOptionTest::testMonteCarloLookback)); |
671 | } |
672 | |
673 | return suite; |
674 | } |
675 | |
676 | |