Polymarket Bot Tutorial · 第8章 / 32章

ボット向けの Polymarket CLOB API: オーダーブックスナップショット用の REST エンドポイント、リアルタイム更新用の WebSocket サブスクリプション、bid/ask の解析、ミッドプライスと深さの計算、コードサンプル。

この章で扱う内容

CLOB API は、注文が署名され、送信され、マッチングされ、そしてオーダーブックが存在する場所です。Polymarket には 2 世代の SDK があります。非推奨の v1 と、現在の v2 です。この章では v2 のみを扱います。2026 年にあなたが出すボットに v1 が含まれていてはいけません。ここでは、REST スナップショットの取得経路、WebSocket 更新チャネル、新規ビルダーがつまずきやすいパースの詳細、そして長時間稼働するボットが数時間で同期ズレを起こさないために必要な再接続ロジックまで解説します。

  • CLOB v1 vs v2(v2 を使用)
  • オーダーブックの REST スナップショット
  • WebSocket サブスクリプション:market と user チャネル
  • bid/ask/depth の解析
  • ミッドプライスと best-bid/ask の計算
  • maker fee、taker fee、rebate
  • コード:WS 接続と price-change イベント処理
  • 再接続とギャップ処理

CLOB v1 vs v2(v2 を使用)

Polymarket は 2 世代の SDK を提供しています。v1(npm の @polymarket/clob-clientpy-clob-client 0.30 未満)は非推奨で、2024 年に追加された複数の注文タイプがありません。v2(Node では @polymarket/clob-client-v2 v1.0.6、Python では py-clob-client 0.34.6 以上)が現在の標準です。

具体的な違いは 3 つあります。v2 は複数結果市場向けの negRisk フラグをサポートしており、これは 2024 年末に NegRisk 取引所が立ち上がって以降必須です。v2 は WebSocket メッセージの型に対する TypeScript 型定義を同梱していますが、v1 は any を返します。v2 は 2025 年 8 月の Gnosis Safe 署名フローをネイティブに処理しますが、v1 ではカスタム署名処理のつなぎ込みが必要です。

この章の以降の内容は、すべて v2 を前提に書かれています。古いチュートリアルで v1 のコードを見かけても、別の証拠がない限り壊れているものとして扱ってください。特に NegRisk 市場への注文発注は、v1 では静かに誤った取引所コントラクトへルーティングされます。

オーダーブックの REST スナップショット

REST スナップショットエンドポイントは、特定時点における単一トークンの完全な板情報を返します。

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"}, ...]
}

価格は小数点 2〜3 桁の文字列で、サイズは株数を表す文字列です(ドルではありません)。bids は高い順、asks は低い順に並びます。hash は重複排除用のマーカーです。変化のない板を繰り返し取得すると同じ hash が返るため、ボット側で処理をスキップできます。

REST スナップショットは、一度だけ確認したい場合(例:エントリー判断時の価格チェック)に適しています。継続監視には、下記の WebSocket チャネルを使ってください。

WebSocket サブスクリプション:market と user チャネル

重要な WebSocket チャネルは 2 つあります。

Market チャネル: wss://ws-subscriptions-clob.polymarket.com/ws/market。1 つまたは複数のトークンを購読し、発生した順にオーダーブック更新を受け取ります。

{"type":"Market","markets":["0xCondId1","0xCondId2"]}

メッセージは変更があるたびに届きます。タイプには book(完全スナップショット)、price_change(差分)、tick_size_change(まれ)、last_trade_price(直近約定価格)が含まれます。

User チャネル: wss://ws-subscriptions-clob.polymarket.com/ws/user。認証が必要で、自分の注文イベント――約定、部分約定、キャンセル――を受け取ります。

{"type":"User","auth":{"apiKey":"...","secret":"...","passphrase":"..."}}

User チャネルは、約定を検知する最もきれいな方法です。orders REST エンドポイントをポーリングする方法はコストが高く、ポーリング間の状態変化を見逃すことがあります。WebSocket なら、マッチャーが承認した瞬間にイベントがプッシュされます。

bid/ask/depth の解析

オーダーブックは、価格帯ごとに集約されたサイズの一覧です。正しく押さえるべき解析ルールが 2 つあります。

