Как создать торгового бота для Polymarket
Большинство руководств по ботам для Polymarket построены на SDK, которых больше не существует. Здесь — актуальный путь от начала до конца: что устанавливать в 2026 году, как на самом деле работает аутентификация, где пробелы в данных и какие сбои убивают ботов в первую неделю.
Большинство руководств по ботам для Polymarket устарели
Polymarket перешёл на единые SDK — polymarket-client для Python и @polymarket/client для TypeScript. Они заменяют py-clob-client, @polymarket/clob-client-v2, @polymarket/builder-relayer-client и @polymarket/builder-signing-sdk. Если руководство советует выполнить pip install py-clob-client или ваш ИИ-ассистент генерирует такой импорт, значит, он опирается на устаревшие материалы. Всё ниже использует актуальные пакеты.
Бот для Polymarket состоит из шести частей: найти рынки, прочитать цены, принять решение, выставить ордера, управлять позициями и не упасть. Первые две простые, четвёртая муторная, а на шестой большинство ботов и погибает. Это руководство проходит весь путь с проверенным кодом и честно говорит о действительно сложных местах.
Шаг 0: решите, чем торгует ваш бот
Это стоит сделать до того, как вы напишете хоть строчку, потому что от этого зависит всё остальное. Рынки Polymarket делятся на два семейства, у которых почти нет ничего общего:
| Событийные рынки | Крипторынки Up/Down | |
|---|---|---|
| Примеры | Выборы, спорт, премии | btc-updown-5m, eth-updown-15m |
| Срок жизни | От недель до месяцев | От 5 минут до 24 часов |
| Что двигает цену | Новости, информация, настроения | Микроструктура и поток ордеров |
| Откуда преимущество | Лучшая информированность | Скорость или лучшее ценообразование |
| Сделок в день | Несколько | Сотни |
| Чувствительность к задержке | Низкая | Высокая |
Если вы автоматизируете, вас почти наверняка интересует второе семейство — высокая частота, непрерывность и достаточно механики, чтобы закодировать. Они подробно разобраны в статье Рынки Up/Down: как они устроены.
Шаг 1: установите актуальный SDK
Оба SDK дают единый клиентский интерфейс ко всем API Polymarket, типизированные модели, единообразную пагинацию и встроенную подпись — а это большой объём муторной работы, которую вы не захотите реализовывать заново.
pip install polymarket-client
# or: uv add polymarket-client / poetry add polymarket-clientКлиентов для Python четыре, и правильный выбор важнее, чем кажется:
| Клиент | Публичные данные | Торговля | Реальное время |
|---|---|---|---|
AsyncPublicClient | Да | Нет | Да |
AsyncSecureClient | Да | Да | Да |
PublicClient | Да | Нет | Нет |
SecureClient | Да | Да | Нет |
subscribe(). Ботам нужны асинхронные варианты; синхронные — для скриптов и блокнотов.Начните с публичного клиента
Поиск рынков, чтение цен, генерацию сигналов и весь конвейер данных можно построить и протестировать с AsyncPublicClient — без ключей, без кошелька, без риска. Переходите на AsyncSecureClient только тогда, когда будете готовы выставить ордер. Большинство ошибок проявляется задолго до этого.
Шаг 2: найдите рынки
Для поиска публичных рынков учётные данные вообще не нужны. Пагинатор SDK работает одинаково во всех методах получения списков, так что этот шаблон применим везде.
import asyncio
from polymarket import AsyncPublicClient
async def main() -> None:
async with AsyncPublicClient() as client:
pages = client.list_markets(closed=False, page_size=50)
# Iterate items directly when page boundaries do not matter.
async for market in pages.iter_items():
if "updown" in (market.slug or ""):
print(market.slug, market.condition_id)
asyncio.run(main())Здесь важно усвоить две вещи. Во-первых, page.next_cursor — это непрозрачный курсор: сохраняйте его как есть, если хотите продолжить обход позже, и не пытайтесь его разбирать или конструировать. Во-вторых, у /markets самый жёсткий лимит запросов на всей платформе — 300 запросов / 10 с, поэтому кешируйте метаданные рынков, а не запрашивайте их список заново в горячем цикле. Все цифры — в справочнике по лимитам запросов.
Шаг 3: пройдите аутентификацию
Для торговли нужен кошелёк. Python SDK создаёт подписанта из локального приватного ключа:
import os
from polymarket import AsyncSecureClient
client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
)Под капотом Polymarket использует два уровня аутентификации: L1 — это подпись кошелька EIP-712, которая доказывает, что вы контролируете адрес, и позволяет создать или получить учётные данные API; L2 подписывает каждый приватный запрос этими учётными данными с помощью HMAC-SHA256. SDK берёт на себя оба уровня — это главная причина им пользоваться.
Две ошибки аутентификации, с которыми вы столкнётесь
Неполные учётные данные — L2 нужны ключ, секрет и passphrase. С двумя из трёх получается корректно сформированный запрос с непроверяемой подписью. Рассинхронизация часов — подпись включает метку времени, поэтому контейнер, у которого часы отстают на несколько секунд, получает отказ. Обе проявляются одинаково бесполезным сообщением Unauthorized/Invalid api key. Полная расшифровка — в справочнике по ошибкам.
Обращение с ключами
Этот приватный ключ может перемещать средства. Храните его в переменных окружения или менеджере секретов — никогда в исходном коде, никогда в образе контейнера, никогда в репозитории, который вы собираетесь запушить. Используйте отдельный кошелёк, пополненный ровно на ту сумму, которую боту позволено потерять, — тогда ошибка будет ограничена балансом, а не вашим здравомыслием в 3 часа ночи.
Шаг 4: получайте цены в реальном времени
Самая частая архитектурная ошибка в первом боте — опрашивать /book по таймеру. Это сжигает бюджет лимита запросов, добавляет задержку и плохо масштабируется. Вместо этого подписывайтесь — SDK объединяет несколько потоков в один поток событий.
from polymarket.streams import MarketSpec
async with await client.subscribe([MarketSpec(token_ids=[token_id])]) as stream:
async for event in stream:
# MarketBookEvent | MarketPriceChangeEvent | MarketLastTradePriceEvent
# | MarketTickSizeChangeEvent | MarketBestBidAskEvent
# | NewMarketEvent | MarketResolvedEvent
handle(event)Если вы подключаетесь к WebSocket напрямую, а не через SDK, вы обязаны отправлять PING каждые 10 секунд, иначе соединение будет разорвано, а чтобы вообще получать события best_bid_ask, new_market и market_resolved, нужен параметр custom_feature_enabled: true. Это и шаблоны переподключения разобраны в руководстве по WebSocket. Отдельно: 15 сентября 2026 года референсные цены переехали на PolyBolt — если ваш бот получает поток спот-цен, см. руководство по миграции.
Шаг 5: выставьте ордер
Рыночный ордер — самое простое, что работает, и amount указывается в pUSD, которые вы готовы потратить, а не в акциях:
market = await client.get_market(slug="<market-slug>")
token_id = market.outcomes.yes.token_id
assert token_id is not None
response = await client.place_market_order(
token_id=token_id,
side="BUY",
amount="10", # spend up to 10 pUSD
)
if not response.ok:
raise RuntimeError(response.message)
# Settlement is on-chain and asynchronous — this waits for it.
hashes = await client.wait_for_order_fill_settlement(response)С лимитными ордерами в стакане раньше всего остального вас укусят два правила. Цены должны лежать на сетке тиков рынка — вызывайте GET /tick-size для каждого рынка, а не предполагайте 0.01, потому что размер тика различается и может уменьшаться по мере взросления рынка. А ордера меньше минимального размера рынка отклоняются сразу. Оба случая возвращаются как 400 с конкретными сообщениями, перечисленными в справочнике по ошибкам.
200 на пакетный запрос не означает успех
Когда вы отправляете несколько ордеров, ошибки по отдельным ордерам возвращаются внутри массива ответа 200 в виде отдельных полей errorMsg. Проходите по массиву и проверяйте каждую запись. Боты, которые проверяют только HTTP-статус, молча верят, что у них есть позиции, которых нет, — по-настоящему опасный класс ошибок.
Шаг 6: узнайте, работает ли стратегия
Всё, что выше, — это обвязка, а обвязка — лёгкая половина. Трудный вопрос в том, зарабатывает ли ваша стратегия, и здесь создание ботов для Polymarket резко расходится с созданием ботов для спотового криптовалютного рынка.
Тестовой сети нет. Ни песочницы, ни демо-API, ни эндпоинта для бумажной торговли. Этих слов нет нигде в документации Polymarket. Ваш первый настоящий ордер — это реальные деньги против реальных контрагентов. Четыре обходных пути — в статье Есть ли у Polymarket тестовая сеть.
И исторического стакана тоже нет. После разрешения рынка его стакан возвращает 404. Сохраняется ценовой ряд примерно с одной точкой в минуту, и то лишь если запрашивать с явно заданными началом и концом:
TOK=<token_id_of_a_resolved_market>
START=<epoch seconds the window opened>
END=$((START + 300))
curl -s "https://clob.polymarket.com/book?token_id=$TOK"
# {"error":"No orderbook exists for the requested token id"} HTTP 404
curl -s "https://clob.polymarket.com/prices-history?market=$TOK&startTs=$START&endTs=$END&fidelity=1"
# about 6 points for a 5-minute market; interval=max returns [] for older onesПоэтому бэктестить по данным Polymarket нельзя. Нужны снимки на конкретный момент времени, которые кто-то записал, пока рынки работали. Именно это даёт PolyTest для крипторынков Up/Down — 240M+ снимков, 8 уровней глубины стакана на сторону, метки времени с точностью до долей секунды, разрешившиеся рынки сохраняются:
import os, requests
BASE = "https://api.polytest.io/api/v1"
H = {"X-API-Key": os.environ["POLYTEST_KEY"]}
markets = requests.get(f"{BASE}/markets",
params={"coin": "btc", "market_type": "5m", "limit": 50},
headers=H).json()["markets"]
snaps = requests.get(f"{BASE}/markets/{markets[0]['id']}/snapshots",
params={"include_orderbook": "true"},
headers=H).json()["snapshots"]
def simulate_fill(levels, qty):
"""Walk the ladder. Mid-price is not a price you can trade at."""
filled = cost = 0.0
for lvl in levels:
take = min(lvl["size"], qty - filled)
cost += take * lvl["price"]
filled += take
if filled >= qty:
break
return cost / filled if filled >= qty else None
print(simulate_fill(snaps[0]["orderbook"]["asks"], 500))Ошибка, которая обесценивает бэктест
Если вы тестируете на средней цене, вы проверяете арифметическое тождество, а не стратегию. Спреды на рынках Up/Down регулярно составляют 2–5 центов на контракте, который рассчитывается по $1, — часто это шире заявленного преимущества. Проходите по стакану и берите то исполнение, которое вы реально получили бы. Одно это изменение убивает большинство стратегий — и это именно то, что нужно: дёшево и до того, как вы вложите в них деньги.
Шаг 7: переживите встречу с продакшеном
Бот, который час торгует правильно, а потом зависает, хуже, чем отсутствие бота. Вот сбои, которые действительно случаются, примерно в порядке частоты:
| Сбой | Что вы видите | Что делать |
|---|---|---|
| Лимит запросов | Растёт задержка, а не число ошибок | Cloudflare замедляет, а не отклоняет — следите за p99, а не за долей ошибок |
| Перезапуск механизма сопоставления | 425 Too Early | Повторяйте с отступом; не поднимайте тревогу из-за единичных случаев |
| Биржа на паузе | 503 | Перестаньте открывать позиции. Отменяйте ордера, если это разрешено |
| Режим cancel-only | 503 на новые ордера | Путь отмены должен работать независимо от пути входа |
| Режим post-only | 503 + retry_after_seconds | Подождите ровно столько, затем повторите |
| Несовпадение подписанта | 400, ошибка подписанта ордера | Устаревшие учётные данные от другого кошелька — получите их заново |
| Обрыв WebSocket | Тишина | Отсутствие данных не равно отсутствию изменений — отслеживайте возраст последнего сообщения |
| Рассинхронизация часов | 401 у бота, который вчера работал | Запустите NTP; на этом теряют больше всего времени |
Тишина — опасное состояние
Оборвавшийся WebSocket не выбрасывает исключение — он просто перестаёт доставлять сообщения. Бот, который держит позицию и не получает обновлений, спокойно поверит, что рынок застыл на последней увиденной цене. Отслеживайте возраст последнего сообщения и считайте устаревание ошибкой, а не затишьем.
- Тестируйте путь отмены до пути входа. Не суметь выйти гораздо хуже, чем не суметь войти.
- Ограничивайте экспозицию в коде, а не в голове. Жёсткий лимит позиции превращает логическую ошибку в небольшой убыток, а не в неограниченный.
- Сделайте аварийный выключатель и действительно проверьте его. Непроверенный аварийный выключатель — это просто комментарий.
- Добавляйте случайный разброс к запланированным задачам. Обработчики, просыпающиеся в одну и ту же секунду, создают синхронный всплеск, намного превышающий ваш средний темп.
- Логируйте каждый запрос и ответ в первую неделю. Они вам понадобятся, а восстановить их потом невозможно.
- Оповещайте по категориям, а не по сообщениям. Polymarket встраивает ID ордеров и цены в строки ошибок, поэтому оповещения по сырым строкам — чистый шум.
Реалистичный порядок разработки
Последовательность, которая тратит меньше всего времени и денег:
- Интеграция только на чтение, публичный клиент, без ключей. Докажите, что поиск рынков, парсинг, пагинация и отступ работают. Один день, бесплатно.
- Бэктест стратегии на исторических стаканах с честным моделированием исполнения. Большинство идей умирают здесь. В этом и смысл.
- Бумажная торговля вживую минимум неделю — записывайте предполагаемые ордера в лог вместо отправки. Это доказывает аптайм, переподключения и тайминг.
- Выход в живую торговлю с минимальным размером с жёстким лимитом экспозиции и проверенным аварийным выключателем.
- Масштабируйтесь, только когда живые цифры совпадают с бумажными. Если они расходятся, ваша модель исполнения была неверной — вернитесь к шагу 2.
Шаги 1, 3 и 4 можно построить за несколько дней. Для шага 2 нужны данные, которые Polymarket не хранит, и именно этот шаг надёжнее всего экономит деньги. Начните бесплатный пробный период в PolyTest — этого хватит, чтобы выяснить, переживёт ли ваша идея спред.
Часто задаваемые вопросы
- Как создать торгового бота для Polymarket?
- Установите актуальный единый SDK — polymarket-client для Python или @polymarket/client для TypeScript. Используйте AsyncPublicClient для поиска рынков и чтения цен без учётных данных, затем AsyncSecureClient с приватным ключом для торговли. Подпишитесь на рыночный канал WebSocket вместо опроса цен, выставляйте ордера через place_market_order и обрабатывайте ответы 425, 429 и 503 с отступом. Перед выходом в живую торговлю проведите бэктест на исторических снимках стакана с реалистичным моделированием исполнения, поскольку у Polymarket нет тестовой сети и он не хранит историю стакана.
- Какой SDK использовать для бота Polymarket в 2026 году?
- Используйте единые SDK: polymarket-client для Python или @polymarket/client для TypeScript. Они заменяют старые пакеты py-clob-client, @polymarket/clob-client-v2, @polymarket/builder-relayer-client и @polymarket/builder-signing-sdk. Единый SDK для Rust находится в разработке. Большинство руководств и сгенерированного ИИ кода по-прежнему ссылаются на устаревшие пакеты, поэтому проверяйте импорты, прежде чем доверять фрагменту кода.
- Можно ли торговать на бумаге на Polymarket, прежде чем рисковать реальными деньгами?
- Не средствами Polymarket — тестовой сети, песочницы или демо-API нет. Прослойку для бумажной торговли можно сделать самостоятельно: запустите цикл живой стратегии на продакшен-данных рынка и записывайте предполагаемые ордера вместе с состоянием стакана в тот момент, а не отправляйте их. Это докажет, что бот надёжен в эксплуатации, но исполнение здесь предполагается, так что о месте в очереди и влиянии на рынок вы не узнаете.
- Как провести бэктест торгового бота для Polymarket?
- Нужны исторические снимки стакана от третьей стороны, потому что API Polymarket возвращает 404 для стакана разрешившегося рынка и хранит историю цен лишь примерно с одной точкой в минуту. Загрузите снимки на конкретный момент времени с полной глубиной, воспроизводите их в хронологическом порядке, не заглядывая вперёд, и моделируйте исполнение, проходя по уровням стакана, а не предполагая среднюю цену. Спреды Up/Down в 2-5 центов на контракте за $1 обычно больше заявленного преимущества, поэтому бэктесты на средней цене вводят в заблуждение.
- Почему мой бот для Polymarket получает Unauthorized/Invalid api key?
- Чаще всего по одной из двух причин. Либо ваш набор учётных данных неполон — для аутентификации L2 нужны API-ключ, секрет и passphrase вместе, — либо системные часы рассинхронизировались: подпись запроса включает метку времени, и расхождение в несколько секунд делает её недействительной. Реже — вы получили учётные данные для другого кошелька, а не того, с которого торгуете.
- Нужен ли боту для Polymarket WebSocket, или можно опрашивать?
- Используйте WebSocket. Опрос /book по таймеру сжигает бюджет лимита запросов, добавляет задержку и плохо масштабируется. Подпишитесь на рыночный канал CLOB для публичных обновлений стакана и цен и на пользовательский канал для обновлений по вашим ордерам и сделкам. Если вы подключаетесь к WebSocket напрямую, а не через SDK, отправляйте PING каждые 10 секунд, чтобы соединение не обрывалось.
Получите исторические данные, которые Polymarket не хранит
PolyTest записывает крипторынки Polymarket Up/Down в процессе их работы — 240M+ снимков с 8 уровнями глубины стакана, метками времени с точностью до долей секунды и сохранёнными разрешившимися рынками. Начните с 3-дневного бесплатного пробного периода.
Читать дальше
Преимущество на отставании от спота в Polymarket реально. И глубиной около $100.
Покупайте сторону, которую спот уже подтвердил, до того как Polymarket пересчитает цену. В отличие от большинства заявляемых стратегий, эта выдерживает проверку данными — с z-оценкой 15. Причина, по которой она не сделала всех богатыми, лежит в стакане.
WebSocket в Polymarket: данные в реальном времени без обрывов
Опрос стакана — самый частый способ впустую сжечь бюджет лимита запросов. Вот четыре потока, параметры подписки, которые никто не документирует, и сбой, при котором ваш бот получает тишину и принимает её за затишье.
Python SDK для Polymarket в 2026 году (и уход с py-clob-client)
Если ваш код всё ещё импортирует ClobClient из py_clob_client, он построен на устаревшем пакете — как и большая часть кода для Polymarket, который напишет вам ИИ-ассистент. Вот актуальный SDK и как на него перейти.