Introducing QuantLib: American Option Pricing with Dividends

In this post, the last I’m planning to write on the subject of option pricing, I will cover the mechanics of valuing options subject to American exercise rules, taking into account dividend payments expected prior to option expiration.  Specifically, I will show how to calculate the value of a series of Intel (INTC) Calls expiring on Feb 21, 2014 where a dividend of .22 is expected to be paid on Feb 5, 2014.

An American option, unlike a European option, can be exercised anytime before the option expires. This privilege carries with it a premium, making American options generally more expensive than their European counterparts, all else being equal. A dividend represents an expected future cash flow which reduces the value of the underlying forward price, which in turn affects the value of the corresponding options. More specifically, dividends reduce the value of a call option and increase the value of a put option, again, all else being equal, compared to the same option without a dividend.

In previous posts, I’ve relied on Black-Scholes as my theoretical option pricing model. Black-Scholes, strictly speaking, does not support pricing American options with dividends without making an adjustment to the forward price to account for the drop in the underlying price on the ex-dividend date. As such, it is common practice to use lattice models, such as binomial trees or finite differences to price American options. Alternatively, if speed is a principal concern, one of the American analytic approximations, such as Barone-Adesi-Whaley might be a good choice. Fortunately, QuantLib provides support for many different types of American option pricing models, which tend to vary along dimensions of speed, numerical stability and complexity while producing very nearly the same option valuations.

In this post, I’ll utilize the Crank-Nicolson Finite Differences lattice model as the pricing engine, which is implemented by the FDAmericanEngine<CrankNicolson> class. Also, rather than presenting an overly simplified example that assumes flat term structures for interest rates, dividends and volatility, I’ll bootstrap the required dividend, interest rate and volatility curves from Interactive Brokers market data (see screenshot below) and current LIBOR rates. This should give you a good feel for what’s required to value options in the ‘real world’.


So let’s start by building the necessary market data curves. First, I’ll present a function that will be called to construct the INTC volatility smile for the Feb 2014 calls. (For an introduction to volatility smiles see my earlier post here.)  The function relies on the QuantLib BlackVarianceSurface class, which supports strike-level volatilities. Here is the code:

#include <cstdlib>
#include <iostream>
#include <ql/quantlib.hpp>

using namespace QuantLib;

