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