npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

xrocket-exchange

v0.1.1

Published

TypeScript SDK for the xRocket Exchange REST and WebSocket APIs

Readme

xrocket-exchange

Строго типизированный SDK для актуальных REST и WebSocket API xRocket Exchange. Пакет поддерживает TypeScript, JavaScript, ESM и CommonJS, сохраняет денежные значения строками без потери точности и по умолчанию подключается к mainnet.

Возможности

  • все 26 опубликованных REST-операций Exchange API;
  • все WebSocket-методы и 7 каналов подписки;
  • mainnet по умолчанию и явный режим testnet: true;
  • типы запросов, ответов, пагинации, событий и Problem Details;
  • отдельные классы HTTP-, API-, validation-, rate-limit-, network-, timeout- и WebSocket-ошибок;
  • безопасные повторы только идемпотентных GET и DELETE запросов;
  • ESM, CommonJS, декларации TypeScript и source maps.

Установка

npm install xrocket-exchange

Требуется Node.js 18 или новее.

Быстрый старт

import { XRocketClient } from "xrocket-exchange";

const client = new XRocketClient({
  apiKey: "API_TOKEN",
});

const balances = await client.account.getBalances();
console.log("balances:", balances);

const tickers = await client.marketData.getTicker({
  symbols: ["GRAM-USDT"],
});
console.log("tickers:", tickers);

В JavaScript используется тот же API:

import { XRocketClient } from "xrocket-exchange";

const client = new XRocketClient();
const symbols = await client.marketData.listSymbols();

console.log("symbols:", symbols);

Аутентификация и окружения

Bearer Token создаётся в Telegram-боте xRocket: Меню → Настройки → Настройки биржи → API токен. SDK добавляет Authorization: Bearer <token> только к приватным REST-запросам. Публичные market-data методы и account.getTradeFees() работают без токена.

Не публикуйте токен, не добавляйте его в репозиторий и не используйте в браузерном клиентском коде: он предоставляет доступ к торговле, переводам и выводу средств.

Mainnet используется по умолчанию:

const client = new XRocketClient({
  apiKey: "API_TOKEN",
});

Для testnet нужен отдельный testnet-токен:

const client = new XRocketClient({
  apiKey: "API_TOKEN",
  testnet: true,
});

| Назначение | Mainnet | Testnet | | --- | --- | --- | | REST | https://exchange.api.xrocket.exchange | https://exchange.api.testnet.xrocket.exchange | | WebSocket | wss://exchange.app-api.xrocket.exchange/ | wss://exchange.app-api.testnet.xrocket.exchange/ |

XRocketClientOptions

| Поле | Тип | По умолчанию | Описание | | --- | --- | --- | --- | | apiKey | string | — | Bearer Token для приватных методов. | | testnet | boolean | false | Выбирает testnet REST- и WebSocket-адреса. | | baseURL | string | адрес выбранной сети | Подмена REST origin для прокси или тестового сервера. | | timeout | number | 30000 | Тайм-аут REST-запроса в миллисекундах. | | retry | number \| RetryOptions | 0 | Число повторов либо их расширенная конфигурация. | | fetch | typeof fetch | globalThis.fetch | Совместимая реализация fetch. |

const client = new XRocketClient({
  apiKey: "API_TOKEN",
  baseURL: "https://exchange.example.com",
  timeout: 10_000,
  retry: {
    attempts: 2,
    minDelayMs: 300,
    maxDelayMs: 2_000,
  },
});

RetryOptions содержит attempts, minDelayMs и maxDelayMs. Автоматические повторы применяются только к GET и DELETE при сетевой ошибке, тайм-ауте, HTTP 429 или 5xx. Мутации POST не повторяются, чтобы случайно не создать ордер, перевод или вывод дважды.

Структура SDK

flowchart LR
  Client[XRocketClient] --> Market[marketData]
  Client --> Account[account]
  Client --> Orders[orders]
  Client --> Funding[funding]
  Client --> Transfers[transfers]
  Client --> Health[health]
  Client --> Socket[XRocketWebSocket]
  Market --> REST[xRocket REST API]
  Account --> REST
  Orders --> REST
  Funding --> REST
  Transfers --> REST
  Health --> REST
  Socket --> WS[xRocket WebSocket API]

Полный справочник методов

Все сигнатуры ниже являются публичным API пакета.

XRocketClient

| Метод или свойство | Результат | Описание | | --- | --- | --- | | new XRocketClient(options?) | XRocketClient | Создаёт REST-клиент. options необязателен для публичных методов. | | baseURL | string | Фактический REST base URL без завершающего /. | | marketData | MarketDataResource | Публичные рыночные данные и курсы. | | account | AccountResource | Торговые балансы и комиссии. | | orders | OrdersResource | Создание, оценка, получение и отмена ордеров. | | funding | FundingResource | Основные балансы и выводы. | | transfers | TransfersResource | Переводы между funding и trading. | | health() | Promise<Health> | Проверяет состояние REST-сервиса: GET /health. | | createWebSocket(options?) | XRocketWebSocket | Создаёт отдельное WebSocket-подключение с сетью и токеном REST-клиента. |

