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