/v5/order/cancel-batch, лимитированный 10 ордерами на запрос. Чтобы обойти это ограничение и отправить сотни отмен одновременно, мы разбиваем массив ордеров на чанки и отправляем их параллельно через асинхронный пул соединений aiohttp с помощью asyncio.gather.Исходный код
При проектировании высокочастотных торговых систем (HFT) скорость реакции на изменение рынка имеет решающее значение. Например, при резком изменении рыночных условий или получении сигналов о рисках (таких как изменение греков опционов, о получении которых в реальном времени мы писали в статье Deribit WebSocket: Получение Греков Опционов в Реальном Времени с Asyncio), необходимо мгновенно очистить стакан от своих лимитных ордеров.
В отличие от простых парсеров (например, как в гайде Парсинг истории фандинга OKX на Python Asyncio: Гайд), где критична глубина выгрузки данных, в HFT-скриптах отмены ордеров на первом месте стоит сетевой round-trip time (RTT). Для еще большего ускорения и обхода лимитов на один IP-адрес профессиональные кванты применяют распределенные прокси и ротацию ключей, аналогично схеме, описанной в материале Настройка ротации API-ключей Binance для HFT на Python.
Ниже представлен готовый к продакшену класс на Python, реализующий асинхронную пакетную отмену ордеров на бирже Bybit V5 с автоматическим разбиением на чанки и параллельной отправкой запросов.
import asyncio
import hmac
import hashlib
import time
import json
import aiohttp
class BybitBatchCanceler:
def __init__(self, api_key: str, api_secret: str, demo: bool = False):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = 'https://api-demo.bybit.com' if demo else 'https://api.bybit.com'
self.endpoint = '/v5/order/cancel-batch'
def _generate_signature(self, timestamp: str, recv_window: str, payload: str) -> str:
val = timestamp + self.api_key + recv_window + payload
return hmac.new(
self.api_secret.encode('utf-8'),
val.encode('utf-8'),
hashlib.sha256
).hexdigest()
async def cancel_batch_chunk(self, session: aiohttp.ClientSession, category: str, orders: list) -> dict:
timestamp = str(int(time.time() * 1000))
recv_window = '5000'
payload_data = {
'category': category,
'request': orders
}
payload_str = json.dumps(payload_data)
signature = self._generate_signature(timestamp, recv_window, payload_str)
headers = {
'X-BAPI-API-KEY': self.api_key,
'X-BAPI-SIGN': signature,
'X-BAPI-TIMESTAMP': timestamp,
'X-BAPI-RECV-WINDOW': recv_window,
'Content-Type': 'application/json'
}
url = f'{self.base_url}{self.endpoint}'
try:
async with session.post(url, headers=headers, data=payload_str) as response:
return await response.json()
except Exception as e:
return {'retCode': -1, 'retMsg': str(e), 'result': {}}
async def cancel_all_orders(self, category: str, order_ids: list) -> list:
chunk_size = 10
chunks = [order_ids[i:i + chunk_size] for i in range(0, len(order_ids), chunk_size)]
async with aiohttp.ClientSession() as session:
tasks = []
for chunk in chunks:
formatted_orders = [{'orderId': oid} for oid in chunk]
tasks.append(self.cancel_batch_chunk(session, category, formatted_orders))
results = await asyncio.gather(*tasks)
return results
async def main():
API_KEY = 'your_api_key'
API_SECRET = 'your_api_secret'
canceler = BybitBatchCanceler(api_key=API_KEY, api_secret=API_SECRET, demo=True)
# Пример списка ID ордеров для отмены
orders_to_cancel = [
'order_id_1', 'order_id_2', 'order_id_3',
'order_id_4', 'order_id_5', 'order_id_6',
'order_id_7', 'order_id_8', 'order_id_9',
'order_id_10', 'order_id_11', 'order_id_12'
]
print(f'Запуск отмены {len(orders_to_cancel)} ордеров...')
start_time = time.perf_counter()
responses = await canceler.cancel_all_orders(category='linear', order_ids=orders_to_cancel)
end_time = time.perf_counter()
print(f'Выполнено за {end_time - start_time:.4f} сек.')
print(json.dumps(responses, indent=2))
if __name__ == '__main__':
asyncio.run(main()) Разбор параметров
category: Категория торгового инструмента на Bybit V5 (например,linearдля бессрочных USDT-фьючерсов,inverseдля коин-маржинальных контрактов илиspotдля спотового рынка).order_ids: Список уникальных идентификаторов ордеров (orderId), которые необходимо отменить. Также можно использоватьorderLinkId, изменив структуру форматирования в коде.chunk_size: Размер пакета для одного запроса. По спецификации Bybit V5 для Единого Торгового Аккаунта (UTA) максимальный размер равен 10 ордерам.asyncio.gather: Метод библиотекиasyncio, запускающий конкурентное выполнение нескольких корутин отправки запросов, что позволяет утилизировать пропускную способность сети на максимум и обойти последовательные задержки.
Как запустить
Для запуска скрипта вам понадобится интерпретатор Python версии 3.8 или выше, а также установленная библиотека aiohttp для выполнения асинхронных HTTP-запросов.
Установите зависимость с помощью пакетного менеджера pip:
pip install aiohttp Замените значения переменных API_KEY и API_SECRET на ваши реальные ключи от демо- или реального аккаунта Bybit. Если вы используете реальный аккаунт, установите параметр demo=False при инициализации класса BybitBatchCanceler. Запустите скрипт командой:
python batch_cancel.py 



