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 #ifdef BOOST_MSVC
22 # include <ql/auto_link.hpp>
23 #endif
24 #include <ql/instruments/vanillaoption.hpp>
25 #include <ql/pricingengines/vanilla/binomialengine.hpp>
26 #include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
27 #include <ql/pricingengines/vanilla/analytichestonengine.hpp>
28 #include <ql/pricingengines/vanilla/baroneadesiwhaleyengine.hpp>
29 #include <ql/pricingengines/vanilla/bjerksundstenslandengine.hpp>
30 #include <ql/pricingengines/vanilla/batesengine.hpp>
31 #include <ql/pricingengines/vanilla/integralengine.hpp>
32 #include <ql/pricingengines/vanilla/fdeuropeanengine.hpp>
33 #include <ql/pricingengines/vanilla/fdbermudanengine.hpp>
34 #include <ql/pricingengines/vanilla/fdamericanengine.hpp>
35 #include <ql/pricingengines/vanilla/mceuropeanengine.hpp>
36 #include <ql/pricingengines/vanilla/mcamericanengine.hpp>
37 #include <ql/time/calendars/target.hpp>
38 #include <ql/utilities/dataformatters.hpp>
39
40 #include <boost/timer.hpp>
41 #include <iostream>
42 #include <iomanip>
43
44 using namespace QuantLib;
45
46 #if defined(QL_ENABLE_SESSIONS)
47 namespace QuantLib {
48
49 Integer sessionId() { return 0; }
50
51 }
52 #endif
53
54
55 int main(int, char* []) {
56
57 try {
58
59 boost::timer timer;
60 std::cout << std::endl;
61
62 // set up dates
63 Calendar calendar = TARGET();
64 Date todaysDate(15, May, 1998);
65 Date settlementDate(17, May, 1998);
66 Settings::instance().evaluationDate() = todaysDate;
67
68 // our options
69 Option::Type type(Option::Put);
70 Real underlying = 36;
71 Real strike = 40;
72 Spread dividendYield = 0.00;
73 Rate riskFreeRate = 0.06;
74 Volatility volatility = 0.20;
75 Date maturity(17, May, 1999);
76 DayCounter dayCounter = Actual365Fixed();
77
78 std::cout << "Option type = " << type << std::endl;
79 std::cout << "Maturity = " << maturity << std::endl;
80 std::cout << "Underlying price = " << underlying << std::endl;
81 std::cout << "Strike = " << strike << std::endl;
82 std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate)
83 << std::endl;
84 std::cout << "Dividend yield = " << io::rate(dividendYield)
85 << std::endl;
86 std::cout << "Volatility = " << io::volatility(volatility)
87 << std::endl;
88 std::cout << std::endl;
89 std::string method;
90 std::cout << std::endl ;
91
92 // write column headings
93 Size widths[] = { 35, 14, 14, 14 };
94 std::cout << std::setw(widths[0]) << std::left << "Method"
95 << std::setw(widths[1]) << std::left << "European"
96 << std::setw(widths[2]) << std::left << "Bermudan"
97 << std::setw(widths[3]) << std::left << "American"
98 << std::endl;
99
100 std::vector<Date> exerciseDates;
101 for (Integer i=1; i<=4; i++)
102 exerciseDates.push_back(settlementDate + 3*i*Months);
103
104 ext::shared_ptr<Exercise> europeanExercise(
105 new EuropeanExercise(maturity));
106
107 ext::shared_ptr<Exercise> bermudanExercise(
108 new BermudanExercise(exerciseDates));
109
110 ext::shared_ptr<Exercise> americanExercise(
111 new AmericanExercise(settlementDate,
112 maturity));
113
114 Handle<Quote> underlyingH(
115 ext::shared_ptr<Quote>(new SimpleQuote(underlying)));
116
117 // bootstrap the yield/dividend/vol curves
118 Handle<YieldTermStructure> flatTermStructure(
119 ext::shared_ptr<YieldTermStructure>(
120 new FlatForward(settlementDate, riskFreeRate, dayCounter)));
121 Handle<YieldTermStructure> flatDividendTS(
122 ext::shared_ptr<YieldTermStructure>(
123 new FlatForward(settlementDate, dividendYield, dayCounter)));
124 Handle<BlackVolTermStructure> flatVolTS(
125 ext::shared_ptr<BlackVolTermStructure>(
126 new BlackConstantVol(settlementDate, calendar, volatility,
127 dayCounter)));
128 ext::shared_ptr<StrikedTypePayoff> payoff(
129 new PlainVanillaPayoff(type, strike));
130 ext::shared_ptr<BlackScholesMertonProcess> bsmProcess(
131 new BlackScholesMertonProcess(underlyingH, flatDividendTS,
132 flatTermStructure, flatVolTS));
133
134 // options
135 VanillaOption europeanOption(payoff, europeanExercise);
136 VanillaOption bermudanOption(payoff, bermudanExercise);
137 VanillaOption americanOption(payoff, americanExercise);
138
139 // Analytic formulas:
140
141 // Black-Scholes for European
142 method = "Black-Scholes";
143 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
144 new AnalyticEuropeanEngine(bsmProcess)));
145 std::cout << std::setw(widths[0]) << std::left << method
146 << std::fixed
147 << std::setw(widths[1]) << std::left << europeanOption.NPV()
148 << std::setw(widths[2]) << std::left << "N/A"
149 << std::setw(widths[3]) << std::left << "N/A"
150 << std::endl;
151
152 // semi-analytic Heston for European
153 method = "Heston semi-analytic";
154 ext::shared_ptr<HestonProcess> hestonProcess(
155 new HestonProcess(flatTermStructure, flatDividendTS,
156 underlyingH, volatility*volatility,
157 1.0, volatility*volatility, 0.001, 0.0));
158 ext::shared_ptr<HestonModel> hestonModel(
159 new HestonModel(hestonProcess));
160 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
161 new AnalyticHestonEngine(hestonModel)));
162 std::cout << std::setw(widths[0]) << std::left << method
163 << std::fixed
164 << std::setw(widths[1]) << std::left << europeanOption.NPV()
165 << std::setw(widths[2]) << std::left << "N/A"
166 << std::setw(widths[3]) << std::left << "N/A"
167 << std::endl;
168
169 // semi-analytic Bates for European
170 method = "Bates semi-analytic";
171 ext::shared_ptr<BatesProcess> batesProcess(
172 new BatesProcess(flatTermStructure, flatDividendTS,
173 underlyingH, volatility*volatility,
174 1.0, volatility*volatility, 0.001, 0.0,
175 1e-14, 1e-14, 1e-14));
176 ext::shared_ptr<BatesModel> batesModel(new BatesModel(batesProcess));
177 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
178 new BatesEngine(batesModel)));
179 std::cout << std::setw(widths[0]) << std::left << method
180 << std::fixed
181 << std::setw(widths[1]) << std::left << europeanOption.NPV()
182 << std::setw(widths[2]) << std::left << "N/A"
183 << std::setw(widths[3]) << std::left << "N/A"
184 << std::endl;
185
186 // Barone-Adesi and Whaley approximation for American
187 method = "Barone-Adesi/Whaley";
188 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
189 new BaroneAdesiWhaleyApproximationEngine(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 // Bjerksund and Stensland approximation for American
198 method = "Bjerksund/Stensland";
199 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
200 new BjerksundStenslandApproximationEngine(bsmProcess)));
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 // Integral
209 method = "Integral";
210 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
211 new IntegralEngine(bsmProcess)));
212 std::cout << std::setw(widths[0]) << std::left << method
213 << std::fixed
214 << std::setw(widths[1]) << std::left << europeanOption.NPV()
215 << std::setw(widths[2]) << std::left << "N/A"
216 << std::setw(widths[3]) << std::left << "N/A"
217 << std::endl;
218
219 // Finite differences
220 Size timeSteps = 801;
221 method = "Finite differences";
222 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
223 new FDEuropeanEngine<CrankNicolson>(bsmProcess,
224 timeSteps,timeSteps-1)));
225 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
226 new FDBermudanEngine<CrankNicolson>(bsmProcess,
227 timeSteps,timeSteps-1)));
228 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
229 new FDAmericanEngine<CrankNicolson>(bsmProcess,
230 timeSteps,timeSteps-1)));
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 << bermudanOption.NPV()
235 << std::setw(widths[3]) << std::left << americanOption.NPV()
236 << std::endl;
237
238 // Binomial method: Jarrow-Rudd
239 method = "Binomial Jarrow-Rudd";
240 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
241 new BinomialVanillaEngine<JarrowRudd>(bsmProcess,timeSteps)));
242 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
243 new BinomialVanillaEngine<JarrowRudd>(bsmProcess,timeSteps)));
244 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
245 new BinomialVanillaEngine<JarrowRudd>(bsmProcess,timeSteps)));
246 std::cout << std::setw(widths[0]) << std::left << method
247 << std::fixed
248 << std::setw(widths[1]) << std::left << europeanOption.NPV()
249 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
250 << std::setw(widths[3]) << std::left << americanOption.NPV()
251 << std::endl;
252 method = "Binomial Cox-Ross-Rubinstein";
253 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
254 new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess,
255 timeSteps)));
256 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
257 new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess,
258 timeSteps)));
259 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
260 new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess,
261 timeSteps)));
262 std::cout << std::setw(widths[0]) << std::left << method
263 << std::fixed
264 << std::setw(widths[1]) << std::left << europeanOption.NPV()
265 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
266 << std::setw(widths[3]) << std::left << americanOption.NPV()
267 << std::endl;
268
269 // Binomial method: Additive equiprobabilities
270 method = "Additive equiprobabilities";
271 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
272 new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess,
273 timeSteps)));
274 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
275 new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess,
276 timeSteps)));
277 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
278 new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess,
279 timeSteps)));
280 std::cout << std::setw(widths[0]) << std::left << method
281 << std::fixed
282 << std::setw(widths[1]) << std::left << europeanOption.NPV()
283 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
284 << std::setw(widths[3]) << std::left << americanOption.NPV()
285 << std::endl;
286
287 // Binomial method: Binomial Trigeorgis
288 method = "Binomial Trigeorgis";
289 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
290 new BinomialVanillaEngine<Trigeorgis>(bsmProcess,timeSteps)));
291 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
292 new BinomialVanillaEngine<Trigeorgis>(bsmProcess,timeSteps)));
293 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
294 new BinomialVanillaEngine<Trigeorgis>(bsmProcess,timeSteps)));
295 std::cout << std::setw(widths[0]) << std::left << method
296 << std::fixed
297 << std::setw(widths[1]) << std::left << europeanOption.NPV()
298 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
299 << std::setw(widths[3]) << std::left << americanOption.NPV()
300 << std::endl;
301
302 // Binomial method: Binomial Tian
303 method = "Binomial Tian";
304 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
305 new BinomialVanillaEngine<Tian>(bsmProcess,timeSteps)));
306 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
307 new BinomialVanillaEngine<Tian>(bsmProcess,timeSteps)));
308 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
309 new BinomialVanillaEngine<Tian>(bsmProcess,timeSteps)));
310 std::cout << std::setw(widths[0]) << std::left << method
311 << std::fixed
312 << std::setw(widths[1]) << std::left << europeanOption.NPV()
313 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
314 << std::setw(widths[3]) << std::left << americanOption.NPV()
315 << std::endl;
316
317 // Binomial method: Binomial Leisen-Reimer
318 method = "Binomial Leisen-Reimer";
319 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
320 new BinomialVanillaEngine<LeisenReimer>(bsmProcess,timeSteps)));
321 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
322 new BinomialVanillaEngine<LeisenReimer>(bsmProcess,timeSteps)));
323 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
324 new BinomialVanillaEngine<LeisenReimer>(bsmProcess,timeSteps)));
325 std::cout << std::setw(widths[0]) << std::left << method
326 << std::fixed
327 << std::setw(widths[1]) << std::left << europeanOption.NPV()
328 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
329 << std::setw(widths[3]) << std::left << americanOption.NPV()
330 << std::endl;
331
332 // Binomial method: Binomial Joshi
333 method = "Binomial Joshi";
334 europeanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
335 new BinomialVanillaEngine<Joshi4>(bsmProcess,timeSteps)));
336 bermudanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
337 new BinomialVanillaEngine<Joshi4>(bsmProcess,timeSteps)));
338 americanOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
339 new BinomialVanillaEngine<Joshi4>(bsmProcess,timeSteps)));
340 std::cout << std::setw(widths[0]) << std::left << method
341 << std::fixed
342 << std::setw(widths[1]) << std::left << europeanOption.NPV()
343 << std::setw(widths[2]) << std::left << bermudanOption.NPV()
344 << std::setw(widths[3]) << std::left << americanOption.NPV()
345 << std::endl;
346
347 // Monte Carlo Method: MC (crude)
348 timeSteps = 1;
349 method = "MC (crude)";
350 Size mcSeed = 42;
351 ext::shared_ptr<PricingEngine> mcengine1;
352 mcengine1 = MakeMCEuropeanEngine<PseudoRandom>(bsmProcess)
353 .withSteps(timeSteps)
354 .withAbsoluteTolerance(0.02)
355 .withSeed(mcSeed);
356 europeanOption.setPricingEngine(mcengine1);
357 // Real errorEstimate = europeanOption.errorEstimate();
358 std::cout << std::setw(widths[0]) << std::left << method
359 << std::fixed
360 << std::setw(widths[1]) << std::left << europeanOption.NPV()
361 << std::setw(widths[2]) << std::left << "N/A"
362 << std::setw(widths[3]) << std::left << "N/A"
363 << std::endl;
364
365 // Monte Carlo Method: QMC (Sobol)
366 method = "QMC (Sobol)";
367 Size nSamples = 32768; // 2^15
368
369 ext::shared_ptr<PricingEngine> mcengine2;
370 mcengine2 = MakeMCEuropeanEngine<LowDiscrepancy>(bsmProcess)
371 .withSteps(timeSteps)
372 .withSamples(nSamples);
373 europeanOption.setPricingEngine(mcengine2);
374 std::cout << std::setw(widths[0]) << std::left << method
375 << std::fixed
376 << std::setw(widths[1]) << std::left << europeanOption.NPV()
377 << std::setw(widths[2]) << std::left << "N/A"
378 << std::setw(widths[3]) << std::left << "N/A"
379 << std::endl;
380
381 // Monte Carlo Method: MC (Longstaff Schwartz)
382 method = "MC (Longstaff Schwartz)";
383 ext::shared_ptr<PricingEngine> mcengine3;
384 mcengine3 = MakeMCAmericanEngine<PseudoRandom>(bsmProcess)
385 .withSteps(100)
386 .withAntitheticVariate()
387 .withCalibrationSamples(4096)
388 .withAbsoluteTolerance(0.02)
389 .withSeed(mcSeed);
390 americanOption.setPricingEngine(mcengine3);
391 std::cout << std::setw(widths[0]) << std::left << method
392 << std::fixed
393 << std::setw(widths[1]) << std::left << "N/A"
394 << std::setw(widths[2]) << std::left << "N/A"
395 << std::setw(widths[3]) << std::left << americanOption.NPV()
396 << std::endl;
397
398 // End test
399 double seconds = timer.elapsed();
400 Integer hours = int(seconds/3600);
401 seconds -= hours * 3600;
402 Integer minutes = int(seconds/60);
403 seconds -= minutes * 60;
404 std::cout << " \nRun completed in ";
405 if (hours > 0)
406 std::cout << hours << " h ";
407 if (hours > 0 || minutes > 0)
408 std::cout << minutes << " m ";
409 std::cout << std::fixed << std::setprecision(0)
410 << seconds << " s\n" << std::endl;
411 return 0;
412
413 } catch (std::exception& e) {
414 std::cerr << e.what() << std::endl;
415 return 1;
416 } catch (...) {
417 std::cerr << "unknown error" << std::endl;
418 return 1;
419 }
420 }
421