Блог им. 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()