console.log(client.baseURL);

const health = await client.health();
console.log(health.status);

const socket = client.createWebSocket({
  pingIntervalMs: 30_000,
  requestTimeout: 10_000,
});

client.marketData

Все методы публичны и не требуют apiKey.

| Метод | REST | Результат | Описание | | --- | --- | --- | --- | | listSymbols() | GET /api/v1/symbols | Promise<Symbol[]> | Возвращает все торговые пары. | | getSymbol(symbol) | GET /api/v1/symbols/:symbol | Promise<Symbol> | Возвращает правила и состояние одной пары. | | listAssets() | GET /api/v1/assets | Promise<Asset[]> | Возвращает все активы. | | getAsset(asset) | GET /api/v1/assets/:asset | Promise<Asset> | Возвращает точность и доступные направления перевода актива. | | getTicker(options?) | GET /api/v1/ticker/24h | Promise<Ticker[]> | Возвращает 24-часовые тикеры выбранных или всех пар. | | getCandles(options) | GET /api/v1/candles | Promise<Candle[]> | Возвращает свечи за обязательный временной диапазон. | | getOrderbook(options) | GET /api/v1/orderbook | Promise<Orderbook> | Возвращает снимок стакана с выбранной глубиной и точностью. | | getTrades(symbol) | GET /api/v1/trades | Promise<Trade[]> | Возвращает последние публичные сделки пары. | | getRates(options) | GET /api/v1/rates | Promise<Rates> | Возвращает курсы активов в базовой фиатной валюте. |

Примеры всех методов:

const symbols = await client.marketData.listSymbols();
const symbol = await client.marketData.getSymbol("GRAM-USDT");

const assets = await client.marketData.listAssets();
const asset = await client.marketData.getAsset("GRAM");

const tickers = await client.marketData.getTicker({
  symbols: ["GRAM-USDT", "BTC-USDT"],
});

const candles = await client.marketData.getCandles({
  symbol: "GRAM-USDT",
  type: "1hour",
  startAt: "2026-01-01T00:00:00.000Z",
  endAt: "2026-01-02T00:00:00.000Z",
});

const orderbook = await client.marketData.getOrderbook({
  symbol: "GRAM-USDT",
  depth: 20,
  precision: "0.001",
});

const trades = await client.marketData.getTrades("GRAM-USDT");

const rates = await client.marketData.getRates({
  base: "USD",
  assets: ["GRAM", "USDT"],
});

Допустимая глубина стакана: 5 | 10 | 20 | 50 | 100 | 200 | 500. Допустимую precision конкретной пары берите из Symbol.precisions.

client.account

| Метод | Авторизация | REST | Результат | Описание | | --- | --- | --- | --- | --- | | getBalances() | да | GET /api/v1/accounts/trading/balances | Promise<TradingBalance[]> | Возвращает trading-балансы. | | getTradeFees(options?) | нет | GET /api/v1/trade-fees | Promise<TradeFee[]> | Возвращает maker/taker комиссии выбранных или всех пар. |

const tradingBalances = await client.account.getBalances();

const allFees = await client.account.getTradeFees();
const selectedFees = await client.account.getTradeFees({
  symbols: ["GRAM-USDT", "BTC-USDT"],
});

client.orders

Все методы требуют apiKey.

| Метод | REST | Результат | Описание | | --- | --- | --- | --- | | place(order) | POST /api/v1/orders | Promise<Order> | Размещает limit, market, stop-limit или stop-market ордер. | | estimate(order) | POST /api/v1/orders/estimate | Promise<OrderEstimate> | Рассчитывает параметры и комиссию без размещения ордера. | | get(identifier) | GET /api/v1/order | Promise<Order> | Возвращает ордер по orderId либо clientOrderId. | | cancel(identifier) | DELETE /api/v1/order | Promise<string[]> | Отменяет ордер и возвращает массив отменённых ID. | | listActive() | GET /api/v1/orders/active | Promise<Order[]> | Возвращает все активные ордера. | | listHistory(query?) | GET /api/v1/orders/history | Promise<Page<Order>> | Возвращает одну страницу истории. | | iterateHistory(query?) | несколько запросов history | AsyncGenerator<Order> | Последовательно обходит все страницы истории. |

Примеры всех типов ордеров:

const limitOrder = await client.orders.place({
  symbol: "GRAM-USDT",
  side: "buy",
  type: "limit",
  size: "10",
  price: "0.075",
  timeInForce: "GTC",
  clientOrderId: "limit-001",
});

const marketByFunds = await client.orders.place({
  symbol: "GRAM-USDT",
  side: "buy",
  type: "market",
  funds: "10",
  timeInForce: "IOC",
});

const marketBySize = await client.orders.place({
  symbol: "GRAM-USDT",
  side: "sell",
  type: "market",
  size: "10",
  timeInForce: "FOK",
});

const stopLimit = await client.orders.place({
  symbol: "GRAM-USDT",
  side: "sell",
  type: "stopLimit",
  stopPrice: "0.070",
  size: "10",
  price: "0.069",
  timeInForce: "GTC",
});

