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