EquityOption.cpp

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

/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <ql/qldefines.hpp>
#ifdef BOOST_MSVC
# include <ql/auto_link.hpp>
#endif
#include <ql/instruments/vanillaoption.hpp>
#include <ql/pricingengines/vanilla/binomialengine.hpp>
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
#include <ql/pricingengines/vanilla/baroneadesiwhaleyengine.hpp>
#include <ql/pricingengines/vanilla/bjerksundstenslandengine.hpp>
#include <ql/pricingengines/vanilla/batesengine.hpp>
#include <ql/pricingengines/vanilla/integralengine.hpp>
#include <ql/pricingengines/vanilla/fdeuropeanengine.hpp>
#include <ql/pricingengines/vanilla/fdbermudanengine.hpp>
#include <ql/pricingengines/vanilla/fdamericanengine.hpp>
#include <ql/pricingengines/vanilla/mceuropeanengine.hpp>
#include <ql/pricingengines/vanilla/mcamericanengine.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/utilities/dataformatters.hpp>
#include <boost/timer.hpp>
#include <iostream>
#include <iomanip>
using namespace QuantLib;
#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;
}
}