pythonquantlibquantlib-swig

How to advance the day in Quantlib


My understanding is that in order to advance the day, you do something like this:

ql.Settings.instance().evaluation_date = calculation_date + 1

However, when I execute the following code, I get the same value for the options:

import QuantLib as ql     

# option data
maturity_date = ql.Date(15, 1, 2016)
spot_price = 127.62
strike_price = 130
volatility = 0.20 # the historical vols for a year
dividend_rate =  0.0163
option_type = ql.Option.Call

risk_free_rate = 0.001
day_count = ql.Actual365Fixed()
#calendar = ql.UnitedStates()
calendar = ql.TARGET()

calculation_date = ql.Date(8, 5, 2015)
ql.Settings.instance().evaluationDate = calculation_date

# construct the European Option
payoff = ql.PlainVanillaPayoff(option_type, strike_price)
exercise = ql.EuropeanExercise(maturity_date)
european_option = ql.VanillaOption(payoff, exercise)

spot_handle = ql.QuoteHandle(
    ql.SimpleQuote(spot_price)
)

flat_ts = ql.YieldTermStructureHandle(
    ql.FlatForward(calculation_date, risk_free_rate, day_count)
)

dividend_yield = ql.YieldTermStructureHandle(
    ql.FlatForward(calculation_date, dividend_rate, day_count)
)

flat_vol_ts = ql.BlackVolTermStructureHandle(
    ql.BlackConstantVol(calculation_date, calendar, volatility, day_count)
)

bsm_process = ql.BlackScholesMertonProcess(spot_handle, 
                                           dividend_yield, 
                                           flat_ts, 
                                           flat_vol_ts)

european_option.setPricingEngine(ql.AnalyticEuropeanEngine(bsm_process))
bs_price = european_option.NPV()
print "The theoretical European price is ", bs_price   

payoff = ql.PlainVanillaPayoff(option_type, strike_price)
settlement = calculation_date
am_exercise = ql.AmericanExercise(settlement, maturity_date)
american_option = ql.VanillaOption(payoff, am_exercise)

#Once you have the american option object you can value them using the binomial tree method:

binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", 100)
american_option.setPricingEngine(binomial_engine)
print "The theoretical American price is ", american_option.NPV()                                  

ql.Settings.instance().evaluation_date = calculation_date + 1

print "The theoretical European price is ", european_option.NPV() 
print "The theoretical American price is ", american_option.NPV()

[idf@node3 python]$ python european_option.py 
The theoretical European price is  6.74927181246
The theoretical American price is  6.85858045945
The theoretical European price is  6.74927181246
The theoretical American price is  6.85858045945
[idf@node3 python]$ 

EDIT

Changed the code to as per suggested below, but the day change makes no difference in the computation.

[idf@node3 python]$ python advance_day.py 
The theoretical European price is  6.74927181246
The theoretical American price is  6.85858045945
The theoretical European price is  6.74927181246
The theoretical American price is  6.85858045945
[idf@node3 python]$ 

Here are the code changes per the suggestions.

import QuantLib as ql     

# option data
maturity_date = ql.Date(15, 1, 2016)
spot_price = 127.62
strike_price = 130
volatility = 0.20 # the historical vols for a year
dividend_rate =  0.0163
option_type = ql.Option.Call

risk_free_rate = 0.001
day_count = ql.Actual365Fixed()
#calendar = ql.UnitedStates()
calendar = ql.TARGET()

calculation_date = ql.Date(8, 5, 2015)
ql.Settings.instance().evaluationDate = calculation_date

# construct the European Option
payoff = ql.PlainVanillaPayoff(option_type, strike_price)
exercise = ql.EuropeanExercise(maturity_date)
european_option = ql.VanillaOption(payoff, exercise)

spot_handle = ql.QuoteHandle(
    ql.SimpleQuote(spot_price)
)

flat_ts = ql.YieldTermStructureHandle(
    ql.FlatForward(0, calendar, risk_free_rate, day_count)
)

dividend_yield = ql.YieldTermStructureHandle(
    ql.FlatForward(0, calendar, dividend_rate, day_count)
)

flat_vol_ts = ql.BlackVolTermStructureHandle(
    ql.BlackConstantVol(0, calendar, volatility, day_count)
)

bsm_process = ql.BlackScholesMertonProcess(spot_handle, 
                                           dividend_yield, 
                                           flat_ts, 
                                           flat_vol_ts)

european_option.setPricingEngine(ql.AnalyticEuropeanEngine(bsm_process))
bs_price = european_option.NPV()
print "The theoretical European price is ", bs_price   

payoff = ql.PlainVanillaPayoff(option_type, strike_price)
settlement = calculation_date
am_exercise = ql.AmericanExercise(settlement, maturity_date)
american_option = ql.VanillaOption(payoff, am_exercise)

#Once you have the american option object you can value them using the binomial tree method:

binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", 100)
american_option.setPricingEngine(binomial_engine)
print "The theoretical American price is ", american_option.NPV()                                  

ql.Settings.instance().evaluation_date = calculation_date + 1
# Also tried calendar.advance(calculation_date,1,ql.Days)

print "The theoretical European price is ", european_option.NPV() 
print "The theoretical American price is ", american_option.NPV()

Solution

  • The calculation date is not all there is to it. You're setting up your curves so that their reference date is fixed (that is, you're calling the constructor that takes a date; see this post for details, or this video for an example).

    If you specify a reference date, that's used independently of the calculation date; that's because they aren't necessarily the same (for instance, you might want an interest-rate curve to be based on the spot date instead of today's date). Therefore, even when you change the calculation date, the volatility and rates returned from the curves will still be relative to their reference date, which hasn't moved.

    To get the effect you want, you can create the curves so that they move with the evaluation date; for instance, instead of

    ql.FlatForward(calculation_date, risk_free_rate, day_count)
    

    you can use

    ql.FlatForward(0, calendar, risk_free_rate, day_count)
    

    which means that the reference date is specified as "0 business days after the calculation date", or in other words, as the calculation date. The volatility curve has a similar constructor.