Разработка движка бэктестинга на JavaScript/TypeScript для криптовалют

Расчёты на истории расходятся с реальными результатами, а проверка стратегии занимает часы. Мы разрабатываем движок бэктестинга на JavaScript под ключ: переносим расчёты на реальные данные и сокращаем время проверки стратегии в разы.
Часто задаваемые вопросы

Почему бэктестинг JavaScript ломается на реальных данных

Самодельный прогон стратегии выглядит убедительно до первой сверки с логом реальных сделок. Расхождение с реальными сделками возникает не из-за ошибки в формуле, а из-за допущений, которые в код просто не выписали.

Первое — модель исполнения заявок. Когда рыночная заявка срабатывает по цене закрытия бара, а лимитная — по касанию уровня, движок неявно отдаёт стратегии цену лучше, чем она получит в стакане. За кадром остаются проскальзывание, частичное исполнение и очередь заявок на одном уровне цены.

Второе — данные. Большие массивы истории в наивной реализации грузятся в память целиком и перебираются одним циклом. На нескольких годах минутных свечей это уже не помещается в отведённую память: прогон замедляется, потом зависает, а результаты между запусками становятся несопоставимы.

Третье — незамеченное подглядывание в будущее. Индикатор, посчитанный по всей серии сразу, к моменту сигнала уже знает дальнейшие котировки; то же происходит при пересчёте на изменившемся составе данных, на смене таймзоны и на пропусках в истории.

Поэтому бэктестинг JavaScript начинается не с цикла по свечам, а с описания модели рынка: правила исполнения, комиссии, ограничения ликвидности, порядок подгрузки истории. Разбираем, как это закрывается в движке проверки стратегий.

Что даёт собственный движок бэктестинга на JavaScript

Пока набор стратегий считается во внешнем сервисе или скриптом в один поток, цикл проверки гипотезы растягивается на часы. Движок на JavaScript/TypeScript живёт в том же рантайме, что и торговая логика, и раздаёт прогоны по ядрам процесса: набор из десятков конфигураций считается за минуты. Разница не в «оптимизации», а в том, что исследователь успевает проверить десять вариантов параметров, пока не потерял нить.

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

Воспроизводимость держится на фиксированном снапшоте рыночных данных, закреплённой модели комиссий и проскальзывания и сохранённом зерне генератора случайностей. Тот же прогон, повторённый через месяц или на другой машине, даёт совпадающую кривую доходности и совпадающий список сделок. Вместе с этим уходит класс отказов, когда стратегия выглядит рабочей на истории только из-за расхождения данных или незамеченной подгонки под прошлое.

Стоимость простоя расчётов обычно недооценивают: пока считается один вариант, очередь гипотез стоит, а решения принимаются по устаревшей картине. Когда прогон занимает минуты, спорные идеи отсеиваются на истории, а не на живом счёте.

Подходы к расчёту: событийный, векторный и потоковый

Выбор режима расчёта определяет, насколько поведение стратегии на истории совпадёт с её поведением в бою. Векторный расчёт обрабатывает весь ряд котировок одной операцией: он быстр и удобен для первичного отбора гипотез, но не воспроизводит порядок заявок внутри бара и проскальзывает мимо пересечений стопа и цели. Событийный расчёт прогоняет данные событие за событием — это тот же порядок, что и в режиме проверки стратегий на живом рынке.

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

Режим Когда подходит Ограничения
Векторный расчёт Массовый перебор гипотез, индикаторные и портфельные стратегии на длинной истории Не моделирует внутрибарные события, завышает результат у стратегий со стопами
Событийный расчёт Заявки, частичные исполнения, работа по нескольким инструментам сразу Считает медленнее, требует полных данных о заявках и комиссиях
Потоковая обработка котировок Сверка с живым потоком и повторное проигрывание записанной сессии Ошибка в данных проходит без предупреждения, нужен отдельный контур контроля

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