const stopMarket = await client.orders.place({
  symbol: "GRAM-USDT",
  side: "sell",
  type: "stopMarket",
  stopPrice: "0.070",
  size: "10",
  timeInForce: "IOC",
});

У market-ордера нужно передать ровно одно поле: size или funds. TypeScript не разрешит передать оба поля или не передать ни одного.

Оценка, получение, отмена и история:

const estimate = await client.orders.estimate({
  symbol: "GRAM-USDT",
  side: "buy",
  type: "market",
  funds: "10",
  timeInForce: "IOC",
});

const byRocketId = await client.orders.get({ orderId: limitOrder.id });
const byClientId = await client.orders.get({ clientOrderId: "limit-001" });

const cancelledIds = await client.orders.cancel({
  orderId: limitOrder.id,
});

const activeOrders = await client.orders.listActive();

const historyPage = await client.orders.listHistory({
  symbol: "GRAM-USDT",
  side: "buy",
  startAt: "2026-01-01T00:00:00.000Z",
  endAt: "2026-02-01T00:00:00.000Z",
  currentPage: 1,
  pageSize: 50,
  hideCanceled: false,
});

for await (const order of client.orders.iterateHistory({
  symbol: "GRAM-USDT",
  pageSize: 100,
})) {
  console.log(order.id, order.status);
}

Если одновременно передать xRocket ID и клиентский ID, официальный API отдаёт приоритет xRocket ID.

client.funding

Все методы требуют apiKey. Funding и trading — независимые балансы: для торговли средства сначала переводятся в trading.

| Метод | REST | Результат | Описание | | --- | --- | --- | --- | | getBalances() | GET /api/v1/accounts/funding/balances | Promise<FundingBalance[]> | Возвращает funding-балансы. | | getWithdrawalQuotas(network, asset) | GET /api/v1/accounts/funding/withdrawal-quotas | Promise<WithdrawalQuotas> | Возвращает доступную сумму, минимум, точность и комиссию вывода. | | createWithdrawal(request) | POST /api/v1/accounts/funding/withdrawals | Promise<Withdrawal> | Создаёт вывод. clientWithdrawalId обязателен для защиты от дублей. | | getWithdrawal(identifier) | GET /api/v1/accounts/funding/withdrawal | Promise<Withdrawal> | Возвращает вывод по xRocket или клиентскому ID. | | listWithdrawals(query?) | GET /api/v1/accounts/funding/withdrawals | Promise<Page<Withdrawal>> | Возвращает страницу истории выводов. | | iterateWithdrawals(query?) | несколько запросов withdrawals | AsyncGenerator<Withdrawal> | Обходит все страницы истории выводов. |

const fundingBalances = await client.funding.getBalances();

const quotas = await client.funding.getWithdrawalQuotas("TON", "GRAM");

const withdrawal = await client.funding.createWithdrawal({
  clientWithdrawalId: "withdrawal-001",
  network: "TON",
  asset: "GRAM",
  address: "UQ...",
  amount: "1",
  comment: "1919537769",
});

const byWithdrawalId = await client.funding.getWithdrawal({
  withdrawalId: withdrawal.withdrawalId,
});

const byClientWithdrawalId = await client.funding.getWithdrawal({
  clientWithdrawalId: "withdrawal-001",
});

const withdrawalsPage = await client.funding.listWithdrawals({
  assets: ["GRAM"],
  startAt: "2026-01-01T00:00:00.000Z",
  endAt: "2026-02-01T00:00:00.000Z",
  currentPage: 1,
  pageSize: 50,
});

for await (const item of client.funding.iterateWithdrawals({
  assets: ["GRAM"],
  pageSize: 100,
})) {
  console.log(item.withdrawalId, item.status);
}

Поддерживаемые OpenAPI-контрактом сети: TON, BSC, ETH, BTC, TRX, SOL.

client.transfers

Все методы требуют apiKey.

| Метод | REST | Результат | Описание | | --- | --- | --- | --- | | create(request) | POST /api/v1/accounts/transfers | Promise<TransferResult> | Перемещает актив между funding и trading. | | get(identifier) | GET /api/v1/accounts/transfer | Promise<Transfer> | Возвращает перевод по xRocket или клиентскому ID. | | list(query?) | GET /api/v1/accounts/transfers | Promise<Page<Transfer>> | Возвращает страницу истории переводов. | | iterate(query?) | несколько запросов transfers | AsyncGenerator<Transfer> | Обходит все страницы истории переводов. |

const transfer = await client.transfers.create({
  asset: "GRAM",
  amount: "10",
  from: "funding",
  to: "trading",
  clientTransferId: "fund-to-trade-001",
});

const byTransferId = await client.transfers.get({
  transferId: transfer.transferId,
});

const byClientTransferId = await client.transfers.get({
  clientTransferId: "fund-to-trade-001",
});

