Все материалыENGINEERING · Архитектура · 2 МИН ЧТЕНИЯ

Архитектура бэктестов: почему тесты на TradingView — самообман

Что должен эмулировать бэктест, чтобы его результат имел отношение к реальности: очередь ордеров, частичное исполнение, проскальзывание и стоимость дискового ввода-вывода.

Толпа считает бэктестом прогон индикаторов по истории свечей. Нажал кнопку в TradingView — получил красивый график эквити.

Проблема в том, что базовые платформы используют примитивную логику исполнения: если цена коснулась вашего лимита, система рисует вам профит. В реальности вас уничтожит механика сведения ордеров.

Ниже — как устроен наш контур симуляции и почему он построен именно так.

Данные и узкое место ввода-вывода

Мы парсим историю напрямую с биржи. Нас не интересуют OHLCV-свечи: нужна тиковая история и слепки стакана, чтобы считать сдвиги лимитных плотностей.

Главный враг бэктеста — дисковый ввод-вывод. Если гонять массивы с диска, вычисления займут годы. Поэтому архитектура выстроена через Redis, а во время боевых прогонов и оптимизации параметров вся тяжёлая математика висит в оперативной памяти. Скорость доступа к памяти позволяет прокручивать тысячи итераций за адекватное время.

Это не оптимизация ради оптимизации. Пока прогон занимает сутки, вы проверяете одну гипотезу в день. Когда он занимает минуты, вы проверяете двадцать.

Почему ядро самописное

Мы не используем публичные библиотеки для симуляции торгов: они не учитывают рыночную микроструктуру.

Тяжёлая математика, расчёт положительного матожидания и логика риск-модуля написаны на Python. Компоненты, критичные к асинхронности и многопоточности — коннекторы к биржам и вебхуки, — на Go.

Разделение не по вкусу, а по характеру нагрузки: счётная часть выигрывает от экосистемы Python, сетевая — от дешёвых горутин.

Главная инженерная задача: эмуляция реальности

Заставить бэктест показывать правду сложнее, чем написать саму стратегию. Ядро симулирует то, чего не видят ритейл-платформы:

  • Очередь ордеров. Тот факт, что цена коснулась вашего уровня, не означает, что лимит исполнили. Вы могли быть тысячным в очереди.
  • Частичное исполнение. Объём ликвидности на уровне может быть меньше вашего сайза.
  • Проскальзывание. Удар по рынку съедает стакан.

Если ваш бэктест показывает 100% winrate — ваш код врёт.

Наша система закладывает операционные убытки и пессимизирует исполнение ещё на этапе симуляции. Мы искусственно делаем условия хуже рыночных. Стратегия, которая выживает в заведомо худших условиях, имеет шанс выжить в реальных. Обратное неверно.

Иллюзия нулевого пинга

Ритейл одержим идеей арендовать сервер поближе к бирже, чтобы получить пинг в одну миллисекунду. Для нашей инфраструктуры это не имеет значения: мы работаем через вебхуки, и задержка в ±50 мс нас не пугает.

Почему? Потому что мы не занимаемся HFT-арбитражем и не пытаемся обогнать маркетмейкера на микросекундах. Алгоритмы разбирают структурные неэффективности и торгуют математическое ожидание на дистанции. При таком подходе 50 мс — статистическая погрешность, которая поглощается средним размером тейка.

Вопрос не в том, насколько быстро вы шлёте ордер. Вопрос в том, насколько точно ваша модель описывает хаос ликвидности.

Что из этого стоит забрать

Бэктест — это не проверка стратегии. Это проверка того, насколько честно вы умеете моделировать собственное исполнение. Красивая кривая эквити при наивной модели исполнения говорит только о качестве модели, а не о качестве стратегии.

Читать дальше