Этапы разработки движка проверки торговых стратегий

Порядок работ выстроен так, чтобы результат каждого этапа можно было проверить до перехода к следующему. Проверка стратегий на истории — это в первую очередь вопрос воспроизводимости: один и тот же набор данных и код должны давать одинаковый отчёт независимо от машины и времени запуска. Если этого нет, любые выводы о доходности стратегии ничего не стоят.

  1. Аудит расчётного контура. Разбираем, как результаты считаются сейчас: где теряется история сделок, учитываются ли комиссии и проскальзывание, есть ли заглядывание в будущие данные относительно момента входа.
  2. Проектирование схемы данных. Определяем формат хранения котировок и сделок, правила сведения разных таймфреймов, версионирование наборов, чтобы старый прогон можно было повторить через месяцы.
  3. Настройка окружения расчётов. Фиксируем источник времени, изолируем зависимости, добиваемся идентичного отчёта при запуске локально и на сервере.
  4. Реализация ядра. Пишем обработчик потока котировок, расчёт метрик и отчётность; ядро остаётся чистым, без привязки к конкретному источнику данных.
  5. Контрольные наборы данных. Готовим эталонные прогоны с заранее известным результатом: расхождение по числу сделок или итоговой кривой — блокирующий сигнал, а не повод продолжать.
  6. Передача движка команде. Отдаём конфигурации, описание схемы данных, регламент сопровождения и порядок добавления новых инструментов, проводим разбор с вашими инженерами.

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

Что остаётся в репозитории после сдачи движка

Отдаём не «код и созвон», а рабочий репозиторий, в котором ваша команда продолжает работу без нас. Ниже — что лежит в ветке на момент сдачи и зачем нужен каждый артефакт.

  • Конфигурации окружения — отдельные наборы для локального, тестового и рабочего контура: источники котировок, лимиты параллельных прогонов, пути к хранилищу результатов. Секреты подтягиваются из переменных окружения и в репозиторий не попадают.
  • Схема данных расчётов — сущности свечей, сделок, ордеров и прогонов с типами полей и связями. Новая метрика встраивается в неё без правки существующих таблиц.
  • Политики доступа к данным — роли для сервисов и людей: чтение истории, запись результатов, доступ к торговым ключам. Ключи подключаются только на чтение.
  • Регламент обновления истории — как догружаются пропущенные свечи, чем проверяются разрывы и дубли, в каком порядке идёт пересчёт. Неполный прогон помечается явно, а не выдаётся за результат.
  • Документация движка — точки расширения с примерами на JavaScript и TypeScript: как добавить индикатор, тип ордера, модель комиссий и проскальзывания.
  • Тесты и эталонные прогоны — фиксированный набор данных и ожидаемые метрики, по которым видно, что после правок результат не поехал.
  • Инструкция по запуску и откату — как поднять движок с нуля и вернуться к предыдущей версии.

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

Кейс: расчёты ускорились, расхождения в отчётах исчезли

Заказчик — команда количественной торговли криптовалютами — считал стратегии набором разрозненных скриптов: историю тянули из разных источников, комиссии и проскальзывание учитывали вручную, а месячный отчёт сходился с биржевой выпиской только после правок в таблице. Один прогон по длинной истории занимал часы, поэтому гипотезы проверяли редко и крупными партиями.

Мы собрали единый движок на JavaScript и TypeScript: слой загрузки приводит котировки из разных источников к одной схеме, расчётный модуль воспроизводит порядок событий внутри свечи, а модель сделки учитывает комиссию, проскальзывание и частичные исполнения. Историю берём через CoinGecko и биржевые архивы, а результаты прогонов складываем в промежуточное хранилище — повторный расчёт по тем же данным не начинается с нуля.

Что изменилось на замерах

  • время прогона по многолетней истории — с часов до минут на том же наборе данных;
  • доля расхождений с реальными сделками — с десятой части сделок до единичных случаев;
  • ручные правки отчётов — с десятков строк на отчёт до нуля.

