Глава 27 из 33

Коротко о главном

Полимаркет предоставляет три публичных API: CLOB (торговля), Gamma (поиск рынков) и Data (аналитика). Официальный Python SDK - py-clob-client 0.34.6. Аутентификация использует API-ключ + подпись ECDSA, ордера подписываются через EIP-712 с помощью proxy-кошелька на Polygon. Лимиты запросов ограничивают вас примерно 60 ордерами в минуту на ключ. Главный подводный камень для новых разработчиков - проблема сопоставления condition_id → token_id между Gamma и CLOB: решите её первой, и всё остальное встанет на места. Каждый месяц на Полимаркет зарабатывается около 40 млн $ на liquidity rewards и спреде, который ловят боты, - почти всё это пользователями API.

Что вы узнаете: как три API сочетаются друг с другом, как установить и настроить py-clob-client, как аутентифицироваться через proxy-кошелёк, как получать рынки и order book, как размещать и отменять ордера, как стримить обновления цены в реальном времени по WebSocket, точные лимиты запросов и как чисто делать backoff, а также готовую к продакшену архитектуру бота, которую можно расширять.
Предварительные требования: пополненный аккаунт Полимаркет хотя бы с одной завершённой ручной сделкой, Python 3.8+ (или Node.js) и базовое знакомство с HTTP, JSON и асинхронным кодом. Если вы ещё не торговали вручную, начните с первой сделки, прежде чем подключать бота.
01
Глава первая

Часть 1: Три API

Полимаркет чётко разделяет зоны ответственности между тремя отдельными сервисами. Использование правильного API для каждой задачи сохраняет бота быстрым, простым и в рамках лимитов.

APIБазовый URLНазначениеНужна аутентификация
CLOB APIclob.polymarket.comРазмещать, отменять и отслеживать ордера. Читать order book. Запрашивать позиции.Да (для торговли)
Gamma APIgamma-api.polymarket.comПросматривать рынки, получать метаданные, изображения, цены исходов, объём, срок, теги.Нет (публичный)
Data APIdata-api.polymarket.comИсторические сделки, снимки позиций, аналитика пользователей, данные лидерборда.Нет (публичный)

Типичный цикл бота использует Gamma для поиска рынков, CLOB для получения order book и размещения сделок и Data для офлайн-бэктеста стратегии. Думайте о Gamma как о «каталоге», о CLOB как о «бирже», а о Data как о «складе».

Совет профессионала: Gamma и Data не требуют аутентификации. Их можно исследовать через curl или браузер прямо сейчас - без аккаунта. Это отличный способ прототипировать ещё до создания API-ключа.
02
Глава вторая

Часть 2: Аутентификация и модель proxy-кошелька

Полимаркет не подписывает сделки приватным ключом вашего основного кошелька. Вместо этого используется proxy-кошелёк в стиле Gnosis Safe: ваш основной кошелёк авторизует proxy, а proxy исполняет все сделки на Polygon. Ваш API-бот общается с этим proxy.

Что вам нужно

  • API-ключ - создайте в Settings → Developer на Полимаркет
  • Приватный ключ - ключ вашего торгового кошелька (НЕ seed-фраза основного MetaMask)
  • Адрес funder - адрес вашего proxy-кошелька (показан в Settings → Wallet)
  • Chain ID - 137 (Polygon mainnet)
  • Тип подписи - 1 (POLY_PROXY, стандарт для розничных пользователей)
Несогласуемые правила безопасности: никогда не коммитьте приватный ключ в git. Используйте переменные окружения (.env) или менеджер секретов. Никогда не вставляйте ключи в Discord, в issues на GitHub или в ChatGPT. Считайте, что любой ключ, попавший в буфер обмена, уже скомпрометирован. Меняйте ключи при любых сомнениях.
03
Глава третья

Часть 3: Установка py-clob-client

Официальный Python SDK - самый быстрый путь от нуля до первого ордера. Используем версию 0.34.6, актуальную на апрель 2026.

# Create a virtual environment first
python3 -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows

# Install the SDK
pip install py-clob-client==0.34.6 requests websocket-client python-dotenv

Базовая настройка клиента

import os
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON

load_dotenv()

client = ClobClient(
 host="https://clob.polymarket.com",
 key=os.environ["POLY_PRIVATE_KEY"],
 chain_id=POLYGON, # 137
 signature_type=1, # POLY_PROXY
 funder=os.environ["POLY_FUNDER"],
)

# One-time: derive and cache API credentials
client.set_api_creds(client.create_or_derive_api_creds())

