0. Импортируем нужные библиотеки
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
1. Извлекаем данные о расчетных параметрах КБД
Расчетные параметры на конкретную дату указаны внизу страницы
www.moex.com/ru/marketdata/indices/state/g-curve/
# 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()
2. Определяем функции для расчета G-curve для непрерывного начисления и КБД (в процентах годовых)
# Функция для расчета бескупонной доходности по методу Нельсона-Сигеля
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 YT
3. Построение графика
# Временные точки для расчета (например, от 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()
Результат:
График нужен скорее для иллюстрации статей, а вот функции GT и KBD понадобятся для исследования различных моделей. Они хороши тем, что достаточно гладкие и по ним легко построить форвардную кривую и ее производную. Некоторые авторы берут дискретные точки на КБД Мосбиржи и затем приближают кривую сплайнами, но это часто вызывает проблемы при дальнейшем моделировании. Например, попытки рассчитать форвардные ставки, чтобы найти «ожидания» рынка по будущим краткосрочным ставкам, могут привести к достаточно волатильным результатам. Построение КБД непосредственно по формулам из документации Мосбиржи позволяет избежать подобных проблем.
Код сам по себе довольно сырой и каждый желающий может его дополнить и улучшить.
upd: Можно построить на одном графике спотовую и форвардные кривые, чтобы посмотреть «ожидаемую» рынком динамику коротких ставок (без учета премии за риск) Используются данные за 07.10.2024. Грубо говоря, форвардная кривая — это «предсказание» будущих ставок RUONIA на основе текущей спотовой кривой. Понятно, что дальний конец нерелевантен, т.к. ставки не могут застыть на плато. Это результат теоретической модели. Кроме того, КБД не учитывает «реальные ожидания» многих участников о повышении коротких ставок вплоть до 22% в ближайшие месяцы.
#Построение графиков спотовой и форвардной кривой
# Функция для расчета цены бескупонной облигации 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()