Polymarket Bot Tutorial · Розділ 8 із 32
Polymarket CLOB API для botів: REST endpoints для order book snapshots, WebSocket subscriptions для real-time updates, parsing bids/asks, обчислення mid-price і depth, code samples.
Що охоплює цей розділ
CLOB API - це місце, де orders підписуються, надсилаються, match-аться, і де живе order book. У Polymarket є два покоління SDK - застарілий v1 і поточний v2. У цьому розділі розглядається лише v2; v1 не має з’являтися в жодному bot, який ви випускаєте у 2026 році. Ми пройдемо шлях REST snapshot, канал WebSocket updates, деталі parsing, на яких спотикаються нові розробники, і reconnect logic, без якої long-running bot за кілька годин роз’їжджається із синхронізації.
- CLOB v1 vs v2 (use v2)
- Order book REST snapshot
- WebSocket subscriptions: market and user channels
- Parsing bids/asks/depth
- Computing mid-price and best-bid/ask
- Maker fees, taker fees, rebates
- Code: connect WS and process price-change events
- Reconnect and gap-handling
CLOB v1 vs v2 (use v2)
Polymarket підтримує два покоління SDK. v1 (@polymarket/clob-client на npm, py-clob-client <0.30) застарілий і не має кількох типів orders, доданих у 2024 році. v2 (@polymarket/clob-client-v2 v1.0.6 у Node, py-clob-client 0.34.6+ у Python) - поточний стандарт.
Три конкретні відмінності. v2 підтримує прапорець negRisk для markets із кількома outcome - це потрібно з моменту запуску NegRisk exchange наприкінці 2024 року. v2 постачається з TypeScript types для формату повідомлень WebSocket; v1 повертає any. v2 нативно обробляє signature flow Gnosis Safe від серпня 2025 року; v1 потребує custom signing glue.
Уся решта цього розділу написана з припущенням, що всюди використовується v2. Якщо ви бачите код v1 в старішому tutorial, вважайте його зламаним, доки не доведено протилежне - особливо placement orders у NegRisk markets під v1 може мовчки піти до неправильного exchange contract.
Order book REST snapshot
REST snapshot endpoint повертає повний book для одного token у конкретний момент часу.
GET https://clob.polymarket.com/book?token_id=<ERC1155_TOKEN_ID>
Форма відповіді:
{
"market": "0x...",
"asset_id": "5413...",
"timestamp": "1715600000000",
"hash": "0x...",
"bids": [{"price":"0.45","size":"120"}, {"price":"0.44","size":"380"}, ...],
"asks": [{"price":"0.47","size":"85"}, {"price":"0.48","size":"210"}, ...]
}
Ціни подаються як strings із 2-3 знаками після коми; sizes - як strings, що представляють кількість shares (не долари). Bids відсортовані від вищої до нижчої ціни, asks - від нижчої до вищої. hash - це marker дедуплікації: повторні запити до незмінного book повертають той самий hash, і ваш bot може пропустити обробку.
REST snapshot - правильний вибір для разових перевірок (price check під час ухвалення рішення про вхід). Для безперервного моніторингу використовуйте канал WebSocket нижче.
WebSocket subscriptions: market and user channels
Мають значення два канали WebSocket.
Market channel: wss://ws-subscriptions-clob.polymarket.com/ws/market. Підписуйтеся на один або багато tokens; отримуйте order-book updates у міру їх появи.
{"type":"Market","markets":["0xCondId1","0xCondId2"]}
Повідомлення приходять при кожній зміні. Типи включають book (повний snapshot), price_change (delta), tick_size_change (рідко) і last_trade_price (останнє виконання).
User channel: wss://ws-subscriptions-clob.polymarket.com/ws/user. Authenticated; отримуйте події власних orders - fills, partial fills, cancellations.
{"type":"User","auth":{"apiKey":"...","secret":"...","passphrase":"..."}}
User channel - найчистіший спосіб виявити fill. Polling endpoint REST для orders коштує дорожче і може пропускати зміни стану між запитами; WebSocket надсилає подію в момент, коли matcher її підтверджує.
Parsing bids/asks/depth
Order book - це список цінових рівнів із агрегованим обсягом. Є дві конвенції parsing, які важливо зробити правильно.
Напрямок order: bids - це buy orders (хтось хоче КУПИТИ за цією ціною). Коли ВАШ bot продає, ви б’єте bid. Коли ваш bot купує, ви піднімаєте ask. Polymarket UI показує той самий напрямок; на деяких інших exchanges він інвертований.
Сортування: bids приходять відсортованими за спаданням (найкращий bid першим). asks приходять відсортованими за зростанням (найкращий ask першим). Найкращий bid - це bids[0]; найкращий ask - asks[0]. Увага: public WebSocket іноді надсилає partial book updates, які не відсортовані заздалегідь - завжди перестраховуйтеся й повторно сортуйте після будь-якого merge.
Depth на рівні - це грошовий обсяг, який можна виконати: price * size. Depth на перших 5 рівнях - поширений metric ліквідності: sum(b.price * b.size for b in bids[:5]). Якщо depth на top-5 менший за $100, book є неліквідним, і більшість припущень strategy ламаються.
Computing mid-price and best-bid/ask
Три похідні цінові точки, які потрібні вашому bot.
- Best bid / best ask:
bids[0].priceіasks[0].price. Ціни, за якими ви реально можете торгувати, одна share. - Mid-price:
(best_bid + best_ask) / 2. Математичний центр spread. Корисний для valuation; ви ніколи не торгуєте по mid. - VWAP price for size N: проходьте book, доки cumulative size не досягне N, і повертайте size-weighted average price. Реальна вартість КУПІВЛІ N shares прямо зараз з урахуванням sweep у глибші рівні.
Крайній випадок: порожня сторона bid або ask (немає тих, хто продає, або немає тих, хто купує) означає, що book односторонній. У структурі markets Polymarket це трапляється для resolved або майже resolved markets, де одна сторона стоїть на 0.999, а ніхто не пропонує ліквідність на стороні, що програла. Сприймайте best-bid = 0 або best-ask = 1 як сигнал «не торгувати».
Maker fees, taker fees, rebates
Більшу частину своєї історії Polymarket узагалі не брав trading fees. У 2026 році це змінилося: fees запровадили на початку року на 15-хвилинних crypto markets, 30 березня 2026 поширили на Sports, а потім викотили на більшість категорій. Будь-який туторіал, що досі стверджує, ніби Polymarket без fees, застарів - і той, хто проґавить це у високочастотній strategy, буде тихо з'їдений. Ось як модель працює насправді, станом на середину 2026 року.
Спершу про дві сторони кожної угоди. Maker - це той, хто кладе в book спокійний limit order, що лишається чекати; taker - це той, хто надсилає order, який виконується одразу проти вже наявної ліквідності. Makers і далі сплачують нуль fee та ще й отримують rebate; fee сплачують лише takers.
Taker fee - це не фіксований відсоток. Вона йде за кривою, що залежить і від розміру order, і від ціни:
fee = shares × feeRate × price × (1 - price)
Член price × (1 - price) максимальний за ціни 0.50 (справжній ринок «орел чи решка») і зменшується до 0 або 1. Інакше кажучи, на найбільш невизначених markets ви платите найвищу fee, а на майже вирішених - майже нічого. feeRate задається за категоріями:
- Crypto: feeRate 0.07 (найвища, пік близько 1.8% ефективних), maker rebate 20%.
- Sports: feeRate 0.03 (пік близько 0.75%), maker rebate 25%.
- Finance, Politics, Tech, Mentions: feeRate 0.04, maker rebate 25%.
- Economics, Culture, Weather, загальне: feeRate 0.05, maker rebate 25%.
- Geopolitics та великі світові події: 0, і далі без fee.
Приклад розрахунку. Припустимо, ваш bot бере 100 shares crypto market за ціною 0.50. Fee становитиме 100 × 0.07 × 0.50 × (1 - 0.50) = 100 × 0.07 × 0.25 = $1.75. Якщо ж ви візьмете ті самі 100 shares за 0.90, fee впаде до 100 × 0.07 × 0.90 × 0.10 = $0.63, бо ціна далеко від невизначеної середини. Для bot висновок однозначний: брати ліквідність на волатильних, майже збалансованих crypto та sports markets коштує максимуму fee - отже саме там найвигідніше котируватися як maker і забирати rebate, а не платити fee.
Окрім явної fee, ви щоразу платите bid-ask spread, коли його перетинаєте. Spread - це розрив між найкращою ціною купівлі та найкращою ціною продажу, і для strategy, що входить і виходить як taker, цей розрив - реальна вартість поверх fee. Припускайте 1-3 центи round-trip на типових books, більше - на неліквідних. NegRisk markets (multi-outcome exchange) використовують ту саму модель fee, але сетляться на окремому contract, тож їхні rewards нараховуються окремо. Розділ 19 розглядає liquidity-rewards farming, де забір maker rebates - це сама strategy, а не лише побічний ефект.
Code: connect WS and process price-change events
Мінімальний Node приклад: підключитися, підписатися, логувати кожну подію price-change для одного token.
import WebSocket from "ws";
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
ws.on("open", () => {
ws.send(JSON.stringify({ type: "Market", markets: ["<CONDITION_ID>"] }));
});
ws.on("message", (data) => {
const msg = JSON.parse(data.toString());
if (msg.event_type === "price_change") {
console.log("price_change", msg.asset_id, msg.changes);
} else if (msg.event_type === "book") {
console.log("book snapshot", msg.bids?.[0], msg.asks?.[0]);
}
});
ws.on("close", () => console.log("closed"));
ws.on("error", (e) => console.error("err", e.message));
Комфортно підписуватися на приблизно до 30 tokens на одне WebSocket connection. Понад це краще розподіляти навантаження на кілька connections - сервер іноді тихо відкидає великі subscriptions без помилки, що призводить до мовчазно застарілих reads book.
Reconnect and gap-handling
Long-running WebSocket connection обов’язково обірветься. Cloudflare циклічно перезапускає connections кожні кілька годин; мережі «мигають»; Polymarket іноді розгортає оновлення. Плануйте це заздалегідь.
Стратегія reconnect: при close або error зачекайте min(2^attempt, 30) секунд із jitter, а потім повторно підпишіться. Скидайте лічильник attempt після першого успішного повідомлення після reconnect.
Gap-handling важливіший за швидкість reconnect. Поки WebSocket був від’єднаний, book рухався. Після кожного reconnect повторно отримуйте REST snapshot для кожного підписаного token і звіряйте: будь-які open positions, чий book суттєво зрушив, потребують повторної перевірки стану, exits можуть вимагати спрацювання, alarms можуть бути застарілими. Сценарій «я пропустив 30 секунд book updates» - це тихий убивця long-running bots: вони продовжують працювати на застарілому стані й виставляють orders за цінами, яких уже не існує.
Defensive pattern: робіть snapshot кожного підписаного book раз на хвилину незалежно від стану WebSocket і розглядайте WS як fast-path optimization поверх polling snapshot.










