← Back to the docs
LootX

API

REST и WebSocket для ботов, аналитики и интеграций со сторонними сервисами.

Generated: 2026-09-25 docs.lootx.trade
Начало

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

Первый авторизованный запрос к API LootX за пять минут.

API LootX даёт то же, что видит терминал: инструменты, позиции, ордера, историю и поток рыночных данных. Всё на одном домене https://api.lootx.trade, всё в JSON, всё по HTTPS.

Получите токен

Создайте ключ в кабинете

Кабинет → API → Создать ключ. Ключ показывается один раз — сохраните его сразу.

Проверьте права

Ключу выдаются права: read (данные), trade (ордера), account (баланс и позиции). Выдавайте только нужные.

Сделайте первый запрос

Ключ передаётся заголовком Authorization: Bearer <token>.

Shell
curl https://api.lootx.trade/v1/instruments?exchange=bybit \
  -H "Authorization: Bearer $LOOTX_TOKEN"
TypeScript
const res = await fetch("https://api.lootx.trade/v1/instruments?exchange=bybit", {
  headers: { Authorization: `Bearer ${process.env.LOOTX_TOKEN}` },
});
if (!res.ok) throw new Error(`API ${res.status}: ${await res.text()}`);
const { items } = (await res.json()) as { items: Instrument[] };
Python
import os, requests

r = requests.get(
    "https://api.lootx.trade/v1/instruments",
    params={"exchange": "bybit"},
    headers={"Authorization": f"Bearer {os.environ['LOOTX_TOKEN']}"},
    timeout=10,
)
r.raise_for_status()
items = r.json()["items"]

Ответ:

JSON
{
  "items": [
    {
      "symbol": "BTCUSDT",
      "exchange": "bybit",
      "kind": "linear",
      "tickSize": "0.1",
      "minQty": "0.001",
      "status": "trading"
    }
  ],
  "nextCursor": null
}

Поток данных

TypeScript
const ws = new WebSocket("wss://api.lootx.trade/v1/stream");
ws.onopen = () => {
  ws.send(JSON.stringify({ op: "auth", token: process.env.LOOTX_TOKEN }));
  ws.send(JSON.stringify({ op: "subscribe", channels: ["trades:bybit:BTCUSDT"] }));
};
ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.channel?.startsWith("trades:")) console.log(msg.data.price, msg.data.qty);
};

Дальше

https://docs.lootx.trade/en/api/quickstart

Интеграции

2Интеграция с сервисами

Как соединить LootX с TradingView, Telegram, таблицами и собственными сервисами: вебхуки, подпись запросов, идемпотентность и обработка ошибок.

Интеграция нужна в трёх случаях: сигнал приходит снаружи и должен превратиться в действие; событие происходит внутри и должно уехать наружу; данные нужно забрать по расписанию. LootX закрывает все три — входящими вебхуками, исходящими событиями и REST API.

Карта возможностей

Входящие вебхуки

Секрет и персональный URL лежат в кабинете: откройте поиск по документации на ⌘/Ctrl+K или перейдите в Кабинет → Интеграции. Персональный URL выглядит так:

TEXT
https://api.lootx.trade/v1/hooks/in/9f3c1a7e-0b2d-4c8a-9f01-6e2b7d4c5a10

Он не секрет сам по себе — секретом является подпись. URL можно вставить в любой сервис, умеющий слать HTTP POST.

POST/v1/hooks/in/:hookId

Принимает сигнал и выполняет действие, описанное в теле. Отвечает 202 Accepted, когда сигнал принят к исполнению, и 200 OK, когда он был уже обработан ранее (повтор).

Заголовки

Заголовок Обязателен Описание
Content-Type да application/json
X-LootX-Signature да sha256=<hex> — HMAC-SHA256 тела запроса
X-LootX-Timestamp да Unix-время в секундах; допуск ±300 с

Тело

Поле Тип Описание
action string order, close, cancel-all или notify
exchange string Код биржи: bybit, binance, okx
symbol string Инструмент, например BTCUSDT
side string buy или sell — для action: order
qty string Объём. Строка, не число: см. предупреждение ниже
orderType string market или limit, по умолчанию market
price string Цена для лимитной заявки
stopLoss string Абсолютная цена стопа
takeProfit string Абсолютная цена тейка
requestId string Идентификатор для идемпотентности, до 64 символов
comment string Попадёт в журнал сделок

Подпись запроса

Подпись считается от точного тела запроса — той же последовательности байт, что уходит в сеть. Не пересобирайте JSON перед подписью.