Устойчивость расчётов закрыли отдельно: фиксированный набор периодов истории прогоняется перед каждым изменением, а итог сверяется с фактическими сделками по журналу исполнений. Если расхождение выше порога, сборка не проходит — отчёт с неверной доходностью нельзя выпустить случайно.

Чем отличается проверка на истории от демо-торговли?

Проверка на истории, то есть бэктестинг, прогоняет один и тот же поток котировок многократно, поэтому воспроизводимость прогонов здесь полная: одинаковые входные данные дают одинаковый набор сделок. Отсюда и контроль условий расчёта — комиссии, фандинг, проскальзывание, правила срабатывания лимитных заявок и момент выставления задаются явно и хранятся вместе с версией логики, так что правки стратегии сравниваются на одном и том же отрезке.

Обратная сторона — допущения модели исполнения. Если заявка исполняется по цене закрытия свечи, в расчёте нет очереди заявок, частичного исполнения и задержки сети, результат расходится с реальностью тем сильнее, чем плотнее торговля. Добавьте инструменты, выпавшие из выборки после делистинга, и пропуски в данных — и расчёт покажет устойчивость там, где её нет.

Демонстрационный режим торговли даёт другое: живой поток и настоящий контур исполнения. Здесь видны лимиты запросов к биржевому интерфейсу, отказы заявок, разрывы соединения, реальные спред и глубина. Но повторяемость нулевая — лента не повторяется, поэтому два прогона одной и той же логики несравнимы между собой.

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

Технические сомнения перед стартом разработки

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

Насколько точны расчёты?

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

Как учитываются комиссии и проскальзывание?

Комиссия берётся по факту исполнения: отдельно для встречных и рыночных заявок. Проскальзывание зависит от объёма заявки против ликвидности — модель снимает несколько уровней с книги. Повторяем прогон в ухудшенных условиях: стратегия, которая держится только на идеальном исполнении, отсеивается до запуска.

Совместимо ли с текущим кодом?

Движок подключается как библиотека, ваша логика входов и выходов остаётся основной. Адаптер приводит данные и события к общему виду, а контрольный прогон сверяет сигналы, пока список расхождений не обнулится.

Выдержит ли расчётный контур нагрузку?

История режется на диапазоны и считается параллельно, данные кэшируются между запусками. Многолетний прогон по десяткам инструментов укладывается в минуты, и его можно ставить в ночное окно, не занимая мощности под другие задачи.

Как не подогнать стратегию под историю?

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

Обсудим задачу и вернёмся с оценкой и планом

Чтобы оценка опиралась на ваши данные, а не на усреднённые допущения, для старта достаточно описать задачу и прислать то, что уже наработано. Дальше мы разбираем материалы, задаём уточняющие вопросы и возвращаемся с планом работ, разбитым по этапам, — с указанием, что делаем на каждом и что понадобится от вас.

  • Постановка задачи: какие стратегии тестируем, на каких инструментах и таймфреймах, что считаем результатом одного прогона.
  • Источники данных: откуда берём историю цен и сделок, в каком формате выгрузка, есть ли пропуски и как они помечены.
  • Правила исполнения: комиссии, проскальзывание, лимиты объёма, плечо, логика отмены и частичного заполнения заявок.
  • Метрики на выходе: какие показатели нужны и в каком виде их смотреть — таблица, график, выгрузка в файл.
  • Ограничения окружения: где крутится движок, нужен ли параллельный прогон набора стратегий, важна ли работа в браузере.
  • Эталонные расчёты: пример, которому вы доверяете, чтобы сверить с ним результат нашего движка.
  • Приёмка: какие сценарии обязательны и что для вас означает корректный результат бэктестинга.

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

Опишите задачу в свободной форме — вернёмся с оценкой и планом работ.

Направления блокчейн-разработки

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139