boost::shared_ptr<BlackVolTermStructure> bootstrapVolatilityCurve(const Date& 
evaluationDate, const std::vector<Real>& strikes, 
const std::vector<Volatility>& vols, const Date& expiration) {

    Calendar calendar = UnitedStates(UnitedStates::NYSE);

    std::vector<Date> expirations;

    Matrix volMatrix(strikes.size(), 1);

    //implied volatilities from Interactive Brokers
    for (int i=0; i< vols.size(); ++i) {
        volMatrix[i][0] = vols[i];

    return boost::shared_ptr<BlackVolTermStructure>(new BlackVarianceSurface(evaluationDate, calendar,
expirations, strikes, volMatrix, Actual365Fixed()));		

Next, let’s bootstrap the LIBOR zero rate curve, which supplies the necessary interest rates for discounting option payoffs.  The USD LIBOR rates were obtained from the Web site The code follows below:

#include <cstdlib>
#include <iostream>
#include <ql/quantlib.hpp>
#include <boost/assign/std/vector.hpp>

using namespace QuantLib;

boost::shared_ptr<YieldTermStructure> bootstrapLiborZeroCurve(const Date& evaluationDate) {

    using namespace boost::assign;

    //bootstrap from USD LIBOR rates;
    IborIndex libor = USDLiborON();  
    const Calendar& calendar = libor.fixingCalendar();
    const Date& settlement = calendar.advance(evaluationDate, 2, Days);
    const DayCounter& dayCounter = libor.dayCounter();       
    Settings::instance().evaluationDate() = settlement;

    //rates obtained from 
    Rate overnight = .10490/100.0;
    Rate oneWeek = .12925/100.0;
    Rate oneMonth = .16750/100.0;
    Rate twoMonths = .20700/100.0;
    Rate threeMonths = .23810/100.0;
    Rate sixMonths = .35140/100.0;
    Rate twelveMonths = .58410/100.0;

    std::vector<boost::shared_ptr<RateHelper>> liborRates;
    liborRates += boost::shared_ptrRateHelper>(new DepositRateHelper(overnight,
        boost::shared_ptr<IborIndex>(new USDLiborON()))); 
    liborRates += boost::shared_ptr<RateHelper>(new DepositRateHelper(oneWeek,
        boost::shared_ptr<IborIndex>(new USDLibor(Period(1, Weeks)))));
    liborRates += boost::shared_ptr<RateHelper>(new DepositRateHelper(oneMonth,
        boost::shared_ptr<IborIndex>(new USDLibor(Period(1, Months)))));
    liborRates += boost::shared_ptr<RateHelper>(new DepositRateHelper(twoMonths,
	boost::shared_ptr<IborIndex>(new USDLibor(Period(2, Months)))));
    liborRates += boost::shared_ptr<RateHelper>(new DepositRateHelper(threeMonths,
	boost::shared_ptr<IborIndex>(new USDLibor(Period(3, Months)))));
    liborRates += boost::shared_ptr<RateHelper>(new DepositRateHelper(sixMonths,
	boost::shared_ptr<IborIndex>(new USDLibor(Period(6, Months)))));
    liborRates += boost::shared_ptr<RateHelper>(new DepositRateHelper(twelveMonths,
	boost::shared_ptr<IborIndex>(new USDLibor(Period(12, Months)))));

    //use cubic interpolation
    boost::shared_ptr<YieldTermStructure> yieldCurve = 
    boost::shared_ptr<YieldTermStructure>(new PiecewiseYieldCurve<ZeroYield, 
Cubic>(settlement, liborRates, dayCounter));

    return yieldCurve;	

The final curve that we’ll need to generate provides the annualized dividend yield for Intel.  Here is the code that bootstraps the dividend curve:

#include <cstdlib>
#include <iostream>
#include <ql/quantlib.hpp>
#include <boost/format.hpp>

using namespace QuantLib;

boost::shared_ptr<ZeroCurve> bootstrapDividendCurve(const Date& evaluationDate, 
const Date& expiration, const Date& exDivDate, Real underlyingPrice, Real annualDividend) {

    UnitedStates calendar(UnitedStates::NYSE);
    Settings::instance().evaluationDate() = evaluationDate;
    Real settlementDays = 2.0;

    Real dividendDiscountDays = (expiration - evaluationDate) + settlementDays;
    std::cout << boost::format("Dividend discounting days: %d") %
 dividendDiscountDays << std::endl;
    Rate dividendYield = (annualDividend/underlyingPrice) * dividendDiscountDays/365;

    // ex div dates and yields
    std::vector<Date> exDivDates;
    std::vector<Rate&gt dividendYields;

    //last ex div date and yield
    exDivDates.push_back(calendar.advance(exDivDate, Period(-3, Months), 
ModifiedPreceding, true));

    //currently announced ex div date and yield

    //next ex div date (projected) and yield
    Date projectedNextExDivDate = calendar.advance(exDivDate, Period(3, Months), 
ModifiedPreceding, true); 
    std::cout << boost::format("Next projected ex div date for INTC: %s") % 
projectedNextExDivDate << std::endl;

    return boost::shared_ptr<ZeroCurve>(new ZeroCurve(exDivDates, dividendYields, 
ActualActual(), calendar));


Now that we have the code to bootstrap all of our market data curves, the pricing code to value the Intel Call options is pretty straightforward:

#include <cstdlib>
#include <iostream>
#include <ql/quantlib.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <boost/format.hpp>
#include <boost/assign/std/vector.hpp>

using namespace QuantLib;

BOOST_AUTO_TEST_CASE(testAmericanOptionPricingWithDividends) {

    using namespace boost::assign;

    //set up calendar/dates
    Calendar calendar = UnitedStates(UnitedStates::NYSE);
    Date today(15, Nov, 2013);
    Real settlementDays = 2;
    Date settlement = calendar.advance(today, settlementDays, Days);
    Settings::instance().evaluationDate() = today;

    //define options to price
    Option::Type type(Option::Call);
    Real underlying = 24.52;

    // INTC Feb 21 strikes
    std::vector strikes;
    strikes += 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0;

    // volatility for each strike above
    std::vector vols;
    vols += .23356, .21369, .20657, .20128, .19917, .19978, .20117;

    // Feb 2014 expiration      
    Date expiration(21, Feb, 2014);

    //INTC dividend information - .90 per year paid quarterly
    Date exDivDate(5, Feb, 2014);
    Real annualDividend = .90;

    //build yield term structure from LIBOR rates 
    Handle<YieldTermStructure> yieldTermStructure(bootstrapLiborZeroCurve(today));

    //build dividend term structure
    Handle<YieldTermStructure> dividendTermStructure(bootstrapDividendCurve(today, 
expiration, exDivDate, underlying, annualDividend));

    //build vol term structure 
    Handle<BlackVolTermStructure> volatilityTermStructure(bootstrapVolatilityCurve(today, 
strikes, vols, expiration));

    //instantiate BSM process
    Handle<Quote> underlyingH(boost::shared_ptr(new SimpleQuote(underlying)));
    boost::shared_ptr<BlackScholesMertonProcess> bsmProcess(new BlackScholesMertonProcess(underlyingH, 
dividendTermStructure, yieldTermStructure, volatilityTermStructure));

    //instantiate pricing engine
    boost::shared_ptr<PricingEngine> pricingEngine(new FDAmericanEngine<CrankNicolson>
(bsmProcess, 801, 800));

    //price the options
    boost::shared_ptr<Exercise> americanExercise(new AmericanExercise(settlement, expiration));
    for (Real strike: strikes) {
        boost::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, strike));
	VanillaOption americanOption(payoff, americanExercise);
	Real tv = americanOption.NPV();
	std::cout << boost::format("Intel %s %.2f %s value is: %.2f") % 
expiration % strike % type % tv  << std::endl;
	std::cout << boost::format("Delta: %.4f") % << std::endl;
	std::cout << boost::format("Gamma: %.4f") % americanOption.gamma() << std::endl;

When the code is run it produces the following option values and Greeks for the 22 through 28 strikes:

Intel February 21st, 2014 22.00 Call value is: 2.77
Delta: 0.8287
Gamma: 0.0883
Intel February 21st, 2014 23.00 Call value is: 1.94
Delta: 0.7312
Gamma: 0.1230
Intel February 21st, 2014 24.00 Call value is: 1.28
Delta: 0.5922
Gamma: 0.1486
Intel February 21st, 2014 25.00 Call value is: 0.78
Delta: 0.4380
Gamma: 0.1544
Intel February 21st, 2014 26.00 Call value is: 0.45
Delta: 0.2948
Gamma: 0.1364
Intel February 21st, 2014 27.00 Call value is: 0.24
Delta: 0.1835
Gamma: 0.1046
Intel February 21st, 2014 28.00 Call value is: 0.13
Delta: 0.1068
Gamma: 0.0720

The option values printed above are within or just outside the bid-ask spread for each strike in the Interactive Brokers screenshot. Deviations of a penny or two are likely due to differences in the model employed by Interactive Brokers versus the QuantLib Crank-Nicolson model, differences in input market data, or possibly even a trading opportunity (i.e. “edge”). In fact, recognizing, adjusting for and/or exploiting such deviations between theoretical option values and the market is at the core of modern option pricing and trading.

With that, I’ll bring this somewhat lengthy post to a close. I very much hope that you enjoyed it and learned something about pricing American options on dividend-paying stocks using QuantLib. Please feel free to submit your comments or questions below. I’ll do my best to get back to you as soon as I can. And, of course, have fun with QuantLib!


About Mick Hittesdorf

Financial Systems Architect, Analyst and Developer
This entry was posted in QuantLib and tagged , , , , , , . Bookmark the permalink.

8 Responses to Introducing QuantLib: American Option Pricing with Dividends

  1. Asif says:

    thanks for the insight Mick, one question, it seems as though the options with the lowest strike price have the highest overall price for calls, forgive me but my assumption was that for call options the higher the strike price the higher the cost?

    • The higher the strike, the more ‘out-of-the-money’ a call is, so the less likely it is to have value at expiration. As such, the price of a call, which is the expected value of its payoff at expiration, is less than a call that is less ‘out-of-the-money’ (has a lower strike price). To make this more clear, remember that the payoff for a call at expiration is max(S-K,0), where S is the asset price and K is the strike.

      Thanks for the question. Mick

      • James says:

        Hey Mick,

        Do you have any C# quantlib code for American Options pricing with discrete dividends and all the greeks?

        Best Regards,

      • Sorry but I don’t code in C# very much. It shouldn’t be too difficult though to translate the code I’ve written to price American options with dividends to C# as C++ and C# are not terribly dissimilar. Good luck!

  2. Asif says:

    thanks Mick, this article really has helped me gain more of an understanding of the world of the quant

  3. Asif says:

    sorry one last question on this topic, would the ideal purchase for a call option be one which is most likely to have value at expiration? Or do the strategies vary?

    • Asif,

      Thanks for the question. Very few traders actually hold an option they’ve purchased all the way to expiration. Most are looking to profit as quickly as possible from a mispricing in the option’s volatility or a big move in the underlying, which pretty much amounts to the same thing. With that said, there are many times when a trader is left holding an option at expiration for some reason or another. So the short answer to your question is ‘no’, it is not always ‘ideal’ to purchase a call option with a high probability of being in the money at expiration. However, there are strategies which seek to profit from buying or selling deep ‘in the money’ options, often as an alternative to trading the underlying itself.

      I hope this answers your question. Thanks for reading my blog.


  4. Thanks for a marvelous posting! I definitely enjoyed reading
    it, you happen to be a great author. I will be sure to bookmark your blog and definitely will come back very soon.
    I want to encourage you to ultimately continue your great work, have a nice afternoon!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s