Исходный код, разобранный в статье, опубликован в этом репозитории

Хирурга не пускают в операционную, пока он не отработал шов на тренажёре сотню раз. Пилот садится в полноразмерный симулятор кабины — с теми же тумблерами, той же инерцией, тем же отказом двигателя на взлёте — задолго до первого живого рейса. Никому не приходит в голову учить пилота летать, отправив его в настоящий полёт и ожидая, пока что-нибудь сломается.
А в алготрейдинге мы делаем именно так.
Боль, о которой не пишут в туториалах
Бэктест — зелёный. Paper trading — зелёный. Стратегия математически выверена, look-ahead bias вычищен, метрики честные. Вы пишете адаптер к бирже — тот самый слой, который превращает сигнал { position: 'long', priceOpen: 66817 } в реальный ордер на бирже. И запускаете бота в live.
Дальше начинается то, о чём не пишут в README конкурентов.
Чтобы проверить, что адаптер вообще работает — что ордер уходит, плечо выставляется, positionSide в hedge-режиме не перепутан, дробление количества проходит фильтры биржи, TP/SL встают на правильную сторону — нужно дождаться торгового сигнала.
А сигнала может не быть. День. Два. Трое суток.
Стратегия по своей природе молчит большую часть времени: она ждёт условий, ждёт пробоя, ждёт новость. Вы сидите и смотрите в лог, в котором ничего не происходит. Бот жив, бот здоров, бот ждёт. И вот наступает момент. В 03:47 ночи условия совпадают, бот формирует сигнал, дёргает адаптер и… ловит баг.
ExchangeAPIException: -4061 Order's position side does not match user's setting.
Поздравляю. Вы потратили трое суток ожидания, чтобы узнать, что в hedge-режиме positionSide нужно слать LONG, а не BOTH. Правите одну строку. И снова ждёте трое суток до следующего сигнала, чтобы проверить, что на этот раз не отвалится дробление количества под stepSize.
Цикл отладки интеграции — трое суток на итерацию. При том, что сама правка — одна строчка.
Почему «пристальным взглядом» это не ловится
Очевидный контраргумент: «прочитай код адаптера глазами, найди баг до запуска».
Не работает. Интеграция с биржей — это не про логику, это про сотню микро-несовпадений с реальностью, которые невозможно увидеть в коде:
-
stepSizeиtickSizeу каждой пары свои, и биржа отбракует ордер с лишним знаком после запятой; -
минимальный нотионал (
minNotional) — ордер на $4 пройдёт, а на $3.90 нет; -
в hedge-режиме нужен
positionSide, в one-way — нельзя его слать вообще; -
reduceOnlyна закрытии, иначе откроете встречную позицию вместо выхода; -
stop_loss_limitна споте противSTOP_MARKETна фьючах — разные типы ордеров, разные поля; -
ghost-позиция: биржа показывает остаток в 0.0000001 монеты, и
closePositionуходит в бесконечный цикл.
Каждый из этих пунктов — отдельный поход в документацию биржи, который проверяется только живым вызовом. Глазами вы это не отловите, потому что баг не в вашей логике — баг в том, как ваша логика встречается с конкретными настройками конкретного аккаунта на конкретной бирже.
Стандартный путь отладки выглядит так:
-
Пишу адаптер по докам биржи
-
Запускаю live
-
Жду сигнал (часы — дни)
-
Сигнал приходит -> ловлю -4061 / -1111 / -2010
-
Правлю одну строку
-
GOTO 2
Это не отладка. Это рулетка с интервалом прокрутки в сутки.
Кнопка управления для человека
В backtest-kit есть веб-дашборд (@backtest-kit/ui), и в нём — раздел Manual Control. Это не «посмотреть состояние позиции». Это физические кнопки, которые дёргают те же самые хуки брокер-адаптера, что и реальная стратегия:

-
OPEN POSITION— открыть позицию руками, прямо сейчас; -
COMMIT AVERAGING— усредниться (тот жеonAverageBuyCommit); -
COMMIT BREAKEVEN— перенести стоп в безубыток; -
CLOSE POSITION— закрыть.

