Heston Helper Maturities Must Match Business-Day Conventions
Summary
The document diagnoses a mismatch between a European option’s net present value and the model value reported by a QuantLib Heston calibration helper. The example calibrates a Heston model to implied volatilities and then prices a put with what appear to be the same maturity and model settings, yet the two reported prices differ.
The accepted answer attributes the discrepancy to the helper measuring maturity in business days. It recommends calculating the maturity as the calendar’s business-day count between the valuation date and expiration, rather than using the raw calendar-day difference. With matching maturity conventions, the answer says the two values agree. This is a narrow implementation detail rather than a general pricing result; the document does not discuss other sources of pricing discrepancies or test alternative calendars and conventions.
Key ideas
- Heston calibration helpers interpret maturity using business-day conventions.
- A raw calendar-day difference can create a maturity mismatch with a vanilla option.
- Use the calendar’s business-day count to align the helper and option maturities.
- The example concerns a specific QuantLib setup and does not examine other possible causes of price differences.
Tags
Full text
# Difference between modelValue from HestonModelHelper and NPV() from VanillaOption
# Difference between modelValue from HestonModelHelper and NPV() from VanillaOption
I am trying to calibrate an Heston model and price vanilla option using Quantlib 1.15 and Python 2.7. I use the following code
```
import QuantLib as ql
#=====INITIALIZE INPUTS
day_count = ql.Actual365Fixed()
calendar = ql.TARGET()
calculation_date = ql.Date(2,1,2018)
ql.Settings.instance().evaluationDate = calculation_date
spot = 100.
dividend_yield = ql.QuoteHandle(ql.SimpleQuote(0.0))
risk_free_rate = 0.0
dividend_rate = 0.0
flat_ts = ql.YieldTermStructureHandle( ql.FlatForward(calculation_date, risk_free_rate, day_count) )
dividend_ts = ql.YieldTermStructureHandle( ql.FlatForward(calculation_date, dividend_rate, day_count) )
expiration_dates = ql.Date(2,1,2019)
strikes = [ 80, 90, 95, 97.5, 100, 102.5, 105, 110, 120 ]
data = [ 0.199634, 0.17565, 0.165468, 0.160864, 0.156803, 0.152733, 0.148948, 0.142499, 0.133428 ]
#=====CALIBRATE HESTON
v0 = 0.01; kappa = 0.2; theta = 0.02; rho = -0.75; sigma = 0.5;
process = ql.HestonProcess(flat_ts, dividend_ts,
ql.QuoteHandle(ql.SimpleQuote(spot)),
v0, kappa, theta, sigma, rho)
model = ql.HestonModel(process)
engine = ql.AnalyticHestonEngine(model, 0.0001, 1000)
date = expiration_dates
heston_helpers = []
for j, s in enumerate(strikes):
t = (date - calculation_date )
p = ql.Period(t, ql.Days)
sigma_implied = data[j]
helper = ql.HestonModelHelper(p, calendar, spot, s,
ql.QuoteHandle(ql.SimpleQuote(sigma_implied)),
flat_ts,
dividend_ts
)
helper.setPricingEngine(engine)
heston_helpers.append(helper)
lm = ql.LevenbergMarquardt(1e-8, 1e-8, 1e-8)
model.calibrate(heston_helpers, lm,
ql.EndCriteria(500, 50, 1.0e-8,1.0e-8, 1.0e-8))
v0 = model.v0(); rho = model.rho(); kappa = model.kappa(); theta = model.theta(); sigma = model.sigma();
#=====PRICING
payoff = ql.PlainVanillaPayoff(ql.Option.Put, 97.5)
exercise = ql.EuropeanExercise(date)
european_option = ql.VanillaOption(payoff, exercise)
european_option.setPricingEngine(engine)
print "Price using VanillaOption = %2.4f" %european_option.NPV()
print "Price using HestonModelHelper = %2.4f" %heston_helpers[3].modelValue()
```
The result printed is :
Price using VanillaOption = 5.7024
Price using HestonModelHelper = 6.4268
I don't understand why european_option.NPV() and heston_helpers[3].modelValue() provide different values even if I use the same parameters.
Thanks in advance for your help
## Answer by Cornholio (score 0, accepted)
https://quant.stackexchange.com/a/46213
The `HestonModelHelper` assumes that the maturity is measured in business days. So set
```
t = calendar.businessDaysBetween(calculation_date, date)
```
which gives you the same values.Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.