Блог им. imagic
import requests import pandas as pd import numpy as np import matplotlib.pyplot as plt
# URL для API MOEX, данные по ZCYC (zero coupon yield curve) url = "https://iss.moex.com/iss/engines/stock/zcyc/securities.json" # Запрос на получение данных response = requests.get(url) data = response.json() # Извлекаем данные из секции 'params' columns = data['params']['columns'] values = data['params']['data'] # Преобразуем в DataFrame df = pd.DataFrame(values, columns=columns) # Выбираем нужные столбцы: B1, B2, B3, T1, G1, ..., G9 df_selected = df[['tradedate', 'tradetime', 'B1', 'B2', 'B3', 'T1', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'G9']] # Извлекаем параметры для функции GT из df_selected beta0 = df_selected['B1'].values[0] beta1 = df_selected['B2'].values[0] beta2 = df_selected['B3'].values[0] tau = df_selected['T1'].values[0] g_values = df_selected[['G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'G9']].values[0].tolist()
# Функция для расчета бескупонной доходности по методу Нельсона-Сигеля
def GT(t, beta0, beta1, beta2, tau, g_values): #Непрерывная доходность
# Основные члены модели
term1 = beta0 + beta1 * tau*(1 - np.exp(-t / tau))/t
term2 = beta2 *((1 - np.exp(-t / tau))*tau/t - np.exp(-t / tau))
# Инициализация параметров a_i и b_i
a_values = np.zeros(9)
b_values = np.zeros(9)
# Инициализация первых значений
a_values[0] = 0 # a_1 = 0
a_values[1] = 0.6 # a_2 = 0.6
b_values[0] = a_values[1] # b_1 = a_2
k = 1.6 # параметр для рекуррентного расчета
# Рекуррентное вычисление a_i и b_i
for i in range(2, 9):
a_values[i] = a_values[i - 1] + k**(i - 1)
b_values[i - 1] = b_values[i - 2] * k
# Расчет суммы по экспоненциальным членам
term3 = 0
for i in range(9):
if b_values[i] != 0: # Добавляем проверку на нулевые значения b_i
term3 += g_values[i] * np.exp(-((t - a_values[i])**2) / (b_values[i]**2))
GT = term1 + term2 + term3
return GT/10000
def KBD(t, beta0, beta1, beta2, tau, g_values): #Кривая бескупонной доходности в % годовых
YT = 100 * (np.exp(GT(t, beta0, beta1, beta2, tau, g_values)) - 1)
return YT3. Построение графика# Временные точки для расчета (например, от 0 до 30 лет)
t_vals = np.linspace(0.01, 30, 1500)
# Расчет КБД для всех точек
KBD_vals = KBD(t_vals, beta0, beta1, beta2, tau, g_values)
# Построение графика
plt.figure(figsize=(12, 6))
plt.tick_params(axis='both', which='major', labelsize=18)
plt.plot(t_vals, KBD_vals, label='КБД',linewidth=3, linestyle='-',color = 'darkred')
plt.title('КБД Мосбиржи',fontsize=22)
plt.xlabel('Срок до погашения, лет',fontsize=20)
plt.ylabel('Доходность, %',fontsize=20)
plt.grid(False)
#plt.legend()
plt.show()Результат:
#Построение графиков спотовой и форвардной кривой
# Функция для расчета цены бескупонной облигации P(0,T) на основе данных Мосбиржи
def P_0_T(beta0, beta1, beta2, tau, g_values, T):
R_T = GT(T, beta0, beta1, beta2, tau, g_values) # Бескупонная непрерывно начисляемая ставка
return np.exp(-R_T * T)
# Форвардная ставка F(0,T)
def F_0_T(T, beta0, beta1, beta2, tau, g_values, dt=1e-4):
P_T_plus_dt = P_0_T(beta0, beta1, beta2, tau, g_values, T + dt)
P_T = P_0_T(beta0, beta1, beta2, tau, g_values, T)
F_0_T = -(np.log(P_T_plus_dt) - np.log(P_T)) / dt
return F_0_T
def F_0_T_eff(T, beta0, beta1, beta2, tau, g_values, dt=1e-4): #форвардная ставка при годовом (эффективном) начислении, в % годовых
FT = 100*(np.exp(F_0_T(T, beta0, beta1, beta2, tau, g_values, dt=1e-4)) - 1)
return FT
# Временные точки для расчета (например, от 0 до 30 лет)
t_vals = np.linspace(0.1, 30, 1500)
# Расчет кривых для всех точек
KBD_vals = KBD(t_vals, beta0, beta1, beta2, tau, g_values)
F_0_T_eff_vals = F_0_T_eff(t_vals, beta0, beta1, beta2, tau, g_values, dt=1e-4)
# Построение графика
plt.figure(figsize=(12, 6))
plt.tick_params(axis='both', which='major', labelsize=18)
plt.plot(t_vals, KBD_vals, label='КБД Мосбиржи' ,linewidth=3, linestyle='-',color = 'darkred')
plt.plot(t_vals, F_0_T_eff_vals, label='Форвардная кривая',linewidth=3, linestyle='-',color = 'darkgreen')
plt.title('Спотовая и форвардная кривые',fontsize=22)
plt.xlabel('Срок до погашения, лет',fontsize=20)
plt.ylabel('Доходность, %',fontsize=20)
plt.grid(False)
plt.legend(fontsize=18)
plt.show()