Вы проверяете не строку кода — вы проверяете, как система работает целиком: адаптер -> движок -> персистентность -> открытие позиции -> защитные ордера. Это разница между «прочитал код функции» и «увидел, как функция отработала внутри живого организма».

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

1. Ручной хедж против инсайда, которого стратегия не видит.
Ваша стратегия торгует LONG на минутных свечах — она ловит микроструктуру, локальные импульсы, но по построению слепа к макрокартине. И тут вы узнаёте раньше рынка: через час выходит решение по ставке / суд по делистингу / взлом крупной биржи — что-то, после чего рынок панически сложится. Переписывать минутную стратегию под это бессмысленно: она про другое. Но у вас открыта LONG-позиция, и она словит весь обвал.
Вы открываете руками глобальный SHORT поверх — тем же OPEN POSITION, с minuteEstimatedTime: Infinity, как зонтик над всем портфелем. Минутная стратегия продолжает свою работу как ни в чём не бывало, а ручной шорт нейтрализует системный риск ровно на то окно, пока новость не отыграется. Дашборд при этом честно покажет обе позиции и суммарный PNL — вы видите чистую экспозицию, а не гадаете. После новости закрываете шорт кнопкой и возвращаетесь к нормальному режиму.
2. Выход из положения, когда стратегия потеряла edge, но бот обязан действовать.
Реальность production-деплоя в хедж-фонде или на управляемом счёте: бот должен показывать активность для отчётности. А стратегия может неделями не видеть эффективной точки входа — рынок в боковике, условия не совпадают, edge временно испарился. Формально всё правильно: лучше не торговать, чем торговать мусор. Но перед инвестором/комплаенсом «бот ничего не делал две недели» выглядит как «бот сломался».
Ручное управление закрывает этот разрыв. Вы можете руками открыть/закрыть контролируемую позицию под отчётный период, зафиксировать осмысленное действие в JSONL-логах (с note, объясняющим причину), и при этом не калечить автоматическую логику. Стратегия остаётся чистой, а отчётность — живой. Это не костыль в коде, который потом придётся вычищать, — это операторское решение поверх неизменного движка.
3. Забрать локальный памп руками, под который стратегия не заточена.
Стратегия — это всегда специализация. Если она настроена ловить откаты к средней, она по построению не входит в вертикальный памп — для неё это «слишком далеко от точки входа, риск/прибыль не сходится». И она права в 95% случаев. Но вот конкретно сейчас вы своими глазами видите, как монета летит на объёме, и понимаете, что это движение реально.
Писать под это отдельный детектор пампа (Hawkes, CUSUM, объёмные z-скоры — у меня для этого есть отдельная библиотека) — оверкилл ради разового случая. Проще нажать OPEN POSITION, войти руками, а дальше отдать позицию движку: тот же commitBreakeven перетащит стоп в безубыток, commitAveraging усреднит на откате, трейлинг заберёт прибыль. Вы дали системе точку входа, которую стратегия пропустила, — но всё управление позицией после входа осталось автоматическим и проверенным.
Во всех трёх случаях работает одно и то же свойство: ручное действие идёт через тот же движок, что и автоматическое. Открытая руками позиция — это не «внешняя сделка мимо системы», а полноценный сигнал в персистентном хранилище, с тем же lifecycle, той же отрисовкой, теми же защитными ордерами и теми же DCA/breakeven/trailing-хуками. Оператор и алгоритм работают в одном контуре, а не в двух параллельных вселенных, которые потом не сойдутся в отчёте.
Итог: чем заменяется трое суток ожидания
|
|
Стандартный путь |
backtest-kit |
|
Как запустить проверку адаптера |
Дождаться торгового сигнала |
Нажать кнопку |
|
Время до первой ошибки |
Часы — дни |
Секунды |
|
Что проверяется |
Отдельный вызов |
Вся цепочка: адаптер, движок, персистентность, UI, защитные ордера |
|
Достоверность |
Реальный путь, но ждать |
Реальный путь, прямо сейчас |
Спасибо за внимание!
Не является торговой рекомендацией
ссылка на оригинал статьи https://habr.com/ru/articles/1049878/