Вызов create_or_derive_api_creds() подписывает сообщение вашим приватным ключом и обменивает его на API-ключ, secret и passphrase. Закэшируйте их в .env после первого запуска, чтобы не обращаться к эндпоинту вывода при каждом старте.

Рабочий пример - минимальный .env:
POLY_PRIVATE_KEY=0xabc...
POLY_FUNDER=0xdef...
POLY_API_KEY=...
POLY_SECRET=...
POLY_PASSPHRASE=...
04
Глава четвёртая

Часть 4: Поиск рынков через Gamma

Прежде чем торговать, нужно найти стоящие рынки. Gamma возвращает JSON со всем, что показывает интерфейс Полимаркет: вопрос, исходы, цены, объём за 24ч, срок, теги и изображения.

import requests

resp = requests.get(
 "https://gamma-api.polymarket.com/markets",
 params={
 "active": "true",
 "closed": "false",
 "tag_slug": "politics",
 "limit": 20,
 "order": "volume24hr",
 "ascending": "false",
 },
 timeout=10,
)
resp.raise_for_status()
markets = resp.json()

for m in markets:
 print(f"{m['slug']:50} Yes ${float(m['outcomePrices'][0]):.3f} Vol24h ${m.get('volume24hr', 0):,.0f}")

Полезные параметры запроса Gamma

ПараметрЧто делает
tag_slugФильтр по категории (politics, sports, crypto, culture и т. д.)
active=trueТолько рынки, принимающие сделки сейчас
closed=falseСкрыть разрешённые рынки
order=volume24hrСортировка по недавнему объёму (сигнал ликвидности)
end_date_minДата ISO - пропустить рынки, разрешающиеся слишком скоро
limitДо 500 на страницу (используйте offset для пагинации)
05
Глава пятая

Часть 5: Сопоставление condition_id → token_id

Это проблема номер 1 в разработке ботов для Полимаркет. Gamma возвращает condition_id (один на рынок). Сделки CLOB используют token_id (один на исход). Вам всегда нужны оба.

Ошибка: передача condition_id в эндпоинты CLOB, ожидающие token_id. Вы получите загадочную ошибку 'invalid token'. Всегда сначала сопоставляйте, потом торгуйте.
# Each Gamma market object contains 'clobTokenIds' - a JSON string array
import json

market = markets[0]
token_ids = json.loads(market['clobTokenIds']) # ['7410...', '1120...']
yes_token = token_ids[0] # First outcome
no_token = token_ids[1] # Second outcome

# Alternative: ask CLOB directly using condition_id
info = client.get_market(condition_id=market['conditionId'])
yes_token = info['tokens'][0]['token_id']

Подвох с порядком исходов

Массивы outcomes и clobTokenIds в Gamma индексированы совместно. Всегда читайте метку исхода, а не предполагайте, что индекс 0 - это «Yes». На рынках с несколькими исходами (NegRisk, «Оскар», выборы) индекс 0 может быть «Kamala Harris» или «Taylor Swift» - порядок детерминирован, но специфичен для рынка.

06
Глава шестая

Часть 6: Чтение order book

book = client.get_order_book(token_id=yes_token)

best_bid = float(book.bids[0].price) if book.bids else None
best_ask = float(book.asks[0].price) if book.asks else None
mid = (best_bid + best_ask) / 2 if best_bid and best_ask else None
spread = best_ask - best_bid if best_bid and best_ask else None

print(f"Bid {best_bid} Ask {best_ask} Mid {mid:.4f} Spread {spread:.4f}")

Order book возвращается как отсортированные массивы (bids по убыванию, asks по возрастанию). У каждого уровня есть price и size. Чтобы оценить slippage для более крупного ордера, пройдите по стакану и накапливайте объём, пока не наберёте целевой размер.

07
Глава седьмая

Часть 7: Размещение ордеров

Лимитный ордер (GTC - по умолчанию)

from py_clob_client.clob_types import OrderArgs, OrderType

args = OrderArgs(
 token_id=yes_token,
 price=0.45,
 size=100, # Shares, not dollars. 100 shares @ $0.45 = $45 max cost.
 side="BUY",
)
signed_order = client.create_order(args)
response = client.post_order(signed_order, OrderType.GTC)
print(response)

Вызов create_order подписывает структурированное сообщение EIP-712 вашим приватным ключом. post_order отправляет его в CLOB. Вы никогда не передаёте сырые приватные ключи по сети - только подписанные ордера.

Типы ордеров

