головна сторінка

API

WebSocket

Дані Seeryx у реальному часі через один WebSocket: підписка на раунд (обидві книги ордерів, угоди, обсяг) і на акаунт; формати повідомлень і перепідключення.

Підключення

URL
wss://seeryx.com/ws
Підписки
Один раунд і один акаунт на з’єднання. Нова підписка того самого типу замінює стару; щоб стежити за кількома раундами, відкрийте кілька з’єднань.
Доставка
Перше повідомлення після підписки містить повний стан. Далі повідомлення надходить лише тоді, коли його вміст змінився, і одразу після зміни.

Підписка

Ви надсилаєте
{ "subscribe": "round", "slug": "btc-updown-5m-1791916200" }
{ "subscribe": "account", "token": "eyJ0eXAiOiJKV1Qi..." }
Slug раунду або slug подієвого ринку; для акаунта потрібен ваш токен входу.

Повідомлення

Ви отримуєте
{ "type": "round", "slug": "btc-updown-5m-1791916200",
  "up":   { "bids": [[13, 10], [12, 100]], "asks": [[16, 100], [17, 100]] },
  "down": { "bids": [[84, 100], [83, 100]], "asks": [[87, 10], [88, 100]] },
  "trades": [{ "id": 13, "side": "down", "action": "buy", "shares": 10, "price": 53, "time": 1791916330063 }, ...],
  "volume": { "volume": 55.845724, "trades": 13 } }

{ "type": "account",
  "positions": [{ "slug": "btc-updown-5m-1791916200", "side": "down", "shares": 50.3462, "cost": 18.126554 }],
  "orders": [{ "id": 25538, "slug": "btc-updown-5m-1791916200", "side": "down", "action": "buy", "shares": 10, "limit": 48, "createdAt": 1791916330045 }],
  "balance": { "available": 474.097965, "locked": 0, "total": 474.097965, "currency": "USDC" },
  "heads": { "activity": 116, "settled": null } }

{ "type": "error", "error": "Invalid subscription or authentication" }
  • round: обидві книги ордерів повністю (як GET /api/orderbook), останні 100 угод і обсяг.
  • account: рівно GET /api/account. Коли змінюється heads, прочитайте нові рядки з /api/activity або /api/settled.
  • error не закриває сокет; після нього можна надіслати іншу підписку.

Heartbeat-повідомлення

Сервер надсилає WebSocket ping кожні 30 секунд; бібліотеки WebSocket самі відповідають pong. З’єднання, з якого 120 секунд нічого не надходить, закривається. Надішліть першу підписку протягом 10 секунд після підключення.

Перепідключення

  • Перепідключайтеся зі зростанням затримки (1, 2, 4 … до 30 секунд) і підписуйтеся знову: перше повідомлення приносить повний стан, тож нічого не буде пропущено.
  • Закриття з причиною "Token expired" означає, що перед підпискою на акаунт треба знову ввійти.
  • Коли раунд завершується, підпишіться на наступний у тому самому з’єднанні.

Ліміти й коди закриття

ЛімітЗначенняУ разі перевищення
Розмір повідомлення4 КБЗ’єднання закривається
Повідомлення з одного з’єднання60 на хвилинуЗакриття 1008 "Rate limit"
Повідомлення підписки600 на хвилину з однієї IP-адреси, 60 на акаунт{ "type": "error", "error": "Too many subscriptions" }
Відкриті з’єднання50 з однієї IP-адресиHTTP 429 до upgrade, з Retry-After
Перша підпискаПротягом 10 секундЗакриття 1008 "Handshake timeout"
Тиша120 секунд без жодного фреймуЗакриття 1008 "Idle timeout"
Непрочитані повідомлення256 у черзіЗакриття 1013 "Slow consumer"
Токен входуДоки не завершиться строк його дії або його не відкличутьЗакриття 1008 "Token expired" або "Token revoked"

Код

live.ts
// live.ts: a WebSocket client that reconnects by itself. Node 22+ (WebSocket is built in) or any browser.
import { ACCESS, BASE } from './seeryx';

export type Message = { type: 'round' | 'account' | 'error' } & Record<string, unknown>;

export function live(options: { slug?: string; token?: string; onMessage: (message: Message) => void }) {
  let socket: WebSocket | null = null;
  let attempt = 0;
  let stopped = false;
  let { slug, token } = options;
  const gate = ACCESS ? { access: ACCESS } : {};
  const send = (message: object) => {
    if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ ...message, ...gate }));
  };

  function connect() {
    socket = new WebSocket(`${BASE.replace(/^http/, 'ws')}/ws`);
    socket.onopen = () => {
      attempt = 0;
      if (slug) send({ subscribe: 'round', slug });
      if (token) send({ subscribe: 'account', token });
    };
    socket.onmessage = (event) => options.onMessage(JSON.parse(String(event.data)));
    socket.onclose = (event) => {
      if (stopped) return;
      if (event.reason === 'Access code required') { stopped = true; return; } // get a new access token
      if (event.reason === 'Token expired') token = undefined;                   // sign in again for it
      setTimeout(connect, Math.min(30_000, 1000 * 2 ** attempt++));              // 1 s, 2 s, 4 s ... 30 s
    };
  }
  connect();

  return {
    // one round per connection: subscribing to the next round replaces the previous one
    follow(next: string) { slug = next; send({ subscribe: 'round', slug }); },
    close() { stopped = true; socket?.close(); },
  };
}
Клієнт, який сам перепідключається.
watch.ts
import { api, loadWallet, signIn } from './seeryx';
import { live } from './live';

const token = await signIn(await loadWallet());
const { slug } = await api<{ slug: string }>('/markets/BTC/5m/current');
live({
  slug, token, onMessage: (m) => {
    if (m.type === 'round') console.log('Up book', m.up);
    if (m.type === 'account') console.log('cash', m.balance);
  },
});