EquityOption.cpp

This example evaluates European, American and Bermudan options using different methods

00001 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 
00020 // the only header you need to use QuantLib
00021 #define BOOST_LIB_DIAGNOSTIC
00022 #  include <ql/quantlib.hpp>
00023 #undef BOOST_LIB_DIAGNOSTIC
00024 
00025 #ifdef BOOST_MSVC
00026 /* Uncomment the following lines to unmask floating-point
00027    exceptions. Warning: unpredictable results can arise...
00028 
00029    See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481
00030    Is there anyone with a definitive word about this?
00031 */
00032 // #include <float.h>
00033 // namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); }
00034 #endif
00035 
00036 #include <boost/timer.hpp>
00037 #include <iostream>
00038 #include <iomanip>
00039 
00040 using namespace QuantLib;
00041 
00042 #if defined(QL_ENABLE_SESSIONS)
00043 namespace QuantLib {
00044 
00045     Integer sessionId() { return 0; }
00046 
00047 }
00048 #endif
00049 
00050 
00051 int main(int, char* []) {
00052 
00053     try {
00054 
00055         boost::timer timer;
00056         std::cout << std::endl;
00057 
00058         // our options
00059         Option::Type type(Option::Put);
00060         Real underlying = 36;
00061         Real strike = 40;
00062         Spread dividendYield = 0.00;
00063         Rate riskFreeRate = 0.06;
00064         Volatility volatility = 0.20;
00065 
00066         Date todaysDate(15, May, 1998);
00067         Date settlementDate(17, May, 1998);
00068         Settings::instance().evaluationDate() = todaysDate;
00069 
00070         Date maturity(17, May, 1999);
00071         DayCounter dayCounter = Actual365Fixed();
00072 
00073         std::cout << "Option type = "  << type << std::endl;
00074         std::cout << "Maturity = "        << maturity << std::endl;
00075         std::cout << "Underlying price = "        << underlying << std::endl;
00076         std::cout << "Strike = "                  << strike << std::endl;
00077         std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate)
00078                   << std::endl;
00079         std::cout << "Dividend yield = " << io::rate(dividendYield)
00080                   << std::endl;
00081         std::cout << "Volatility = " << io::volatility(volatility)
00082                   << std::endl;
00083         std::cout << std::endl;
00084 
00085         std::string method;
00086 
00087         std::cout << std::endl ;
00088 
00089         // write column headings
00090         Size widths[] = { 35, 14, 14, 14 };
00091         std::cout << std::setw(widths[0]) << std::left << "Method"
00092                   << std::setw(widths[1]) << std::left << "European"
00093                   << std::setw(widths[2]) << std::left << "Bermudan"
00094                   << std::setw(widths[3]) << std::left << "American"
00095                   << std::endl;
00096 
00097         std::vector<Date> exerciseDates;
00098         for (Integer i=1; i<=4; i++)
00099             exerciseDates.push_back(settlementDate + 3*i*Months);
00100 
00101         boost::shared_ptr<Exercise> europeanExercise(
00102                                          new EuropeanExercise(maturity));
00103 
00104         boost::shared_ptr<Exercise> bermudanExercise(
00105                                          new BermudanExercise(exerciseDates));
00106 
00107         boost::shared_ptr<Exercise> americanExercise(
00108                                          new AmericanExercise(settlementDate,
00109                                                               maturity));
00110 
00111         Handle<Quote> underlyingH(
00112             boost::shared_ptr<Quote>(new SimpleQuote(underlying)));
00113 
00114         // bootstrap the yield/dividend/vol curves
00115         Handle<YieldTermStructure> flatTermStructure(
00116             boost::shared_ptr<YieldTermStructure>(
00117                 new FlatForward(settlementDate, riskFreeRate, dayCounter)));
00118         Handle<YieldTermStructure> flatDividendTS(
00119             boost::shared_ptr<YieldTermStructure>(
00120                 new FlatForward(settlementDate, dividendYield, dayCounter)));
00121         Handle<BlackVolTermStructure> flatVolTS(
00122             boost::shared_ptr<BlackVolTermStructure>(
00123                 new BlackConstantVol(settlementDate, volatility, dayCounter)));
00124 
00125         boost::shared_ptr<StrikedTypePayoff> payoff(
00126                                         new PlainVanillaPayoff(type, strike));
00127 
00128         boost::shared_ptr<StochasticProcess> stochasticProcess(
00129                  new BlackScholesMertonProcess(underlyingH, flatDividendTS,
00130                                                flatTermStructure, flatVolTS));
00131 
00132         // options
00133 
00134         VanillaOption europeanOption(stochasticProcess, payoff,
00135                                      europeanExercise);
00136 
00137         VanillaOption bermudanOption(stochasticProcess, payoff,
00138                                      bermudanExercise);
00139 
00140         VanillaOption americanOption(stochasticProcess, payoff,
00141                                      americanExercise);
00142 
00143         // Analytic formulas:
00144 
00145         // Black-Scholes for European
00146         method = "Black-Scholes";
00147         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00148                                                  new AnalyticEuropeanEngine));
00149         std::cout << std::setw(widths[0]) << std::left << method
00150                   << std::fixed
00151                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00152                   << std::setw(widths[2]) << std::left << "N/A"
00153                   << std::setw(widths[3]) << std::left << "N/A"
00154                   << std::endl;
00155 
00156         // Barone-Adesi and Whaley approximation for American
00157         method = "Barone-Adesi/Whaley";
00158         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00159                                    new BaroneAdesiWhaleyApproximationEngine));
00160         std::cout << std::setw(widths[0]) << std::left << method
00161                   << std::fixed
00162                   << std::setw(widths[1]) << std::left << "N/A"
00163                   << std::setw(widths[2]) << std::left << "N/A"
00164                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00165                   << std::endl;
00166 
00167         // Bjerksund and Stensland approximation for American
00168         method = "Bjerksund/Stensland";
00169         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00170                                   new BjerksundStenslandApproximationEngine));
00171         std::cout << std::setw(widths[0]) << std::left << method
00172                   << std::fixed
00173                   << std::setw(widths[1]) << std::left << "N/A"
00174                   << std::setw(widths[2]) << std::left << "N/A"
00175                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00176                   << std::endl;
00177 
00178         // Integral
00179 
00180         method = "Integral";
00181         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00182                                                          new IntegralEngine));
00183         std::cout << std::setw(widths[0]) << std::left << method
00184                   << std::fixed
00185                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00186                   << std::setw(widths[2]) << std::left << "N/A"
00187                   << std::setw(widths[3]) << std::left << "N/A"
00188                   << std::endl;
00189 
00190         // Finite differences
00191 
00192         Size timeSteps = 801;
00193 
00194         method = "Finite differences";
00195         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00196                               new FDEuropeanEngine(timeSteps,timeSteps-1)));
00197         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00198                               new FDBermudanEngine(timeSteps,timeSteps-1)));
00199         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00200                               new FDAmericanEngine(timeSteps,timeSteps-1)));
00201         std::cout << std::setw(widths[0]) << std::left << method
00202                   << std::fixed
00203                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00204                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00205                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00206                   << std::endl;
00207 
00208         // Binomial method
00209 
00210         method = "Binomial Jarrow-Rudd";
00211         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00212             new BinomialVanillaEngine<JarrowRudd>(timeSteps)));
00213         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00214             new BinomialVanillaEngine<JarrowRudd>(timeSteps)));
00215         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00216             new BinomialVanillaEngine<JarrowRudd>(timeSteps)));
00217         std::cout << std::setw(widths[0]) << std::left << method
00218                   << std::fixed
00219                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00220                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00221                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00222                   << std::endl;
00223 
00224         method = "Binomial Cox-Ross-Rubinstein";
00225         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00226             new BinomialVanillaEngine<CoxRossRubinstein>(timeSteps)));
00227         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00228             new BinomialVanillaEngine<CoxRossRubinstein>(timeSteps)));
00229         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00230             new BinomialVanillaEngine<CoxRossRubinstein>(timeSteps)));
00231         std::cout << std::setw(widths[0]) << std::left << method
00232                   << std::fixed
00233                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00234                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00235                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00236                   << std::endl;
00237 
00238         method = "Additive equiprobabilities";
00239         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00240             new BinomialVanillaEngine<AdditiveEQPBinomialTree>(timeSteps)));
00241         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00242             new BinomialVanillaEngine<AdditiveEQPBinomialTree>(timeSteps)));
00243         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00244             new BinomialVanillaEngine<AdditiveEQPBinomialTree>(timeSteps)));
00245         std::cout << std::setw(widths[0]) << std::left << method
00246                   << std::fixed
00247                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00248                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00249                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00250                   << std::endl;
00251 
00252         method = "Binomial Trigeorgis";
00253         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00254             new BinomialVanillaEngine<Trigeorgis>(timeSteps)));
00255         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00256             new BinomialVanillaEngine<Trigeorgis>(timeSteps)));
00257         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00258             new BinomialVanillaEngine<Trigeorgis>(timeSteps)));
00259         std::cout << std::setw(widths[0]) << std::left << method
00260                   << std::fixed
00261                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00262                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00263                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00264                   << std::endl;
00265 
00266         method = "Binomial Tian";
00267         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00268             new BinomialVanillaEngine<Tian>(timeSteps)));
00269         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00270             new BinomialVanillaEngine<Tian>(timeSteps)));
00271         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00272             new BinomialVanillaEngine<Tian>(timeSteps)));
00273         std::cout << std::setw(widths[0]) << std::left << method
00274                   << std::fixed
00275                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00276                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00277                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00278                   << std::endl;
00279 
00280         method = "Binomial Leisen-Reimer";
00281         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00282             new BinomialVanillaEngine<LeisenReimer>(timeSteps)));
00283         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00284             new BinomialVanillaEngine<LeisenReimer>(timeSteps)));
00285         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00286             new BinomialVanillaEngine<LeisenReimer>(timeSteps)));
00287         std::cout << std::setw(widths[0]) << std::left << method
00288                   << std::fixed
00289                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00290                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00291                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00292                   << std::endl;
00293 
00294         method = "Binomial Joshi";
00295         europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00296             new BinomialVanillaEngine<Joshi4>(timeSteps)));
00297         bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00298             new BinomialVanillaEngine<Joshi4>(timeSteps)));
00299         americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
00300             new BinomialVanillaEngine<Joshi4>(timeSteps)));
00301         std::cout << std::setw(widths[0]) << std::left << method
00302                   << std::fixed
00303                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00304                   << std::setw(widths[2]) << std::left << bermudanOption.NPV()
00305                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00306                   << std::endl;
00307 
00308         // Monte Carlo Method
00309 
00310         timeSteps = 1;
00311 
00312         method = "MC (crude)";
00313         Size mcSeed = 42;
00314 
00315         boost::shared_ptr<PricingEngine> mcengine1;
00316         mcengine1 =
00317             MakeMCEuropeanEngine<PseudoRandom>().withSteps(timeSteps)
00318                                                 .withTolerance(0.02)
00319                                                 .withSeed(mcSeed);
00320         europeanOption.setPricingEngine(mcengine1);
00321         // Real errorEstimate = europeanOption.errorEstimate();
00322         std::cout << std::setw(widths[0]) << std::left << method
00323                   << std::fixed
00324                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00325                   << std::setw(widths[2]) << std::left << "N/A"
00326                   << std::setw(widths[3]) << std::left << "N/A"
00327                   << std::endl;
00328 
00329         method = "MC (Sobol)";
00330         Size nSamples = 32768;  // 2^15
00331 
00332         boost::shared_ptr<PricingEngine> mcengine2;
00333         mcengine2 =
00334             MakeMCEuropeanEngine<LowDiscrepancy>().withSteps(timeSteps)
00335                                                   .withSamples(nSamples);
00336         europeanOption.setPricingEngine(mcengine2);
00337         std::cout << std::setw(widths[0]) << std::left << method
00338                   << std::fixed
00339                   << std::setw(widths[1]) << std::left << europeanOption.NPV()
00340                   << std::setw(widths[2]) << std::left << "N/A"
00341                   << std::setw(widths[3]) << std::left << "N/A"
00342                   << std::endl;
00343 
00344         method = "MC (Longstaff Schwartz)";
00345         boost::shared_ptr<PricingEngine> mcengine3;
00346         mcengine3 =
00347             MakeMCAmericanEngine<PseudoRandom>().withSteps(100)
00348                                                 .withAntitheticVariate()
00349                                                 .withCalibrationSamples(4096)
00350                                                 .withTolerance(0.02)
00351                                                 .withSeed(mcSeed);
00352         americanOption.setPricingEngine(mcengine3);
00353         std::cout << std::setw(widths[0]) << std::left << method
00354                   << std::fixed
00355                   << std::setw(widths[1]) << std::left << "N/A"
00356                   << std::setw(widths[2]) << std::left << "N/A"
00357                   << std::setw(widths[3]) << std::left << americanOption.NPV()
00358                   << std::endl;
00359 
00360         Real seconds = timer.elapsed();
00361         Integer hours = int(seconds/3600);
00362         seconds -= hours * 3600;
00363         Integer minutes = int(seconds/60);
00364         seconds -= minutes * 60;
00365         std::cout << " \nRun completed in ";
00366         if (hours > 0)
00367             std::cout << hours << " h ";
00368         if (hours > 0 || minutes > 0)
00369             std::cout << minutes << " m ";
00370         std::cout << std::fixed << std::setprecision(0)
00371                   << seconds << " s\n" << std::endl;
00372 
00373         return 0;
00374     } catch (std::exception& e) {
00375         std::cout << e.what() << std::endl;
00376         return 1;
00377     } catch (...) {
00378         std::cout << "unknown error" << std::endl;
00379         return 1;
00380     }
00381 }