Для расчета облигаций с плавающей ставкой или номиналом, при оценке неизвестных денежных потоков, ТрэкРекордс прогнозирует ставки купонов/номинала по сценариям.
Пользователь может использовать собственный сценарий об изменении ставок, нулевой сценарий или вмененный сценарий (по умолчанию)
Нулевой сценарий предполагает, что ставки купонов/номинала остаются на уровне, на котором они были на дату расчета.
Вмененный сценарий предполагает расчет вмененных ставок купонов/номинала на основе информации о торгах линкеров и флоатеров ГЦБ РФ и отдельных корпоративных облигаций. Таким образом вмененный сценарий оценивает ожидания рынка относительно ставок RUONIA, RU_CPI и RU_RATE на дату расчета.
При расчете вмененных показателей RUONIA и RU_CPI ТрэкРекордс использует подход, разработанный Банком России при стресс-тестировании пенсионных фондов и оценке флоатеров и линкеров. В новой редакции Указания Банка России №4060-У предполагается нахождение вмененных ставок инфляции и ставок RUONIA на основе параметров ОФЗ и их цен на рынке. Проект Указания можно почитать по этой ссылке
Для облигаций, у которых размер купона или номинала зависит от ставок RUONIA, RU_CPI и RU_RATE, в ТрэкРекордс по умолчанию используется вмененный сценарий расчета прогнозных базовых индикаторов купона/номинала.
ТрэкРекордс рассчитывает вмененные показатели на основании цен и характеристик ОФЗ и отдельных корпоративных облигаций, у которых есть ликвидность и история торгов. Список облигаций, являющийся базой для расчета вмененных показателей, обновляется раз в квартал. Дополнительном фильтром для выбора базовых облигаций при расчете вмененной ставки RU_RATE является агрегированный рейтинг облигации/эмитента не ниже ruAAA.
Пример расчета вмененных ставок и база расчета на 2024-03-19:
Дата | Ставка | Срок, дней | Срок, лет | Вмененная ставка, % |
ISIN опорной облигации |
Опорная облигация | Цена с НКД |
---|---|---|---|---|---|---|---|
2024-03-19 | RUONIA | 316 | 0,87 | 15,2 | RU000A0JV4L2 | ОФЗ-29006-ПК | 1029,72 |
2024-03-19 | RUONIA | 736 | 2,02 | 13,4 | RU000A101N52 | ОФЗ-29014-ПК | 1033,72 |
2024-03-19 | RUONIA | 1009 | 2,76 | 13,06 | RU000A1025B5 | ОФЗ-29016-ПК | 1033,94 |
2024-03-19 | RUONIA | 1079 | 2,96 | 13,27 | RU000A0JV4M0 | ОФЗ-29007-ПК | 1048,11 |
2024-03-19 | RUONIA | 1282 | 3,51 | 12,81 | RU000A102BV4 | ОФЗ-29020-ПК | 1032,84 |
2024-03-19 | RUONIA | 1674 | 4,59 | 12,54 | RU000A1025A7 | ОФЗ-29015-ПК | 1020,63 |
2024-03-19 | RUONIA | 1947 | 5,33 | 12,35 | RU000A102A49 | ОФЗ-29019-ПК | 1016,21 |
2024-03-19 | RUONIA | 2024 | 5,55 | 12,16 | RU000A0JV4P3 | ОФЗ-29008-ПК | 1080,42 |
2024-03-19 | RUONIA | 2374 | 6,5 | 12,37 | RU000A101KT1 | ОФЗ-29013-ПК | 1026,99 |
2024-03-19 | RUONIA | 2444 | 6,7 | 12,22 | RU000A105B11 | ОФЗ-29021-ПК | 996,54 |
2024-03-19 | RUONIA | 2808 | 7,69 | 12,28 | RU000A102A31 | ОФЗ-29018-ПК | 997,45 |
2024-03-19 | RUONIA | 2969 | 8,13 | 11,98 | RU000A0JV4N8 | ОФЗ-29009-ПК | 1079,47 |
2024-03-19 | RUONIA | 3081 | 8,44 | 12,31 | RU000A1028D5 | ОФЗ-29017-ПК | 997,48 |
2024-03-19 | RUONIA | 3410 | 9,34 | 12,37 | RU000A105G16 | ОФЗ-29022-ПК | 1010,41 |
2024-03-19 | RUONIA | 3809 | 10,44 | 12,28 | RU000A105L19 | ОФЗ-29023-ПК | 989,99 |
2024-03-19 | RUONIA | 3914 | 10,72 | 11,93 | RU000A0JV4Q1 | ОФЗ-29010-ПК | 1076,8 |
2024-03-19 | RUONIA | 4047 | 11,09 | 12,28 | RU000A1066D5 | ОФЗ-29024-ПК | 999,34 |
2024-03-19 | RUONIA | 4894 | 13,41 | 12,38 | RU000A106Z61 | ОФЗ-29025-ПК | 988,16 |
2024-03-19 | RU_CPI | 1415 | 3,88 | 6,96 | RU000A0ZYZ26 | ОФЗ-52002-ИН | 1310,39 |
2024-03-19 | RU_CPI | 2311 | 6,33 | 7,12 | RU000A102069 | ОФЗ-52003-ИН | 1138,65 |
2024-03-19 | RU_CPI | 2920 | 8 | 7,33 | RU000A103MX5 | ОФЗ-52004-ИН | 1051,27 |
2024-03-19 | RU_RATE | 896 | 2,45 | 12,72 | RU000A106TM6 | Банк ВТБ-Б-1-343 | 1005,79 |
2024-03-19 | RU_RATE | 942 | 2,58 | 12,09 | RU000A1075S4 | ИКС 5 Финанс-003P-02-боб | 1017,93 |
2024-03-19 | RU_RATE | 987 | 2,7 | 12,65 | RU000A107B35 | Банк ВТБ-Б-1-346 | 1006,59 |
2024-03-19 | RU_RATE | 998 | 2,73 | 11,77 | RU000A107HG1 | Газпром нефть-003P-08R | 1018,83 |
2024-03-19 | RU_RATE | 1060 | 2,9 | 11,41 | RU000A107UW1 | Газпром нефть-003P-10R | 1011,48 |
2024-03-19 | RU_RATE | 1155 | 3,16 | 11,63 | RU000A107AG6 | Россети Центр-001P-03 | 1006,85 |
2024-03-19 | RU_RATE | 1348 | 3,69 | 11,32 | RU000A107EC7 | Россети Ленэнерго-001P-01 | 1003,44 |
2024-03-19 | RU_RATE | 1487 | 4,07 | 11,36 | RU000A106565 | Газпром нефть-003P-06R | 1035,04 |
2024-03-19 | RU_RATE | 1660 | 4,55 | 11,35 | RU000A106ZU6 | РусГидро-БО-П12 | 1037,02 |
2024-03-19 | RU_RATE | 2092 | 5,73 | 11,19 | RU000A107CG2 | Россети-001Р-11R | 1004,74 |
2024-03-19 | RU_RATE | 3516 | 9,63 | 10,42 | RU000A1077V4 | Росэксимбанк-БО-002Р-04 | 1020,97 |
Грязная цена облигации = Первая ненулевая(close, waprice, bid) / 100 * Непогашенный номинал + нкд.
close - цена закрытия %
waprice - средневзвешенная цена %
bid - цена покупки %
Непогашенный номинал - Номинальная стоимость на дату расчетов
нкд - накопленный купонный доход на дату расчета
Данная кривая используется для расчета безрисковой ставки дисконтирования для денежных потоков с соответвующим сроком. Для входного срока (в днях), который не совпадает точно с одним из предопределенных сроков, используется линейная интерполяцию между двумя ближайшими ставками для расчета ставки (функция get_kbdrate_from_data)
Функция get_implied_inflation предназначена для расчета вмененной инфляции на указанную дату (stated_at) на основе данных о "грязной" цене ценной бумаги (dirty_price), номинале (nom), датах купонных выплат (dc), кривой ОФЗ (daily_rates_on_stated_at) и ставке купона (coupon_rate).
Итеративный процесс поиска инфляции
Функция использует метод бисекции для нахождения значения инфляции, при котором сумма дисконтированных потоков близка к "грязной" цене с заданной точностью. Для каждого шага итерации рассчитывается величина PR (дисконтированная сумма денежных потоков) с учетом текущего значения предполагаемой инфляции, а затем корректируется диапазон поиска в зависимости от того, больше или меньше PR фактической "грязной" цены.
#функция для расчета вмененной инфляции на дату stated_at
def implied_inflation(stated_at, dirty_price, nom, dc, daily_rates_on_stated_at, coupon_rate):
N = len(dc) # Количество купонов
Il = -0.1 # Нижняя граница для инфляции
Ir = 0.6 # Верхняя граница для инфляции
Inf = Il # Начальное значение для предполагаемой инфляции
DEL = 1 # Начальное значение для разницы между "грязной" ценой и ценой, рассчитанной с использованием предполагаемой инфляции
br = 1 # Счетчик для ограничения количества итераций
coupon_rate = float(coupon_rate) # Преобразование ставки купона в число с плавающей точкой
stated_at = pd.to_datetime(stated_at) # Преобразование даты stated_at в объект datetime
dc = pd.to_datetime(dc) # Преобразование дат купонов в объекты datetime
dc = dc.reset_index(drop=True) # Сброс индексов для корректного использования в цикле
while abs(DEL) > 0.00001 and br < 1001:
if DEL > 0:
Il = Inf
else:
Ir = Inf
Inf = (Il + Ir) / 2
PR = 0
for i in range(1, N):
d = dc[i]
if d > stated_at:
cnp_dur = (d - stated_at).days
dKBD = (1 + get_kbdrate_from_data(daily_rates_on_stated_at,cnp_dur,'RUB')/100) ** (cnp_dur / 365)
dInf = (1 + Inf) ** (cnp_dur / 365)
PR += nom * dInf * coupon_rate * (d - dc[i - 1]).days / 365 / dKBD
if i == N - 1:
PR += nom * dInf / dKBD
DEL = dirty_price - PR
br += 1
if br < 1000:
return Inf
else:
return "Error"
Алгоритм расчета вмененной ставки RUONIA или RU_RATE сводится к нахождению разницы между "грязной" ценой облигации и суммой дисконтированных премий к базовой ставке и известных купонов (гарантированных и уже известных платежей), деленной на дисконтированный номинал.
Для RUONIA в качестве ставки дисконтирования используется кривая ОФЗ, т.к. опорными облигациями являются ОФЗ-ПК
Для RU_RATE(ключевая ставка) в качестве ставки дисконтирования используется используется кривая ОФЗ + gspread, рассчитанный по индексу МосБиржи RUCBTR3A3YNS, т.к. опорными облигациями являются корпоративные облигации с рейтингом не ниже ruAAA.
Пример функции расчета на Python:
#пишем функцию для расчета implied_rate для флоатера с премией к базовой ставке
def get_implied_yield(stated_at, dirtyprice, nominal, cashflows, daily_rates_on_stated_at, premium):
"""
Calculates a specific financial metric (interpreted from the provided VBA function).
:param stated_at: Дата расчета
:param pv: Приведенная стоимость
:param Nom: Номинал
:param dc: Календарь денежных потоков
:param daily_rates_on_stated_at: Кривая доходности ОФЗ на дату расчета
:param pvb: Приведенная стоимость без купонов
:param premium: Премия к базовой ставке
:return: Значение implied_rate
"""
PR1 = 0
PR2 = 0
pv = dirtyprice
Nom = nominal
stated_at = pd.to_datetime(stated_at).date()
if len(cashflows) == 0:
return 0
#преобразуем календарь денежных потоков в словарь
dc = cashflows.to_dict('records')
N = len(dc)
for i in range(N):
#получаем дату купона и размер купона в деньгах
d = dc[i]['coupon_date']
cur_coupon = dc[i]['cf_coupon']
if d > stated_at:
#получаем дельту в днях между stated_at и датой купона
delta_days = (d - stated_at).days
#получаем коэффициент дисконирования КБД для дюрации купона
dKBD = (1 + get_kbdrate_from_data(daily_rates_on_stated_at,delta_days,'RUB')/100) ** (delta_days / 365)
#если купон известен то добавляем его к PR2
if cur_coupon > 0:
PR2 += cur_coupon / dKBD
else:
#если первый купон неизвестен, то предыдущая дата купона не может быть найдена значит она = stated_at
if i > 0:
prev_d = dc[i - 1]['coupon_date']
else:
prev_d = stated_at
delta_prev_days = (d - prev_d).days
PR1 += delta_prev_days / 365.0 / dKBD
PR2 += Nom * premium * delta_prev_days / 365 / dKBD
if i == N - 1:
PR2 += Nom / dKBD
return (pv - PR2) / (PR1 * Nom)
Расчет вмененной ставки для разных сроков погашения рассчитывается при помощи двух опорных вмененных ставок, рассчитанных по двум облигациям с ближайшим меньшим и ближайшим большим сроком до погашения:
impl_rate_for_duration = (1+yield_low)^((dur_up - dur)/(dur_up - dur_low))*(1+yield_up)^((dur - dur_low)/(dur_up - dur_low)) - 1
где:
impl_rate_for_duration - вмененная ставка с учетом срока до погашения ценной бумаги
yield_up - значение вмененной ставки, расчитанной по облигации, которая имеет ближайший больший, чем у оцениваемой облигации, срок до погашения.
yield_low - значение вмененной ставки, расчитанной по облигации, которая имеет ближайший меньший, чем у оцениваемой облигации, срок до погашения.
dur_up - срок до погашения облигации, которая имеет ближайший больший, чем у оцениваемой облигации, срок до погашения.
dur_low - срок до погашения облигации, которая имеет ближайший меньший, чем у оцениваемой облигации, срок до погашения.
dur - срок до погашения оцениваемой облигации.
при отсутствии вмененной ставкисо срокос до погашения меньше срока до погашения оцениваемой облигации, значение показателя принимается равным значению фактической ставки на расчетную дату;
оферты по облигациям игнорируются при оценке dur_low и dur_up