1
$\begingroup$

I'm trying to use the QuantLib library to price American options that pay discrete dividends.

The call options are priced with good accuracy (generally <0.1% error), however the same inputs for a put option have a large discrepancy (>50% error) compared to the expected values (obtained from Hoadley's excel software), especially for deep out-the-money put options.

The function I am using is defined as so:

import QuantLib as ql

def get_option_price(
    valuation_date = [1,7,2021],
    expiry_date = [1,7,2022],
    strike_price = 70,
    underlying_price = 100,
    volatility = 0.30,
    risk_free_rate = 0.06,
    dividends = [],
    is_american = True,
    is_call = True
    ):
    
    div_dates = []
    div_values = []
    for div in dividends:
        date = div[0]
        value = div[1]
        div_dates.append(ql.Date(*date))
        div_values.append(value)
    
    # Reformat dates from list into QL date format
    valuation_date = ql.Date(*valuation_date)
    expiry_date = ql.Date(*expiry_date)
    ql.Settings.instance().setEvaluationDate(valuation_date)
    day_count = ql.Actual365Fixed()
    calendar = ql.Australia()
    
    # Reformat prices and rates from list into QL format
    underlying_price = ql.QuoteHandle(ql.SimpleQuote(underlying_price))
    risk_free_rate = ql.YieldTermStructureHandle(
        ql.FlatForward(valuation_date, risk_free_rate, day_count))
    volatility = ql.BlackVolTermStructureHandle(
        ql.BlackConstantVol(valuation_date, calendar, volatility, day_count))
    
    # Create option
    if is_call:
        payoff = ql.PlainVanillaPayoff(ql.Option.Call, strike_price)
    else:
        payoff = ql.PlainVanillaPayoff(ql.Option.Put, strike_price)
    if is_american:
        exercise = ql.AmericanExercise(valuation_date, expiry_date)
    else:
        exercise = ql.EuropeanExercise(expiry_date)
    option = ql.DividendVanillaOption(payoff, exercise, div_dates,
                                          div_values)
    
    # Black Scholes process
    process = ql.BlackScholesProcess(underlying_price,
                                          risk_free_rate,
                                          volatility)
    
    # Create option's pricing engine
    precision_steps = 500
    engine = ql.FdBlackScholesVanillaEngine(process, precision_steps, precision_steps - 1)
    option.setPricingEngine(engine)
    
    # Price the option
    return option.NPV()

Here are some sample inputs/outputs for call options that generally do as I expect:

price = get_option_price(dividends = [[[1,7,2021], 30]], is_call = True)
print(price)
>> 29.999999999999044
# (Expected: 30.0004)

price = get_option_price(dividends = [[[1,1,2022], 30]], is_call = True)
print(price)
>> 32.32516446072868
# (Expected: 32.3034)

price = get_option_price(dividends = [[[30,6,2022], 30]], is_call = True)
print(price)
>> 34.969800332368024
# (Expected: 34.9839)

Here are the sample inputs/outputs for put options that are inaccurate:

price = get_option_price(dividends = [[[1,7,2021], 30]], is_call = False)
print(price)
>> 6.670681910486139
# (Expected: 6.6734)

price = get_option_price(dividends = [[[1,1,2022], 30]], is_call = False)
print(price)
>> 8.262009670450684
# (Expected: 6.2855)

price = get_option_price(dividends = [[[30,6,2022], 30]], is_call = False)
print(price)
>> 8.882930513922709
# (Expected: 5.6245)

The inaccuracy generally worsens as the dividend date approaches expiry, or if the dividend value increases.

  1. Is there something wrong with my implementation, or is this a limitation of the pricing engine I am using? If it's the latter:
  2. Are there alternative pricing engines that can handle discrete dividends? (as far as I'm aware QuantLib's BinomialVanillaEngine doesn't support discrete dividend scheduling)
$\endgroup$
3
$\begingroup$

The pricing engine FdBlackScholesVanillaEngine supports two types of discrete dividend models, the so called spot model and the escrowed dividend model. The default is the spot dividend model. I have to admit that I do not know your reference "Hoadley's excel software" in detail but looking at the results it seems that this software is using the escrowed dividend model. You can switch to the latter one in QuantLib with

engine = ql.FdBlackScholesVanillaEngine.make(
    process,
    tGrid=precision_steps,
    xGrid=precision_steps - 1,
    cashDividendModel=ql.FdBlackScholesVanillaEngine.Escrowed
)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.