/*! Equity Option */ /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ // the only header you need to use QuantLib #include <ql/quantlib.hpp> #ifdef BOOST_MSVC /* Uncomment the following lines to unmask floating-point exceptions. Warning: unpredictable results can arise... See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481 */ // #include <float.h> // namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); } #endif #include <boost/timer.hpp> #include <iostream> #include <iomanip> using namespace QuantLib; #ifdef BOOST_MSVC # ifdef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN # include <ql/auto_link.hpp> # define BOOST_LIB_NAME boost_system # include <boost/config/auto_link.hpp> # undef BOOST_LIB_NAME # define BOOST_LIB_NAME boost_thread # include <boost/config/auto_link.hpp> # undef BOOST_LIB_NAME # endif #endif #if defined(QL_ENABLE_SESSIONS) namespace QuantLib { Integer sessionId() { return 0; } } #endif int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; // set up dates Calendar calendar = TARGET(); Date todaysDate(15, May, 1998); Date settlementDate(17, May, 1998); Settings::instance().evaluationDate() = todaysDate; // our options Option::Type type(Option::Put); Real underlying = 36; Real strike = 40; Spread dividendYield = 0.00; Rate riskFreeRate = 0.06; Volatility volatility = 0.20; Date maturity(17, May, 1999); DayCounter dayCounter = Actual365Fixed(); std::cout << "Option type = " << type << std::endl; std::cout << "Maturity = " << maturity << std::endl; std::cout << "Underlying price = " << underlying << std::endl; std::cout << "Strike = " << strike << std::endl; std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate) << std::endl; std::cout << "Dividend yield = " << io::rate(dividendYield) << std::endl; std::cout << "Volatility = " << io::volatility(volatility) << std::endl; std::cout << std::endl; std::string method; std::cout << std::endl ; // write column headings Size widths[] = { 35, 14, 14, 14 }; std::cout << std::setw(widths[0]) << std::left << "Method" << std::setw(widths[1]) << std::left << "European" << std::setw(widths[2]) << std::left << "Bermudan" << std::setw(widths[3]) << std::left << "American" << std::endl; std::vector<Date> exerciseDates; for (Integer i=1; i<=4; i++) exerciseDates.push_back(settlementDate + 3*i*Months); boost::shared_ptr<Exercise> europeanExercise( new EuropeanExercise(maturity)); boost::shared_ptr<Exercise> bermudanExercise( new BermudanExercise(exerciseDates)); boost::shared_ptr<Exercise> americanExercise( new AmericanExercise(settlementDate, maturity)); Handle<Quote> underlyingH( boost::shared_ptr<Quote>(new SimpleQuote(underlying))); // bootstrap the yield/dividend/vol curves Handle<YieldTermStructure> flatTermStructure( boost::shared_ptr<YieldTermStructure>( new FlatForward(settlementDate, riskFreeRate, dayCounter))); Handle<YieldTermStructure> flatDividendTS( boost::shared_ptr<YieldTermStructure>( new FlatForward(settlementDate, dividendYield, dayCounter))); Handle<BlackVolTermStructure> flatVolTS( boost::shared_ptr<BlackVolTermStructure>( new BlackConstantVol(settlementDate, calendar, volatility, dayCounter))); boost::shared_ptr<StrikedTypePayoff> payoff( new PlainVanillaPayoff(type, strike)); boost::shared_ptr<BlackScholesMertonProcess> bsmProcess( new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS)); // options VanillaOption europeanOption(payoff, europeanExercise); VanillaOption bermudanOption(payoff, bermudanExercise); VanillaOption americanOption(payoff, americanExercise); // Analytic formulas: // Black-Scholes for European method = "Black-Scholes"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new AnalyticEuropeanEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // semi-analytic Heston for European method = "Heston semi-analytic"; boost::shared_ptr<HestonProcess> hestonProcess( new HestonProcess(flatTermStructure, flatDividendTS, underlyingH, volatility*volatility, 1.0, volatility*volatility, 0.001, 0.0)); boost::shared_ptr<HestonModel> hestonModel( new HestonModel(hestonProcess)); europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new AnalyticHestonEngine(hestonModel))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // semi-analytic Bates for European method = "Bates semi-analytic"; boost::shared_ptr<BatesProcess> batesProcess( new BatesProcess(flatTermStructure, flatDividendTS, underlyingH, volatility*volatility, 1.0, volatility*volatility, 0.001, 0.0, 1e-14, 1e-14, 1e-14)); boost::shared_ptr<BatesModel> batesModel(new BatesModel(batesProcess)); europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BatesEngine(batesModel))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Barone-Adesi and Whaley approximation for American method = "Barone-Adesi/Whaley"; americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BaroneAdesiWhaleyApproximationEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << "N/A" << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Bjerksund and Stensland approximation for American method = "Bjerksund/Stensland"; americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BjerksundStenslandApproximationEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << "N/A" << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Integral method = "Integral"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new IntegralEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Finite differences Size timeSteps = 801; method = "Finite differences"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new FDEuropeanEngine<CrankNicolson>(bsmProcess, timeSteps,timeSteps-1))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new FDBermudanEngine<CrankNicolson>(bsmProcess, timeSteps,timeSteps-1))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new FDAmericanEngine<CrankNicolson>(bsmProcess, timeSteps,timeSteps-1))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Jarrow-Rudd method = "Binomial Jarrow-Rudd"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<JarrowRudd>(bsmProcess,timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<JarrowRudd>(bsmProcess,timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<JarrowRudd>(bsmProcess,timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; method = "Binomial Cox-Ross-Rubinstein"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Additive equiprobabilities method = "Additive equiprobabilities"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Trigeorgis method = "Binomial Trigeorgis"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Trigeorgis>(bsmProcess,timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Trigeorgis>(bsmProcess,timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Trigeorgis>(bsmProcess,timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Tian method = "Binomial Tian"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Tian>(bsmProcess,timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Tian>(bsmProcess,timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Tian>(bsmProcess,timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Leisen-Reimer method = "Binomial Leisen-Reimer"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<LeisenReimer>(bsmProcess,timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<LeisenReimer>(bsmProcess,timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<LeisenReimer>(bsmProcess,timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Joshi method = "Binomial Joshi"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Joshi4>(bsmProcess,timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Joshi4>(bsmProcess,timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Joshi4>(bsmProcess,timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Monte Carlo Method: MC (crude) timeSteps = 1; method = "MC (crude)"; Size mcSeed = 42; boost::shared_ptr<PricingEngine> mcengine1; mcengine1 = MakeMCEuropeanEngine<PseudoRandom>(bsmProcess) .withSteps(timeSteps) .withAbsoluteTolerance(0.02) .withSeed(mcSeed); europeanOption.setPricingEngine(mcengine1); // Real errorEstimate = europeanOption.errorEstimate(); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Monte Carlo Method: QMC (Sobol) method = "QMC (Sobol)"; Size nSamples = 32768; // 2^15 boost::shared_ptr<PricingEngine> mcengine2; mcengine2 = MakeMCEuropeanEngine<LowDiscrepancy>(bsmProcess) .withSteps(timeSteps) .withSamples(nSamples); europeanOption.setPricingEngine(mcengine2); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Monte Carlo Method: MC (Longstaff Schwartz) method = "MC (Longstaff Schwartz)"; boost::shared_ptr<PricingEngine> mcengine3; mcengine3 = MakeMCAmericanEngine<PseudoRandom>(bsmProcess) .withSteps(100) .withAntitheticVariate() .withCalibrationSamples(4096) .withAbsoluteTolerance(0.02) .withSeed(mcSeed); americanOption.setPricingEngine(mcengine3); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << "N/A" << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // End test double seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; std::cout << " \nRun completed in "; if (hours > 0) std::cout << hours << " h "; if (hours > 0 || minutes > 0) std::cout << minutes << " m "; std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl; return 0; } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "unknown error" << std::endl; return 1; } }