注文方向: bids は買い注文です(誰かがこの価格で買いたい)。あなたのボットが売る場合は bid にぶつけます。買う場合は ask を取ります。Polymarket の UI も同じ向きで表示しますが、他の取引所では逆の場合があります。

ソート順: bids は降順で届きます(最良 bid が先頭)。asks は昇順で届きます(最良 ask が先頭)。最良 bid は bids[0]、最良 ask は asks[0] です。注意:公開 WebSocket は、事前ソートされていない部分更新を送ることがあります。マージ後は必ず防御的に再ソートしてください。

ある価格帯の depth は、実際に取引可能なドル価値です。price * size で求めます。トップ 5 段の depth は、よく使われる流動性指標です。sum(b.price * b.size for b in bids[:5]) で計算できます。トップ 5 の depth が 100 ドル未満なら、その板は流動性が低く、ほとんどの戦略前提が崩れます。

ミッドプライスと best-bid/ask の計算

ボットに必要な 3 つの派生価格です。

  • Best bid / best ask: bids[0].priceasks[0].price。実際に 1 株で売買できる価格です。
  • Mid-price: (best_bid + best_ask) / 2。スプレッドの数学的な中心です。評価には便利ですが、mid で実際に約定することはありません。
  • サイズ N の VWAP 価格: N に達するまで板を順にたどり、出来高加重平均価格を返します。より深い価格帯まで食い込むことを織り込んだ、いま N 株を BUY したときの実コストです。

例外ケースとして、bid 側または ask 側が空(誰も売っていない、あるいは誰も買っていない)なら、その板は片側だけの状態です。Polymarket の市場構造では、解決済み、または解決間近の市場で起こり、片側が 0.999 で反対側に流動性提供者がいない場合があります。best-bid = 0 または best-ask = 1 は「取引しない」シグナルとして扱ってください。

maker fee、taker fee、rebate

Polymarket はその歴史の大半で取引手数料をいっさい取っていませんでした。それが 2026 年に変わりました。年初に 15 分足の暗号資産市場で手数料が導入され、2026 年 3 月 30 日に Sports へ拡大され、以降ほとんどのカテゴリへ展開されました。いまだに Polymarket は手数料無料だと書いているチュートリアルはすべて古く、高頻度戦略でこれを見落とせば静かに食い潰されます。2026 年半ば時点で、このモデルが実際にどう動くのかを説明します。

まず各取引の両側です。maker は板に待機する指値注文を置いてそのまま待つ側、taker はすでにある流動性に対して即座に約定する注文を出す側です。maker は引き続き手数料 ゼロ で、さらに rebate も受け取ります。手数料を払うのは taker だけです。

taker fee は固定のパーセントではありません。注文サイズと価格の両方に依存する曲線に従います。

fee = shares × feeRate × price × (1 - price)

price × (1 - price) の項は価格 0.50(本当のコイン投げ市場)で最大になり、0 や 1 に近づくほど縮みます。言い換えると、最も不確実な市場では最も高い手数料を払い、ほぼ決着した市場ではほとんど払いません。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、引き続き手数料無料。

計算例を挙げます。ボットが暗号資産市場で価格 0.50 のときに 100 shares を取るとします。手数料は 100 × 0.07 × 0.50 × (1 - 0.50) = 100 × 0.07 × 0.25 = $1.75 です。同じ 100 shares を 0.90 で取ると、価格が不確実な中央から離れているため手数料は 100 × 0.07 × 0.90 × 0.10 = $0.63 まで下がります。ボットにとっての教訓は明快です。変動が大きくほぼ拮抗した暗号資産・スポーツ市場で流動性を取るのが最も手数料がかかる――だからこそ、まさにそこで手数料を払うより maker としてクォートして rebate を受け取るほうがはるかに有利です。

明示的な手数料に加えて、bid-ask スプレッドを跨ぐたびにそのスプレッドも払います。スプレッドは最良買値と最良売値の差で、taker として出入りする戦略にとっては、その差が手数料に上乗せされる実コストです。一般的な板では往復で 1〜3 セント、流動性の低い板ではそれ以上と見積もってください。NegRisk 市場(multi-outcome exchange)は同じ手数料モデルを使いますが、別のコントラクト上で決済されるため、報酬は別に加算されます。第 19 章では、maker rebate を受け取ること自体が副作用ではなく戦略そのものになる流動性報酬ファーミングを扱います。

