1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*!
4 Copyright (C) 2005, 2006, 2007, 2009 StatPro Italia srl
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include <ql/qldefines.hpp>
21#if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC)
22# include <ql/auto_link.hpp>
23#endif
24#include <ql/instruments/vanillaoption.hpp>
25#include <ql/math/integrals/tanhsinhintegral.hpp>
26#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
27#include <ql/pricingengines/vanilla/analyticeuropeanvasicekengine.hpp>
28#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
29#include <ql/pricingengines/vanilla/baroneadesiwhaleyengine.hpp>
30#include <ql/pricingengines/vanilla/batesengine.hpp>
31#include <ql/pricingengines/vanilla/binomialengine.hpp>
32#include <ql/pricingengines/vanilla/bjerksundstenslandengine.hpp>
33#include <ql/pricingengines/vanilla/fdblackscholesvanillaengine.hpp>
34#include <ql/pricingengines/vanilla/integralengine.hpp>
35#include <ql/pricingengines/vanilla/mcamericanengine.hpp>
36#include <ql/pricingengines/vanilla/mceuropeanengine.hpp>
37#include <ql/pricingengines/vanilla/qdfpamericanengine.hpp>
38#include <ql/time/calendars/target.hpp>
39#include <ql/utilities/dataformatters.hpp>
40
41#include <iostream>
42#include <iomanip>
43
44using namespace QuantLib;
45
46int main(int, char* []) {
47
48 try {
49
50 std::cout << std::endl;
51
52 // set up dates
53 Calendar calendar = TARGET();
54 Date todaysDate(15, May, 1998);
55 Date settlementDate(17, May, 1998);
56 Settings::instance().evaluationDate() = todaysDate;
57
58 // our options
59 Option::Type type(Option::Put);
60 Real underlying = 36;
61 Real strike = 40;
62 Spread dividendYield = 0.00;
63 Rate riskFreeRate = 0.06;
64 Volatility volatility = 0.20;
65 Date maturity(17, May, 1999);
66 DayCounter dayCounter = Actual365Fixed();
67
68 std::cout << "Option type = " << type << std::endl;
69 std::cout << "Maturity = " << maturity << std::endl;
70 std::cout << "Underlying price = " << underlying << std::endl;
71 std::cout << "Strike = " << strike << std::endl;
72 std::cout << "Risk-free interest rate = " << io::rate(r: riskFreeRate)
73 << std::endl;
74 std::cout << "Dividend yield = " << io::rate(r: dividendYield)
75 << std::endl;
76 std::cout << "Volatility = " << io::volatility(v: volatility)
77 << std::endl;
78 std::cout << std::endl;
79 std::string method;
80 std::cout << std::endl ;
81
82 // write column headings
83 Size widths[] = { 35, 14, 14, 14 };
84 std::cout << std::setw(widths[0]) << std::left << "Method"
85 << std::setw(widths[1]) << std::left << "European"
86 << std::setw(widths[2]) << std::left << "Bermudan"
87 << std::setw(widths[3]) << std::left << "American"
88 << std::endl;
89
90 std::vector<Date> exerciseDates;
91 for (Integer i=1; i<=4; i++)
92 exerciseDates.push_back(x: settlementDate + 3*i*Months);
93
94 auto europeanExercise = ext::make_shared<EuropeanExercise>(args&: maturity);
95
96 auto bermudanExercise = ext::make_shared<BermudanExercise>(args&: exerciseDates);
97
98 auto americanExercise = ext::make_shared<AmericanExercise>(args&: settlementDate, args&: maturity);
99
100 Handle<Quote> underlyingH(ext::make_shared<SimpleQuote>(args&: underlying));
101
102 // bootstrap the yield/dividend/vol curves
103 Handle<YieldTermStructure> flatTermStructure(
104 ext::make_shared<FlatForward>(args&: settlementDate, args&: riskFreeRate, args&: dayCounter));
105 Handle<YieldTermStructure> flatDividendTS(
106 ext::make_shared<FlatForward>(args&: settlementDate, args&: dividendYield, args&: dayCounter));
107 Handle<BlackVolTermStructure> flatVolTS(
108 ext::make_shared<BlackConstantVol>(args&: settlementDate, args&: calendar, args&: volatility,
109 args&: dayCounter));
110 auto payoff = ext::make_shared<PlainVanillaPayoff>(args&: type, args&: strike);
111 auto bsmProcess = ext::make_shared<BlackScholesMertonProcess>(
112 args&: underlyingH, args&: flatDividendTS, args&: flatTermStructure, args&: flatVolTS);
113
114 // options
115 VanillaOption europeanOption(payoff, europeanExercise);
116 VanillaOption bermudanOption(payoff, bermudanExercise);
117 VanillaOption americanOption(payoff, americanExercise);
118
119 // Analytic formulas:
120
121 // Black-Scholes for European
122 method = "Black-Scholes";
123 europeanOption.setPricingEngine(ext::make_shared<AnalyticEuropeanEngine>(args&: bsmProcess));
124 std::cout << std::setw(widths[0]) << std::left << method
125 << std::fixed
126 << std::setw(widths[1]) << std::left << europeanOption.NPV()
127 << std::setw(widths[2]) << std::left << "N/A"
128 << std::setw(widths[3]) << std::left << "N/A"
129 << std::endl;
130
131 //Vasicek rates model for European
132 method = "Black Vasicek Model";
133 Real r0 = riskFreeRate;
134 Real a = 0.3;
135 Real b = 0.3;
136 Real sigma_r = 0.15;
137 Real riskPremium = 0.0;
138 Real correlation = 0.5;
139 auto vasicekProcess = ext::make_shared<Vasicek>(args&: r0, args&: a, args&: b, args&: sigma_r, args&: riskPremium);
140 europeanOption.setPricingEngine(ext::make_shared<AnalyticBlackVasicekEngine>(args&: bsmProcess, args&: vasicekProcess, args&: correlation));
141 std::cout << std::setw(widths[0]) << std::left << method
142 << std::fixed
143 << std::setw(widths[1]) << std::left << europeanOption.NPV()
144 << std::setw(widths[2]) << std::left << "N/A"
145 << std::setw(widths[3]) << std::left << "N/A"
146 << std::endl;
147
148 // semi-analytic Heston for European
149 method = "Heston semi-analytic";
150 auto hestonProcess = ext::make_shared<HestonProcess>(args&: flatTermStructure, args&: flatDividendTS,
151 args&: underlyingH, args: volatility*volatility,
152 args: 1.0, args: volatility*volatility, args: 0.001, args: 0.0);
153 auto hestonModel = ext::make_shared<HestonModel>(args&: hestonProcess);
154 europeanOption.setPricingEngine(ext::make_shared<AnalyticHestonEngine>(args&: hestonModel));
155 std::cout << std::setw(widths[0]) << std::left << method
156 << std::fixed
157 << std::setw(widths[1]) << std::left << europeanOption.NPV()
158 << std::setw(widths[2]) << std::left << "N/A"
159 << std::setw(widths[3]) << std::left << "N/A"
160 << std::endl;
161
162 // semi-analytic Bates for European
163 method = "Bates semi-analytic";
164 auto batesProcess = ext::make_shared<BatesProcess>(args&: flatTermStructure, args&: flatDividendTS,
165 args&: underlyingH, args: volatility*volatility,
166 args: 1.0, args: volatility*volatility, args: 0.001, args: 0.0,
167 args: 1e-14, args: 1e-14, args: 1e-14);
168 auto batesModel = ext::make_shared<BatesModel>(args&: batesProcess);
169 europeanOption.setPricingEngine(ext::make_shared<BatesEngine>(args&: batesModel));
170 std::cout << std::setw(widths[0]) << std::left << method
171 << std::fixed
172 << std::setw(widths[1]) << std::left << europeanOption.NPV()
173 << std::setw(widths[2]) << std::left << "N/A"
174 << std::setw(widths[3]) << std::left << "N/A"
175 << std::endl;
176
177 // Barone-Adesi and Whaley approximation for American
178 method = "Barone-Adesi/Whaley";
179 americanOption.setPricingEngine(ext::make_shared<BaroneAdesiWhaleyApproximationEngine>(args&: bsmProcess));
180 std::cout << std::setw(widths[0]) << std::left << method
181 << std::fixed
182 << std::setw(widths[1]) << std::left << "N/A"
183 << std::setw(widths[2]) << std::left << "N/A"
184 << std::setw(widths[3]) << std::left << americanOption.NPV()
185 << std::endl;
186
187 // Bjerksund and Stensland approximation for American
188 method = "Bjerksund/Stensland";
189 americanOption.setPricingEngine(ext::make_shared<BjerksundStenslandApproximationEngine>(args&: bsmProcess));
190 std::cout << std::setw(widths[0]) << std::left << method
191 << std::fixed
192 << std::setw(widths[1]) << std::left << "N/A"
193 << std::setw(widths[2]) << std::left << "N/A"
194 << std::setw(widths[3]) << std::left << americanOption.NPV()
195 << std::endl;
196
197 // QD+ fixed-point engine for American
198 method = "QD+ fixed-point (fast)";
199 americanOption.setPricingEngine(ext::make_shared<QdFpAmericanEngine>
200 (args&: bsmProcess, args: QdFpAmericanEngine::fastScheme()));
201 std::cout << std::setw(widths[0]) << std::left << method
202 << std::fixed
203 << std::setw(widths[1]) << std::left << "N/A"
204 << std::setw(widths[2]) << std::left << "N/A"
205 << std::setw(widths[3]) << std::left << americanOption.NPV()
206 << std::endl;
207
208 method = "QD+ fixed-point (accurate)";
209 americanOption.setPricingEngine(ext::make_shared<QdFpAmericanEngine>
210 (args&: bsmProcess, args: QdFpAmericanEngine::accurateScheme()));
211 std::cout << std::setw(widths[0]) << std::left << method
212 << std::fixed
213 << std::setw(widths[1]) << std::left << "N/A"
214 << std::setw(widths[2]) << std::left << "N/A"
215 << std::setw(widths[3]) << std::left << americanOption.NPV()
216 << std::endl;
217
218 method = "QD+ fixed-point (high precision)";
219 americanOption.setPricingEngine(ext::make_shared<QdFpAmericanEngine>
220 (args&: bsmProcess, args: QdFpAmericanEngine::highPrecisionScheme()));
221 std::cout << std::setw(widths[0]) << std::left << method
222 << std::fixed
223 << std::setw(widths[1]) << std::left << "N/A"
224 << std::setw(widths[2]) << std::left << "N/A"
225 << std::setw(widths[3]) << std::left << americanOption.NPV()
226 << std::endl;
227
228 // Integral
229 method = "Integral";
230 europeanOption.setPricingEngine(ext::make_shared<IntegralEngine>(args&: bsmProcess));
231 std::cout << std::setw(widths[0]) << std::left << method
232 << std::fixed
233 << std::setw(widths[1]) << std::left << europeanOption.NPV()
234 << std::setw(widths[2]) << std::left << "N/A"
235 << std::setw(widths[3]) << std::left << "N/A"
236 << std::endl;
237
238 // Finite differences
239 Size timeSteps = 801;
240 method = "Finite differences";
241 auto fdengine =
242 ext::make_shared<FdBlackScholesVanillaEngine>(args&: bsmProcess,
243 args&: timeSteps,
244 args: timeSteps-1);
245 europeanOption.setPricingEngine(fdengine);
246 bermudanOption.setPricingEngine(fdengine);
247 americanOption.setPricingEngine(fdengine);
248 std::cout << std::setw(widths[0]) << std::left << method
249 << std::fixed
250 << std::setw(widths[1]) << std::left << europeanOption.NPV()
251 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
252 << std::setw(widths[3]) << std::left << americanOption.NPV()
253 << std::endl;
254
255 // Binomial method: Jarrow-Rudd
256 method = "Binomial Jarrow-Rudd";
257 auto jrEngine = ext::make_shared<BinomialVanillaEngine<JarrowRudd>>(args&: bsmProcess, args&: timeSteps);
258 europeanOption.setPricingEngine(jrEngine);
259 bermudanOption.setPricingEngine(jrEngine);
260 americanOption.setPricingEngine(jrEngine);
261 std::cout << std::setw(widths[0]) << std::left << method
262 << std::fixed
263 << std::setw(widths[1]) << std::left << europeanOption.NPV()
264 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
265 << std::setw(widths[3]) << std::left << americanOption.NPV()
266 << std::endl;
267
268 // Binomial method: Cox-Ross-Rubinstein
269 method = "Binomial Cox-Ross-Rubinstein";
270 auto crrEngine = ext::make_shared<BinomialVanillaEngine<CoxRossRubinstein>>(args&: bsmProcess, args&: timeSteps);
271 europeanOption.setPricingEngine(crrEngine);
272 bermudanOption.setPricingEngine(crrEngine);
273 americanOption.setPricingEngine(crrEngine);
274 std::cout << std::setw(widths[0]) << std::left << method
275 << std::fixed
276 << std::setw(widths[1]) << std::left << europeanOption.NPV()
277 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
278 << std::setw(widths[3]) << std::left << americanOption.NPV()
279 << std::endl;
280
281 // Binomial method: Additive equiprobabilities
282 method = "Additive equiprobabilities";
283 auto aeqpEngine = ext::make_shared<BinomialVanillaEngine<AdditiveEQPBinomialTree>>(args&: bsmProcess, args&: timeSteps);
284 europeanOption.setPricingEngine(aeqpEngine);
285 bermudanOption.setPricingEngine(aeqpEngine);
286 americanOption.setPricingEngine(aeqpEngine);
287 std::cout << std::setw(widths[0]) << std::left << method
288 << std::fixed
289 << std::setw(widths[1]) << std::left << europeanOption.NPV()
290 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
291 << std::setw(widths[3]) << std::left << americanOption.NPV()
292 << std::endl;
293
294 // Binomial method: Binomial Trigeorgis
295 method = "Binomial Trigeorgis";
296 auto trigeorgisEngine = ext::make_shared<BinomialVanillaEngine<Trigeorgis>>(args&: bsmProcess, args&: timeSteps);
297 europeanOption.setPricingEngine(trigeorgisEngine);
298 bermudanOption.setPricingEngine(trigeorgisEngine);
299 americanOption.setPricingEngine(trigeorgisEngine);
300 std::cout << std::setw(widths[0]) << std::left << method
301 << std::fixed
302 << std::setw(widths[1]) << std::left << europeanOption.NPV()
303 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
304 << std::setw(widths[3]) << std::left << americanOption.NPV()
305 << std::endl;
306
307 // Binomial method: Binomial Tian
308 method = "Binomial Tian";
309 auto tianEngine = ext::make_shared<BinomialVanillaEngine<Tian>>(args&: bsmProcess, args&: timeSteps);
310 europeanOption.setPricingEngine(tianEngine);
311 bermudanOption.setPricingEngine(tianEngine);
312 americanOption.setPricingEngine(tianEngine);
313 std::cout << std::setw(widths[0]) << std::left << method
314 << std::fixed
315 << std::setw(widths[1]) << std::left << europeanOption.NPV()
316 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
317 << std::setw(widths[3]) << std::left << americanOption.NPV()
318 << std::endl;
319
320 // Binomial method: Binomial Leisen-Reimer
321 method = "Binomial Leisen-Reimer";
322 auto lrEngine = ext::make_shared<BinomialVanillaEngine<LeisenReimer>>(args&: bsmProcess, args&: timeSteps);
323 europeanOption.setPricingEngine(lrEngine);
324 bermudanOption.setPricingEngine(lrEngine);
325 americanOption.setPricingEngine(lrEngine);
326 std::cout << std::setw(widths[0]) << std::left << method
327 << std::fixed
328 << std::setw(widths[1]) << std::left << europeanOption.NPV()
329 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
330 << std::setw(widths[3]) << std::left << americanOption.NPV()
331 << std::endl;
332
333 // Binomial method: Binomial Joshi
334 method = "Binomial Joshi";
335 auto joshiEngine = ext::make_shared<BinomialVanillaEngine<Joshi4>>(args&: bsmProcess, args&: timeSteps);
336 europeanOption.setPricingEngine(joshiEngine);
337 bermudanOption.setPricingEngine(joshiEngine);
338 americanOption.setPricingEngine(joshiEngine);
339 std::cout << std::setw(widths[0]) << std::left << method
340 << std::fixed
341 << std::setw(widths[1]) << std::left << europeanOption.NPV()
342 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
343 << std::setw(widths[3]) << std::left << americanOption.NPV()
344 << std::endl;
345
346 // Monte Carlo Method: MC (crude)
347 timeSteps = 1;
348 method = "MC (crude)";
349 Size mcSeed = 42;
350 auto mcengine1 = MakeMCEuropeanEngine<PseudoRandom>(bsmProcess)
351 .withSteps(steps: timeSteps)
352 .withAbsoluteTolerance(tolerance: 0.02)
353 .withSeed(seed: mcSeed);
354 europeanOption.setPricingEngine(mcengine1);
355 // Real errorEstimate = europeanOption.errorEstimate();
356 std::cout << std::setw(widths[0]) << std::left << method
357 << std::fixed
358 << std::setw(widths[1]) << std::left << europeanOption.NPV()
359 << std::setw(widths[2]) << std::left << "N/A"
360 << std::setw(widths[3]) << std::left << "N/A"
361 << std::endl;
362
363 // Monte Carlo Method: QMC (Sobol)
364 method = "QMC (Sobol)";
365 Size nSamples = 32768; // 2^15
366
367 auto mcengine2 = MakeMCEuropeanEngine<LowDiscrepancy>(bsmProcess)
368 .withSteps(steps: timeSteps)
369 .withSamples(samples: nSamples);
370 europeanOption.setPricingEngine(mcengine2);
371 std::cout << std::setw(widths[0]) << std::left << method
372 << std::fixed
373 << std::setw(widths[1]) << std::left << europeanOption.NPV()
374 << std::setw(widths[2]) << std::left << "N/A"
375 << std::setw(widths[3]) << std::left << "N/A"
376 << std::endl;
377
378 // Monte Carlo Method: MC (Longstaff Schwartz)
379 method = "MC (Longstaff Schwartz)";
380 auto mcengine3 = MakeMCAmericanEngine<PseudoRandom>(bsmProcess)
381 .withSteps(steps: 100)
382 .withAntitheticVariate()
383 .withCalibrationSamples(samples: 4096)
384 .withAbsoluteTolerance(tolerance: 0.02)
385 .withSeed(seed: mcSeed);
386 americanOption.setPricingEngine(mcengine3);
387 std::cout << std::setw(widths[0]) << std::left << method
388 << std::fixed
389 << std::setw(widths[1]) << std::left << "N/A"
390 << std::setw(widths[2]) << std::left << "N/A"
391 << std::setw(widths[3]) << std::left << americanOption.NPV()
392 << std::endl;
393
394 // End test
395 return 0;
396
397 } catch (std::exception& e) {
398 std::cerr << e.what() << std::endl;
399 return 1;
400 } catch (...) {
401 std::cerr << "unknown error" << std::endl;
402 return 1;
403 }
404}
405

source code of quantlib/Examples/EquityOption/EquityOption.cpp