const transfersPage = await client.transfers.list({
  assets: ["GRAM"],
  from: "funding",
  to: "trading",
  startAt: "2026-01-01T00:00:00.000Z",
  endAt: "2026-02-01T00:00:00.000Z",
  currentPage: 1,
  pageSize: 50,
});

for await (const item of client.transfers.iterate({
  assets: ["GRAM"],
  pageSize: 100,
})) {
  console.log(item.transferId, item.status);
}

Пагинация

orders.listHistory(), funding.listWithdrawals() и transfers.list() возвращают единый формат:

interface Page<T> {
  items: T[];
  currentPage: number;
  pageSize: number;
  totalNum: number;
  totalPage: number;
}

Async iterator начинает с первой страницы и прекращает запросы после totalPage.

WebSocket API

Создание подключения

client.createWebSocket() наследует токен и сеть REST-клиента:

const socket = client.createWebSocket({
  pingIntervalMs: 30_000,
  requestTimeout: 10_000,
});

await socket.connect();

Независимый клиент:

import { XRocketWebSocket } from "xrocket-exchange";

const socket = new XRocketWebSocket({
  apiKey: "API_TOKEN",
  testnet: true,
});

await socket.connect();

connect() автоматически вызывает authenticate(), если задан apiKey, и запускает документированный keepalive ping каждые 30 секунд.

XRocketWebSocketOptions

| Поле | Тип | По умолчанию | Описание | | --- | --- | --- | --- | | apiKey | string | — | Токен для balances и activeOrders. | | testnet | boolean | false | Выбирает testnet WebSocket URL. | | webSocketURL | string | адрес выбранной сети | Подмена URL для прокси или тестового сервера. | | pingIntervalMs | number | 30000 | Интервал keepalive; 0 отключает автоматический ping. | | requestTimeout | number | 30000 | Время ожидания ответа JSON-RPC в миллисекундах. |

Все методы и свойства XRocketWebSocket

| Метод или свойство | Результат | Описание | | --- | --- | --- | | new XRocketWebSocket(options?) | XRocketWebSocket | Создаёт объект подключения без открытия сокета. | | url | string | Фактический WebSocket URL. | | connected | boolean | true, когда сокет находится в состоянии OPEN. | | connect() | Promise<void> | Открывает соединение, при наличии токена аутентифицируется и запускает keepalive. | | authenticate() | Promise<WebSocketSuccessResult> | Отправляет метод auth; без apiKey выбрасывает XRocketAuthenticationError. | | ping() | Promise<WebSocketSuccessResult> | Отправляет документированный JSON-RPC ping. | | subscribe(subscription) | Promise<WebSocketSubscriptionResult<T>> | Подписывается на один из 7 типизированных каналов. | | unsubscribe(channel) | Promise<WebSocketSuccessResult> | Отписывается от канала по имени. | | unsubscribeAll() | Promise<WebSocketSuccessResult> | Отменяет все подписки текущего соединения. | | onSubscription(listener) | () => void | Добавляет обработчик push-событий и возвращает функцию его удаления. | | close(code?, reason?) | void | Останавливает keepalive, закрывает сокет и отклоняет ожидающие запросы. |

Пример каждого управляющего метода:

console.log(socket.url, socket.connected);

await socket.connect();
console.log(socket.connected); // true

// Обычно connect() уже выполнил auth автоматически.
await socket.authenticate();
await socket.ping();

const removeListener = socket.onSubscription((event) => {
  console.log(event.channel, event.data);
});

await socket.subscribe({
  channel: "ticker",
  symbol: "GRAM-USDT",
  interval: "1day",
});

await socket.unsubscribe("ticker");
await socket.unsubscribeAll();

removeListener();
socket.close(1000, "Работа завершена");

Все каналы WebSocket

| Канал | Авторизация | Параметры | Push-данные | | --- | --- | --- | --- | | ticker | нет | symbol, interval | { ticker: Ticker } | | allTickers | нет | interval | { tickers: Ticker[] } | | orderbook | нет | symbol, optional depth, precision | WebSocketOrderbookUpdate | | trades | нет | symbol | { trades: Trade[] } | | candles | нет | symbol, type, optional snapshot | { candle: Candle } | | balances | да | — | { balances: Balance[] } | | activeOrders | да | — | { orders: WebSocketOrder[] } |

Примеры всех подписок:

await socket.subscribe({
  channel: "ticker",
  symbol: "GRAM-USDT",
  interval: "1day",
});

await socket.subscribe({
  channel: "allTickers",
  interval: "1hour",
});

const initialOrderbook = await socket.subscribe({
  channel: "orderbook",
  symbol: "GRAM-USDT",
  depth: 20,
  precision: "0.001",
});
console.log(initialOrderbook.bids, initialOrderbook.asks);

await socket.subscribe({
  channel: "trades",
  symbol: "GRAM-USDT",
});

const initialCandles = await socket.subscribe({
  channel: "candles",
  symbol: "GRAM-USDT",
  type: "1hour",
  snapshot: {
    startAt: "2026-01-01T00:00:00.000Z",
    endAt: "2026-01-02T00:00:00.000Z",
  },
});
console.log(initialCandles.candles);