ТипКодПоведениеКогда использовать
Good Till CancelledGTCЛежит в стакане до исполнения или отмены вамиПо умолчанию. Большинство маркет-мейкинга и лимитных стратегий.
Good Till DateGTDАвтоотменяется в заданное времяПо событию: «отмени за 5 мин до объявления ФРС»
Fill or KillFOKДолжен исполнить весь объём немедленно или полностью отменитьсяНоги арбитража, где частичное исполнение губит сделку
Fill and KillFAKИсполняет что может по лимитной цене, остаток отменяетАгрессивный тейк - работает как рыночный ордер с потолком цены

Отмена

# Single order
client.cancel(order_id="0xabc...")

# Cancel all orders on a specific market
client.cancel_market_orders(market=market['conditionId'])

# Nuclear option: cancel everything
client.cancel_all()
08
Глава восьмая

Часть 8: Стриминг по WebSocket

Опрашивать Gamma каждую секунду расточительно, и вы быстро упрётесь в лимиты. Фид WebSocket стримит обновления order book и сделок в реальном времени с задержкой меньше секунды.

import json, websocket

WS_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

def on_open(ws):
 ws.send(json.dumps({
 "type": "market",
 "assets_ids": [yes_token, no_token],
 }))

def on_message(ws, message):
 event = json.loads(message)
 if event.get("event_type") == "price_change":
 print(f"{event['market']} {event['side']} {event['price']} size={event['size']}")

ws = websocket.WebSocketApp(
 WS_URL,
 on_open=on_open,
 on_message=on_message,
)
ws.run_forever(ping_interval=20)

Существует два фида: /market (публичный order book и сделки) и /user (ваши собственные события ордеров и исполнений, с аутентификацией). Продакшен-боты обычно подключаются к обоим, автоматически переподключаются при разрыве и считают WebSocket источником истины о текущем состоянии стакана.

Heartbeat и переподключения: отправляйте ping каждые 20 секунд. Если пропущено два pong - переподключайтесь. При переподключении всегда сначала загружайте order book заново через REST, затем переподписывайтесь - иначе локальный стакан разойдётся с реальностью.
09
Глава девятая

Часть 9: Лимиты запросов и backoff

Класс эндпоинтаЛимитВсплеск
Размещение ордеров (CLOB)~60 / мин на API-ключ~10 / сек
Отмена ордеров~120 / мин~20 / сек
Чтение рыночных данных (стакан CLOB)~300 / минвыше, зависит
Gamma APIЩедрый; уважайте 429-
Сообщения WebSocketБез практического лимита на входе-

При получении HTTP 429 сервер возвращает заголовок Retry-After. Реализуйте экспоненциальный backoff с jitter:

import random, time

def post_with_backoff(fn, *args, max_retries=6):
 for attempt in range(max_retries):
 try:
 return fn(*args)
 except Exception as e:
 if "429" in str(e):
 sleep = (2 ** attempt) + random.random()
 time.sleep(min(sleep, 30))
 continue
 raise
 raise RuntimeError("Too many retries")
10
Глава десятая

Часть 10: Эталонная архитектура бота

У любого надёжного бота Полимаркет одни и те же шесть компонентов. Стройте каждый как отдельный модуль; держите их слабо связанными.

КомпонентОтветственностьИспользуемые API
СканерПлановая задача: тянуть рынки по вашим критериям (теги, объём, дни до срока)Gamma
Движок ценПоддерживать локальные order book в реальном времени по WebSocketCLOB WS
Генератор сигналовЧистая функция: состояние стакана + метаданные → целевая позиция- (в памяти)
Менеджер ордеровСравнить текущие ордера с целью, разместить/отменить минимальноCLOB REST
Менеджер рисковПрименять лимиты на рынок, дневные лимиты убытка, предохранители- (в памяти + БД)
Логгер и журналСохранять каждое решение, исполнение, отмену. Питает налоговые отчёты и отладку.SQLite / Postgres
Сначала надёжность: прежде чем оптимизировать PnL, убедитесь, что бот может чисто перезапуститься в 3 часа ночи в воскресенье без человека. Это означает идемпотентное размещение ордеров (используйте client-side order ID), устойчивое состояние и автоматические оповещения (Telegram, Discord, PagerDuty) на любое необработанное исключение.

Часть 11: Частые режимы отказа

  • Устаревшие данные WebSocket - отслеживайте время последнего сообщения по каждому активу; если нет обновлений >30с на активном рынке, форсируйте обновление через REST.
  • Коллизии nonce - py-clob-client управляет nonce ордеров за вас, но если пишете свой подписчик, инкрементируйте nonce на каждом ордере.
  • Недостаточный баланс - всегда проверяйте баланс pUSD перед отправкой; стакан может показывать ваш ордер, но матчинг его отклонит.
  • Рынок на паузе или разрешается - проверяйте market.active && !market.closed перед торговлей. Обновления Gamma отстают от CLOB на несколько секунд у момента разрешения.
  • Несоответствие адаптера NegRisk - рынки с несколькими исходами идут через отдельный адаптер NegRisk. SDK с этим справляется, но убедитесь, что ваш ордер ушёл в нужный venue.
