WebSocket Bybit: heartbeat, обрывы и состояние, которому нельзя верить
Оборванный WebSocket — это не ошибка для лога. Это момент, когда картина мира вашего бота и реальность тихо расходятся.
Инженеры Vizanix · об авторе
- РАЗДЕЛ
- Bybit API
- ОПУБЛИКОВАНО
- 2026-08-28
- ГЛАВ
- 6
- ЧИТАТЬ ДАЛЬШЕ
- 3
- ЯЗЫК
- написано на русском
Любой WebSocket-клиент рано или поздно переподключается. Вопрос, отделяющий продакшн-бота от скрипта, — что ему разрешено делать в первые секунды после того, как соединение вернулось.
Во время обрыва биржа продолжала торговать. Ордера исполнялись. Позиции менялись. Состояние бота в памяти теперь исторический документ. Бот, который сразу после переподключения возобновляет цикл стратегии, принимает решения по этому документу.
Heartbeat — это проверка живости, а не формальность
Bybit ждёт периодический ping и отвечает pong. Ценность не в протокольной вежливости, а в том, что TCP-соединение может оставаться открытым и при этом полностью мёртвым. Файрвол выбросил поток, сокет не выдал ошибку, и бот благополучно ждёт рыночных данных, которые никогда не придут.
Отсюда правило: измеряйте время с последнего полученного сообщения, а не с последней ошибки. Если ничего не приходило дольше ожидаемого интервала — соединение мертво, что бы ни утверждал объект сокета. Разрывайте его сами.
# Живость измеряется по входящему трафику, а не по мнению сокета.
last_msg = time.monotonic()
async def watchdog(ws, idle_limit=30.0):
while True:
await asyncio.sleep(5)
if time.monotonic() - last_msg > idle_limit:
log.warning("нет данных %.1f с — принудительный reconnect", idle_limit)
await ws.close() # восстановлением займётся путь переподключения
returnНомера последовательности существуют, чтобы понять, что вы пропустили
Потоки стакана инкрементальны: снимок, а затем дельты, каждая со своим идентификатором последовательности. Если последовательность прыгнула — вы пропустили обновление, и локальный стакан неверен. Не слегка неверен, а произвольно: пропущенная дельта вполне может оказаться той, что убрала уровень, против которого вы собираетесь котироваться.
На разрыв последовательности есть ровно одна правильная реакция: выбросить локальный стакан и запросить свежий снимок. Интерполировать, игнорировать небольшие разрывы или надеяться, что «само пересинхронизируется», — это способы торговать против стакана, которого не существует.
Переподписка не происходит сама
Переподключение восстанавливает транспорт. Оно не восстанавливает ваши подписки. Обработчик, который открыл сокет и вышел, оставляет вас подключёнными к потоку, который ничего не шлёт, — а его затем справедливо убьёт watchdog выше, породив цикл переподключений, похожий на сетевую проблему и ею не являющийся.
Держите набор подписок явным состоянием и считайте переподписку частью процедуры подключения, а не чем-то, что случается после. Bybit ограничивает число аргументов в одном запросе подписки, так что большой универсум требует батчинга.
Шардируйте заранее
Один сокет на тысячу топиков — это единая точка отказа с долгим восстановлением: когда он падает, падает всё, а шторм переподписки случается ровно тогда, когда площадке и без того тяжело.
Наш конвейер свечей работает на десяти шардах. В устойчивом режиме это около 1850 топиков по 725 монетам, питающих 1450 буферов, при примерно 900 сообщениях в секунду. Когда падает один шард, девять остальных продолжают работать, и стратегия теряет десятую часть универсума на несколько секунд, а не весь.
Шардирование ещё и делает отказ читаемым. «Шард 7 лежит» — это утверждение, с которым можно что-то сделать. «WebSocket моргает» — нет.
Что бот обязан сделать до возврата к торговле
Именно этот шаг чаще всего пропускают. После переподключения, прежде чем циклу стратегии будет позволено выпустить хоть один ордер:
- Перечитать открытые ордера через REST и сверить с локальным состоянием.
- Перечитать позиции и сравнить с тем, что бот считал открытым.
- Перечитать баланс — маржа могла измениться по причинам, не связанным с этим ботом.
- Запросить свежие снимки стакана по каждому подписанному символу.
- И только затем разблокировать стратегию.
Сверка время от времени что-нибудь находит. Ордер, исполнившийся во время разрыва. Позицию, закрытую стопом. Частичное исполнение, изменившее среднюю цену. Любое из этого, обработанное по устаревшему состоянию, — это реальный убыток. И любое из этого невидимо для бота, который доверяет своей памяти.
Переподключения нормальны; незамеченные — нет
За один залогированный период наш исполнитель восстановился после 48 автоматических переподключений без единого ручного вмешательства. Эта цифра не повод для гордости — переподключения обыденны. Важно, что каждое было зафиксировано, каждое запустило сверку и ни одно не породило дублирующий ордер.
Считайте их. Частота переподключений, внезапно выросшая втрое, говорит вам что-то о сети, хосте или площадке — и говорит до инцидента, а не во время него. Этому место в том же health-выводе, что задержкам и reject rate; см. мониторинг торгового бота.
Статья описывает инженерную практику. Это не инвестиционная рекомендация. Vizanix разрабатывает программное обеспечение и не обещает торговую доходность.