await socket.subscribe({ channel: "balances" });
await socket.subscribe({ channel: "activeOrders" });

События образуют discriminated union по event.channel, поэтому TypeScript сужает тип data автоматически:

const removeListener = socket.onSubscription((event) => {
  switch (event.channel) {
    case "ticker":
      console.log(event.data.ticker.last);
      break;
    case "orderbook":
      console.log(event.data.snapshot, event.data.bids);
      break;
    case "candles":
      console.log(event.data.candle[0]);
      break;
    case "balances":
      console.log(event.data.balances);
      break;
    case "activeOrders":
      console.log(event.data.orders);
      break;
  }
});

У orderbook поле snapshot: true обозначает полный снимок; последующие snapshot: false — инкрементальные изменения. Свеча имеет нестандартный порядок [start, open, close, high, low, baseVolume].

Схемы и модели данных

Все типы ниже экспортируются из корня пакета:

import {
  XRocketClient,
  type ApiProblem,
  type Candle,
  type Order,
  type PlaceOrderRequest,
  type Symbol,
  type WebSocketSubscriptionEvent,
} from "xrocket-exchange";

Decimal — это alias для string. Не преобразовывайте суммы и цены в обычный JavaScript number, если важна точность.

Базовые типы и enum-значения

| Тип | Значения или назначение | | --- | --- | | Decimal | string для цен, сумм, комиссий и объёмов. | | OrderSide | "buy" \| "sell" | | OrderStatus | "working" \| "rejected" \| "cancelled" \| "completed" \| "expired" \| "pending" \| "sending" | | TimeInForce | "GTC" \| "IOC" \| "FOK" | | TransferAccount | "trading" \| "funding" | | TransferStatus | "pending" \| "completed" \| "error" | | WithdrawalStatus | "pending" \| "completed" | | Network | "TON" \| "BSC" \| "ETH" \| "BTC" \| "TRX" \| "SOL" | | CandleInterval | 1min, 5min, 15min, 30min, 1hour, 2hour, 4hour, 8hour, 12hour, 1day, 1week, 1month | | TickerInterval | CandleInterval без 1week и 1month | | SymbolLabel | new, xrock, contest, featured, delisting_warning, delisting | | ApiProblemType | Template literal `/api/problems/${string}` | | ApiProblemKind | client_data_validation, validation, forbidden, not_found, internal |

Конфигурация и пагинация

| Модель | Поля | | --- | --- | | XRocketClientOptions | apiKey, testnet, baseURL, timeout, retry, fetch | | XRocketWebSocketOptions | apiKey, testnet, webSocketURL, pingIntervalMs, requestTimeout | | RetryOptions | optional attempts, minDelayMs, maxDelayMs | | Pagination | currentPage, pageSize, totalNum, totalPage | | Page<T> | Все поля Pagination плюс items: T[] |

Рыночные модели

interface Balance {
  asset: string;
  balance: Decimal;
  available: Decimal;
  holds: Decimal;
}

type TradingBalance = Balance;
type FundingBalance = Balance;

interface Asset {
  asset: string;
  precision: number;
  availableTransfers: Array<
    "fundingToTrading" | "tradingToFunding"
  >;
}

interface Symbol {
  symbol: string;
  baseAsset: string;
  quoteAsset: string;
  baseMinSize: Decimal;
  quoteMinSize: Decimal;
  baseMaxSize: Decimal;
  quoteMaxSize: Decimal;
  minPrice: Decimal;
  maxPrice: Decimal;
  baseIncrement: Decimal;
  quoteIncrement: Decimal;
  priceIncrement: Decimal;
  enableTrading: boolean;
  precisions: string[];
  labels: SymbolLabel[];
  openTime?: string;
}

interface Ticker {
  symbol: string;
  startTime: string;
  endTime: string;
  open: Decimal;
  close: Decimal;
  high: Decimal;
  low: Decimal;
  changeRate: Decimal;
  changePrice: Decimal;
  baseVolume: Decimal;
  quoteVolume: Decimal;
  last: Decimal;
}

type Candle = [
  start: string,
  open: Decimal,
  close: Decimal,
  high: Decimal,
  low: Decimal,
  baseVolume: Decimal,
];

type OrderbookLevel = [price: Decimal, size: Decimal];

interface Orderbook {
  sequence: string;
  bids: OrderbookLevel[];
  asks: OrderbookLevel[];
  askTotalAmount: Decimal;
  bidTotalAmount: Decimal;
}

interface Trade {
  tradeId: string;
  price: Decimal;
  side: OrderSide;
  time: string;
  size: Decimal;
}

interface AssetRate {
  rate: Decimal;
  rateChangePercent24h: Decimal;
}

type Rates = Record<string, AssetRate>;

Модели ордеров

Общие поля любого REST-ордера находятся в OrderBase:

interface OrderBase {
  id: string;
  clientOrderId?: string;
  symbol: string;
  side: OrderSide;
  status: OrderStatus;
  createdAt: string;
  updatedAt: string;
  dealSize: Decimal;
  dealFunds: Decimal;
  fee: Decimal;
  feeAsset: string;
  remark?: string;
  timeInForce: TimeInForce;
  baseAsset: string;
  quoteAsset: string;
}