Ограничение testnet: Полимаркет не запускает публичный testnet в 2026. «Paper trading» означает размещение крошечных реальных ордеров (1-5 $) на рынках с низкой ликвидностью. Заложите несколько долларов на первую неделю отладки - это сэкономит сотни позже.

Часть 12: Liquidity rewards через API

Полимаркет распределяет около 5 млн $/мес на общие liquidity rewards плюс 5 млн $+/мес на награды специально для спорта (см. Liquidity rewards). Подавляющая часть идёт API-маркет-мейкерам, которые держат узкие двусторонние котировки на тысячах рынков.

Формула наград поощряет ордера у середины, размер и время в стакане. Минимальный цикл маркет-мейкинга:

  1. Прочитайте order book целевого рынка
  2. Вычислите справедливую середину (например, VWAP трёх верхних уровней с каждой стороны)
  3. Выставьте bid на mid − spread_target/2 и ask на mid + spread_target/2
  4. На каждом обновлении WebSocket переоценивайте цену, если котировка ушла больше чем на тик от цели
  5. Отменяйте и выходите, если стакан истончается или выходят новости

Часть 13: Выход в продакшен

  • Хостинг: VPS за 6 $/мес (Hetzner, DigitalOcean) в Европе или US-East хватит большинству ботов. Размещайте рядом с Polygon RPC, если нужна задержка меньше 10мс.
  • RPC: используйте Alchemy, Infura или QuickNode для надёжного Polygon RPC. Бесплатных тарифов хватает, пока не отправляете сотни ордеров в минуту.
  • Мониторинг: Prometheus + Grafana для метрик; Telegram-бот для оповещений. Логируйте каждый отправленный order ID и каждое полученное исполнение.
  • Бэкапы: сохраняйте состояние каждую минуту. Если VPS умрёт посреди исполнения, вы захотите продолжить за секунды, а не сверять вручную.
  • Налоги: ваш логгер - это и есть аудиторский след (см. Налоговое руководство).

Часть 14 - Проверенные профессиональные советы по API Полимаркет

Двенадцать продакшен-привычек от операторов живых ботов.
  1. Кэшируйте API-credentials после первого вызова вывода - create_or_derive_api_creds() ограничен по частоте и медленный. Храните apiKey/secret/passphrase в .env и загружайте при старте.
  2. Используйте signature_type=2 (GNOSIS_SAFE), если сначала подключили браузерный кошелёк, а signature_type=1 (POLY_PROXY) - только для email-аккаунтов Magic-link. Несовпадение типа даёт 401 'invalid api key'.
  3. Задавайте funder как адрес вашего proxy-кошелька Полимаркет, а не EOA. Ключ подписи живёт в EOA; средства живут в proxy. Их путаница - баг аутентификации номер 1.
  4. Индексируйте исходы по метке, никогда по позиции - clobTokenIds[outcomes.index("Yes")], а не clobTokenIds[0]. У рынков NegRisk и «Оскар» произвольный порядок.
  5. Синхронизируйте часы перед подписью - POLY_TIMESTAMP должен попадать в узкое окно. Дрейф NTP на дешёвом VPS тихо ломает аутентификацию. Запустите chrony или systemd-timesyncd.
  6. Перезагружайте REST-стакан при каждом переподключении WebSocket перед переподпиской. WebSocket даёт дельты; если пропустите дельту во время переподключения, локальный стакан разойдётся с реальностью, и вы будете котировать проигрышные цены.
  7. Никогда не отправляйте всплеском больше 10 ордеров в секунду - эндпоинт /order троттлит на 500/10с во всплеске и 3 000/10мин на длинной дистанции. Добавьте client-side token-bucket rate limiter; Cloudflare ставит в очередь, а не отбрасывает, так что слепые ретраи раздувают очередь.
  8. Используйте cancel_market_orders(market=conditionId) при остановке, а не cancel_all(). Отмена в рамках рынка идемпотентна и безопаснее, если бот падает посреди цикла на одном рынке.
  9. Отслеживайте heartbeatMs по каждому активу - добавьте watchdog, форсирующий обновление любого рынка без обновлений 30с на живом рынке. Устаревшие WS-фиды - самый частый источник фантомной альфы.
  10. Логируйте order ID до отправки, а не после. Идемпотентность требует, чтобы клиент владел ID, чтобы восстановление после падения переотправляло без дублирующих исполнений.
  11. Используйте HeartBeats API (с января 2026) для автоотмены при разрыве. Задайте интервал heartbeat 5с; сервер отменит все ваши лежащие ордера, если пропустит два heartbeat.
  12. Сделайте paper-trade ордерами на 1 $ на тонком рынке в течение 48 часов перед масштабированием. У Полимаркет нет testnet; крошечные реальные ордера - единственный надёжный способ проверить аутентификацию, подпись, обработку исполнений и поток отмен.

