Мы, команда блокчейн-инженеров, нередко получаем запросы на интеграцию торговых ботов с 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 дня и поможем избежать типичных ошибок интеграции. Получите консультацию по вашему проекту — обсудим стратегию, риски и технические детали.