コード:WS 接続と price-change イベント処理

最小限の Node 例です。接続し、購読し、1 つのトークンについてすべての price-change イベントをログします。

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));

1 つの WebSocket 接続で、およそ 30 トークンまでなら問題なく購読できます。それを超える場合は複数接続に分けてください。サーバーは大きな購読をエラーなしで落とすことがあり、その結果、静かに stale な板を読み続けることになります。

再接続とギャップ処理

長時間稼働する WebSocket 接続は切断されます。Cloudflare は数時間ごとに接続をローテーションし、ネットワークは瞬断し、Polymarket 側もデプロイを行います。そこまで見越して設計してください。

再接続戦略は次のとおりです。close または error の後、ジッター付きで min(2^attempt, 30) 秒待ち、その後に再購読します。再接続後、最初に正常なメッセージを受け取った時点で attempt カウンタをリセットします。

再接続速度以上に重要なのは、ギャップ処理です。WebSocket が切れていた間に、板は動いています。再接続のたびに、購読中の各トークンについて REST スナップショットを再取得し、整合性を取ってください。板が意味のあるほど動いた未決済ポジションは状態再確認が必要で、出口注文は発火させるべきかもしれませんし、アラートは古くなっている可能性があります。「30 秒分の板更新を取り逃した」ケースは、長時間稼働ボットの静かな致命傷です。古い状態のまま動き続け、もはや存在しない価格で注文を出してしまいます。

防御的なパターンとして、WebSocket の状態に関係なく、購読中の各板を 1 分ごとにスナップショットし、WS はスナップショットポーリングの上に乗る高速経路として扱ってください。

よくある質問

Polymarket の CLOB API エンドポイントは何ですか?
ベースの CLOB エンドポイントは https://clob.polymarket.com(REST)と wss://ws-subscriptions-clob.polymarket.com/ws/market(WebSocket)です。これらは @polymarket/clob-client-v2 と py-clob-client で使われる V2 エンドポイントです。
オーダーブックを読むのに API キーは必要ですか?
いいえ。オーダーブックの読み取り(スナップショットと WebSocket サブスクリプション)は公開されており、認証は不要です。API キーが必要なのは、注文の発注・キャンセルと、口座固有データ(ポジション、約定履歴)の読み取りだけです。
CLOB の WebSocket はどれくらい速く価格更新をプッシュしますか?
注文がマッチしたのと同じ速さです。活発な市場では数百ミリ秒ごとに更新され、薄い市場では実際の注文が入ったときだけ更新されます。深さの変化と約定イベントは同じ WS チャネルを流れるため、イベントタイプを見て正しく処理してください。
Polymarket のオーダーブックのミッドプライスはどう計算しますか?
best_bid と best_ask の両方があるなら mid = (best_bid + best_ask) / 2 です。ない場合は last_trade_price を代替値として使ってください。best_bid が best_ask より大きく離れている薄い板では、mid は無意味になり得ます。mid を公正価格として扱う前に、必ずスプレッドも確認してください。
2026 年の Polymarket の maker fee はいくらですか?
Maker は 0% を払い、rebate を受け取ります(暗号資産は 20%、その他ほとんどのカテゴリは 25%)。手数料を払うのは taker だけで、固定ではありません。fee = shares × feeRate × price × (1 - price) に従うため、0.50 付近のコイン投げ市場で最大になり、両端に向かって縮みます。カテゴリ別の feeRate は 0.03(Sports、実効ピーク約 0.75%)から 0.07(Crypto、実効ピーク約 1.8%)までで、Geopolitics はいまだ手数料無料です。この maker-taker の非対称性こそ、アクティブなボットがほぼ常に成行でスプレッドを跨ぐのではなく、待機する指値でクォートする理由です。
WebSocket の切断はどう処理しますか?
指数バックオフ(1 秒、2 秒、4 秒、最大 30 秒)で再接続し、同じ市場を再購読し、ギャップを埋めるために REST スナップショットを再取得します。古いオーダーブックは絶対に信用しないでください。5 秒以上切断されていたなら、注文を出す前に必ず新しいスナップショットを取得してください。