| Модель | Специальные поля | | --- | --- | | LimitOrder | type: "limit", size, funds, price | | MarketOrder | type: "market", optional size, optional funds | | StopLimitOrder | type: "stopLimit", size, funds, price, stopTriggered, stopPrice | | StopMarketOrder | type: "stopMarket", size, stopTriggered, stopPrice | | Order | Union четырёх моделей выше. |

Модели запросов:

| Модель | Обязательные специальные поля | | --- | --- | | LimitOrderRequest | type: "limit", symbol, side, size, price, timeInForce | | MarketOrderRequest | type: "market", symbol, side, ровно одно из size/funds, timeInForce: "IOC" \| "FOK" | | StopLimitOrderRequest | type: "stopLimit", symbol, side, stopPrice, size, price, timeInForce | | StopMarketOrderRequest | type: "stopMarket", symbol, side, stopPrice, size, timeInForce: "IOC" \| "FOK" | | PlaceOrderRequest | Union четырёх запросов выше. |

clientOrderId во всех запросах необязателен; официальный OpenAPI ограничивает его длину диапазоном 1–64 символа. OrderIdentifier требует orderId или clientOrderId и допускает оба значения одновременно.

OrderEstimateBase содержит symbol, side, fee, feeAsset, timeInForce; OrderEstimate добавляет поля конкретного типа ордера. TradeFee имеет форму { symbol, standard: { maker, taker } }. OrderHistoryQuery содержит symbol, side, startAt, endAt, currentPage, pageSize, hideCanceled.

Переводы и выводы

interface TransferRequest {
  asset: string;
  amount: Decimal;
  from: TransferAccount;
  to: TransferAccount;
  clientTransferId?: string;
}

interface TransferResult extends TransferRequest {
  transferId: string;
  status: "pending" | "completed";
}

interface Transfer extends TransferRequest {
  transferId: string;
  status: TransferStatus;
  createdAt: string;
  updatedAt: string;
}

interface CreateWithdrawalRequest {
  clientWithdrawalId: string;
  network: Network;
  asset: string;
  address: string;
  amount: Decimal;
  comment?: string;
}

interface Withdrawal {
  withdrawalId: string;
  clientWithdrawalId?: string;
  network: Network;
  address: string;
  asset: string;
  amount: Decimal;
  comment?: string;
  status: WithdrawalStatus;
  txHash?: string;
  txLink?: string;
  fee: Decimal;
  feeAsset: string;
  createdAt: string;
  updatedAt: string;
}

interface WithdrawalQuotas {
  availableAmount: Decimal;
  precision: number;
  withdrawMinSize: Decimal;
  withdrawFee: Decimal;
  withdrawFeeAsset: string;
}

Дополнительные экспортируемые модели:

  • TransferIdentifier — требует transferId или clientTransferId;
  • TransferHistoryQuery — assets, from, to, диапазон времени и пагинация;
  • WithdrawalIdentifier — требует withdrawalId или clientWithdrawalId;
  • WithdrawalQuery — assets, диапазон времени и пагинация.

Health

interface HealthComponent {
  name: string;
  status: "up" | "down";
  message?: string;
}

interface Health {
  status: "ok" | "error" | "shutting_down";
  info: HealthComponent[];
  error: HealthComponent[];
  details: HealthComponent[];
}

WebSocket-модели

Экспортируются все запросы подписок: TickerSubscription, AllTickersSubscription, OrderbookSubscription, TradesSubscription, CandlesSubscription, BalancesSubscription, ActiveOrdersSubscription и их union WebSocketSubscription.

interface WebSocketSuccessResult {
  success: true;
}

interface WebSocketOrderbookSubscriptionResult
  extends Orderbook, WebSocketSuccessResult {}

interface WebSocketCandlesSubscriptionResult
  extends WebSocketSuccessResult {
  candles?: Candle[];
}

interface WebSocketOrderbookUpdate extends Orderbook {
  snapshot: boolean;
}

WebSocketSubscriptionResult<T> выбирает результат по типу подписки. WebSocketOrder повторяет union ордеров без REST-полей baseAsset и quoteAsset, отсутствующих в официальной схеме WebSocket-события. WebSocketSubscriptionEvent объединяет push-события всех семи каналов.

Обработка ошибок

Иерархия исключений

XRocketError
├─ XRocketHttpError
│  └─ XRocketApiError
│     ├─ XRocketAuthenticationError
│     ├─ XRocketValidationError
│     └─ XRocketRateLimitError
├─ XRocketNetworkError
├─ XRocketTimeoutError
└─ XRocketWebSocketError

