Подписка на стакан Hyperliquid в реальном времени через WebSocket
-
hyperliquid orderbook websocket
-
real time hyperliquid api
-
hyperliquid стакан в реальном времени
-
hyperliquid websocket подписка
-
hyperliquid l2 book
Стакан заявок — одна из ключевых структур данных любой биржи. Он в реальном времени показывает распределение всех неисполненных лимитных ордеров по ценам и объёмам. Для квант-ботов, арбитражных систем и аналитических панелей доступ к актуальному стакану с задержкой на уровне миллисекунд часто критичен: от этого зависит, насколько корректно стратегия видит рынок.
Hyperliquid предоставляет полноценный WebSocket API, через который можно получать снапшоты и инкрементальные обновления L2-стакана. Ниже разберём формат подписки, структуру сообщений, поддержку локального стакана, переподключение и другие инженерные детали, которые важны в продакшене. Если ты не хочешь поднимать собственную инфраструктуру ради торговли бессрочниками, в OneKey Perps уже есть готовое отображение рынка в реальном времени и удобный торговый workflow.
WebSocket или REST polling
Перед техническими деталями важно понять, почему для стакана обычно используют WebSocket, а не постоянный опрос REST API.
REST polling подходит для редких запросов: получить состояние рынка, проверить позицию, загрузить исторические данные. Но для стакана в реальном времени он быстро упирается в задержки, лимиты запросов и риск пропустить быстрые изменения ликвидности.
WebSocket держит постоянное соединение и позволяет серверу пушить обновления сразу после изменения стакана. Для сценариев, где важны bid/ask, спред, глубина и реакция на изменение ликвидности, WebSocket практически незаменим. Полные спецификации лучше сверять с официальной документацией Hyperliquid.
Установка WebSocket-соединения
Endpoint
WebSocket endpoint Hyperliquid, актуальность которого нужно сверять с официальной документацией:
wss://api.hyperliquid.xyz/ws
Базовый пример подключения
import asyncio
import json
import websockets
WS_URL = "wss://api.hyperliquid.xyz/ws"
async def connect_and_subscribe():
async with websockets.connect(WS_URL) as ws:
# Отправляем сообщение подписки
subscribe_msg = {
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC"
}
}
await ws.send(json.dumps(subscribe_msg))
# Постоянно принимаем сообщения
async for raw_msg in ws:
msg = json.loads(raw_msg)
handle_message(msg)
asyncio.run(connect_and_subscribe())
Формат подписки
Подписка на L2-стакан
Подписка на L2-стакан BTC:
{
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC"
}
}
Hyperliquid позволяет подписываться на несколько торговых пар одновременно. Для этого достаточно отправить отдельное сообщение подписки по каждому инструменту: обновления будут приходить независимо.
Отписка
{
"method": "unsubscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC"
}
}
Типы сообщений: снапшот и инкрементальные обновления
Начальный снапшот
После подписки сервер сначала отправляет полный снимок стакана: все актуальные ценовые уровни и объёмы на них.
{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1714500000000,
"levels": [
[
[{"px": "64000.0", "sz": "0.5", "n": 3}],
[{"px": "64010.0", "sz": "0.3", "n": 2}]
]
]
}
}
px— цена.sz— совокупный объём заявок на этом уровне. Если значение равно0, уровень считается очищенным.n— количество заявок на ценовом уровне.
Bids обычно идут от высокой цены к низкой, asks — от низкой к высокой.
Delta-обновления
После снапшота сервер отправляет только изменившиеся ценовые уровни:
{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1714500000100,
"levels": [
[
[{"px": "63990.0", "sz": "0.0", "n": 0}],
[{"px": "64010.0", "sz": "0.8", "n": 4}]
]
]
}
}
Если sz равен "0" или 0, значит все заявки на этом уровне были исполнены или отменены. Такой уровень нужно удалить из локального стакана.
Поддержка локального стакана
Главная сложность при работе с WebSocket-данными — корректно поддерживать локальное состояние стакана.
from sortedcontainers import SortedDict
class LocalOrderBook:
def __init__(self, coin):
self.coin = coin
self.bids = SortedDict(lambda x: -float(x)) # bids: цена от высокой к низкой
self.asks = SortedDict(lambda x: float(x)) # asks: цена от низкой к высокой
self.last_time = None
def apply_snapshot(self, data):
self.bids.clear()
self.asks.clear()
levels = data["levels"][0]
for bid in levels[0]:
if float(bid["sz"]) > 0:
self.bids[bid["px"]] = float(bid["sz"])
for ask in levels[1]:
if float(ask["sz"]) > 0:
self.asks[ask["px"]] = float(ask["sz"])
self.last_time = data["time"]
def apply_delta(self, data):
levels = data["levels"][0]
for bid in levels[0]:
px, sz = bid["px"], float(bid["sz"])
if sz == 0:
self.bids.pop(px, None)
else:
self.bids[px] = sz
for ask in levels[1]:
px, sz = ask["px"], float(ask["sz"])
if sz == 0:
self.asks.pop(px, None)
else:
self.asks[px] = sz
self.last_time = data["time"]
@property
def best_bid(self):
return next(iter(self.bids.items()), None)
@property
def best_ask(self):
return next(iter(self.asks.items()), None)
@property
def mid_price(self):
bid = self.best_bid
ask = self.best_ask
if bid and ask:
return (float(bid[0]) + float(ask[0])) / 2
return None
@property
def spread(self):
bid = self.best_bid
ask = self.best_ask
if bid and ask:
return float(ask[0]) - float(bid[0])
return None
Логика обработки сообщений
Нужно отличать начальный снапшот от последующих delta-обновлений и применять их к локальному состоянию.
order_book = LocalOrderBook("BTC")
is_initialized = False
def handle_message(msg):
global is_initialized
if msg.get("channel") != "l2Book":
return
data = msg["data"]
if not is_initialized:
order_book.apply_snapshot(data)
is_initialized = True
print(
f"Стакан инициализирован: "
f"{len(order_book.bids)} bid-уровней, "
f"{len(order_book.asks)} ask-уровней"
)
else:
order_book.apply_delta(data)
# Печатаем текущий top of book
print(
f"Best bid: {order_book.best_bid}, "
f"best ask: {order_book.best_ask}, "
f"spread: {order_book.spread}"
)
Переподключение при обрыве соединения
WebSocket-соединение может оборваться из-за сетевых проблем или действий сервера. В продакшене обязательно нужен автоматический reconnect.
import asyncio
import json
import websockets
async def subscribe_with_reconnect(coin, handle_fn, max_retries=None):
retries = 0
while max_retries is None or retries < max_retries:
try:
async with websockets.connect(
WS_URL,
ping_interval=20,
ping_timeout=10
) as ws:
retries = 0 # успешное подключение — сбрасываем счётчик
is_initialized = False
await ws.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": coin}
}))
async for raw_msg in ws:
handle_fn(json.loads(raw_msg))
except (websockets.ConnectionClosed, ConnectionError, OSError) as e:
retries += 1
wait = min(2 ** retries, 60) # exponential backoff, максимум 60 секунд
print(f"Соединение разорвано ({e}), повтор через {wait} сек. Попытка #{retries}")
is_initialized = False # после reconnect ждём новый снапшот
await asyncio.sleep(wait)
При переподключении нужно сбрасывать флаг is_initialized, потому что после нового соединения сервер снова отправит полный снапшот, а не продолжит поток delta-сообщений с места обрыва.
Timestamp и порядок сообщений
Сообщения WebSocket Hyperliquid содержат timestamp. Его можно использовать для базовой проверки порядка сообщений. В продакшене стоит делать следующее:
- хранить timestamp последнего обработанного сообщения;
- если новое сообщение старше уже обработанного, считать его устаревшим и отбрасывать;
- если долго нет новых сообщений, отправлять ping или принудительно переподключаться;
- для высокочастотных стратегий рассмотреть инфраструктуру с меньшей сетевой задержкой: более близкие узлы, стабильный канал, специализированный хостинг.
Подписка на несколько монет
Один WebSocket-канал может обслуживать несколько подписок. Обычно лучше переиспользовать одно соединение, чем создавать отдельный socket под каждую пару.
coins = ["BTC", "ETH", "SOL"]
order_books = {coin: LocalOrderBook(coin) for coin in coins}
async def multi_subscribe():
async with websockets.connect(WS_URL) as ws:
for coin in coins:
await ws.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": coin}
}))
async for raw_msg in ws:
msg = json.loads(raw_msg)
if msg.get("channel") == "l2Book":
coin = msg["data"]["coin"]
if coin in order_books:
order_books[coin].apply_delta(msg["data"])
В реальной системе для каждой монеты также нужно отдельно отслеживать состояние инициализации, timestamp последнего сообщения и health checks.
Где используется стакан в реальном времени
Типичные сценарии применения L2-данных:
- Торговые боты: динамическая перестановка лимитных ордеров с учётом спреда и глубины.
- Арбитраж: мониторинг разницы цен между Hyperliquid и другими площадками, например dYdX или GMX.
- Аналитические панели: визуализация глубины стакана, истории спреда и распределения ликвидности.
- Риск-мониторинг: алерты при резком расширении спреда, исчезновении ликвидности или аномальном движении best bid / best ask.
OneKey Perps: без собственной инфраструктуры
Поднимать WebSocket-инфраструктуру, поддерживать локальные стаканы, следить за reconnection logic и хранить tick data — это отдельная инженерная задача. Если твоя цель не разработка квант-системы, а практичная торговля бессрочниками, проще использовать готовый интерфейс.
OneKey Perps даёт отображение рынка в реальном времени и торговые функции в одном workflow. В связке с аппаратным кошельком OneKey ты получаешь безопасное подписание транзакций и можешь сосредоточиться на торговом решении, а не на поддержке низкоуровневого data pipeline.
Если ты подключаешься к Web3-приложениям через WalletConnect, ориентируйся на документацию WalletConnect. OneKey поддерживает стандартный протокол WalletConnect и позволяет безопасно взаимодействовать с dApp, включая приложения экосистемы Hyperliquid.
Ненавязчивый следующий шаг: скачай или открой OneKey, подключи кошелёк и попробуй OneKey Perps на небольшом размере позиции, предварительно разобравшись с рисками плеча и бессрочных контрактов.
Частые вопросы
Q1: Есть ли лимиты на количество WebSocket-соединений?
Да, лимиты могут существовать. Актуальные ограничения нужно проверять в официальной документации Hyperliquid. На практике лучше не открывать слишком много соединений с одного IP: для нескольких пар обычно разумнее использовать одно WebSocket-соединение и несколько подписок внутри него.
Q2: Если потерять одно delta-сообщение, локальный стакан станет неправильным?
Да. Если delta-обновление потерялось из-за сети или сбоя обработки, локальный стакан может разойтись с реальным состоянием на сервере. Если ты видишь признаки рассинхронизации — например, аномальный спред, резкий скачок top of book или долгое отсутствие сообщений — лучше закрыть соединение и подписаться заново, чтобы получить свежий снапшот.
Q3: Можно ли использовать real-time стакан для бэктеста?
Напрямую — не очень удобно. Для бэктеста нужны исторические данные, а WebSocket даёт текущий поток. Рабочий подход — сохранять real-time данные как tick data, накапливать историю и затем использовать её для исследований. Для грубого первичного тестирования можно также комбинировать это с историческими K-line API Hyperliquid.
Q4: Как понять, что WebSocket-соединение здоровое?
Отслеживай как минимум два показателя:
- сколько времени прошло с последнего полученного сообщения;
- насколько адекватна ping/pong-задержка.
Если последнее сообщение было слишком давно, например больше заданного порога в 5 секунд, или ping/pong начинает деградировать, запускай принудительный reconnect.
Q5: OneKey-кошелёк увеличивает задержку торговли?
Подписание через аппаратный кошелёк OneKey обычно занимает сотни миллисекунд, и для большинства ручных или полуавтоматических стратегий это не является критичным фактором. Для ультравысокочастотных сценариев, где заявки отправляются десятки раз в секунду, можно разделять роли: горячий кошелёк или API Agent-субаккаунт для частых подписей, а основной капитал — под защитой OneKey.
Итоги
Подписка на стакан в реальном времени — базовый компонент качественной торговой или аналитической системы. WebSocket API Hyperliquid устроен достаточно понятно: ты подписываешься на l2Book, получаешь начальный снапшот, затем применяешь delta-обновления и следишь за корректностью локального состояния.
Если ты строишь маркет-мейкинг-бота, арбитражную систему или аналитический dashboard, этот подход даст хороший фундамент. Если же тебе нужен не backend для стаканов, а удобный вход в торговлю ончейн-бессрочниками, используй OneKey Perps: готовый интерфейс, real-time рынок и безопасное подписание через OneKey.
Риск-предупреждение: данные стакана отражают состояние рынка только в конкретный момент времени и не должны быть единственной основой для торгового решения. Автоматические стратегии на основе стакана могут привести к значительным убыткам из-за волатильности, технических сбоев, потери сообщений или ошибок в логике стратегии. Торговля бессрочными контрактами и использование плеча связаны с высоким риском, включая риск полной потери капитала. Этот материал предназначен только для технического ознакомления и не является инвестиционной, финансовой или юридической рекомендацией.



