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

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Клас доставки, фрейм якого перетнув межу безпеки.
causemessage_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. Скопійоване фіксоване значення виходить за межі допустимого вікна і відхиляється.

ПолеТипОпис
walletstringАдреса гаманця, якому належить заявка
symbolstringСимвол опціону
sidestring"Buy" або "Sell"
sizestringРозмір контракту, точно відповідає підписаному значенню
pricestringЛімітна ціна, точно відповідає підписаному значенню
tifstringНеобов'язковий time-in-force, за замовчуванням "gtc"
routestringВикористовуйте "book_only". Пропущений маршрут досі приймається, але він застарілий, і дата його припинення вже минула.
client_idstringНеобов'язковий клієнтський ID заявки
reduce_onlybooleanНеобов'язковий, за замовчуванням false. Заявка reduce-only підписує PlaceOrderReduceOnly і вимагає явного route
mmp_enabledbooleanНеобов'язковий, за замовчуванням false
builder_code_addressstringНеобов'язковий гаманець builder code
nonceintegerОбов'язковий nonce підпису: поточний Unix-час у мілісекундах. Він має бути в межах двох днів до і одного дня після поточного часу. Кожен nonce приймається один раз для кожного підписанта, і заявки через REST і WebSocket мають спільний простір nonce. Повторно відтворений фрейм відхиляється.
signaturestringПідпис 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
}
ПолеТипОпис
symbolstringСимвол опціону
option_token_addressstring?Ончейн-адреса токена опціону, якщо відома
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,
"builder_code_address": null,
"builder_code_fee": null,
"instrument_type": "option"
}
ПолеТипОпис
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, поки комісії майданчика на етапі запуску вимкнені
instrument_typestring"option"

Оновлення портфеля (автентифіковано)​

Надсилається в каналі portfolio для позицій, балансів, маржі та греків. Під час підписки сервер надсилає знімок Initial, а потім інкрементні оновлення.

type кожного фрейму є видом оновлення:

Вид оновленняПоля, крім timestamp
Initialpositions
PositionUpdateposition
BalanceUpdatetotal_margin_used
MarginUpdatespan_margin, margin_summary, margin_mode, total_margin_used, available_balance
GreeksUpdateper_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_statehealthy, pre_liquidation, in_liquidation або liquidated
liquidation_modepartial, full або null, коли жоден процес ліквідації не активний
margin_modestandard або 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
}
ПолеТипОпис
pricesarrayМасив записів {underlying, price, timestamp} для кожного відстежуваного базового активу
prices[].underlyingstringСимвол базового активу (наприклад, "BTC", "ETH")
prices[].pricestringПоточна спотова/індексна ціна в USD
prices[].timestampintegerЧас спостереження джерела для цього базового активу в Unix-мілісекундах
timestampintegerНайстаріший час спостереження джерела, включений у 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
}
ПолеТипОпис
instrumentstringСимвол опціону
best_bidstringНеобов'язкова найкраща агрегована ціна бід
best_askstringНеобов'язкова найкраща агрегована ціна аск
bid_ivnumberНеобов'язкова імпліцитна волатильність найкращого бід
ask_ivnumberНеобов'язкова імпліцитна волатильність найкращого аск
indicative_bid_sizestringНеобов'язковий загальний розмір бід за всіма постачальниками
indicative_ask_sizestringНеобов'язковий загальний розмір аск за всіма постачальниками
num_providersintegerКількість активних постачальників котирувань
rfq_provider_quotesarrayНеобов'язкові котирування кожного постачальника: wallet, bid_price, ask_price, max_bid_size, max_ask_size, updated_at
published_atintegerЧас публікації цього рядка за годинником API, Unix-мілісекунди. Вимірюйте вік rfq_provider_quotes відносно цього значення, а не свого локального годинника
timestampintegerЧас останнього котирування постачальника, 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_atintegerЧас надсилання пакета за годинником API, Unix-мілісекунди. flushed_at - published_at є затримкою пакетування рядка
updatesarrayРядки, опубліковані протягом вікна, від найстаріших

Котирування 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"
}
ПолеТипОпис
messagestringОпис, зрозумілий людині
codestringНеобов'язкова стабільна причина. unknown_channel: Subscribe вказав канал, якого не існує
channelstringНеобов'язкове. Канал, указаний у відхиленому 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(())
}