LootX

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

новое Версия v1 Обновлено 23 сентября 2026 г. Эта страница в PDF

Как соединить 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 и недоставленные вебхуки приходят туда, где вы их увидите.

FAQ

Можно ли торговать по сигналам TradingView через LootX?
Да. Алерт TradingView отправляет вебхук на ваш персональный URL в LootX, а терминал исполняет действие по правилам, которые вы задали: тип ордера, объём, стоп и ограничения по риску.
Что защищает вебхук от постороннего?
Каждый входящий вебхук подписан: заголовок X-LootX-Signature содержит HMAC-SHA256 от тела запроса на вашем секрете. Запрос без корректной подписи и свежей метки времени отклоняется.
Что будет, если сервис отправит один и тот же сигнал дважды?
Поле requestId делает запрос идемпотентным: повтор с тем же идентификатором в течение 24 часов возвращает результат первой обработки и не создаёт второй ордер.