TypeScript
import crypto from "node:crypto";

const secret = process.env.LOOTX_HOOK_SECRET!;
const body = JSON.stringify({
  action: "order",
  exchange: "bybit",
  symbol: "BTCUSDT",
  side: "buy",
  qty: "0.01",
  stopLoss: "61250",
  requestId: crypto.randomUUID(),
});
const ts = Math.floor(Date.now() / 1000).toString();
const signature = crypto
  .createHmac("sha256", secret)
  .update(`${ts}.${body}`)
  .digest("hex");

const res = await fetch(HOOK_URL, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-lootx-timestamp": ts,
    "x-lootx-signature": `sha256=${signature}`,
  },
  body,
});
Python
import hmac, hashlib, json, os, time, uuid, requests

secret = os.environ["LOOTX_HOOK_SECRET"].encode()
body = json.dumps({
    "action": "order",
    "exchange": "bybit",
    "symbol": "BTCUSDT",
    "side": "buy",
    "qty": "0.01",
    "stopLoss": "61250",
    "requestId": str(uuid.uuid4()),
}, separators=(",", ":"))          # без пробелов: подписываем то, что отправляем
ts = str(int(time.time()))
sig = hmac.new(secret, f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()

requests.post(HOOK_URL, data=body, timeout=10, headers={
    "content-type": "application/json",
    "x-lootx-timestamp": ts,
    "x-lootx-signature": f"sha256={sig}",
})
PHP
$secret = getenv('LOOTX_HOOK_SECRET');
$body = json_encode([
  'action' => 'order',
  'exchange' => 'bybit',
  'symbol' => 'BTCUSDT',
  'side' => 'buy',
  'qty' => '0.01',
  'requestId' => bin2hex(random_bytes(8)),
], JSON_UNESCAPED_SLASHES);
$ts = (string) time();
$sig = hash_hmac('sha256', "$ts.$body", $secret);

$ch = curl_init(HOOK_URL);
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => $body,
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json',
    "X-LootX-Timestamp: $ts",
    "X-LootX-Signature: sha256=$sig",
  ],
]);
curl_exec($ch);

TradingView

TradingView не умеет подписывать запросы — он шлёт произвольный текст на URL. Поэтому для алертов TradingView в кабинете включается отдельный режим: Интеграции → TradingView → Создать канал. Канал выдаёт URL с одноразово-длинным идентификатором и требует, чтобы в теле присутствовал ваш channelToken.

Создайте канал

Кабинет → Интеграции → TradingView. Скопируйте URL и токен.

Опишите правила исполнения

Здесь же задаются ограничения: какие инструменты разрешены, максимальный объём, обязателен ли стоп, что делать при встречном сигнале. Эти правила сильнее содержимого алерта — сигнал не сможет превысить заданный лимит.

Вставьте сообщение в алерт

В окне алерта TradingView выберите Webhook URL и вставьте тело:

JSON
{
  "channelToken": "chn_8d2f…",
  "action": "order",
  "exchange": "bybit",
  "symbol": "{{ticker}}",
  "side": "{{strategy.order.action}}",
  "qty": "{{strategy.order.contracts}}",
  "requestId": "{{timenow}}-{{ticker}}",
  "comment": "{{strategy.order.comment}}"
}

Проверьте на бумажном счёте

У канала есть переключатель Тестовый режим: сигналы проходят весь путь, ордера не отправляются, а результат виден в журнале.

Идемпотентность

requestId — единственное, что отделяет «повторили запрос, потому что не получили ответ» от «открыли вторую позицию».

Ситуация Без requestId С requestId
Таймаут сети, клиент повторил два ордера один ордер, второй ответ — 200 с тем же результатом
Сервис отправил дубль алерта два ордера один ордер
Осознанный повтор через сутки новый ордер новый ордер (окно идемпотентности — 24 часа)
JSON
{
  "accepted": true,
  "duplicate": false,
  "requestId": "2f0f9b1c-…",
  "orderId": "1f0c9d2a-6b41-4f7e-9a02-5c8b1e3d7a44",
  "status": "filled",
  "filledQty": "0.01",
  "avgPrice": "61840.5"
}

Исходящие события

Обратное направление: LootX сообщает вашему сервису, что произошло.

POST<ваш

Мы отправляем событие POST-запросом с теми же заголовками подписи, что ждём от вас. Ответ 2xx считается доставкой; любой другой код или таймаут в 10 секунд — поводом для повтора.

Повторы

