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 }