Шпаргалка: ситуация → действие

СитуацияДействиеПочему
401 'invalid api key' на первом вызовеПроверьте, что signature_type совпадает с происхождением кошелька, а funder - адрес proxyНесовпадение типа 1 и 2 - 80% ошибок 401; EOA вместо funder - остальное
Ордера отклоняются с 'insufficient balance'Запрашивайте /balance-allowance перед каждым ордером и резервируйте локальноCLOB резервирует залог в момент отправки; два параллельных ордера могут забронировать дважды
Троттлинг 429 на эндпоинте /orderBackoff с jitter: 2^attempt + random() с потолком 30сCloudflare троттлит, а не отклоняет; наивный ретрай раздувает очередь
WebSocket отключился посреди сделкиСнимите снимок стакана через REST, сверьте локальное состояние, затем переподпишитесьДельты за время разрыва теряются; снимок ресинхронизирует ценовые лестницы
Ордер размещён, но нет подтверждения исполненияЗапросите /data/order/{id} в течение 5с; если pending - ждите; если не найден - заменитеРедко, но восстановимо; по умолчанию «проверь состояние, потом действуй»
Рынок разрешился во время активной котировкиОтмените все открытые ордера на этом conditionId по событию разрешенияОрдера после разрешения могут зависнуть как зомби-исполнения при причудах адаптера
Запускаете бота маркет-мейкингаКотируйте в пределах 2 центов от середины с размером 100+ акцийФормула наград взвешивает узость + размер + время в стакане; узко + размер + постоянно побеждает
Запускаете арбитражного бота на нескольких исходахИспользуйте FOK для каждой ноги, а не GTCЧастичное исполнение ноги A при полной ноге B = непокрытая экспозиция и мгновенный убыток
Впервые строите ботаСначала стройте сканер, потом движок цен, потом сигнал - никогда сигнал первымСигналы без чистого состояния стакана - ловушки корреляции; сначала запустите «трубы»
Продакшен-бот упал в 3 ночиИмейте автоперезапуск systemd + оповещение Telegram + устойчивое состояниеЛюбой бот без присмотра упадёт; вопрос лишь в том, перезапустится ли он чисто
Рабочий пример: минимальный цикл маркет-мейкера для liquidity rewards.

Цель. Заработать liquidity rewards на политическом рынке среднего объёма с ценой около 0.48 Yes / 0.52 No и спредом 2 цента. Дневной пул наград ~40 $ для этого рынка.

Настройка. Подпишитесь по WebSocket на оба token_id. Кэшируйте последнюю видимую mid. Задайте spread_target = 0.02, size = 200 акций на сторону, reprice_threshold = 0.005 (5 тиков).

Цикл. На каждом обновлении стакана по WS: вычислите новую mid = VWAP трёх лучших bids и asks. Если |текущие котировки − целевая mid| > reprice_threshold, отмените оба существующих ордера, выставьте новый bid на mid-0.01 и новый ask на mid+0.01. Ограничьте переоценку до одного раза в 2 секунды на сторону.

Риск. Максимальный инвентарь на сторону = 1 000 акций. Если инвентарь > 500, расширьте спред на этой стороне на 0.005 на каждые 100 акций. Предохранитель: если mid сдвигается >0.05 за 60 секунд, отмените всё и сделайте паузу на 5 минут.

Результат (реальный прогон за 7 дней). Исполнено ~14 000 акций по 680 ордерам, уплачено 0 $ комиссий taker (сторона maker), заработано 31.40 $ на liquidity rebate, чистый направленный P&L составил -4.10 $ (мелкие убытки по инвентарю). Чистыми +27.30 $ за 7 дней на 500 $ рабочего капитала = ~8% в месяц. Масштабируется линейно на 30-50 одновременных рынках на одном VPS.

Главный вывод

Трейдеры, которые стабильно зарабатывают на Полимаркет, относятся к руководству по api polymarket как к системе, а не к интуиции. Сохраните числа выше - они и есть разница между 7,6% прибыльных кошельков и остальными.

Что дальше?