Пять попыток с задержками 10 с, 1 мин, 5 мин, 30 мин, 2 ч. После пятой неудачи событие отмечается как недоставленное и остаётся в журнале интеграции — оттуда его можно отправить вручную.

Событие Когда Полезная нагрузка
order.filled Заявка исполнена целиком ордер, цена, комиссия
order.partially_filled Частичное исполнение ордер, исполненный объём
position.closed Позиция закрыта итоговый P&L, причина
stop.triggered Сработал стоп цена срабатывания
alert.fired Сработал ценовой алерт инструмент, условие
risk.limit_reached Достигнут дневной лимит убытка лимит, факт
JSON
{
  "id": "evt_01J8X…",
  "type": "position.closed",
  "createdAt": "2026-09-22T11:04:19.442Z",
  "data": {
    "exchange": "bybit",
    "symbol": "BTCUSDT",
    "side": "long",
    "qty": "0.01",
    "entryPrice": "61840.5",
    "exitPrice": "62310.0",
    "realizedPnl": "4.695",
    "reason": "take_profit"
  }
}
Пример приёмника на Fastify
TypeScript
import Fastify from "fastify";
import crypto from "node:crypto";

const app = Fastify();
const secret = process.env.LOOTX_HOOK_SECRET!;

// Нужно сырое тело: подпись считается от байтов, а не от разобранного объекта.
app.addContentTypeParser("application/json", { parseAs: "buffer" }, (_req, body, done) =>
  done(null, body),
);

app.post("/lootx", async (req, reply) => {
  const raw = req.body as Buffer;
  const ts = String(req.headers["x-lootx-timestamp"] ?? "");
  const got = String(req.headers["x-lootx-signature"] ?? "").replace(/^sha256=/, "");

  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
    return reply.code(401).send({ error: "stale timestamp" });
  }
  const want = crypto.createHmac("sha256", secret).update(`${ts}.${raw}`).digest("hex");
  const ok =
    got.length === want.length &&
    crypto.timingSafeEqual(Buffer.from(got, "hex"), Buffer.from(want, "hex"));
  if (!ok) return reply.code(401).send({ error: "bad signature" });

  const event = JSON.parse(raw.toString("utf8"));
  // Отвечаем сразу, обрабатываем потом: медленный приёмник провоцирует повторы.
  queueMicrotask(() => handle(event));
  return reply.code(200).send({ ok: true });
});

Telegram и таблицы

Для двух самых частых сценариев готовые каналы есть в кабинете — писать приёмник не нужно.

Интеграции → Telegram → Подключить, дальше бот попросит подтвердить аккаунт. Выберите, какие события слать и в какой чат; поддерживаются личные сообщения и группы, в группе бот должен быть администратором.

Интеграции → Google Sheets. Каждая закрытая сделка добавляется строкой: время, инструмент, направление, объём, цены входа и выхода, комиссия, P&L, комментарий. Удобно для собственной статистики и налоговой отчётности.

Универсальный вариант: узел Webhook как приёмник наших событий и HTTP Request для отправки сигналов нам. Подпись считается узлом Crypto (HMAC SHA256) от строки timestamp.body.

Лимиты

Направление Лимит При превышении
Входящие вебхуки 60 запросов/мин на канал 429, заголовок Retry-After
REST, чтение 600 запросов/мин на ключ 429
REST, торговля 120 запросов/мин на ключ 429
WebSocket-подписки 200 каналов на соединение ошибка подписки 4029
99.95%
Доставка вебхуков

за последние 90 дней, с учётом повторов

< 120 мс
Медиана обработки

от приёма сигнала до выставленного ордера

Чек-лист перед боем

Секреты не в коде

Секрет вебхука и API-ключ — в переменных окружения или менеджере секретов, а не в репозитории.

Тестовый режим пройден

Канал отработал на тестовом режиме, в журнале интеграции видны ожидаемые действия.

Стоп в каждом сигнале

stopLoss передаётся всегда, а в правилах канала включён обязательный стоп.

Ограничения выставлены

Максимальный объём, белый список инструментов и дневной лимит убытка настроены так, что ошибка в вашем коде не превратится в потерю депозита.

Есть наблюдение

Событие risk.limit_reached и недоставленные вебхуки приходят туда, где вы их увидите.

https://docs.lootx.trade/en/api/integrations

REST

3Счёт, баланс и позиции

Чтение состояния аккаунта: доступные средства, открытые позиции, плечо.

Состояние счёта читается одним запросом на биржу. LootX нормализует ответ: у Bybit, Binance и OKX разные названия полей и разные единицы, здесь они одинаковые.

