WebSocket API
Потокова передача даних у реальному часі для торгівлі опціонами на Hypercall.
Перегляньте інтерактивний довідник WebSocket API, щоб зручніше переглядати живі приклади та деталі схем.
Завантажте специфікацію AsyncAPI для програмного використання.
Об'єкти повідомлень WebSocket можуть отримувати нові поля без попереднього повідомлення. Клієнти мають ігнорувати поля, які вони не розпізнають. Hypercall не видаляє поля з наявної схеми повідомлення.
Підключення
Підключіться до wss://HOST/ws:
Ендпоінти:
- Основна мережа (production):
wss://api.hypercall.xyz/ws - Локально:
ws://localhost:3000/ws
Тестнет тимчасово вимкнено, доки Hypercall не отримає більше HYPE у тестнеті.
Стиснення
/ws підтримує RFC 7692 permessage-deflate. Увімкніть його у своєму клієнті, щоб скоротити трафік ринкових даних: на записаному production-трафіку незалежно стиснуті фрейми приблизно на 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-повідомлення в обох випадках однакові. Production поки не приймає це розширення, тому production-фрейми зараз не стискаються. - Ваш клієнт може стискати те, що надсилає. Сервер розпаковує кожне повідомлення клієнта до 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= досі підтримується для зворотної сумісності, але він застарілий і буде видалений у майбутньому релізі. Віддавайте перевагу підходу через повідомлення, описаному вище.
Підтримка активності з'єднання
Сервер застосовує heartbeat WebSocket:
- Надсилає керуючий фрейм
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-інтерфейс, оскільки повторне відтворення за курсором поки недоступне. Повністю завислe з'єднання може завершитися раніше, ніж клієнт прочитає причину закриття, тому клієнти мають використовувати цей порядок відновлення і для некоректного закриття.
Використовуйте окремі з'єднання для високочастотних публічних ринкових даних і для автентифікованих команд або приватних потоків. Класи доставки визначають метрики та поведінку відновлення, але фрейми в межах одного з'єднання все одно проходять через один упорядкований шлях запису в сокет. Тому завислий публічний запис може затримати наступні приватні фрейми на тому самому з'єднанні, доки дедлайн запису не закриє його.
Підписка на канали
Надішліть 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. На production-трафіку з вікном за замовчуванням рядки чекали в середньому 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 | Так | Експериментальні комбо-канали. Див. Експериментальний 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 є лише ілюстрацією. Генеруйте новий для кожної заявки з поточного 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". Пропущений маршрут досі приймається, але він застарілий, і дата його припинення вже минула. |
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 (див. Експериментальний 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 ідентифікує гаманець, відновлює підписки після перепідключення і відповідає на ping-запити сервера за вас. За замовчуванням він пропонує стиснення 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(())
}