xrocket-exchange
v0.1.1
Published
TypeScript SDK for the xRocket Exchange REST and WebSocket APIs
Maintainers
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Официальная документация
- REST API
- REST OpenAPI / Swagger mainnet
- REST OpenAPI / Swagger testnet
- WebSocket API
- Тестирование интеграции
Лицензия
MIT
