BDT二叉树无套利算法的构建
我们假设零息债券如下表所示。表中,0.20代表的是一年即期利率的波动率,依次类推,0.19代表的是两年即期利率的波动率。
image.png
BDT模型假设了利率服从对数正态分布,导致波动率是利率取自然对数之后的波动率。因此有:
image.png
除了上面式子,BDT和其它无套利模型构成的二叉树定价模型是完全一样的。
步骤1:构建第一阶段利率二叉树。
二叉树根部数据就是0.1,因此不用做任何修改。对于第一阶段二叉树的两个端点r_H,r_L需要使用两年期的即期利率和其对应的波动率,方程为:
image.png
步骤2:构建第二阶段利率二叉树:
需要使用三年期即期利率以及相应的波动率。首先需要解决的是当年的三年期即期利率在一年后变成两年期的即期利率r_H3和r_L3。
根据风险中性定价方程和波动率方程分别为:
步骤3:构建第三阶段利率二叉树
同上需要使用四年的零息债券和其对应的波动率0.17构建方程:
image.png
image.png
image.png
更多阶二叉树依此类推,以上就是基于无套利定价二叉树算法。
import math
import pandas as pd
import numpy as np
import sympy as sp
import os
data = pd.read_excel("BDT.xlsx")
N = data.shape[0]
p_tilda = 1 / 2
data.index = np.arange(1, N + 1)
data['Rate'] = (data['Rate']) / 10
T = data['Term']
T.index = np.arange(0, N)
T = np.array(T)
T1 = T / 360
"probability matrix"
P = np.full((N + 1, N + 1), p_tilda)
"rates matrix"
R = np.zeros((N, N))
R[0, 0] = data['Rate'][1]
AD = np.zeros((N, N, N, N))
AD[0, 0, 0, 0] = 1
TIME_ZERO_BOND_PRICES = []
def martingale_pricing(i, n, j):
if i == j - 1:
ad = P[n - 1, j - 1] / (1 + R[n - 1, j - 1]) ** (T1[n - 1])
elif i == j:
ad = (1 - P[n - 1, j]) / (1 + R[n - 1, j]) ** (T1[n - 1])
else:
ad = 0
AD[n - 1, i, n, j] = ad
def jfi(n, i, m, j):
if j == 0:
ad = AD[n, i, m - 1, j] * (1 - P[m - 1][j]) / (1 + R[m - 1][j]) ** (T1[m - 1])
elif 0 < j & j < m:
ad = AD[n, i, m - 1, j] * (1 - P[m - 1][j]) / (1 + R[m - 1][j]) ** (T1[m - 1]) + AD[n, i, m - 1, j - 1] * (
P[m - 1][j - 1]) / (1 + R[m - 1][j - 1]) ** (T1[m - 1])
elif j == m:
ad = AD[n, i, m - 1, j - 1] * (P[m - 1][j - 1]) / (1 + R[m - 1][j - 1]) ** (T1[m - 1])
AD[n, i, m, j] = ad
def rate_vol(x):
return (1 / 2) * math.log(x)
yield_tree = np.zeros((2, N))
for n in range(1, N):
r = sp.Symbol('r')
sigma = sp.Symbol('sigma')
for j in range(0, n + 1):
jfi(0, 0, n, j)
bond_price = pow(1 + data['Rate'][n + 1], -(n + 1) * T1[n]) #
TIME_ZERO_BOND_PRICES.append(bond_price)
if n == 1:
rate_volatility = math.exp(2 * data['VAR'][n + 1] / math.sqrt(1 / T1[n]))
B = sum([AD[0, 0, n, j] / (1 + r * pow(rate_volatility, j)) for j in range(0, n + 1)])
rate = np.array([sp.re(x) for x in sp.solvers.solve(bond_price - B, r)])
rate = rate[rate > 0][0]
else:
B = sum([AD[0, 0, n, j] / (1 + r * pow(sigma / math.sqrt(1 / T1[n]), j)) for j in range(0, n + 1)])
if n > 1:
Y = []
for i in range(0, 2):
p = 0
for j in range(0, n + 1):
martingale_pricing(i, n, j)
if n > 2:
jfi(1, i, n, j)
p += (AD[1, i, n, j]) / (1 + r * pow(sigma / math.sqrt(1 / T1[n - 1]), j)) # P[1,i,n+1]
Y.append(pow(p, -(1 / n)) - 1)
solution = sp.nsolve(
[Y[0] * math.exp(2 * data['VAR'][n + 1] / math.sqrt(1 / T1[n - 1])) - Y[1], bond_price - B],
[r, sigma], [0.08, 0.01])
rate = solution[0]
rate_volatility = solution[1]
if n > 1:
for i in range(0, 2):
p = 0 #
for j in range(0, n + 1):
p += (AD[1, i, n, j]) / (1 + rate * pow(rate_volatility, j)) # P[1,i,n+1]
yield_tree[i, n] = pow(p, -(1 / n)) - 1
print('Time {} rate volatility is {}'.format(n, rate_vol(rate_volatility)))
R[n][0] = rate # save the rate to the tree
for j in range(1, n + 1):
R[n][j] = R[n][0] * pow(rate_volatility, j)
R = R * 10
I = []
for row in R:
I.extend(row[np.nonzero(row)][::-1])
def get_list_by_size(row_size, L="L", R="H"):
try:
row_size = int(row_size)
except Exception:
raise ValueError("row_size:应该为数å—")
H = ['0']
for i in range(1, row_size + 1):
H.extend([size * R + L * (i - size) for size in range(i, -1, -1)])
return H
H = get_list_by_size(N - 1)
Z = {'EdgeLabel': H,
'Rate': I}
frame = pd.DataFrame(Z)
print(frame)
输出结果:
image.png
网友评论