Мы, команда блокчейн-инженеров, нередко получаем запросы на интеграцию торговых ботов с KuCoin. На первый взгляд API выглядит стандартно — REST и WebSocket. Но практика показывает: разработчики спотыкаются на аутентификации v2 (passphrase в base64), динамическом WebSocket URL и лимитах. Однажды клиент потерял 2 ETH из-за неправильного timestamp — ордера уходили с задержкой и попадали в stale-zone. Разберём, как собрать отказоустойчивого бота, который экономит средства и нервы. Экономия на комиссиях при переходе с рыночных ордеров на лимитные может достигать 30%. А правильная подписка на WebSocket снижает задержку и косвенно сохраняет ещё 0.5% slippage.
Как работает аутентификация KuCoin и почему она нестандартна?
KuCoin использует HMAC-SHA256 подпись. Ключевой нюанс: passphrase тоже подписывается и кодируется в base64. Ниже — рабочий класс для генерации заголовков.
import hmac
import hashlib
import base64
import time
import json
import httpx
class KuCoinClient:
BASE_URL = "https://api.kucoin.com"
FUTURES_URL = "https://api-futures.kucoin.com"
def __init__(self, api_key: str, api_secret: str, passphrase: str):
self.api_key = api_key
self.api_secret = api_secret
# KuCoin v2 подпись: passphrase тоже подписывается
self.passphrase = base64.b64encode(
hmac.new(api_secret.encode(), passphrase.encode(), hashlib.sha256).digest()
).decode()
def _sign(self, timestamp: str, method: str, endpoint: str, body: str = "") -> str:
str_to_sign = timestamp + method.upper() + endpoint + body
return base64.b64encode(
hmac.new(self.api_secret.encode(), str_to_sign.encode(), hashlib.sha256).digest()
).decode()
def _headers(self, method: str, endpoint: str, body: str = "") -> dict:
timestamp = str(int(time.time() * 1000))
return {
"KC-API-KEY": self.api_key,
"KC-API-SIGN": self._sign(timestamp, method, endpoint, body),
"KC-API-TIMESTAMP": timestamp,
"KC-API-PASSPHRASE": self.passphrase,
"KC-API-KEY-VERSION": "2",
"Content-Type": "application/json"
}
Согласно документации KuCoin, timestamp должен отличаться от серверного не более чем на 5 секунд — иначе 401000. Мы в своих проектах синхронизируем часы через NTP и при каждом запросе читаем серверное время из заголовка KC-API-TIMESTAMP ответа.
Пошаговая инструкция создания API-ключа
- Войдите в аккаунт KuCoin, перейдите в
Настройки→API. - Нажмите Создать API-ключ.
- Выберите версию 2.
- Задайте passphrase (минимум 8 символов).
- Сохраните API-ключ, секрет и passphrase — они показываются один раз.
- В коде используйте класс выше, передав ключи.
Что быстрее: REST или WebSocket?
| Критерий | REST API | WebSocket API |
|---|---|---|
| Задержка получения цены | 200–500 мс (polling) | 10–50 мс (push) |
| Нагрузка на сервер | Высокая (опрос) | Низкая (подписка) |
| Требуемые соединения | Одноразовые HTTP | Постоянное WS |
| Лимиты | 30 запросов/с | 100 подключений на аккаунт |
Для высокочастотных стратегий WebSocket в 5–10 раз быстрее REST. Но стабильность WebSocket-соединения требует реализации переподключения и keepalive.
Как размещать ордера через REST
async def place_order(
self,
symbol: str, # 'BTC-USDT'
side: str, # 'buy' или 'sell'
order_type: str, # 'limit' или 'market'
size: str = None,
price: str = None,
funds: str = None # для market buy по котируемой
) -> dict:
endpoint = "/api/v1/orders"
payload = {
"clientOid": str(int(time.time() * 1000)), # уникальный ID клиента
"symbol": symbol,
"side": side,
"type": order_type
}
if order_type == "limit":
payload["size"] = size
payload["price"] = price
elif side == "buy" and funds:
payload["funds"] = funds # купить на $X USDT
else:
payload["size"] = size
body = json.dumps(payload)
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.BASE_URL}{endpoint}",
content=body,
headers=self._headers("POST", endpoint, body)
)
result = response.json()
if result.get("code") != "200000":
raise KuCoinError(f"Order error: {result.get('msg')}")
return result["data"]
async def get_accounts(self, currency: str = None) -> list:
endpoint = "/api/v1/accounts"
if currency:
endpoint += f"?currency={currency}"
async with httpx.AsyncClient() as client:
response = await client.get(
f"{self.BASE_URL}{endpoint}",
headers=self._headers("GET", endpoint)
)
return response.json().get("data", [])
Обратите внимание: clientOid должен быть уникальным для каждого ордера. Мы генерируем его на основе timestamp и случайной строки, чтобы избежать коллизий при повторной отправке. Это критично для стратегий, где мы переставляем ордера при изменении цены.
Почему WebSocket требует динамический URL?
KuCoin не публикует статический WebSocket URL — его нужно запросить. Это повышает безопасность: токен истекает через 24 часа. Наш клиент получает endpoint, подключается и поддерживает keepalive.
async def get_ws_endpoint(self, private: bool = False) -> dict:
endpoint = "/api/v1/bullet-private" if private else "/api/v1/bullet-public"
method = "POST" if private else "POST"
headers = self._headers(method, endpoint) if private else {"Content-Type": "application/json"}
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.BASE_URL}{endpoint}",
headers=headers
)
data = response.json()["data"]
server = data["instanceServers"][0]
token = data["token"]
ws_url = f"{server['endpoint']}?token={token}&connectId={int(time.time()*1000)}"
ping_interval = server["pingInterval"] / 1000 # в секундах
return {"url": ws_url, "ping_interval": ping_interval}
async def subscribe_ticker(self, symbols: list[str]):
ws_data = await self.get_ws_endpoint(private=False)
async with websockets.connect(ws_data["url"]) as ws:
await ws.send(json.dumps({
"id": str(int(time.time() * 1000)),
"type": "subscribe",
"topic": f"/market/ticker:{','.join(symbols)}",
"privateChannel": False,
"response": True
}))
async def keepalive():
while True:
await asyncio.sleep(ws_data["ping_interval"])
await ws.send(json.dumps({"id": "ping", "type": "ping"}))
asyncio.create_task(keepalive())
async for message in ws:
data = json.loads(message)
if data["type"] == "message" and "data" in data:
await self.on_ticker(data["data"])
Типичная проблема WebSocket: разрыв соединения без уведомления. При долгом отсутствии торговли KuCoin может закрыть сокет без отправки close frame. Наш обработчик ошибок автоматически переподключается с экспоненциальной задержкой (1с, 2с, 4с... до 30с). Это покрывает 99% случаев.
Какие ошибки встречаются чаще всего?
| Ошибка | Причина | Решение |
|---|---|---|
code: "400003" |
Неверная подпись | Проверить алгоритм HMAC, passphrase, timestamp |
code: "401000" |
Просроченный timestamp | Разница с сервером не более 5 секунд |
code: "429000" |
Превышение лимита запросов | Ввести задержку, использовать заголовки лимитов |
KuCoin API возвращает code: "200000" при успехе (не HTTP статус). Проверяйте именно это поле.
Особенности Futures API KuCoin
KuCoin предоставляет отдельный домен для фьючерсного API: https://api-futures.kucoin.com. Аутентификация идентична spot-версии, но эндпоинты отличаются. Для фьючерсных ботов важно правильно задавать leverage и marginType (isolated или cross) в теле ордера. Ставка финансирования (funding rate) обновляется каждые 8 часов — эта информация доступна через /api/v1/funding-rate/{symbol}/current. Слежение за funding rate позволяет избегать нежелательного списания при удержании позиции через расчётный период.
Что входит в работу под ключ
- Проектирование архитектуры бота (стратегия, управление рисками, выбор между spot/futures)
- Реализация клиента с обработкой лимитов, переподключений и логированием всех ошибок
- Тестирование на sandbox-окружении (полное покрытие сценариев: ордера, отмена, частичное исполнение)
- Развёртывание на вашем сервере или в облаке (Docker, systemd, мониторинг через Grafana)
- Документация по API и эксплуатации: как перезапускать, как менять стратегии
- Поддержка в течение 30 дней после запуска: фикс багов, консультации
Мы гарантируем стабильность: наша команда имеет 7+ лет опыта в крипто-трейдинге. Для запуска бота в продакшене свяжитесь с нами — подготовим архитектуру за 2 дня и поможем избежать типичных ошибок интеграции. Получите консультацию по вашему проекту — обсудим стратегию, риски и технические детали.







