Python

Calibration of CIR85 model to Euribor Rates

#
# 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()