GET/v1/account

Баланс и доступные средства по всем подключённым биржам или по одной.

Параметры

Параметр Тип Описание
exchange string Код биржи. Без него — все подключённые
currency string Валюта расчёта, по умолчанию USDT
JSON
{
  "items": [
    {
      "exchange": "bybit",
      "accountType": "UNIFIED",
      "equity": "1284.41",
      "available": "1140.02",
      "usedMargin": "144.39",
      "unrealizedPnl": "12.77",
      "currency": "USDT"
    }
  ]
}

Позиции

GET/v1/positions

Открытые позиции. Закрытые сюда не попадают — для них есть история сделок.

Поля ответа

Поле Описание
symbol Инструмент
side long или short
qty Размер позиции в контрактах
entryPrice Средняя цена входа
markPrice Маркировочная цена биржи
liquidationPrice Цена ликвидации, null если её нет
unrealizedPnl Нереализованный результат
leverage Плечо инструмента

Плечо

POST/v1/positions/leverage

Меняет плечо инструмента. Биржи по-разному относятся к смене плеча при открытой позиции: часть запрещает, часть пересчитывает маржу. Ответ 422 leverage_change_rejected означает, что отказала биржа, а не мы.

https://docs.lootx.trade/en/api/rest-account

REST

4Ордера

Создание, изменение и отмена заявок, чтение истории исполнений.

Создание заявки

POST/v1/orders

Создаёт заявку. Идемпотентен по requestId — подробности в разделе Интеграция с сервисами.

Тело

Поле Тип Обязательно Описание
exchange string да Код биржи
symbol string да Инструмент
side string да buy / sell
type string да market / limit / stop_market / stop_limit
qty string да Объём
price string для limit Цена
triggerPrice string для stop Цена срабатывания
stopLoss string нет Абсолютная цена стопа
takeProfit string нет Абсолютная цена тейка
reduceOnly bool нет Только уменьшать позицию
postOnly bool нет Отменить, если станет тейкером
requestId string нет, но нужно Ключ идемпотентности
Shell
curl -X POST https://api.lootx.trade/v1/orders \
  -H "Authorization: Bearer $LOOTX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "exchange": "bybit",
    "symbol": "BTCUSDT",
    "side": "buy",
    "type": "limit",
    "qty": "0.01",
    "price": "61500",
    "postOnly": true,
    "requestId": "a1b2c3"
  }'
TypeScript
const order = await lootx.post("/v1/orders", {
  exchange: "bybit",
  symbol: "BTCUSDT",
  side: "buy",
  type: "limit",
  qty: "0.01",
  price: "61500",
  postOnly: true,
  requestId: crypto.randomUUID(),
});

Изменение и отмена

PATCH/v1/orders/:id

Меняет цену и/или объём активной заявки. Реализовано как cancel/replace там, где биржа не умеет изменять заявку: id в ответе может отличаться от запрошенного — ориентируйтесь на него, а не на старый.

DELETE/v1/orders/:id

Отменяет заявку. 404 означает, что заявки уже нет: либо исполнилась, либо отменена раньше. Это не ошибка вашего кода — проверьте историю.

DELETE/v1/orders

Отменяет все заявки: по инструменту (symbol), по бирже (exchange) или вообще все. Единственный запрос в API, который мы советуем повесить на горячую клавишу.

История

GET/v1/fills

Исполнения, от новых к старым. Курсорная пагинация: cursor из nextCursor, окно не больше 31 дня за один проход.

Параметр Описание
from, to ISO-8601, границы периода
symbol Фильтр по инструменту
limit До 500 записей за страницу

https://docs.lootx.trade/en/api/rest-orders

WebSocket

5Каналы и подписки

Что можно слушать в реальном времени и как не потерять соединение.

Один сокет, произвольное число подписок: wss://api.lootx.trade/v1/stream.

Подключение

TypeScript
const ws = new WebSocket("wss://api.lootx.trade/v1/stream");

ws.onopen = () => {
  ws.send(JSON.stringify({ op: "auth", token: TOKEN }));
  ws.send(JSON.stringify({ op: "subscribe", channels: ["book:bybit:BTCUSDT:25"] }));
};

// Пинг раз в 20 секунд — иначе сервер закроет соединение по таймауту.
setInterval(() => ws.readyState === 1 && ws.send(JSON.stringify({ op: "ping" })), 20_000);

Каналы