| Класс | Дополнительные поля | Когда возникает | | --- | --- | --- | | XRocketError | endpoint, method, стандартный cause | Базовый класс всех ошибок SDK. | | XRocketHttpError | status, requestId, body | HTTP-ошибка с неструктурированным ответом. | | XRocketApiError | code, problem | HTTP-ошибка с Problem Details. code равен problem.type. | | XRocketAuthenticationError | поля XRocketApiError | Нет apiKey, HTTP 401 или private WebSocket-подписка без токена. | | XRocketValidationError | поля XRocketApiError | HTTP 400 с kind: "validation" или "client_data_validation". | | XRocketRateLimitError | retryAfter | HTTP 429; задержка хранится в миллисекундах. | | XRocketNetworkError | cause | Сетевой запрос не был выполнен. | | XRocketTimeoutError | cause | Истёк REST- или WebSocket-тайм-аут. | | XRocketWebSocketError | code, data | Ошибка соединения, отправки или JSON-RPC. |

import {
  XRocketApiError,
  XRocketAuthenticationError,
  XRocketNetworkError,
  XRocketRateLimitError,
  XRocketTimeoutError,
  XRocketValidationError,
  XRocketWebSocketError,
} from "xrocket-exchange";

try {
  await client.orders.listActive();
} catch (error) {
  if (error instanceof XRocketAuthenticationError) {
    console.error("Проверьте API-токен");
  } else if (error instanceof XRocketRateLimitError) {
    console.error("Повторить через, мс:", error.retryAfter);
  } else if (error instanceof XRocketValidationError) {
    console.error(error.code, error.problem?.info);
  } else if (error instanceof XRocketApiError) {
    console.error(error.status, error.problem);
  } else if (error instanceof XRocketTimeoutError) {
    console.error("Превышено время ожидания");
  } else if (error instanceof XRocketNetworkError) {
    console.error("Сетевая ошибка", error.cause);
  } else if (error instanceof XRocketWebSocketError) {
    console.error(error.code, error.data);
  }
}

Токен не включается в исключения.

Схема REST Problem Details

interface ApiProblem<TInfo = ApiProblemInfo> {
  type?: `/api/problems/${string}`;
  title?: string;
  status?: number;
  detail?: string;
  instance?: string;
  kind?:
    | "client_data_validation"
    | "validation"
    | "forbidden"
    | "not_found"
    | "internal";
  info?: TInfo;
  [key: string]: unknown;
}

Пример ответа API:

{
  "type": "/api/problems/exchange_incorrect_decimal_places",
  "title": "Incorrect decimal places",
  "status": 400,
  "detail": "Incorrect decimal places",
  "instance": "/api/problems/instances/ada06f0c-8ca8-457a-9906-4feb8c999169",
  "kind": "validation",
  "info": {
    "maxDecimals": 6,
    "field": "amount"
  }
}

Типизированные модели problem.info

| Модель | Поля | | --- | --- | | ClientDataProblemValidationInfo | errors: ClientValidationErrors; рекурсивная карта полей и массивов сообщений. | | ExchangeTooBigIntervalValidationInfo | maxIntervalInSeconds: number | | ExchangeIncorrectPrecisionInfo | precisions: string[] | | ExchangeIncorrectDecimalPlacesInfo | optional maxDecimals: number, optional field: string | | ExchangeRangeInfo | optional min: Decimal, optional max: Decimal | | WithdrawalAddressValidationInfo | reason: WithdrawalAddressValidationReason | | WithdrawalAmountValidationInfo | reason: WithdrawalAmountValidationReason | | WithdrawalCommentValidationInfo | reason: WithdrawalCommentValidationReason | | WithdrawalAmountUnavailableInfo | availableAmount: Decimal | | ApiProblemInfo | Union всех моделей выше. |

Значения reason:

  • WithdrawalAddressValidationReason: incorrect_format, verification_failed, dns_no_assigned, fraudulent_address;
  • WithdrawalAmountValidationReason: less_than_minimum, greater_than_maximum, incorrect_precision, invalid_value, insufficient_balance_to_pay_fee, verification_failed;
  • WithdrawalCommentValidationReason: comment_required, comment_unavailable, invalid_format, max_length_exceeded.

Каталог REST problem types

