Vasicek_and_CIR_calibration.ipynb

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize

Untitled

Untitled

Untitled

def zero_coupon(tau, r0, kappa, theta, sigma, model): #tau=(T-t),kappa=a
    #tau = tau(:); 잔존만기
    if model == 'Vasicek':
        B = (1 - np.exp(-kappa * tau)) / kappa
        A = (theta - sigma**2 / (2 * kappa**2)) * (B - tau) - (sigma**2 / (4 * kappa)) * B**2
    elif model == 'CIR':
        g = np.sqrt(kappa**2 + 2 * sigma**2)  
        tmp = 2 * kappa * theta / sigma**2
        tmp1 = kappa * tau / 2
        tmp2 = g * tau / 2
        
        A = tmp * np.log(np.exp(tmp1) / (np.cosh(tmp2) + (kappa / g) * np.sinh(tmp2)))
        #B = 2. / (kappa + g * (1. / np.tanh(g * tau / 2)))
        tanh = np.tanh(g * tau / 2)
        B = 2. * tanh / (kappa * tanh + g)
    else:
        print('zero_coupon: model must be "Vasicek" or "CIR"!')
        return -1

    #n1 = len(r0)
    n2 = len(A)
    #A_ = repmat(A,1,n1)
    A_ = A
    #B_ = repmat(B,1,n1)
    B_ = B

    #r_ = repmat(r0,n2,1)
    r_ = np.repeat(r0, n2) #r0를 n2번 반복시킨 배열을 생성

    p = np.exp(A_ - B_ * r_)
    return p

def swapRates(tau, p, mat):
    
    tmax = mat[-1]
    
    ttemp = np.arange(0.5, tmax + 0.5, 0.5)
    ptemp = np.interp(ttemp, tau, p) 
    #선형보간 x 에서 평가 된 주어진 이산 데이터 포인트 ( xp , fp )가 
    #있는 함수에 대한 1 차원 조각 별 선형 보간을 반환합니다 .

    dis = np.cumsum(ptemp)
    #np.cumsum: 각 원소들의 누적 합을 나타냄
    #dis = dis(:);

    # linear interpolation
    pmat = np.interp(mat, tau, p)

    index = (2 * mat).astype(int) - 1
    S = 100 * 2 * (1 - pmat) / dis[index]
    
    return S

def liborRates(tau, p, mat):
    pmat = np.interp(mat, tau, p)
    L = 100 * (1. / pmat - 1) / mat
    return L

def objFunc1(params, tau, LIBOR, SWAP, model):
    # unpack params
    r0 = params[0]
    kappa = params[1]
    theta = params[2]
    sigma = params[3]

    p = zero_coupon(tau, r0, kappa, theta, sigma, model)
    # now that we have zero-coupon bond prices p(t,T)
    # now it is time to calculate MODEL LIBOR rates and SWAP rates
    S = swapRates(tau, p, SWAP[:,0])
    L = liborRates(tau, p, LIBOR[:,0])

    # the goal is to minimize the distance between model rates and market rates
    rel1 = (S - SWAP[:,1]) / SWAP[:,1]
    rel2 = (L - LIBOR[:,1]) / LIBOR[:,1]

    #rel1 = (S-SWAP(:,2))
    #rel2 = (L-LIBOR(:,2))

    #mae = (sum(abs(rel1))+sum(abs(rel2)))
    mae = np.sum(rel1**2) + np.sum(rel2**2)
    
    return mae

def calibration(fun, param_0, tau, LIBOR, SWAP, model):
     # change tolerance
    opt = {'maxiter':1000, 'maxfev':5e3}
    sol = minimize(objFunc1, params1, args = (tau, LIBOR, SWAP, model), method='Nelder-Mead', options=opt)
    print(sol.message)
    par = np.array(sol.x)
    print('parameters = ' + str(par))
    r_star = par[0]
    kappa_star = par[1]   #알파
    theta_star = par[2]    #베타
    sigma_star = par[3]   #시그마는 왜 나오는거지 횡단면 자료...? curve fitting한것 같기도
    p = zero_coupon(tau, r_star, kappa_star, theta_star, sigma_star, model)
    L = liborRates(tau, p, LIBOR[:, 0])
    S = swapRates(tau, p, SWAP[:, 0])
    return p, L, S

Data

LIBOR = np.array([[1/12, 1.49078, 2.2795],
    [2/12, 1.52997, 2.33075],
    [3/12, 1.60042, 2.43631],
    [6/12, 1.76769, 2.63525],
    [12/12, 2.04263, 2.95425]])

SWAP = np.array([[2, 2.013, 3.0408],
    [3, 2.1025, 3.1054],
    [5, 2.195, 3.1332],
    [7, 2.2585, 3.1562],
    [10, 2.3457, 3.199],
    [15, 2.4447, 3.2437],
    [30, 2.5055, 3.227]])

Parameters

# we are going out 30 years every month (1/12)
tau = np.arange(0, 30 + 1/12, 1/12) #np.arange(시작점, 끝점, step size)

# initial starting point
# r0 = 0.02 kappa0 = 2 theta0 = 0.5 sigma0 = 0.1
params = np.array([0.02, 1.0, 0.05, 0.3]);

params1 = np.array([0.25, 5, 0.2, 0.1]);
params2 = np.array([0.25, 5, 0.2, 0.1]);

Vasicek (2 dates)

model = 'Vasicek'
# first date
whichOne = 1
p11, L11, S11 = calibration(objFunc1, params1, tau, LIBOR[:,[0,whichOne]], SWAP[:,[0,whichOne]], model)

Optimization terminated successfully. parameters = [0.01445667 0.9474955 0.02918339 0.0952191 ]

# second date
whichOne = 2
p12, L12, S12 = calibration(objFunc1, params1, tau, LIBOR[:,[0,whichOne]], SWAP[:,[0,whichOne]], model)

Optimization terminated successfully. parameters = [2.13256819e-02 2.82039824e+00 3.21754393e-02 1.27705822e-05]

CIR