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