Эта страница переведена автоматически. Оригинал на английском языке является каноническим. Читать на английском
Перейти к основному содержимому

WebSocket API

Потоковая передача данных в реальном времени для торговли опционами на Hypercall.

Интерактивный справочник

Ознакомьтесь с интерактивным справочником по WebSocket API для более удобного просмотра с живыми примерами и деталями схемы.

Машиночитаемая спецификация

Скачайте спецификацию AsyncAPI для программного использования.

Подключение

Подключайтесь к wss://HOST/ws:

Эндпоинты:

  • Продакшн: wss://api.hypercall.xyz/ws
  • Локально: ws://localhost:3000/ws
Статус тестнета

Тестнет временно отключён, пока Hypercall не приобретёт больше тестового HYPE.

Идентификация кошелька

Чтобы получать данные по аутентифицированным каналам (заявки, исполнения, портфель), идентифицируйте свой кошелёк после подключения, отправив сообщение 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Класс доставки, кадр которого пересёк границу безопасности.
causemessage_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 не указаны, пересылаются все обновления для вашего кошелька.

Фильтрация цепочки опционов

Канал 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"Все экспирации
option_type"call", "put" или опустить для обоихОба

Доступные каналы

КаналТребуется аутентификацияОписание
orderbookНетОбновления книги заявок L2 для всех символов
tradesНетПубличная лента сделок
market_updatesНетИзменения листинга рынков (создан/удалён/истёк)
options_chainНетИнкрементальные обновления цепочки опционов (фильтруются по symbols, expiry, option_type)
index_pricesНетСпотовые/индексные цены в реальном времени для всех базовых активов
indicative_market_dataНетПоток провайдеров котировок из allowlist. Пока недоступен в общем доступе
order_updatesДаИзменения статуса ваших заявок (фильтруются по символу)
fillsДаИсполнения ваших сделок (фильтруются по символу)
portfolioДаОбновления ваших позиций и баланса
liquidationДаИзменения состояния вашей ликвидации
competitionДаСводка вашего P&L по соревнованию, ранг и итоговая статистика
competition_engagementДаИзменения ранга, разрыв до следующего ранга и итоговое положение
rfqДаКотировки RFQ, обновления статуса и уведомления об исполнении

Типы сообщений

Размещение заявки (аутентифицировано)

Разместите заявку через командный путь 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": 1000,
"signature": "0x..."
}
ПолеТипОписание
walletstringАдрес кошелька, которому принадлежит заявка
symbolstringСимвол опциона
sidestring"Buy" или "Sell"
sizestringРазмер контракта, точно совпадающий с подписанным значением
pricestringЛимитная цена, точно совпадающая с подписанным значением
tifstringНеобязательное время действия, по умолчанию "gtc"
routestringНеобязательный маршрут. Используйте "book_only" для WebSocket-заявок с учётом маршрута. Пропущенный маршрут остаётся допустимым как минимум до 4 июля 2026 года.
client_idstringНеобязательный клиентский идентификатор заявки
nonceintegerУникальный nonce подписи
signaturestringПодпись EIP-712 PlaceOrder

WebSocket PlaceOrder в настоящее время направляет заявку напрямую в книгу заявок. route="best_execution" и route="rfq_only" отклоняются в WebSocket, поскольку этот путь пока не выполняет маршрутизацию RPI/RFQ. Используйте POST /order для best_execution.

Обновление книги заявок

Снимок/обновление книги заявок L2 для символа.

{
"type": "OrderbookUpdate",
"symbol": "BTC-20260131-100000-C",
"bids": [["95000.5", "10.5"], ["94999.0", "25.0"]],
"asks": [["95001.0", "8.0"], ["95002.5", "15.0"]],
"timestamp": 1737331200000
}
ПолеТипОписание
symbolstringСимвол опциона
bidsarrayУровни бида в виде кортежей [price, size], размер указан в человекочитаемых контрактах
asksarrayУровни аска в виде кортежей [price, size], размер указан в человекочитаемых контрактах
timestampintegerUnix-метка времени (миллисекунды)

Сделка

Публичное событие сделки.

{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "buy",
"timestamp": 1737331200000
}
ПолеТипОписание
symbolstringСимвол опциона
pricestringЦена сделки в USD
sizestringРазмер сделки в контрактах
sidestringСторона агрессора (buy или sell)
timestampintegerUnix-метка времени (миллисекунды)

Исполнение (аутентифицировано)

Уведомление об исполнении вашей сделки.

{
"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
}
ПолеТипОписание
order_idintegerID вашей заявки
fill_idintegerID исполнения
symbolstringСимвол опциона
sidestringСторона сделки (buy или sell)
pricestringЦена исполнения в USD
sizestringОбъём исполнения в контрактах
timestampintegerUnix-таймстамп (миллисекунды)
wallet_addressstringАдрес вашего кошелька
feestringВзимаемая торговая комиссия. Возвращает 0, пока комиссии стартовой площадки отключены
trade_idintegerУникальный ID сделки
is_takerbooleanБыли ли вы тейкером
builder_code_addressstring?Кошелёк builder code (если есть)
builder_code_feestring?Комиссия builder code. Возвращает null, пока комиссии стартовой площадки отключены

