WebSocket API
Потоковая передача данных в реальном времени для торговли опционами на Hypercall.
Откройте интерактивный справочник WebSocket API: в нём удобнее просматривать живые примеры и описание схем.
Скачайте спецификацию AsyncAPI для программного использования.
В объектах сообщений WebSocket могут появляться новые поля без предварительного уведомления. Клиенты обязаны игнорировать поля, которые им неизвестны. Hypercall не удаляет поля из существующей схемы сообщения.
Подключение
Подключитесь к wss://HOST/ws:
Эндпоинты:
- Продакшн:
wss://api.hypercall.xyz/ws - Локально:
ws://localhost:3000/ws
Тестнет временно отключён, пока Hypercall не получит больше HYPE в тестнете.
Сжатие
/ws поддерживает permessage-deflate по RFC 7692. Включите его в клиенте, чтобы сократить трафик рыночных данных: на записанном продакшн-трафике независимо сжатые фреймы примерно на 45% меньше, а пакеты индексных цен примерно на 70% меньше.
- Клиент предлагает расширение в запросе на апгрейд, например
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits. - Если сервер принимает расширение, ответ
101содержитSec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover. В этом случае каждое сообщение сервера сжимается отдельно, без общего скользящего окна, а сообщения размером менее нескольких сотен байт по-прежнему отправляются без сжатия. Библиотека распаковывает их прозрачно. - Если в ответе нет заголовка
Sec-WebSocket-Extensions, все фреймы передаются без сжатия. Сжатие включается отдельно для каждого окружения, поэтому клиенты должны поддерживать оба варианта; JSON-сообщения в обоих случаях идентичны. Продакшн пока не принимает расширение, поэтому сейчас фреймы в продакшне не сжимаются. - Клиент может сжимать отправляемые данные. Сервер распаковывает каждое сообщение клиента объёмом до 256 KiB и закрывает соединение, если сообщение превышает этот предел.
Браузеры согласуют расширение автоматически. Большинству библиотек изменения не нужны:
Python (websockets включает сжатие по умолчанию):
import websockets
async with websockets.connect("wss://api.hypercall.xyz/ws", compression="deflate") as ws:
...
Node.js (ws):
import WebSocket from "ws";
const ws = new WebSocket("wss://api.hypercall.xyz/ws", { perMessageDeflate: true });
Rust: WsClient из hypercall-client по умолчанию согласует permessage-deflate (см. Пример: клиент на Rust). Обычные tungstenite и tokio-tungstenite пока его не реализуют, поэтому подключаются без сжатия. Чтобы использовать сжатие со своим сокетом, возьмите клиент с его поддержкой, например yawc:
let url = "wss://api.hypercall.xyz/ws".parse()?;
let mut ws = yawc::WebSocket::connect(url)
.with_options(yawc::Options::default().with_low_latency_compression())
.await?;
Идентификация кошелька
Чтобы получать данные по аутентифицированным каналам (заявки, исполнения, портфель), после подключения идентифицируйте свой кошелёк сообщением Authenticate:
{"type": "Authenticate", "wallet": "0x1234..."}
Сервер отвечает подтверждением:
{"type": "Authenticated", "wallet": "0x1234..."}
После получения Authenticated можно подписываться на аутентифицированные каналы. Если адрес кошелька некорректен, сервер отвечает сообщением Error, а соединение остаётся открытым.
Параметр запроса ?wallet= пока поддерживается для обратной совместимости, но считается устаревшим и будет удалён в одном из следующих релизов. Используйте описанный выше способ через сообщение.
Поддержание соединения
Сервер применяет WebSocket-heartbeat:
- Отправляет управляющий фрейм
Pingкаждые 20 секунд - Ожидает ответный
Pongв течение 60 секунд - Закрывает соединение с кодом
1008и причинойpong timeout, если клиент перестаёт отвечать
Браузерные реализации WebSocket обрабатывают ping/pong автоматически. Многие Rust-библиотеки для WebSocket, включая tungstenite и tokio-tungstenite, тоже сами обрабатывают управляющие фреймы ping/pong. Прежде чем добавлять ручную обработку Pong, сверьтесь с документацией своей библиотеки. Собственные реализации и реализации на «сырых» сокетах обязаны отвечать на фреймы Ping фреймами Pong.
Восстановление медленного потребителя
Сервер закрывает соединение /ws, если оно не успевает выгрузить исходящие данные в пределах настроенных защитных лимитов по числу сообщений, объёму закодированных байт, возрасту очереди или времени записи в сокет. Если соединение ещё способно принять фрейм закрытия, сервер использует код 1008 и компактную JSON-причину:
{"error":"slow_consumer","class":"ordered_public","cause":"message_age","recovery":"snapshot_resubscribe"}
Поля причины:
| Поле | Значение |
|---|---|
class | Класс доставки, фрейм которого превысил защитный предел. |
cause | message_limit, byte_limit, message_age или write_timeout. |
recovery | Обязательное следующее действие, например resubscribe, snapshot_resubscribe, portfolio_refetch или rest_reconcile. |
После любого разрыва переподключитесь, при необходимости снова идентифицируйте кошелёк, повторно подпишитесь и сверьте текущее состояние, прежде чем обрабатывать новые события. Упорядоченным публичным каналам нужен свежий снимок. Приватные каналы событий требуют сверки через авторитетный REST-интерфейс, поскольку воспроизведение по курсору пока недоступно. Полностью зависшее соединение может оборваться раньше, чем клиент прочитает причину закрытия, поэтому этот порядок восстановления нужно применять и при некорректном закрытии.
Используйте отдельные соединения для высокочастотных публичных рыночных данных и для аутентифицированных команд или приватных потоков. Классы доставки определяют метрики и поведение при восстановлении, но фреймы одного соединения всё равно проходят через единый упорядоченный путь записи в сокет. Поэтому зависшая запись публичных данных может задержать последующие приватные фреймы на том же соединении, пока дедлайн записи не закроет его.
Подписка на каналы
Чтобы подписаться, отправьте JSON-сообщение:
{"type": "Subscribe", "channel": "orderbook"}
Чтобы отписаться:
{"type": "Unsubscribe", "channel": "orderbook"}
Вы получите подтверждение:
{"type": "Subscribed", "channel": "orderbook"}
Фильтрация по символам
Каналы order_updates и fills поддерживают необязательный фильтр symbols. Если он задан, сервер отправляет только сообщения, базовый актив которых совпадает с одним из указанных символов.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
Принимаются как тикеры базовых активов ("BTC"), так и полные имена инструментов ("BTC-20260131-100000-C"). Чтобы добавить символы, отправьте ещё один Subscribe. Чтобы убрать конкретные символы:
{"type": "Unsubscribe", "channel": "order_updates", "symbols": ["BTC"]}
Если symbols не указаны, пересылаются все обновления по вашему кошельку.
Публичные каналы orderbook, trades, indicative_market_data и indicative_market_data_v2 тоже принимают symbols, но сопоставляют их с точными символами инструментов (без учёта регистра), а не с базовыми активами:
{"type": "Subscribe", "channel": "orderbook", "symbols": ["BTC-20260131-100000-C"]}
Без symbols эти каналы передают данные по всем инструментам.
Фильтрация опционной доски
Канал options_chain поддерживает фильтрацию по символам инструментов, дате экспирации и типу опциона:
{
"type": "Subscribe",
"channel": "options_chain",
"symbols": ["BTC-20260131-100000-C"],
"expiry": "2026-01-31",
"option_type": "call"
}
| Фильтр | Значения | По умолчанию |
|---|---|---|
symbols | Массив полных символов инструментов (например, ["BTC-20260131-100000-C"]) | Все инструменты |
expiry | Строка даты "YYYY-MM-DD", сопоставляется с календарной датой экспирации инструмента по UTC | Все экспирации |
option_type | "call", "put", "both" или не указан (оба) | Оба |
Пакетная доставка
indicative_market_data_v2 передаёт те же строки, что и indicative_market_data, но по несколько строк в одном фрейме:
{"type": "Subscribe", "channel": "indicative_market_data_v2", "symbols": ["BTC-20260131-100000-C"]}
Сервер подтверждает подписку с пакетным маркером:
{"type": "Subscribed", "channel": "indicative_market_data_v2", "batch": true}
Затем он удерживает каждую строку не дольше одного пакетного окна (по умолчанию 25 мс) и отправляет строки, опубликованные за это окно, одним фреймом IndicativeMarketDataBatch. Пакет также отправляется досрочно, как только в нём набирается 256 строк.
- Определение поддержки: только подтверждение с
"batch": trueозначает, что сервер работает в пакетном режиме. Серверы, выпущенные до появления этого канала, подтверждают любое имя канала без маркера и затем ничего не присылают, а текущие серверы отвечают на неизвестный канал ошибкойunknown_channel. В обоих случаях отпишитесь отindicative_market_data_v2и подпишитесь наindicative_market_dataна том же соединении. - Задержка: строка приходит максимум на одно окно позже, чем в
indicative_market_data. На продакшн-трафике с окном по умолчанию строки ждали в среднем 12 мс и никогда не дольше 25 мс. Если вы реагируете на отдельные обновления котировок в пределах миллисекунд, используйте канал v1. - Трафик: само по себе пакетирование экономит около 7% байт. Основная экономия достигается вместе с
permessage-deflate: сжатые пакеты примерно в 9,8 раза меньше несжатых однострочных фреймов при подписке без фильтра и примерно в 8,3 раза меньше для одной опционной доски, тогда как построчное сжатие даёт лишь около 1,8 раза. - Порядок: строки в
updatesидут в порядке публикации, и пакеты приходят по порядку. Строки не схлопываются, поэтому один инструмент может встретиться в пакете несколько раз. Применяя строки по порядку, вы получите то же состояние, что и на канале v1. - Снимок: снимок при подписке также приходит пакетными фреймами.
- Фильтры:
symbolsработает так же, как вindicative_market_data. При сужении фильтра или отписке сервер сначала отправляет строки, уже ожидающие отправки по старому фильтру, а затем подтверждениеUnsubscribed, поэтому строки не теряются и не дублируются. - Независимые каналы: одно соединение может подписаться на оба канала с разными фильтрами.
indicative_market_dataне изменился.
Доступные каналы
| Канал | Требуется аутентификация | Описание |
|---|---|---|
orderbook | Нет | Обновления книги заявок L2 по всем символам |
trades | Нет | Публичная лента сделок |
market_updates | Нет | Изменения листинга рынков (создан/удалён/истёк) |
options_chain | Нет | Инкрементальные обновления опционной доски (фильтр по symbols, expiry, option_type) |
index_prices | Нет | Спотовые/индексные цены всех базовых активов в реальном времени |
indicative_market_data | Нет | Поток котировщиков из списка допуска. Пока не общедоступен |
indicative_market_data_v2 | Нет | Тот же поток в пакетных фреймах. См. Пакетная доставка |
order_updates | Да | Изменения статуса ваших заявок (фильтр по символу) |
fills | Да | Исполнения ваших сделок (фильтр по символу) |
portfolio | Да | Обновления ваших позиций и баланса |
liquidation | Да | Изменения вашего состояния ликвидации |
rfq | Да | Котировки RFQ, обновления статуса и уведомления об исполнении |
experimental_order_updates, experimental_combo_fills | Да | Экспериментальные комбо-каналы. См. Experimental Combo API |
experimental_combo_trades | Нет | Экспериментальные публичные комбо-сделки |
Данные соревнований и лидербордов не отдаются через api.hypercall.xyz ни по WebSocket, ни по REST. Подписка на выведенные из эксплуатации каналы competition или competition_engagement возвращает Error. Торговое приложение получает данные соревнований по HTTP от аналитического сервиса https://api-ui.hypercall.xyz, например GET /v2/competitions, GET /v2/competitions/{id}, GET /v2/competitions/leaderboard и GET /v2/competitions/all-time/leaderboard.
Типы сообщений
Размещение заявки (аутентифицированное)
Размещение заявки через командный путь WebSocket.
{
"type": "PlaceOrder",
"wallet": "0x1234...",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"size": "1",
"price": "100",
"tif": "gtc",
"route": "book_only",
"client_id": "my-order-1",
"nonce": 1780000000000,
"signature": "0x..."
}
Значение nonce выше приведено для иллюстрации. Генерируйте новый nonce для каждой заявки из текущего Unix-времени в миллисекундах, например Date.now() в JavaScript. Скопированное фиксированное значение выйдет за пределы допустимого окна и будет отклонено.
| Поле | Тип | Описание |
|---|---|---|
wallet | string | Адрес кошелька, которому принадлежит заявка |
symbol | string | Символ опциона |
side | string | "Buy" или "Sell" |
size | string | Размер контракта, в точности совпадающий с подписанным значением |
price | string | Лимитная цена, в точности совпадающая с подписанным значением |
tif | string | Необязательный срок действия заявки (time-in-force), по умолчанию "gtc" |
route | string | Используйте "book_only". Заявка без route пока принимается, но этот вариант устарел, и срок его отключения уже прошёл. |
client_id | string | Необязательный клиентский ID заявки |
reduce_only | boolean | Необязательно, по умолчанию false. Заявка reduce-only подписывается как PlaceOrderReduceOnly и требует явного route |
mmp_enabled | boolean | Необязательно, по умолчанию false |
builder_code_address | string | Необязательный кошелёк builder code |
nonce | integer | Обязательный nonce подписи: текущее Unix-время в миллисекундах. Должен находиться в пределах двух дней до и одного дня после текущего времени. Каждый nonce принимается один раз для каждого подписанта, при этом заявки через REST и WebSocket используют общее пространство nonce. Повторно отправленный фрейм отклоняется. |
signature | string | Подпись EIP-712 PlaceOrder |
Соединение должно предварительно отправить Authenticate; иначе сервер ответит {"type": "Error", "message": "Not authenticated"}. Подписантом должен быть wallet либо одобренный для него агент.
Сейчас WebSocket-команда PlaceOrder направляет заявку непосредственно в книгу заявок. route="best_execution" и route="rfq_only" на WebSocket отклоняются, поскольку этот путь пока не выполняет маршрутизацию RPI/RFQ. Для best_execution используйте POST /order.
На принятую заявку сервер отвечает на том же соединении сообщением OrderResult:
{
"type": "OrderResult",
"order_id": 12345,
"status": "ACKED",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"price": "100",
"size": "1"
}
Если заявка не принята, добавляется поле reason. Ошибки валидации, подписи и лимитов запросов приходят как Error, у которого message начинается с Order placement failed:. Последующие изменения статуса приходят в order_updates.
Обновление книги заявок
Снимок/обновление книги заявок L2 по символу.
{
"type": "OrderbookUpdate",
"symbol": "BTC-20260131-100000-C",
"option_token_address": "0x...",
"bids": [["95000.5", "10.5"], ["94999.0", "25.0"]],
"asks": [["95001.0", "8.0"], ["95002.5", "15.0"]],
"timestamp": 1737331200000
}
| Поле | Тип | Описание |
|---|---|---|
symbol | string | Символ опциона |
option_token_address | string? | Ончейн-адрес токена опциона, если известен |
bids | array | Уровни бида в виде кортежей [price, size], размер в контрактах в удобочитаемом формате |
asks | array | Уровни аска в виде кортежей [price, size], размер в контрактах в удобочитаемом формате |
timestamp | integer | Временная метка Unix (миллисекунды) |
Сделка
Публичное событие сделки.
{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "Buy",
"timestamp": 1737331200000
}
| Поле | Тип | Описание |
|---|---|---|
symbol | string | Символ опциона |
price | string | Цена сделки в USD |
size | string | Объём сделки в контрактах |
side | string | Сторона агрессора (Buy или Sell) |
timestamp | integer | Временная метка Unix (миллисекунды) |
Исполнение (аутентифицированное)
Уведомление об исполнении вашей сделки.
{
"type": "Fill",
"order_id": 12345,
"fill_id": 67890,
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"price": "0.0523",
"size": "5.0",
"timestamp": 1737331200000,
"wallet_address": "0x1234...abcd",
"fee": "0",
"trade_id": 99999,
"is_taker": true,
"builder_code_address": null,
"builder_code_fee": null,
"instrument_type": "option"
}
| Поле | Тип | Описание |
|---|---|---|
order_id | integer | ID вашей заявки |
fill_id | integer | ID исполнения |
symbol | string | Символ опциона |
side | string | Ваша сторона в сделке (Buy или Sell) |
price | string | Цена исполнения в USD |
size | string | Объём исполнения в контрактах |
timestamp | integer | Временная метка Unix (миллисекунды) |
wallet_address | string | Адрес вашего кошелька |
fee | string | Списанная торговая комиссия. Возвращает 0, пока комиссии площадки на запуске отключены |
trade_id | integer | Уникальный ID сделки |
is_taker | boolean | Были ли вы тейкером |
builder_code_address | string? | Кошелёк builder code (если есть) |
builder_code_fee | string? | Комиссия builder code. Возвращает null, пока комиссии площадки на запуске отключены |
instrument_type | string | "option" |
Обновление портфеля (аутентифицированное)
Отправляется в канале portfolio для позиций, балансов, маржи и греков. При подписке сервер отправляет снимок Initial, а затем инкрементальные обновления.
type каждого фрейма содержит вид обновления:
| Вид обновления | Поля помимо timestamp |
|---|---|
Initial | positions |
PositionUpdate | position |
BalanceUpdate | total_margin_used |
MarginUpdate | span_margin, margin_summary, margin_mode, total_margin_used, available_balance |
GreeksUpdate | per_leg, aggregate |
timestamp во фреймах portfolio указывается в секундах Unix, в отличие от миллисекундных меток в других каналах.
Пример обновления греков:
{
"type": "GreeksUpdate",
"timestamp": 1737331200,
"per_leg": [
{
"symbol": "BTC-20260131-100000-C",
"quantity": "2.0",
"delta": 0.91,
"gamma": 0.003,
"theta": -0.12,
"vega": 0.44,
"iv": 0.63
}
],
"aggregate": {
"delta": 0.91,
"gamma": 0.003,
"theta": -0.12,
"vega": 0.44,
"iv": 0.63
}
}
Для пустых портфелей обновления греков содержат:
per_leg: []aggregate: null
Обновление заявки (аутентифицированное)
Уведомление об изменении статуса заявки. Обновления PARTIALLY_FILLED включены.
filled_size — это накопленный исполненный объём по всем исполнениям заявки,
а не объём последнего исполнения.
{
"type": "OrderUpdate",
"order_id": 12345,
"client_id": "my-order-1",
"request": {
"price": "0.0523",
"size": "10.0",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"tif": "gtc"
},
"status": "PARTIALLY_FILLED",
"filled_size": "0.75",
"timestamp": 1767225600000,
"reason": null,
"wallet_address": "0x0000000000000000000000000000000000000001",
"instrument_type": "option"
}
status принимает одно из значений ACKED, OPEN, PARTIALLY_FILLED, FILLED, CANCELED или REJECTED. client_id опускается, если у заявки его нет. instrument_type равен "option" или "perp".
Обновление рынка
Изменения листинга рынков.
Рынок создан:
{
"type": "MarketUpdate",
"action": "Created",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1737331200000
}
Рынок истёк:
{
"type": "MarketUpdate",
"action": "Expired",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1738281600000
}
action принимает значения Created, Expired, Deleted (содержит только symbol и timestamp) или ComboCreated (см. Experimental Combo API).
Обновление опционной доски
Отправляется в options_chain при изменении листинга или лучших цен в его книге заявок. action принимает значение Upsert или Remove.
{
"type": "OptionsChainUpdate",
"action": "Upsert",
"currency": "BTC",
"expiry": 1769846400,
"row": {
"strike": 100000.0,
"call": {
"symbol": "BTC-20260131-100000-C",
"bid_price_usd": 1520.0,
"ask_price_usd": 1580.0
}
},
"timestamp": 1737331200000
}
Строка Upsert содержит одну ногу (call или put) с полями ноги из снимка опционной доски: symbol, option_token_address, цены бид и аск, IV, размер в контрактах и номинал в USD, greeks_abs и greeks_cash (пример выше сокращён). Недоступные значения равны null; нога, не затронутая обновлением, опускается. Remove содержит currency, expiry, strike, option_type, symbol и timestamp и отправляется, когда инструмент удаляется, истекает или переходит в состояние ожидания расчёта. expiry указывается в секундах Unix.
Экспирация позиции (аутентифицированное)
Отправляется в канале portfolio, когда ваша позиция рассчитывается при экспирации.
{
"type": "PositionExpired",
"wallet_address": "0x1234...abcd",
"symbol": "BTC-20260131-100000-C",
"position_size": "10.0",
"settlement_price": "105000",
"settlement_value": "500.0",
"settlement_entry_price": "100.0",
"cost_basis": "1000.0",
"net_pnl": "-500.0",
"timestamp": 1738281600000
}
settlement_entry_price, cost_basis и net_pnl равны null, если недоступны.
Изменение состояния ликвидации (аутентифицированное)
Отправляется в канале liquidation, когда меняется состояние ликвидации вашего счёта.
{
"type": "LiquidationStateChange",
"wallet_address": "0x1234...abcd",
"previous_state": "healthy",
"new_state": "pre_liquidation",
"liquidation_mode": "partial",
"margin_mode": "standard",
"equity": "10000.0",
"mm_required": "10500.0",
"maintenance_margin": "-500.0",
"shortfall": "500.0",
"partial_liquidation": { "...": "..." },
"full_liquidation": null,
"timestamp": 1737331200000
}
| Поле | Описание |
|---|---|
previous_state, new_state | healthy, pre_liquidation, in_liquidation или liquidated |
liquidation_mode | partial, full или null, если процесс ликвидации не активен |
margin_mode | standard или portfolio |
equity | Собственные средства счёта |
mm_required | Требование по поддерживающей марже |
maintenance_margin | Собственные средства за вычетом требования по поддерживающей марже |
shortfall | Сумма, на которую счёт не дотягивает до поддерживающей маржи |
partial_liquidation | Ход частичной ликвидации (время входа, целевой уровень собственных средств, дедлайн эскалации, активные ликвидационные заявки, бонус) или null |
full_liquidation | Детали аукциона полной ликвидации (ID аукциона, время начала, необходимая маржа, а по завершении победитель, бонус и транзакция урегулирования) или null |
Значение каждого состояния описано в разделе Ликвидация.
Обновление индексных цен
Пакет спотовых/индексных цен по всем базовым активам.
{
"type": "IndexPriceUpdate",
"prices": [
{"underlying": "BTC", "price": "97250.50", "timestamp": 1737331200000},
{"underlying": "ETH", "price": "3200.00", "timestamp": 1737331199980},
{"underlying": "NVDA", "price": "182.40", "timestamp": 1737331199950}
],
"timestamp": 1737331199950
}
| Поле | Тип | Описание |
|---|---|---|
prices | array | Массив записей {underlying, price, timestamp} по каждому отслеживаемому базовому активу |
prices[].underlying | string | Символ базового актива (например, "BTC", "ETH") |
prices[].price | string | Текущая спотовая/индексная цена в USD |
prices[].timestamp | integer | Время наблюдения в источнике для этого базового актива, миллисекунды Unix |
timestamp | integer | Самое раннее время наблюдения в источнике среди включённых в prices, миллисекунды Unix |
Индикативные рыночные данные
Поток котировщиков из списка допуска с агрегированными лучшими бидом/аском от зарегистрированных поставщиков котировок. Этот канал пока не общедоступен. Используйте рыночные данные через REST и аутентифицированные каналы заявок/исполнений/портфеля, если Hypercall не включил для вашей интеграции стриминг котировщиков.
{
"type": "IndicativeMarketData",
"instrument": "BTC-20260131-100000-C",
"best_bid": "0.0520",
"best_ask": "0.0530",
"indicative_bid_size": "50.0",
"indicative_ask_size": "25.0",
"num_providers": 3,
"published_at": 1737331200004,
"timestamp": 1737331200000
}
| Поле | Тип | Описание |
|---|---|---|
instrument | string | Символ опциона |
best_bid | string | Необязательно: лучшая агрегированная цена бид |
best_ask | string | Необязательно: лучшая агрегированная цена аск |
bid_iv | number | Необязательно: подразумеваемая волатильность лучшего бида |
ask_iv | number | Необязательно: подразумеваемая волатильность лучшего аска |
indicative_bid_size | string | Необязательно: суммарный объём бидов по всем поставщикам |
indicative_ask_size | string | Необязательно: суммарный объём асков по всем поставщикам |
num_providers | integer | Число активных поставщиков котировок |
rfq_provider_quotes | array | Необязательно: котировки по каждому поставщику: wallet, bid_price, ask_price, max_bid_size, max_ask_size, updated_at |
published_at | integer | Время публикации строки по часам API, миллисекунды Unix. Возраст rfq_provider_quotes измеряйте относительно этого значения, а не своих локальных часов |
timestamp | integer | Время последней котировки поставщика, миллисекунды Unix |
Пакет индикативных рыночных данных
Отправляется в канале indicative_market_data_v2 (см. Пакетная доставка). Каждый элемент updates содержит те же поля, что и сообщение IndicativeMarketData, но без type.
{
"type": "IndicativeMarketDataBatch",
"flushed_at": 1737331200025,
"updates": [
{
"instrument": "BTC-20260131-100000-C",
"best_bid": "0.0520",
"best_ask": "0.0530",
"num_providers": 3,
"published_at": 1737331200004,
"timestamp": 1737331200000
},
{
"instrument": "BTC-20260131-105000-C",
"best_bid": "0.0310",
"best_ask": "0.0322",
"num_providers": 2,
"published_at": 1737331200004,
"timestamp": 1737331200001
}
]
}
| Поле | Тип | Описание |
|---|---|---|
flushed_at | integer | Время отправки пакета по часам API, миллисекунды Unix. flushed_at - published_at даёт задержку пакетирования строки |
updates | array | Строки, опубликованные за окно, от самых ранних к поздним |
Котировки RFQ (аутентифицированное)
Котировки, полученные в ответ на ваш запрос RFQ.
{
"type": "RfqQuotes",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"quotes": [
{
"quote_id": "660e8400-e29b-41d4-a716-446655440001",
"net_premium": "52.30",
"expires_at": 1737331225000
}
],
"status": "quoted",
"taker_wallet": "0x1234...abcd"
}
Обновление статуса RFQ (аутентифицированное)
Изменение статуса отправленного вами RFQ.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Синхронизация часов
Отправьте ClockSync с произвольной строкой nonce, чтобы получить отметку часов API на том же соединении, например для оценки транспортной задержки:
{"type": "ClockSync", "nonce": "abc"}
{"type": "ClockSynced", "nonce": "abc", "server_at": 1737331200000}
server_at указывается в миллисекундах Unix. Аутентификация не требуется.
Ошибка
Сообщение об ошибке сервера. Если присутствует code, ориентируйтесь на него, а не на message.
{
"type": "Error",
"message": "Unknown channel 'foobar'",
"code": "unknown_channel",
"channel": "foobar"
}
| Поле | Тип | Описание |
|---|---|---|
message | string | Описание в удобочитаемом виде |
code | string | Необязательно: стабильный код причины. unknown_channel: в Subscribe указан несуществующий канал |
channel | string | Необязательно: канал, указанный в отклонённом Subscribe |
На Subscribe к каналу, которого нет в разделе Доступные каналы, сервер отвечает ошибкой unknown_channel, и подписка не создаётся. Прежние серверы подтверждали любое имя и затем ничего не присылали.
Остальные ошибки содержат только message. Примеры: Authentication required for this channel при подписке на аутентифицированный канал до Authenticate, Not authenticated при PlaceOrder до Authenticate, а также Invalid wallet address: ....
Аутентификация
Для аутентифицированных каналов после подключения требуется сообщение идентификации кошелька:
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
Сообщения в аутентифицированных каналах фильтруются, чтобы показывать только данные вашего кошелька. Для WebSocket-соединений подпись не требуется.
Пример: клиент на Python
import asyncio
import websockets
import json
async def main():
uri = "wss://api.hypercall.xyz/ws"
async with websockets.connect(uri) as ws:
# Identify the wallet before subscribing to authenticated channels.
await ws.send(json.dumps({
"type": "Authenticate",
"wallet": "0xYourWallet"
}))
# Subscribe to orderbook
await ws.send(json.dumps({
"type": "Subscribe",
"channel": "orderbook"
}))
# Subscribe to fills for BTC only
await ws.send(json.dumps({
"type": "Subscribe",
"channel": "fills",
"symbols": ["BTC"]
}))
# Listen for messages
async for message in ws:
data = json.loads(message)
print(f"Received: {data['type']}")
asyncio.run(main())
Пример: клиент на TypeScript
const ws = new WebSocket("wss://api.hypercall.xyz/ws");
ws.onopen = () => {
ws.send(JSON.stringify({ type: "Authenticate", wallet: "0xYourWallet" }));
// Subscribe to channels
ws.send(JSON.stringify({ type: "Subscribe", channel: "orderbook" }));
// Subscribe to order updates filtered to BTC
ws.send(JSON.stringify({
type: "Subscribe",
channel: "order_updates",
symbols: ["BTC"],
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(`Received: ${msg.type}`);
if (msg.type === "OrderbookUpdate") {
console.log(`${msg.symbol}: ${msg.bids.length} bids, ${msg.asks.length} asks`);
}
};
Пример: клиент на Rust
WsClient из крейта hypercall-client сам идентифицирует кошелёк, восстанавливает подписки после переподключения и отвечает на пинги сервера. По умолчанию он предлагает сжатие permessage-deflate и переходит на несжатые фреймы, если сервер его не принимает; JSON-сообщения в обоих случаях одинаковы. Чтобы отключить предложение сжатия, вызовите with_permessage_deflate(false).
use hypercall_client::WsClient;
#[tokio::main]
async fn main() -> hypercall_client::Result<()> {
let ws = WsClient::new();
ws.connect("https://api.hypercall.xyz", None).await?;
ws.subscribe(vec!["orderbook", "index_prices"]).await?;
if let Some(msg) = ws.wait_for_message(|_| true, 5_000).await {
println!("Received: {msg:?}");
}
Ok(())
}