Цю сторінку перекладено автоматично. Оригінал англійською мовою є канонічним. Читати англійською
Перейти к основному содержимому

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= досі підтримується для зворотної сумісності, але вважається застарілим і буде видалений у майбутньому релізі. Надавайте перевагу підходу на основі повідомлень, описаному вище.

Життєздатність з'єднання

Сервер застосовує heartbeat для WebSocket:

  • Надсилає керуючий кадр Ping кожні 20 секунд
  • Очікує відповідний Pong протягом 60 секунд
  • Закриває з'єднання з кодом закриття 1008 і причиною pong timeout, якщо клієнт перестає відповідати

Браузерні реалізації WebSocket обробляють ping/pong автоматично. Багато Rust-бібліотек для websocket, зокрема tungstenite і tokio-tungstenite, також обробляють ping/pong керуючих кадрів за вас. Перевірте документацію бібліотеки вашого клієнта, перш ніж додавати ручну обробку Pong. Власні реалізації або реалізації на основі raw-сокетів мають відповідати на кадри 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НіПотік провайдерів котирувань зі списку дозволених. Поки що загальнодоступний не є
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Необов'язковий time-in-force, за замовчуванням "gtc"
routestringНеобов'язковий маршрут. Використовуйте "book_only" для WebSocket-заявок з урахуванням маршруту. Пропущений маршрут залишається прийнятним щонайменше до 4 липня 2026 року.
client_idstringНеобов'язковий клієнтський ID заявки
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], розмір у зручних для читання контрактах
timestampintegerЧас Unix (мілісекунди)

Угода

Публічна подія угоди.

{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "buy",
"timestamp": 1737331200000
}
ПолеТипОпис
symbolstringСимвол опціону
pricestringЦіна угоди в USD
sizestringРозмір угоди в контрактах
sidestringСторона агресора (buy або sell)
timestampintegerЧас 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
}
ПолеТипОпис
order_idintegerВаш ID заявки
fill_idintegerID виконання
symbolstringСимвол опціону
sidestringСторона угоди (buy або sell)
pricestringЦіна виконання в USD
sizestringОбсяг виконання в контрактах
timestampintegerЧасова мітка Unix (мілісекунди)
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
timestampintegerЧасова мітка 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,
"timestamp": 1737331200000
}
ПолеТипОпис
instrumentstringСимвол опціону
best_bidstringНеобов'язкова найкраща агрегована ціна біду
best_askstringНеобов'язкова найкраща агрегована ціна аску
bid_ivnumberНеобов'язкова імпліцитна волатильність найкращого біду
ask_ivnumberНеобов'язкова імпліцитна волатильність найкращого аску
indicative_bid_sizestringНеобов'язковий сукупний обсяг біду по всіх провайдерах
indicative_ask_sizestringНеобов'язковий сукупний обсяг аску по всіх провайдерах
num_providersintegerКількість активних провайдерів котирувань
timestampintegerЧасова мітка Unix (мілісекунди)

Зміна рангу в змаганні (з автентифікацією)

Сповіщення, коли ваш ранг змінюється в активному змаганні.

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