Во вторник в 10:07 наш исследовательский агент обнаружил краткосрочную бычью ситуацию в индексном ETF. Сигнал по базовому активу был обычным: цена вернулась выше средней за 20 баров, реализованная волатильность снижалась, а на следующую сессию был запланирован макроэкономический релиз. Предлагаемая сделка — колл-спред примерно с двумя неделями до экспирации.
Бэктест одобрил сделку. Бумажная стратегия её не открыла. Сравнив ордера, мы увидели, что временные метки сигналов совпадали, а цены базового актива различались всего на несколько центов. Контракты были разными: в бэктесте купили опцион с экспирацией в пятницу, который выглядел лучшим в исторической цепочке; бумажный процесс выбрал следующую указанную экспирацию — на неделю позже.
Причиной оказалось соединение данных с календарём. Мы считали «дни до экспирации» свойством даты, а затем выбирали контракт с экспирацией, ближайшей к целевой. В исторических данных календарь экспираций включал недельный опцион, добавленный уже после проверяемой даты. Получалось, что бэктест использовал контракт из будущего, и это выглядело безобидным упрощением.
Сначала мы заподозрили ошибку часового пояса. Даты экспирации опционов особенно часто к ней приводят: торговая сессия заканчивается в Нью-Йорке, временные метки биржи могут храниться в UTC, а дата с временем 00:00 может сдвинуться, если конвейер данных небрежно преобразует её. Мы проверили — с временными метками всё было в порядке. Просто во вторник этого контракта ещё не было в историческом наборе.
Первый вывод: для исторической цепочки опционов нужен набор листингов, соответствующий конкретному моменту времени. Дата экспирации контракта показывает, когда он истекает. Она не говорит, когда контракт был добавлен в листинг, когда по нему начались торги и мог ли алгоритм выбрать его в тот момент.
Мы перестроили этап выбора с учётом информации, доступной на момент принятия решения. Для каждого контракта-кандидата потребовали, чтобы временная метка листинга была не позже этого момента, актуальная котировка не выходила за установленный порог давности, а торги проходили в сессию, когда стратегия могла отправить ордер. Ещё мы стали сохранять набор кандидатов до ранжирования. Это оказалось важным: старый бэктест сохранял только победителя, и мы не могли понять, сравнивал ли он пять подходящих контрактов или незаметно выбрал из календаря, которого тогда ещё не существовало.
Затем мы проверили котировки, использованные для оценки спреда. Бэктест независимо брал последнюю котировку по каждой ноге. Аск колла был зафиксирован в 10:06:58, а бид короткого колла — в 10:04:11. По общим правилам обработки данных обе котировки считались достаточно свежими, но за это время рынок изменился. Средняя цена спреда, рассчитанная по асинхронным котировкам ног, может описывать рынок, которого никогда не существовало.
Мы изменили правило: временные метки обеих ног должны различаться не более чем на 2 секунды, а пересечённые рынки и нулевой объём по котировке нужно отбрасывать. Число пригодных исторических наблюдений сократилось. Зато оставшиеся цены стали убедительнее. Если синхронной котировки не было, мы помечали кандидата как недоступного, а не заполняли пробел устаревшей ценой.
Далее пересмотрели правило ранжирования. Агент выбирал экспирацию с наименьшей котируемой стоимостью спреда. Звучит разумно, пока не выясняется, что объём спреда — всего 1 контракт, а котировка устарела. Мы сформулировали цель явно: максимизировать ожидаемую прибыль на единицу определённого риска среди контрактов, прошедших минимальные требования к размеру котировки, её свежести и листингу. В этом примере котируемый спред между двумя ногами у ближайшей подходящей экспирации составлял $0.42, а у более поздней — $0.31. При этом у более дешёвого на вид варианта в короткой ноге отображался всего 1 контракт.
| Кандидат | Экспирация уже была доступна? | Давность котировки | Отображаемый объём короткой ноги | Решение |
|---|---|---|---|---|
| Недельный опцион с экспирацией в пятницу | Нет | Не применимо | Не применимо | Исключить |
| Следующая пятница | Да | 1.4 секунды | 18 контрактов | Подходит |
| Третья пятница месяца | Да | 9.8 секунды | 1 контракт | Исключить из-за устаревшей котировки и малого объёма |
Мы воспроизвели этот случай с новыми правилами. Теперь в бэктесте выбиралась та же экспирация, что и в бумажной стратегии, а причины отклонения остальных контрактов сохранялись. Вход в спред обошёлся на $0.11 дороже, чем при исходном историческом исполнении. Это не означало, что стратегия бесполезна: старая оценка доходности учитывала выбор сделки, который нельзя было воспроизвести в реальной торговле.
Была и небольшая проблема: форматы биржевых символов могут маскировать ошибки экспирации под проблемы качества данных. Мы нашли у поставщика данных код недельного опциона, отличавшийся от кода месячного всего одним символом. Я до сих пор сохраняю исходный символ рядом с нормализованным ключом контракта в журнале аудита. Не самое элегантное решение, зато оно не даёт нам слишком поспешно доверять аккуратному соединению данных.
Если вы строите бэктест опционов, сохраняйте полный набор кандидатов для каждого момента принятия решения вместе со статусом листинга каждого контракта, временными метками котировок, их объёмами и причинами отклонения. Затем сделайте выбор контракта детерминированной функцией от этого снимка данных. Выбор экспирации — часть стратегии. Если бэктест видит контракт, недоступный бумажной стратегии, сравнение сигналов начинается уже после того, как изменился сам ответ.
← Все статьи


