Calibration Series for VSTOXX Volatility Options
import numpy as np
import pandas as pd
Load VSTOXX data from Eurex.
h5 = pd.HDFStore('./data/vstoxx_march_2014.h5', 'r')
vstoxx_index = h5['vstoxx_index']
vstoxx_futures = h5['vstoxx_futures']
vstoxx_options = h5['vstoxx_options']
h5.close()
VSTOXX index for first quarter of 2014.
%matplotlib inline
vstoxx_index['V2TX'].plot()
The VSTOXX futures data.
vstoxx_futures.info()
The VSTOXX options data.
vstoxx_options.info()
Function to calculate all relevant third Fridays.
import datetime as dt
import calendar
def third_friday(date):
day = 21 - (calendar.weekday(date.year, date.month, 1) + 2) % 7
return dt.datetime(date.year, date.month, day)
third_fridays = {}
for month in set(vstoxx_futures['EXP_MONTH']):
third_fridays[month] = third_friday(dt.datetime(2014, month, 1))
third_fridays
def get_option_selection(pricing_date, maturity, tol=0.125):
''' Function selects relevant options data. '''
forward = vstoxx_futures[(vstoxx_futures.DATE == pricing_date)
& (vstoxx_futures.MATURITY == maturity)]['PRICE'].values[0]
option_selection = \
vstoxx_options[(vstoxx_options.DATE == pricing_date)
& (vstoxx_options.MATURITY == maturity)
& (vstoxx_options.TYPE == 'C')
& (vstoxx_options.STRIKE > (1 - tol) * forward)
& (vstoxx_options.STRIKE < (1 + tol) * forward)]
return option_selection, forward
from dx import *
def get_option_models(pricing_date, maturity, option_selection):
''' Models traded options for given option_selection object. '''
me_vstoxx = market_environment('me_vstoxx', pricing_date)
initial_value = vstoxx_index['V2TX'][pricing_date]
me_vstoxx.add_constant('initial_value', initial_value)
me_vstoxx.add_constant('final_date', maturity)
me_vstoxx.add_constant('currency', 'EUR')
me_vstoxx.add_constant('frequency', 'W')
me_vstoxx.add_constant('paths', 25000)
csr = constant_short_rate('csr', 0.01)
# somewhat arbitrarily chosen here
me_vstoxx.add_curve('discount_curve', csr)
# parameters to be calibrated later
me_vstoxx.add_constant('kappa', 1.0)
me_vstoxx.add_constant('theta', 1.2 * initial_value)
me_vstoxx.add_constant('volatility', 1.0)
vstoxx_model = square_root_diffusion('vstoxx_model', me_vstoxx)
# square-root diffusion for volatility modeling
# mean-reverting, positive process
# option parameters and payoff
me_vstoxx.add_constant('maturity', maturity)
payoff_func = 'np.maximum(maturity_value - strike, 0)'
option_models = {}
for option in option_selection.index:
strike = option_selection['STRIKE'].ix[option]
me_vstoxx.add_constant('strike', strike)
option_models[option] = \
valuation_mcs_european_single(
'eur_call_%d' % strike,
vstoxx_model,
me_vstoxx,
payoff_func)
return vstoxx_model, option_models
def calculate_model_values(p0):
''' Returns all relevant option values.
Parameters
===========
p0 : tuple/list
tuple of kappa, theta, volatility
Returns
=======
model_values : dict
dictionary with model values
'''
kappa, theta, volatility = p0
vstoxx_model.update(kappa=kappa,
theta=theta,
volatility=volatility)
model_values = {}
for option in option_models:
model_values[option] = \
option_models[option].present_value(fixed_seed=True)
return model_values
i = 0
def mean_squared_error(p0):
''' Returns the mean-squared error given
the model and market values.
Parameters
===========
p0 : tuple/list
tuple of kappa, theta, volatility
Returns
=======
MSE : float
mean-squared error
'''
if p0[0] < 0 or p0[1] < 5. or p0[2] < 0 or p0[2] > 10.:
return 100
global i, option_selection, vstoxx_model, option_models, first, last
pd = dt.datetime.strftime(
option_selection['DATE'].iloc[0].to_pydatetime(),
'%d-%b-%Y')
mat = dt.datetime.strftime(
option_selection['MATURITY'].iloc[0].to_pydatetime(),
'%d-%b-%Y')
model_values = calculate_model_values(p0)
option_diffs = {}
for option in model_values:
option_diffs[option] = (model_values[option]
- option_selection['PRICE'].loc[option])
MSE = np.sum(np.array(option_diffs.values()) ** 2) / len(option_diffs)
if i % 50 == 0:
if i == 0:
print '%12s %13s %4s %6s %6s %6s --> %6s' % \
('pricing_date', 'maturity_date', 'i', 'kappa',
'theta', 'vola', 'MSE')
print '%12s %13s %4d %6.3f %6.3f %6.3f --> %6.3f' % \
(pd, mat, i, p0[0], p0[1], p0[2], MSE)
i += 1
return MSE
import scipy.optimize as spo
def get_parameter_series(pricing_date_list, maturity_list):
global i, option_selection, vstoxx_model, option_models, first, last
# collects optimization results for later use (eg. visualization)
parameters = pd.DataFrame()
for maturity in maturity_list:
first = True
for pricing_date in pricing_date_list:
option_selection, forward = \
get_option_selection(pricing_date, maturity)
vstoxx_model, option_models = \
get_option_models(pricing_date, maturity, option_selection)
if first is True:
# use brute force for the first run
i = 0
opt = spo.brute(mean_squared_error,
((0.5, 2.51, 1.), # range for kappa
(10., 20.1, 5.), # range for theta
(0.5, 10.51, 5.0)), # range for volatility
finish=None)
i = 0
opt = spo.fmin(mean_squared_error, opt,
maxiter=150, maxfun=250, xtol=0.0000001, ftol=0.0000001)
parameters = parameters.append(
pd.DataFrame(
{'pricing_date' : pricing_date,
'maturity' : maturity,
'initial_value' : vstoxx_model.initial_value,
'kappa' : opt[0],
'theta' : opt[1],
'sigma' : opt[2],
'MSE' : mean_squared_error(opt)}, index=[0,]),
ignore_index=True)
first = False
last = opt
return parameters
%%time
pricing_date_list = pd.date_range('2014/1/2', '2014/3/31', freq='B')
maturity_list = [third_fridays[7]]
parameters = get_parameter_series(pricing_date_list, maturity_list)
paramet = parameters.set_index('pricing_date')
paramet.tail()
Visualization of time series of calibrated parameter values. All MSEs pretty low (i.e. pretty good model fit).
%matplotlib inline
paramet[['kappa', 'theta', 'sigma', 'MSE']].plot(subplots=True, color='b', figsize=(10, 12))
plt.tight_layout()
Calibration results for the last calibration day.
index = paramet.index[-1]
opt = np.array(paramet[['kappa', 'theta', 'sigma']].loc[index])
option_selection = get_option_selection(index, maturity_list[0], tol=0.125)[0]
model_values = np.sort(np.array(calculate_model_values(opt).values()))[::-1]
import matplotlib.pyplot as plt
%matplotlib inline
fix, (ax1, ax2) = plt.subplots(2, sharex=True, figsize=(8, 8))
strikes = option_selection['STRIKE'].values
ax1.plot(strikes, option_selection['PRICE'], label='market quotes')
ax1.plot(strikes, model_values, 'ro', label='model values')
ax1.set_ylabel('option values')
ax1.grid(True)
ax1.legend(loc=0)
wi = 0.25
ax2.bar(strikes - wi / 2., model_values - option_selection['PRICE'],
label='market quotes', width=wi)
ax2.grid(True)
ax2.set_ylabel('differences')
ax2.set_xlabel('strikes')