#
# Calibration of CIR85 model to Euribor Rates
#
#
import sys
sys.path.append('10_mcs')
import math
import numpy as np
np.set_printoptions(suppress=True,
formatter={'all': lambda x: '%7.6f' % x})
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['font.family'] = 'serif'
import scipy.interpolate as sci
from scipy.optimize import fmin
from CIR_zcb_valuation_gen import B
#
# Market Data: Eonia rate (01.10.2014) + Euribor rates
# Source: http://www.emmi-benchmarks.eu
# on 30. September 2014
#
t_list = np.array((1, 7, 14, 30, 60, 90, 180, 270, 360)) / 360.
r_list = np.array((-0.032, -0.013, -0.013, 0.007, 0.043,
0.083, 0.183, 0.251, 0.338)) / 100
factors = (1 + t_list * r_list)
zero_rates = 1 / t_list * np.log(factors)
r0 = r_list[0]
#
# Interpolation of Market Data
#
tck = sci.splrep(t_list, zero_rates, k=3) # cubic splines
tn_list = np.linspace(0.0, 1.0, 24)
ts_list = sci.splev(tn_list, tck, der=0)
de_list = sci.splev(tn_list, tck, der=1)
f = ts_list + de_list * tn_list
# forward rate transformation
def plot_term_structure():
plt.figure(figsize=(8, 5))
plt.plot(t_list, r_list, 'ro', label='rates')
plt.plot(tn_list, ts_list, 'b', label='interpolation', lw=1.5)
# cubic splines
plt.plot(tn_list, de_list, 'g--', label='1st derivative', lw=1.5)
# first derivative
plt.legend(loc=0)
plt.xlabel('time horizon in years')
plt.ylabel('rate')
plt.grid()
#
# Model Forward Rates
#
def CIR_forward_rate(opt):
''' Function for forward rates in CIR85 model.
Parameters
==========
kappa_r: float
mean-reversion factor
theta_r: float
long-run mean
sigma_r: float
volatility factor
Returns
=======
forward_rate: float
forward rate
'''
kappa_r, theta_r, sigma_r = opt
t = tn_list
g = np.sqrt(kappa_r ** 2 + 2 * sigma_r ** 2)
sum1 = ((kappa_r * theta_r * (np.exp(g * t) - 1)) /
(2 * g + (kappa_r + g) * (np.exp(g * t) - 1)))
sum2 = r0 * ((4 * g ** 2 * np.exp(g * t)) /
(2 * g + (kappa_r + g) * (np.exp(g * t) - 1)) ** 2)
forward_rate = sum1 + sum2
return forward_rate
#
# Error Function
#
def CIR_error_function(opt):
''' Error function for CIR85 model calibration. '''
kappa_r, theta_r, sigma_r = opt
if 2 * kappa_r * theta_r < sigma_r ** 2:
return 100
if kappa_r < 0 or theta_r < 0 or sigma_r < 0.001:
return 100
forward_rates = CIR_forward_rate(opt)
MSE = np.sum((f - forward_rates) ** 2) / len(f)
# print opt, MSE
return MSE
#
# Calibration Procedure
#
def CIR_calibration():
opt = fmin(CIR_error_function, [1.0, 0.02, 0.1],
xtol=0.00001, ftol=0.00001,
maxiter=300, maxfun=500)
return opt
#
# Graphical Results Output
#
def plot_calibrated_frc(opt):
''' Plots market and calibrated forward rate curves. '''
forward_rates = CIR_forward_rate(opt)
plt.figure(figsize=(8, 7))
plt.subplot(211)
plt.grid()
plt.ylabel('forward rate $f(0,T)$')
plt.plot(tn_list, f, 'b', label='market')
plt.plot(tn_list, forward_rates, 'ro', label='model')
plt.legend(loc=0)
plt.axis([min(tn_list) - 0.05, max(tn_list) + 0.05,
min(f) - 0.005, max(f) * 1.1])
plt.subplot(212)
plt.grid(True)
wi = 0.02
plt.bar(tn_list - wi / 2, forward_rates - f, width=wi)
plt.xlabel('time horizon in years')
plt.ylabel('difference')
plt.axis([min(tn_list) - 0.05, max(tn_list) + 0.05,
min(forward_rates - f) * 1.1, max(forward_rates - f) * 1.1])
plt.tight_layout()
def plot_zcb_values(p0, T):
''' Plots unit zero-coupon bond values (discount factors). '''
t_list = np.linspace(0.0, T, 20)
r_list = B([r0, p0[0], p0[1], p0[2], t_list, T])
plt.figure(figsize=(8, 5))
plt.plot(t_list, r_list, 'b')
plt.plot(t_list, r_list, 'ro')
plt.xlabel('time horizon in years')
plt.ylabel('unit zero-coupon bond value')
plt.grid()