Обновление портфеля (с аутентификацией)

Обновление потока портфеля по позициям, балансам, марже и грекам.

Пример обновления греков:

{
"type": "PortfolioUpdate",
"timestamp": 1737331200000,
"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

Сводка PnL по соревнованию (с аутентификацией)

Обновление потока соревнования для отображения PnL в шапке/подвале.

{
"type": "CompetitionPnlSummary",
"wallet_address": "0x1234...abcd",
"lifetime_realized_pnl": "1250.50",
"active_competition": {
"competition_id": 7,
"competition_name": "Spring Sprint",
"competition_state": "active",
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null
},
"timestamp": 1737331200000
}

Когда активного соревнования нет, active_competition равно null.

Обновление заявки (с аутентификацией)

Уведомление об изменении статуса заявки.

{
"type": "OrderUpdate",
"order_id": 12345,
"client_order_id": "my-order-1",
"status": "filled",
"filled_size": "10.0",
"remaining_size": "0",
"avg_fill_price": "0.0523"
}

Обновление рынка

Изменения в листинге рынков.

Рынок создан:

{
"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
}

Позиция истекла (с аутентификацией)

Уведомление, когда ваша позиция рассчитывается на экспирации.

{
"type": "PositionExpired",
"wallet_address": "0x1234...abcd",
"symbol": "BTC-20260131-100000-C",
"position_size": "10.0",
"settlement_price": "105000",
"settlement_value": "500.0",
"timestamp": 1738281600000
}

Изменение состояния ликвидации (с аутентификацией)

Изменение состояния ликвидации вашего счёта.

{
"type": "LiquidationStateChange",
"wallet_address": "0x1234...abcd",
"previous_state": "Normal",
"new_state": "Warning",
"equity": "10000.0",
"mm_required": "9500.0",
"shortfall": "0",
"auction_id": null,
"timestamp": 1737331200000
}
СостояниеОписание
NormalСчёт в нормальном состоянии
WarningПриближение к маржин-коллу
LiquidatingАктивен аукцион ликвидации

Обновление индексной цены

Пакетные спотовые/индексные цены для всех базовых активов.

{
"type": "IndexPriceUpdate",
"prices": [
{"underlying": "BTC", "price": "97250.50"},
{"underlying": "ETH", "price": "3200.00"},
{"underlying": "HYPE", "price": "28.50"}
],
"timestamp": 1737331200000
}
ПолеТипОписание
pricesarrayМассив записей {underlying, price} для каждого отслеживаемого базового актива
prices[].underlyingstringСимвол базового актива (например, "BTC", "ETH")
prices[].pricestringТекущая спотовая/индексная цена в USD
timestampintegerUnix-таймстамп (миллисекунды)

Индикативные рыночные данные

Поток от провайдеров котировок из белого списка с агрегированными лучшими бид/аск от зарегистрированных провайдеров котировок. Этот канал пока недоступен в общем доступе. Используйте 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,
"timestamp": 1737331200000
}
ПолеТипОписание
instrumentstringСимвол опциона
best_bidstringОпциональная лучшая агрегированная цена бид
best_askstringОпциональная лучшая агрегированная цена аск
bid_ivnumberОпциональная подразумеваемая волатильность лучшего бида
ask_ivnumberОпциональная подразумеваемая волатильность лучшего аска
indicative_bid_sizestringОпциональный суммарный объём бид по всем провайдерам
indicative_ask_sizestringОпциональный суммарный объём аск по всем провайдерам
num_providersintegerЧисло активных провайдеров котировок
timestampintegerUnix-таймстамп (миллисекунды)

Изменение ранга в соревновании (с аутентификацией)

Уведомление, когда ваш ранг меняется в активном соревновании.

{
"type": "CompetitionRankChange",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"from_rank": 15,
"to_rank": 12,
"delta_places": 3,
"pnl": "420.25",
"timestamp": 1737331200000
}

Обновление отрыва в соревновании (с аутентификацией)

Расстояние до следующего ранга над вами.

{
"type": "CompetitionGapUpdate",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"next_rank": 11,
"gap_metric_value": "50.00",
"timestamp": 1737331200000
}

Итоговое место в соревновании (с аутентификацией)

Отправляется, когда соревнование завершается, с вашими итоговыми результатами.

{
"type": "CompetitionFinalStanding",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null,
"timestamp": 1737331200000
}

Котировки 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"
}

Ошибка

Сообщение об ошибке сервера.

{
"type": "Error",
"message": "Invalid channel: foobar"
}

Аутентификация

Аутентифицированные каналы требуют сообщения с идентификацией кошелька после подключения:

{"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`);
}
};