Лимиты Bybit: очередь запросов, которая переживёт плохую минуту
Лимиты никогда не мешают на спокойном рынке. Они мешают в ту минуту, когда боту срочно надо снять ордер, — и проектировать надо именно под неё.
Инженеры Vizanix · об авторе
- РАЗДЕЛ
- Bybit API
- ОПУБЛИКОВАНО
- 2026-08-28
- ГЛАВ
- 6
- ЧИТАТЬ ДАЛЬШЕ
- 3
- ЯЗЫК
- написано на русском
Ограничение частоты выглядит задачей про пропускную способность, а на деле это задача про планирование. Максимальная частота запросов боту нужна редко. Ему нужно, чтобы правильный запрос ушёл первым, когда всё происходит одновременно.
Представьте сценарий, который реально стоит денег. Цена прыгает гэпом. Три позиции одновременно достигают условий стопа. Поток рыночных данных захлёбывается. Логика котирования хочет обновить сорок ордеров. И где-то в этой очереди стоит отмена, защищающая позицию. Если очередь работает по принципу «первым пришёл — первым ушёл», эта отмена уйдёт после сорока обновлений котировок.
Два ведра, а не одно
Bybit считает лимиты не по одной оси: отдельно по IP и отдельно по UID аккаунта. Один глобальный счётчик внутри бота не моделирует корректно ни то, ни другое. Два бота на одном хосте делят бюджет IP; один бот с двумя ключами делит бюджет UID по каждому ключу, но не между ними.
Моделируйте как есть: ведро по IP и ведро по UID, и запрос обязан получить токен из обоих, прежде чем уйдёт. Это несколько десятков строк, и они убирают целую категорию загадочных отказов.
class Bucket:
"""Token bucket. Пополняется непрерывно, а не пачками по таймеру."""
def __init__(self, rate_per_sec, burst):
self.rate, self.capacity = rate_per_sec, burst
self.tokens, self.ts = burst, time.monotonic()
async def take(self, n=1):
while True:
now = time.monotonic()
self.tokens = min(self.capacity,
self.tokens + (now - self.ts) * self.rate)
self.ts = now
if self.tokens >= n:
self.tokens -= n
return
await asyncio.sleep((n - self.tokens) / self.rate)
async def send(req):
await ip_bucket.take() # оба бюджета, всегда
await uid_bucket[req.key].take()
return await http(req)Приоритет — то, что действительно важно
Присвойте каждому запросу класс и обслуживайте очередь по классу, а не по времени поступления. Рабочий порядок:
| Приоритет | Запрос | Почему |
|---|---|---|
| 0 — высший | Отмена, reduce-only, аварийное закрытие | Снижает риск. Не должен ждать ни за чем. |
| 1 | Постановка stop-loss и take-profit | Защищает открытую позицию, у которой сейчас нет прикрытия. |
| 2 | Ордера на вход | Пропущенный вход стоит возможности, а не капитала. |
| 3 | Обновление котировок, amend | Большой объём, малая ценность каждого. |
| 4 — низший | Опрос баланса, позиций, инструментов | Это и так должно приходить из WebSocket. |
Дисциплина, которую это навязывает, полезна и за пределами лимитов: она заставляет записать в коде, что снижение риска важнее выражения мнения. Большинство ботов, которые ломаются под нагрузкой, нигде этого не записали.
Отступ с джиттером и с потолком
На 10006 слишком много запросов неправильная реакция — немедленный повтор: он углубляет яму. Правильная — экспоненциальный отступ со случайным джиттером, чтобы бот с множеством параллельных воркеров не синхронизировал их в стадо.
Добавьте потолок и алерт. Отступ, который удваивается уже две минуты, — это не временный сбой; что-то сломано структурно, и человек должен об этом знать. Тихий бесконечный повтор — это то, как бот остаётся «поднятым», ничего при этом не делая.
Повторам нужен клиентский ID ордера
Таймаут при постановке ордера — опасный случай: запрос мог пройти. Слепой повтор способен породить две позиции. Клиентский идентификатор ордера делает повтор идемпотентным: площадка распознаёт дубль и отклоняет его вместо исполнения.
Генерируйте идентификатор до первой попытки, сохраняйте его для всех повторов этого логического ордера и храните рядом с локальным состоянием, чтобы сверка после обрыва могла сопоставить по нему.
Пусть площадка сама скажет, где вы
В ответах приходят заголовки с остатком квоты. Читайте их и возвращайте в свои вёдра вместо доверия собственному счётчику: ваша модель может разойтись с реальностью после повторов, перезапусков или из-за второго процесса на том же IP.
Выводите остаток бюджета метрикой. Квота, которая в обычном режиме ползёт к нулю, сообщает, что следующая волатильная минута будет болезненной, — и сообщает заранее.
Лучше снизить нагрузку, чем лучше её планировать
Самый дешёвый запрос — тот, который вы не отправили. Большинство ботов, воюющих с лимитами, опрашивают то, что WebSocket и так присылает: позиции, балансы, статус ордеров. Подпишитесь один раз, держите локальное состояние, а REST используйте для сверки после переподключения и по медленному таймеру как страховку.
Помогает и батчинг там, где площадка его поддерживает: пакетные отмены и amend превращают худший момент котировального движка из сорока запросов в несколько. Одно это изменение обычно стоит больше, чем любая настройка очереди.
Статья описывает инженерную практику. Это не инвестиционная рекомендация. Vizanix разрабатывает программное обеспечение и не обещает торговую доходность.