Канал Формат Что приходит
trades trades:<биржа>:<символ> Каждая сделка: цена, объём, сторона
book book:<биржа>:<символ>:<глубина> Стакан: снимок, затем изменения
kline kline:<биржа>:<символ>:<тф> Свечи, включая незакрытую
orders orders Ваши заявки: создание, исполнение, отмена
positions positions Изменения позиций
account account Баланс и маржа

Стакан: снимок и дельты

Первым сообщением канала book приходит полный снимок ("type": "snapshot"), дальше — только изменившиеся уровни ("type": "delta"). Уровень с нулевым объёмом означает, что уровень исчез.

JSON
{
  "channel": "book:bybit:BTCUSDT:25",
  "type": "delta",
  "seq": 88123,
  "data": { "bids": [["61840.5", "0.42"], ["61840.0", "0"]], "asks": [] }
}

Обрывы

Переподключайтесь с задержкой

1 с, 2 с, 4 с… до 30 с, со случайным разбросом. Десять клиентов, синхронно ломящихся после сетевого сбоя, — это ваш собственный DDoS.

Повторяйте подписки

Сервер не помнит, на что вы были подписаны. После auth отправьте subscribe заново.

Сверьтесь с REST

После долгого обрыва прочитайте позиции и заявки через REST: события, случившиеся без соединения, не будут повторены.

https://docs.lootx.trade/en/api/ws-channels

Справочник

6Ошибки и лимиты

Формат ошибки, коды, лимиты запросов и правильная стратегия повторов.

Любая ошибка API возвращается одинаково — HTTP-кодом и телом:

JSON
{
  "error": {
    "code": "insufficient_balance",
    "message": "Недостаточно средств для открытия позиции",
    "details": { "required": "120.00", "available": "84.31" },
    "requestId": "req_01J8X…"
  }
}

Коды

HTTP code Что делать
400 invalid_request Починить тело запроса; повтор не поможет
401 unauthorized Проверить токен и его срок
401 bad_signature Пересчитать HMAC от сырого тела
403 scope_required У ключа нет нужного права
404 not_found Проверить идентификатор
409 duplicate_request Это не ошибка: requestId уже обработан
422 insufficient_balance Уменьшить объём
422 symbol_not_tradable Инструмент недоступен для торговли
429 rate_limited Подождать Retry-After, затем повторить
502 exchange_unavailable Биржа не отвечает — повторить с задержкой
503 maintenance Плановые работы, см. Retry-After

Повторы

TypeScript
async function withRetry<T>(fn: () => Promise<T>, tries = 5): Promise<T> {
  let lastError: unknown;
  for (let i = 0; i < tries; i++) {
    try {
      return await fn();
    } catch (e) {
      lastError = e;
      const status = (e as { status?: number }).status ?? 0;
      if (status && status < 500 && status !== 429) throw e; // чинить, а не повторять
      const base = Math.min(1000 * 2 ** i, 30_000);
      await new Promise((r) => setTimeout(r, base * (0.7 + Math.random() * 0.6)));
    }
  }
  throw lastError;
}

https://docs.lootx.trade/en/api/errors

Справочник

7Справочник вебхуков

Все события, их поля и порядок доставки.

Справочник по исходящим событиям. Как их принимать и проверять подпись — в разделе Интеграция с сервисами.

Общая оболочка

Поле Тип Описание
id string Идентификатор события, уникален навсегда
type string Тип события
createdAt string ISO-8601, UTC
data object Полезная нагрузка, зависит от типа

order.filled

JSON
{
  "orderId": "1f0c9d2a-…",
  "exchange": "bybit",
  "symbol": "BTCUSDT",
  "side": "buy",
  "orderType": "limit",
  "qty": "0.01",
  "avgPrice": "61840.5",
  "fee": "0.0371",
  "feeCurrency": "USDT",
  "source": "webhook",
  "comment": "breakout long"
}

position.closed

JSON
{
  "exchange": "bybit",
  "symbol": "BTCUSDT",
  "side": "long",
  "qty": "0.01",
  "entryPrice": "61840.5",
  "exitPrice": "62310.0",
  "realizedPnl": "4.695",
  "fee": "0.0742",
  "reason": "take_profit",
  "openedAt": "2026-09-22T10:41:02.120Z"
}

Возможные reason: manual, take_profit, stop_loss, liquidation, opposite_signal.

risk.limit_reached

JSON
{
  "limit": "daily_loss",
  "threshold": "150.00",
  "actual": "151.42",
  "action": "trading_locked"
}

https://docs.lootx.trade/en/api/webhooks