API для маркетмейкеров
REST API TBX Markets для собственного торгового бота. Рынок: USDT/RUB, идентификатор usdtrub. Документация соответствует развёрнутым Peatio 2.6 и Barong 2.6; проверено 14 сентября 2026.
Быстрый старт
- Войдите в профиль, подтвердите почту и включите двухфакторную аутентификацию приложением.
- В разделе «API ключи» нажмите создание ключа и введите текущий шестизначный код 2FA. На телефоне откройте «Мои API ключи».
- Сохраните
kid(публичный идентификатор) иsecret. Секрет показывается только при создании; повторно получить его через список ключей нельзя. - Разместите ключ и секрет в переменных окружения сервера бота. Сначала проверьте чтение балансов и своих заявок.
- Пополните USDT для продаж и RUB для покупок.
lockedуже занят другими заявками или обработкой операций и недоступен для новых ордеров.
Ключ наследует права аккаунта. В текущей версии нет отдельных разрешений «только чтение», «только торговля» и IP-ограничений для каждого ключа. Поле scope не является действующим ограничителем прав. Для отключения или удаления ключа требуется код 2FA. Управление выводом дополнительно проверяет 2FA, адрес получателя и правила вывода; принятые заявки проверяет оператор. Храните ключи на сервере, не в браузерном JavaScript.
Адреса и формат
| Назначение | Адрес |
|---|---|
| Торговый REST API | https://tbxmarkets.com/api/v2/peatio |
| Управление ключами через сессию | https://tbxmarkets.com/api/v2/barong/resource/api_keys |
| Документация | https://tbxmarkets.com/api-docs |
| Пример Python | Скачать клиент |
Все пути в следующих таблицах указаны относительно торгового REST API. Запросы и ответы — JSON, Content-Type: application/json. Публичные методы не требуют ключа. Приватные запросы используют три заголовка подписи, описанные ниже; cookie и CSRF-токен для них не нужны.
Передавайте цены и объёмы десятичными строками, например "84.50" и "1.00". В расчётах используйте Decimal, а не двоичные числа с плавающей точкой. Идентификаторы рынка и валют — в нижнем регистре. price — RUB за 1 USDT, volume — USDT. Время в фильтрах REST — Unix секунды, nonce авторизации — Unix миллисекунды.
Подпись запроса
Алгоритм ключа: HS256, то есть HMAC-SHA256. Строка подписи состоит из nonce и kid без разделителя:
nonce = текущее время Unix в миллисекундах, десятичная строка
payload = nonce + kid
signature = hex(HMAC-SHA256(secret, payload))
| Заголовок | Значение |
|---|---|
X-Auth-Apikey |
kid, полученный при создании ключа |
X-Auth-Nonce |
nonce в миллисекундах |
X-Auth-Signature |
HMAC в hexadecimal, 64 символа |
Секрет применяется как исходная UTF-8 строка: не декодируйте его из hex. Метод, URL, query и тело не входят в подпись этой версии. Используйте только HTTPS и генерируйте новые заголовки для каждого запроса. Сервер принимает nonce при abs(server_ms - nonce) < 5000; синхронизируйте часы через NTP. Nonce — проверка времени, а не ключ идемпотентности; не рассчитывайте на блокировку повторного запроса.
import hashlib, hmac, os, time
key = os.environ["TBX_API_KEY"]
secret = os.environ["TBX_API_SECRET"]
nonce = str(time.time_ns() // 1_000_000)
signature = hmac.new(secret.encode(), (nonce + key).encode(), hashlib.sha256).hexdigest()
headers = {"X-Auth-Apikey": key, "X-Auth-Nonce": nonce, "X-Auth-Signature": signature}
Параметры рынка
Получайте параметры при каждом запуске из GET /public/markets и периодически обновляйте. Значения могут изменяться.
| Параметр USDT/RUB | На дату проверки |
|---|---|
id |
usdtrub |
base_unit / quote_unit |
usdt / rub |
min_amount |
1 USDT |
amount_precision |
2; шаг 0,01 USDT |
price_precision |
2; шаг 0,01 RUB |
min_price / max_price |
0,01 / 100 000 RUB |
| Типы | limit, market |
Шаг объёма 0,01 не означает, что можно поставить заявку на 0,01: минимальный объём сейчас 1 USDT. Базовая ставка maker/taker — 0,2%, но группа аккаунта может иметь другие ставки. Фактические maker_fee и taker_fee возвращаются в ордере. Комиссия удерживается из полученного актива; учитывайте её при расчёте остатков.
Публичные данные
| Метод и путь | Параметры и результат |
|---|---|
GET /public/markets |
Рынки и их параметры; стандартная пагинация page, limit. |
GET /public/markets/usdtrub/depth |
limit 1–1000, по умолчанию 300. Снимок уровней: timestamp, asks, bids. Каждый уровень — [price, volume]. |
GET /public/markets/usdtrub/order-book |
Отдельные ордера; asks_limit, bids_limit 1–200, по умолчанию 20. Для котирования обычно достаточно агрегированного /depth. |
GET /public/markets/usdtrub/tickers |
Текущий ticker с последней ценой и статистикой; at, вложенный ticker. |
GET /public/markets/tickers |
Ticker всех рынков, возможен кэш до 60 секунд. |
GET /public/markets/usdtrub/trades |
Последние публичные сделки, limit 1–1000, по умолчанию 100. Для каждой сделки: id, price, amount, total, created_at, taker_type. |
GET /public/markets/usdtrub/k-line |
Свечи [timestamp, open, high, low, close, volume]. period в минутах; limit 1–10000, по умолчанию 30; time_from, time_to в секундах. |
Периоды свечей: 1, 5, 15, 30, 60, 120, 240, 360, 720, 1440, 4320, 10080. При двух границах времени limit игнорируется. Свечи отражают исполнения на TBX. Обновление стакана само по себе свечу не создаёт.
Пример формы ответа /depth — демонстрационные числа, не текущая котировка:
{"timestamp":1789401892,"asks":[["85.24","1.0"]],"bids":[["83.23","1.0"]]}
Ищите best ask как минимальную цену продаж, best bid как максимальную цену покупок; не полагайтесь на порядок массива. /depth является снимком, а не потоком изменений. Для публичной истории используйте limit: в текущей реализации /trades объявленные timestamp и order_by не применяются к запросу Influx; надёжной исторической пагинации этим методом нет.
Балансы и собственные сделки
| Метод и путь | Параметры и результат |
|---|---|
GET /account/balances |
Балансы доступных валют; page, limit, nonzero=true для ненулевых. Поля currency, balance, locked. |
GET /account/balances/usdt |
Баланс одной валюты. Аналогично /rub. |
GET /market/trades |
Собственные исполнения. market=usdtrub, limit 1–1000, page от 1, order_by=asc/desc, time_from, time_to, type=buy/sell. |
balance — доступно, locked — заблокировано; общий учтённый остаток равен их сумме. У своих сделок также возвращаются fee_currency, fee, fee_amount. Дедуплицируйте исполнения по id. Для устойчивой выгрузки делайте перекрытие по времени и сверяйте идентификаторы, так как во время пагинации могут появиться новые сделки.
Создание ордера
POST /market/orders — приватный подписанный запрос.
| Поле | Требование |
|---|---|
market |
Обязательно: usdtrub |
side |
Обязательно: buy или sell |
volume |
Обязательно: положительная десятичная строка в USDT, не ниже минимума |
ord_type |
limit по умолчанию; также поддерживается market |
price |
Обязательно для limit; для market передавать нельзя |
{"market":"usdtrub","side":"buy","ord_type":"limit","price":"80.00","volume":"1.00"}
Это пример структуры, а не рекомендуемая торговая цена. Выбирайте цену по свежему стакану и своей стратегии. Создание лимитного ордера может сразу привести к исполнению, если цена пересекает встречную сторону.
Основные поля ответа: id, uuid, market, side, ord_type, price, avg_price, state, origin_volume, remaining_volume, executed_volume, maker_fee, taker_fee, trades_count, created_at, updated_at. Сохраните id сразу после ответа. Успешное создание не означает полное исполнение или окончательную фиксацию средств в стакане: обработка проходит асинхронно.
Чтение и отмена ордеров
| Метод и путь | Назначение |
|---|---|
GET /market/orders |
Свои заявки. market=usdtrub, state=wait, limit до 1000 (по умолчанию 100), page от 1. Фильтры type=buy/sell, ord_type, time_from, time_to, order_by=asc/desc. |
GET /market/orders/{id} |
Детали заявки и её сделки. Принимает числовой ID или UUID, выданный сервером. |
POST /market/orders/{id}/cancel |
Запрос отмены одной собственной заявки. |
POST /market/orders/cancel |
Отмена собственных активных заявок. Тело {"market":"usdtrub"}; необязательное side: buy или sell. Без market затрагивает все рынки аккаунта. |
Состояния: pending — принята на обработку; wait — активна, возможны частичные исполнения; done — исполнена; cancel — отменена; reject — отклонена. При частичном исполнении смотрите executed_volume и remaining_volume, а не только state.
Отмена асинхронна. Между запросом отмены и её обработкой остаток может исполниться. Перед заменой дождитесь терминального состояния предыдущей заявки и повторно прочитайте баланс. Массовая отмена выбирает wait; для pending дождитесь обработки, затем перепроверьте.
Ограничения для бота
- Атомарного
post_only,IOC/FOK, пакетного cancel-and-replace и гарантированного self-trade prevention в этом контракте нет. Проверка цены перед заявкой снижает вероятность taker-исполнения, но не исключает гонку. client_order_idиIdempotency-Keyне поддерживаются. Повторный POST после таймаута может создать второй ордер. Сначала сверяйте свои заявки и сделки; при неоднозначности остановите выставление до разрешения ситуации.- Баланс общий с интерфейсом и другими ключами того же аккаунта. Для независимого бота используйте отдельный аккаунт либо единый координатор заявок.
- Не создавайте встречные заявки между собственными ботами. Разделяйте buy/sell, следите за внешним стаканом и ограничивайте инвентарь.
- Не оценивайте глубину по числу строк: реальная ликвидность определяется суммой доступных RUB и USDT. Резервируйте запас для комиссий.
- Фиксированный гарантированный лимит запросов для API-ключей сейчас не опубликован. Для начального подключения используйте не более 1 запроса в секунду на процесс и обновление котировок раз в 30–60 секунд. Это режим интеграции, а не обещание пропускной способности.
При потере связи остановите новые заявки. Сервер не снимает их автоматически при отключении клиента. На старте процесса сначала загрузите оставшиеся заявки предыдущего запуска и восстановите локальное состояние. У бота должен быть явный аварийный режим отмены собственных заявок.
Ошибки и повторные запросы
Ошибки обычно имеют форму {"errors":["код.ошибки"]}. У gateway возможны ответы другого формата; проверяйте HTTP status до разбора JSON.
| Код | Что делать |
|---|---|
401 authz.nonce_expired |
Синхронизировать часы, создать новую подпись. |
401 authz.invalid_signature |
Проверить secret как строку, порядок nonce+kid и названия заголовков. |
401 authz.apikey_not_active / authz.unexistent_apikey |
Ключ отключён или удалён; остановить бота. |
401/403 о правах аккаунта |
Проверить статус аккаунта и уровень доступа; не повторять запрос в цикле. |
404 |
Проверить путь, ID и принадлежность ордера текущему аккаунту. |
422 |
Проверить errors: минимум, precision, баланс, состояние ордера. Сначала исправить данные. |
429 |
Уменьшить частоту; учитывать Retry-After, если есть. |
5xx, timeout |
Для GET — ограниченный exponential backoff с jitter; для записи — сверка состояния перед повтором. |
Для GET можно начать с задержек 1, 2, 4, 8 секунд с небольшим случайным отклонением. На каждой попытке создавайте новый nonce. Не выводите секрет, подпись и полные заголовки в журнал.
Пример Python
Готовый клиент на стандартной библиотеке Python запускается только для чтения балансов и активных ордеров. Он не отправляет ордера автоматически и не повторяет записи после ошибки.
export TBX_API_KEY='ваш_kid'
export TBX_API_SECRET='ваш_secret'
python tbx_api_client.py
Из вашего приложения:
from tbx_api_client import TBXClient
client = TBXClient()
book = client.request("GET", "/public/markets/usdtrub/depth", {"limit": 20}, private=False)
balances = client.request("GET", "/account/balances")
orders = client.request("GET", "/market/orders", {"market": "usdtrub", "state": "wait"})
# Эти строки создают и отменяют РЕАЛЬНУЮ заявку; выполняйте их только явно:
# order = client.request("POST", "/market/orders", {
# "market": "usdtrub", "side": "buy", "ord_type": "limit",
# "price": "80.00", "volume": "1.00"
# })
# client.request("POST", f"/market/orders/{order['id']}/cancel", {})
Пример клиента не является готовой торговой стратегией. Хранение ордеров, восстановление после сбоя, лимиты позиции и сверка исполнения остаются задачей бота.
Управление ключами программно
Обычному боту достаточно торгового REST API. Управление ключами выполняется в профиле через cookie-сессию Barong. Для записей нужен X-CSRF-Token этой сессии и действующий totp_code.
Метод относительно /api/v2/barong/resource/api_keys |
Параметры |
|---|---|
GET / |
Список без secret; page, limit, ordering=asc/desc, order_by. |
POST / |
{"algorithm":"HS256","totp_code":"123456"}; возвращает kid и secret. Код здесь условный. |
PATCH /{kid} |
{"state":"inactive","totp_code":"123456"} для отключения; active для включения. |
DELETE /{kid} |
totp_code в параметрах; успешное удаление возвращает 204. |
При выключенной 2FA создание/изменение/удаление отклоняется: resource.api_key.2fa_disabled; неверный код — resource.api_key.invalid_totp. После создания нового ключа обновите секреты бота, проверьте чтение, затем отключите старый ключ. Не меняйте ключи во время неразрешённого таймаута записи.
Проверка перед запуском
Сверьте настройки рынка, время сервера бота, доступные остатки и фактические комиссии. Проверьте чтение балансов, ошибочную подпись, отключение ключа, создание и отмену одной небольшой заявки, восстановление после рестарта и поведение при таймауте. Не запускайте несколько независимых процессов с одним балансом без координации.
WebSocket используется торговым интерфейсом, но приватное подключение внешнего бота с API-ключом этой документацией не заявляется как проверенное. Для первой интеграции используйте описанные REST-снимки; перед переходом на потоковую модель отдельно проверьте авторизацию, порядок обновлений и восстановление после пропусков.
OTC и RFQ
Терминал OTC / RFQ использует отдельный контракт с общими балансами Peatio. База API: /api/v2/peatio/account/rfq. Авторизация и подпись такие же, как выше; котирование дополнительно требует включения аккаунта в список поставщиков. Обычный API-ключ не даёт права дилера. Отдельные RFQ scopes не заявляются.
| Метод и путь относительно базы | Результат |
|---|---|
GET / |
Доступные возможности, dealer_uid, member_uid, server_time |
GET /requests?view=mine |
Последние 50 своих RFQ |
GET /requests?view=dealer |
Очередь разрешённого поставщика |
POST /requests |
Создание RFQ |
GET /requests/:id |
RFQ, котировки и подтверждение расчёта |
POST /requests/:id/quotes |
Обеспеченная котировка поставщика |
POST /requests/:id/accept |
Полное исполнение выбранной котировки |
POST /requests/:id/cancel |
Отмена собственного RFQ с возвратом резервов |
POST /quotes/:id/cancel |
Отзыв своей котировки |
Тело создания RFQ:
{"market":"usdtrub","side":"buy","amount":"2.00","settlement":"balance","client_reference":"unique-client-reference-0001"}
side — направление клиента: buy означает покупку USDT за RUB. Объём от 1 USDT, максимум два десятичных знака. Заявка действует 24 часа; создание не списывает средства. Поддержан немедленный расчёт между уже пополненными внутренними балансами.
Тело предложения цены:
{"price":"85.25","ttl_seconds":60,"client_reference":"unique-dealer-reference-0001"}
Цена выражена в RUB за 1 USDT, максимум два знака после запятой; срок 30–300 секунд. Итог total округляется до копеек HALF_UP. Публикация резервирует исходящие средства поставщика. Новая котировка заменяет его старую котировку в этой заявке. Конкуренты не видят цены друг друга; клиент видит все адресованные ему предложения. Оператор действует только от явно назначенного ему поставщика, списание относится к dealer_uid.
Тело подтверждения:
{"quote_id":123}
Клиент подтверждает точные суммы до вызова. Успех — RFQ со state: settled, принятой котировкой и settlement_receipt. Изменения обоих балансов проходят одной транзакцией. Частичного исполнения нет. Дополнительная комиссия OTC первой версии равна нулю; маржа может входить в цену поставщика. Внебиржевая сделка не меняет публичные свечи и объём CLOB.
client_reference содержит 16–64 латинских букв, цифр или дефисов. После тайм-аута повторяйте тот же запрос с тем же ключом; новый ключ может создать другой RFQ. Для accept идентификатором повтора служит RFQ и выбранная котировка. При неопределённом ответе сначала прочитайте RFQ. Повтор уже исполненной котировки возвращает тот же результат, повтор другой — отказ.
Ошибки в errors: rfq.insufficient_balance, rfq.quote_expired, rfq.request_closed, rfq.permission_denied, rfq.reference_conflict. 401 — авторизация, 403 — права, 404 — отсутствие или недоступность объекта, 422 — параметры/состояние/средства, 409 — временный конфликт транзакций.
Начальный автоматический поставщик — текущий маркетмейкер: до 10 USDT на котировку, до 3 активных котировок и до 10 USDT совокупного номинала. Цена берётся из его свежих ордеров; у клиента и поставщика должны быть доступные средства. Цена действует 60 секунд, после истечения повторное автоматическое предложение возможно через 120 секунд; оператор может предложить новую цену вручную. Это один поставщик с двумя способами котирования, а не несколько независимых маркетмейкеров.