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 | Клас доставки, чий кадр перетнув межу безпеки. |
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 не вказано, пересилаються всі оновлення для вашого гаманця.
Фільтрація ланцюжка опціонів
Канал 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..."
}
| Поле | Тип | Опис |
|---|---|---|
wallet | string | Адреса гаманця, якому належить заявка |
symbol | string | Символ опціону |
side | string | "Buy" або "Sell" |
size | string | Розмір контракту, який точно відповідає підписаному значенню |
price | string | Лімітна ціна, яка точно відповідає підписаному значенню |
tif | string | Необов'язковий time-in-force, за замовчуванням "gtc" |
route | string | Необов'язковий маршрут. Використовуйте "book_only" для WebSocket-заявок з урахуванням маршруту. Пропущений маршрут залишається прийнятним щонайменше до 4 липня 2026 року. |
client_id | string | Необов'язковий клієнтський ID заявки |
nonce | integer | Унікальний nonce підпису |
signature | string | Підпис 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
}
| Поле | Тип | Опис |
|---|---|---|
symbol | 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
}
| Поле | Тип | Опис |
|---|---|---|
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, поки комісії стартового майданчика вимкнені |
Оновлення портфеля (з автентифікацією)
Оновлення потоку портфеля щодо позицій, балансів, маржі та греків.
Приклад оновлення греків:
{
"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
}
| Поле | Тип | Опис |
|---|---|---|
prices | array | Масив записів {underlying, price} для кожного відстежуваного базового активу |
prices[].underlying | string | Символ базового активу (наприклад, "BTC", "ETH") |
prices[].price | string | Поточна спот/індексна ціна в USD |
timestamp | integer | Часова мітка 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
}
| Поле | Тип | Опис |
|---|---|---|
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 | Кількість активних провайдерів котирувань |
timestamp | integer | Часова мітка 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`);
}
};