Мы повторно запустили сохранённый бэктест и получили другую кривую капитала. Тот же код стратегии, тот же диапазон дат, тот же инструмент. Итоговый баланс отличался на 1.8%, а три сделки сместились на один бар.
Этого достаточно, чтобы результат исследования было трудно считать надёжным. Если коллега, вы сами в будущем или сервис бумажной торговли не могут воспроизвести запуск, невозможно понять, улучшило ли изменение стратегию или просто изменило эксперимент. Вот по какому плану мы искали источник расхождений.
День 1: мы записали, что значит «тот же запуск»
Нашей первой ошибкой было считать файл стратегии самим экспериментом. Это было не так. Запуск также зависел от входных данных, версии движка, календаря, метаданных инструмента и настроек исполнения. Код описывал лишь часть расчёта.
Прежде чем что-либо менять, мы составили манифест запуска. В него вошли коммит стратегии, идентификаторы снимков данных, диапазон дат, площадка, тариф комиссий, источник ставок финансирования, модель исполнения ордеров и версии программного обеспечения. Мы также сохранили созданные ордера и исполнения: одна кривая капитала не показывает, в каком месте два запуска впервые разошлись.
| Артефакт | Что записывать | Почему это важно |
|---|---|---|
| Рыночные данные | ID снимка, версия схемы, корректировки | Поставщики исправляют историю и пересматривают корпоративные действия |
| Исполнение | Уровень комиссий, ряд ставок финансирования, настройки исполнения и влияния на цену | Значения по умолчанию и допущения о счёте меняют результаты |
| Среда выполнения | Коммит кода, версии движка и зависимостей | Библиотеки могут менять порядок, округление или индикаторы |
| Результат | Ордера, исполнения, позиции и метрики | Показывает, в каком месте запуски начинают расходиться |
День 2: мы сравнили сделки, а не Sharpe
Сводные метрики отвлекали от сути. Sharpe у обоих запусков был почти одинаковым, но журналы исполнений показали первое расхождение при расчёте ставки финансирования. В одном запуске ставку применили к позиции, открытой на момент расчёта; в другом — к позиции после ребалансировки на этой временной отметке.
Код стратегии не менялся. Изменился порядок событий в движке. Небольшое обновление версии сделало последовательность явной, тогда как раньше она зависела от того, как сортировались два события.
Мы закрепили порядок в условиях запуска: сначала применить ставку финансирования к позиции, перенесённой на момент расчёта, затем обработать решения стратегии на этой временной отметке. Конкретное правило может различаться в зависимости от площадки и движка. Ошибка — оставлять его неявным.
День 3: файл данных, который считался «тем же самым», оказался другим
После фиксации порядка событий оставшиеся расхождения сосредоточились в нескольких сделках с акциями. Поставщик скорректировал исторические данные с учётом дробления акций. Наш файл имел прежнее название и то же количество строк, поэтому казалось, что он не изменился.
Теперь мы вычисляем отпечаток каждого неизменяемого снимка данных и храним рядом правила корректировок. Хеш сообщает, изменились ли байты, но не объясняет почему. Поэтому в манифесте также указываются источник, время получения и версия преобразования. Для данных, которые пересматриваются, эти сведения становятся частью результата.
Воспроизводимый бэктест должен отвечать на вопрос: «Какую версию прошлого он видел?»
День 4: мы нашли незаметный параметр по умолчанию
Последним отличием оказалась комиссия maker, равная нулю, потому что в конфигурации стратегии это поле не было задано. Более новая версия движка применила комиссию по умолчанию для счёта. Одного этого параметра хватило, чтобы изменить предельные сделки и объяснить большую часть разницы в итоговом балансе.
Мы стали явно задавать настройки, влияющие на экономический результат, и настроили движок так, чтобы он записывал итоговую конфигурацию в журнал запуска. Значения по умолчанию удобны на этапе изучения. Но при сравнении результатов за разные периоды на них нельзя полагаться как на доказательство.
Что мы в следующий раз пропустим
Полдня ушло на сравнение сводных метрик, прежде чем мы посмотрели на первое различающееся исполнение. Не начинайте с этого. Отсортируйте оба журнала событий по времени и найдите первое расхождение: последующие различия часто возникают из-за той же причины.
Мы также больше не будем рассчитывать, что одного образа контейнера достаточно для воспроизводимости запуска. Он фиксирует значительную часть программной среды, но не внешний файл данных, загружаемый во время запуска тариф комиссий или пересмотренную поставщиком историю.
Если результаты бэктеста изменились, сохраните манифесты и журналы обоих запусков, а затем устраняйте источники расхождений по одному. Полезный результат — это не просто кривая, которую можно построить повторно. Это запись, объясняющая, какие данные и допущения её сформировали и почему следующий запуск может отличаться.
← Все статьи


