Продолжаем цикл «Синтетические облигации в алго».
Сегодня первая практическая статья — разбираем робота SyntheticBondsArbitrage. Это классический контанго-арбитраж: робот следит сразу за десятью парами «акция + фьючерс», выбирает самую доходную и держит позицию до фиксации. Свободные деньги в это время работают в фонде ликвидности.
1) Торговая идея
Робот смотрит на десять ликвидных бумаг MOEX — Сбербанк обычка и префы, Газпром, Роснефть, Лукойл, ВТБ, Норникель, Алроса, Аэрофлот, Магнит. По каждой считает контанго ближайшей серии фьючерса и переводит его в годовую доходность.
Дальше всё просто по смыслу:
Если доходность лучшей пары выше порога входа и выше того, что даёт денежный рынок — робот открывает синтетическую облигацию: покупает акцию, продаёт фьючерс.
Если появилась пара заметно доходнее текущей — позиция переносится в неё. (Арбитраж тут)
За несколько дней до экспирации позиция закрывается, при бэквордации — фиксируется досрочно.
Пока позиции нет, свободные деньги лежат в LQDT и зарабатывают ставку.
ВАЖНО!
Робот использует фонд ликвидности. Это такой инструмент, который всегда растёт, давая прибыль в условную ставку ЦБ. На западных площадках самый популярный инструмент называется LQDT, «Ликвидность». У нас в РФ множество других инструментов с похожей механикой и похожими названиями. У брокера Т-Инвестиции такой инструмент называется TMON@, а в тестере это LQDTMOEX. И то, и другое — одно и то же.
2) Источники робота
BotTabSimple — вкладка под базовую акцию. Одна на каждую бумагу из десяти.
BotTabScreener — скринер серий фьючерсов на эту акцию. Из него робот выбирает ближайшую подходящую серию.
BotTabSimple под LQDT — парковка свободных денег. Через эту же вкладку робот читает портфель и свободные средства.
Отдельного типа вкладки для синтетики в движке нет — логика работает напрямую с парами источников. Десять пар разворачиваются автоматически кнопкой авто-деплоя: в реале — по серверу Т-Инвестиций, в тестере — по бумагам выбранного сета (если в сете есть акции MOEX).
Преднастройки для OsData, чтобы скачать нужные данные, можно скачать здесь: https://disk.yandex.ru/d/UlYIxNjh3mkfSQ
3) Индикаторы
Индикаторов нет. Совсем. Робот не смотрит ни на тренд, ни на волатильность, ни на объёмы — только на цены двух инструментов и календарь экспираций.
Метрика решений — абсолютное контанго в процентах:
контанго % = (бид фьючерса / mult − аск акции) / (аск акции / 100)
Поскольку торгуется только ближайшая серия каждой бумаги и сроки у всех пар примерно одинаковые, годовые эквивалентны абсолютным — можно сравнивать и так, и так. Консервативные стороны цен (фьючерс по биду, акция по аску) заложены прямо в формулу: спред учтён до входа, а не после.
4) Логика робота
Робот входит в логику один раз на границу свечи (в реале — по закрытию свечей скринеров с небольшой синхронизирующей паузой). Дальше по шагам:
Шаг 1. Выбор претендента. По каждой из десяти включённых пар робот берёт ближайшую серию фьючерса в окне от Min days to expiration (по умолчанию 18) до 120 дней и считает контанго. Претендентом становится пара с максимальным абсолютным контанго.
Шаг 2. Вход. Позиции нет — робот входит, если контанго претендента не ниже Min yield to entry % (по умолчанию 0.9) и доходность проходит LQDT-гейт: годовая доходность облигации должна быть выше доходности LQDT минимум на Min yield diff over LQDT % ann (по умолчанию 3.75%). Если денежный рынок даёт столько же — зачем вообще арбитраж?
Шаг 3. Перенос. Позиция есть, но претендент лучше неё на Min Yield Diff To Move % ann (по умолчанию 0.7 годовых) — позиция переносится: старая закрывается, открывается новая.
Шаг 4. Выход. Позиция закрывается за Days before expiration to exit дней до экспирации (по умолчанию 7) — ближе к поставке ликвидность падает, держать незачем. Отдельный триггер — бэквордация: контанго схлопнулось досрочно (типично перед дивидендом) — прибыль фиксируется, не дожидаясь срока.
Шаг 5. LQDT-ветка. Пока позиции нет, свободные деньги уходят в LQDT. Его доходность робот вычисляет сам трейлингом за LQDT yield days (по умолчанию 10 дней) — именно от этой цифры считается гейт входа.
А теперь детали кода, ради которых стоит открыть исходник.
1) аварийные выходы. Ноги открываются двумя рыночными ордерами последовательно, и робот честно следит за тем, что могло пойти не так: открылась только одна нога, ноги исполнились в разные дни, нога прошла на премаркете до 10:00 (артефакт укороченных сессий) — во всех этих случаях срабатывает аварийное закрытие. Отдельно: если обнаружилась вторая позиция, закрывается худшая.
2) перекрытие позиции на смене года. С наступлением нового года арбитражная позиция перекрывается на те же объёмы: закрывается и открывается заново. Это осознанное решение — зафиксировать результат внутри налогового периода и не портить налоговый учёт. То же самое с позицией в LQDT. Это работает в тестере, чтобы в том числе обрезать прибыль по годам.
3) YearSummary. По концу теста робот печатает в лог сводку по годам: доходность LQDT против доходности робота и альфу в процентных пунктах, включая тяжёлые годы вроде 2022-го, плюс среднюю альфу за весь тест. Очень наглядная штука для оценки стратегии.
И отдельно про монитор. В кастомной вкладке параметров у робота живёт таблица: по каждой паре — акция (кнопка открывает чарт), редактируемый мультипликатор и ближайшая серия с доходностью «абс% | год%». Открытые позиции подсвечиваются: лонг зелёным, шорт красным, объёмы прямо в скобках. Состояние арбитража видно без единого лога.
5) Исходный код в проекте
Робот лежит в открытом доступе на ГитХабе:
https://github.com/AlexWan/OsEngine/blob/master/project/OsEngine/Robots/SyntheticBond/SyntheticBondsArbitrage.cs
6) Настройки робота
Разбираем параметры по порядку.
Regime — режим работы: On / Off.
Table update — период обновления таблицы монитора. По умолчанию 5 секунд.
Trade pair 1–10 — включение каждой из десяти пар по отдельности.
Fut mult 1–10 — мультипликаторы фьючерсов. Режим Auto подставляет их сам (с учётом исторических изменений), в Manual правятся руками — в том числе прямо из таблицы монитора.
Min yield to entry % — минимальное абсолютное контанго для входа. По умолчанию: 0,9.
Min Yield Diff To Move % ann — минимальная разница годовых доходностей для переноса позиции. По умолчанию: 0,7.
Min days to expiration — ближайшая разрешённая серия: не ближе N дней до экспирации. По умолчанию 18.
Days before expiration to exit — за сколько дней до экспирации выходить. По умолчанию: 7.
Exit on backwardation is on — фиксировать прибыль при уходе фьючерса в бэквордацию.
LQDT regime is on — включить парковку свободных денег в фонд ликвидности.
Min yield diff over LQDT % ann — на сколько годовая доходность облигации должна превышать доходность LQDT для входа и переноса. По умолчанию: 3,75.
LQDT yield days — глубина трейлинга доходности LQDT. По умолчанию: 10 дней.
Volume type / Volume — объём: процент от портфеля, по умолчанию: 85%.
Deploy time frame / Tester deploy time frame — таймфреймы авто-деплоя: Min5 в реале, Min15 в тестере.
Full log is on — подробные решения в лог. Включайте при отладке.
7) Запуск робота в тестере
Соберите сет данных. Преднастройки для OsData, чтобы скачать нужные данные: https://disk.yandex.ru/d/UlYIxNjh3mkfSQ
Нажмите авто-деплой — робот сам создаст десять пар и вкладку под LQDT, подтвердив состав бумаг диалогом.
Таймфрейм — по параметру Tester deploy time frame, по умолчанию 5 минут. Комиссия при деплое проставляется сама: 0.04% на спот и скринеры.
Запустите тест на нескольких годах истории, чтобы захватить разные режимы ставки и дивидендные сезоны — именно они дают бэквордации и досрочные фиксации.
После теста загляните в лог — там YearSummary по годам с альфой над LQDT.
8) Один из вариантов тестирования
Штатные настройки робота, 10 пар + LQDTMOEX, тест за 8 лет: 
*Средняя альфа к LQDT 8%, с учётом комиссий. Но без учёта проскальзываний, что важно! Посмотрим вместе на лекциях.
Следующая статья — SyntheticBondsCurveArbitrage: добавляем вторую серию фьючерса и учимся выбирать по кривой.
Удачных алгоритмов!
P.S. Если будете читать не в момент выхода статьи, по данному сборнику также должны выйти лекции. Запросить их можно у нас в чатах.
Комментарии открыты для друзей!
https://smart-lab.ru/company/os_engine/blog/1024149.php
Скачать OsEngine: https://github.com/AlexWan/OsEngine
Официальная поддержка OsEngine в MAX: https://max
Канал Научный трейдинг в MAX: https://max.ru/
Поддержка OsEngine в Телеграм: https://t.me/osengine_official_support
Канал Научный трейдинг (Bad Quant) в Телеграм: https://t.me/bad_quant