| problem.type | Значение / info | | --- | --- | | /api/problems/client_data_validation | Ошибка параметров или тела; ClientDataProblemValidationInfo. | | /api/problems/user_blocked | Аккаунт заблокирован. | | /api/problems/internal_error | Внутренняя ошибка. OpenAPI также использует этот type для части ошибок валидации вывода; проверяйте kind и info. | | /api/problems/asset_not_found | Актив не найден. | | /api/problems/currency_not_found | Базовая фиатная валюта не найдена. | | /api/problems/exchange_symbol_not_found | Торговая пара не найдена. | | /api/problems/exchange_symbol_not_opened | Торговля парой ещё не открыта. | | /api/problems/exchange_symbol_not_available_for_trading | Пара недоступна для торговли. | | /api/problems/exchange_too_big_interval_validation | Слишком большой интервал; ExchangeTooBigIntervalValidationInfo. | | /api/problems/exchange_incorrect_precision | Неверная точность стакана; ExchangeIncorrectPrecisionInfo. | | /api/problems/exchange_missing_order_identifier | Не передан orderId или clientOrderId. | | /api/problems/exchange_order_not_found | Ордер не найден. | | /api/problems/exchange_amount_more_than_user_balance | Сумма превышает доступный баланс. | | /api/problems/exchange_incorrect_decimal_places | Неверное число знаков; ExchangeIncorrectDecimalPlacesInfo. | | /api/problems/exchange_client_order_id_duplicate | clientOrderId уже использован. | | /api/problems/order_size_and_funds_missing | У market-ордера отсутствуют и size, и funds. | | /api/problems/order_size_and_funds_mutual_exclusion | У market-ордера одновременно переданы size и funds. | | /api/problems/exchange_insufficient_liquidity | Недостаточная ликвидность. | | /api/problems/exchange_price_out_of_range | Цена вне диапазона; ExchangeRangeInfo. | | /api/problems/exchange_stop_price_out_of_range | Stop price вне диапазона; ExchangeRangeInfo. | | /api/problems/exchange_size_out_of_range | Size вне диапазона; ExchangeRangeInfo. | | /api/problems/exchange_funds_out_of_range | Funds вне диапазона; ExchangeRangeInfo. | | /api/problems/exchange_transfer_incorrect_side | Неверное направление внутреннего перевода. | | /api/problems/exchange_transfer_not_available | Перевод недоступен; этот вариант документирован только mainnet OpenAPI. | | /api/problems/exchange_client_transfer_id_duplicate | clientTransferId уже использован. | | /api/problems/exchange_missing_transfer_identifier | Не передан transferId или clientTransferId. | | /api/problems/exchange_transfer_not_found | Перевод не найден. | | /api/problems/withdrawal_unavailable | Вывод недоступен. | | /api/problems/withdrawal_not_allowed | Вывод запрещён для аккаунта. | | /api/problems/network_not_found | Сеть не найдена. | | /api/problems/asset_network_not_found | Актив не поддерживается в выбранной сети. | | /api/problems/wallet_client_withdrawal_id_duplicate | clientWithdrawalId уже использован. | | /api/problems/withdrawal_amount_unavailable | Сумма недоступна; WithdrawalAmountUnavailableInfo. | | /api/problems/wallet_missing_withdrawal_identifier | Не передан withdrawalId или clientWithdrawalId. |

Ошибки WebSocket

WebSocket-сервер возвращает JSON-RPC-подобную ошибку:

{
  "id": "1205411",
  "error": {
    "code": -32034,
    "message": "User Blocked",
    "data": {
      "detail": "Invalid authentication"
    }
  }
}

SDK преобразует её в XRocketWebSocketError: message берётся из ответа, code содержит числовой код, data — дополнительные данные.

| Код | Описание | | --- | --- | | -32700 | Parse error: сервер получил невалидный JSON. | | -32600 | Invalid request: объект запроса не соответствует протоколу. | | -32601 | Method not found. | | -32602 | Invalid params. | | -32603 | Internal JSON-RPC error. | | -32000 | Incorrect decimal places. | | -32001 | Order not found. | | -32002 | Уже подписан на канал. | | -32003 | Попытка отписаться без активной подписки. | | -32005 | Symbol not found. | | -32006 | Symbol incorrect. | | -32007 | Symbol not available. | | -32008 | Interval too big. | | -32009 | Symbol not opened. | | -32010 | Asset not available. | | -32011 | Incorrect precision. | | -32030 | Invalid, expired или revoked token. | | -32032 | Validation error. | | -32033 | Unauthorized. | | -32034 | User blocked. | | -32050 | Rate limit exceeded. |

Полный пример торгового сценария

import {
  XRocketClient,
  XRocketValidationError,
} from "xrocket-exchange";

const client = new XRocketClient({
  apiKey: "API_TOKEN",
  testnet: true,
  timeout: 10_000,
  retry: { attempts: 2, minDelayMs: 250, maxDelayMs: 2_000 },
});

const pair = await client.marketData.getSymbol("GRAM-USDT");
if (!pair.enableTrading) {
  throw new Error("Торговля GRAM-USDT недоступна");
}

await client.transfers.create({
  asset: "USDT",
  amount: "10",
  from: "funding",
  to: "trading",
  clientTransferId: "strategy-funding-001",
});

const estimate = await client.orders.estimate({
  symbol: "GRAM-USDT",
  side: "buy",
  type: "market",
  funds: "10",
  timeInForce: "IOC",
});

console.log("Ожидаемая комиссия:", estimate.fee, estimate.feeAsset);

try {
  const order = await client.orders.place({
    symbol: "GRAM-USDT",
    side: "buy",
    type: "market",
    funds: "10",
    timeInForce: "IOC",
    clientOrderId: "strategy-order-001",
  });

  console.log(order.id, order.status);
} catch (error) {
  if (error instanceof XRocketValidationError) {
    console.error(error.code, error.problem?.info);
  } else {
    throw error;
  }
}

Сначала проверяйте интеграцию в testnet: mainnet и testnet используют разные аккаунты, токены и балансы.

Разработка и проверка пакета

npm run typecheck
npm run lint
npm test
npm run build
npm run pack:check

Официальная документация

Лицензия

MIT