Polymarket Bot Tutorial · บทที่ 8 จาก 32

Polymarket CLOB API สำหรับ bots: REST endpoints สำหรับ order book snapshots, WebSocket subscriptions สำหรับการอัปเดตแบบเรียลไทม์, การแยก bids/asks, การคำนวณ mid-price และ depth, ตัวอย่างโค้ด

บทนี้ครอบคลุมอะไรบ้าง

CLOB API คือจุดที่ orders ถูก sign, ส่ง, match และเป็นที่อยู่ของ order book ด้วย Polymarket มี SDK สองเจเนอเรชัน-v1 ที่เลิกใช้แล้ว และ v2 ที่ใช้อยู่ในปัจจุบัน บทนี้ครอบคลุมเฉพาะ v2; v1 ไม่ควรปรากฏใน bot ใด ๆ ที่คุณส่งมอบในปี 2026 เราจะพาไล่ดูเส้นทาง REST snapshot, ช่องทางอัปเดต WebSocket, รายละเอียดการ parsing ที่มักทำให้ builder มือใหม่สะดุด และ logic สำหรับ reconnect ที่หากไม่มีมัน bot ที่รันยาว ๆ จะค่อย ๆ หลุด sync ภายในไม่กี่ชั่วโมง

  • 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) เลิกใช้แล้วและขาด order types หลายแบบที่เพิ่มเข้ามาในปี 2024 ส่วน v2 (@polymarket/clob-client-v2 v1.0.6 ใน Node, py-clob-client 0.34.6+ ใน Python) คือมาตรฐานปัจจุบัน

มีความแตกต่างสำคัญ 3 ข้อ v2 รองรับ flag negRisk สำหรับ multi-outcome markets-ซึ่งจำเป็นตั้งแต่เปิดตัว NegRisk exchange ในช่วงปลายปี 2024 v2 มาพร้อม TypeScript types สำหรับรูปแบบข้อความ WebSocket; ส่วน v1 จะคืนค่าเป็น any v2 จัดการ flow การ sign ของ Gnosis Safe ในเดือนสิงหาคม 2025 ได้แบบ native; ส่วน v1 ต้องใช้ custom signing glue

เนื้อหาที่เหลือของบทนี้จะเขียนโดยอิง v2 ตลอดทั้งบท หากคุณเห็นโค้ด v1 ใน tutorial เก่า ให้ถือว่ายังใช้ไม่ได้จนกว่าจะพิสูจน์ได้เป็นอย่างอื่น-โดยเฉพาะการวาง order ในตลาด NegRisk ภายใต้ v1 จะถูกส่งไปยัง exchange contract ที่ผิดแบบเงียบ ๆ

Order book REST snapshot

REST snapshot endpoint จะคืนค่า book ทั้งหมดของ token เดียว ณ จุดเวลาใดจุดเวลาหนึ่ง

GET https://clob.polymarket.com/book?token_id=<ERC1155_TOKEN_ID>

รูปแบบ response:

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

ราคาเป็น string ที่มีทศนิยม 2-3 ตำแหน่ง; sizes เป็น string ที่แทนจำนวน shares (ไม่ใช่ดอลลาร์) bids เรียงจากสูงไปต่ำ, asks เรียงจากต่ำไปสูง hash คือ marker สำหรับ deduplication-การ poll book เดิมซ้ำ ๆ ที่ไม่มีการเปลี่ยนแปลงจะได้ hash เดิม และ bot ของคุณสามารถข้ามการประมวลผลได้

REST snapshot เหมาะกับการ lookup แบบครั้งเดียว (เช่นเช็กราคาตอนตัดสินใจเข้าเทรด) สำหรับการติดตามแบบต่อเนื่อง ให้ใช้ WebSocket channel ด้านล่าง

WebSocket subscriptions: market and user channels

มี WebSocket channels สองแบบที่สำคัญ

Market channel: wss://ws-subscriptions-clob.polymarket.com/ws/market Subscribe ได้หนึ่ง token หรือหลาย token; รับ order-book updates ทันทีที่เกิดขึ้น

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

ข้อความจะเข้ามาทุกครั้งที่มีการเปลี่ยนแปลง Types ได้แก่ book (full snapshot), price_change (delta), tick_size_change (พบไม่บ่อย), และ last_trade_price (ราคาที่ fill ล่าสุด)

User channel: wss://ws-subscriptions-clob.polymarket.com/ws/user ต้อง auth; ใช้รับ event ของ order คุณเอง-fills, partial fills, cancellations

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

User channel เป็นวิธีที่สะอาดที่สุดในการตรวจจับ fill การ poll orders REST endpoint มีต้นทุนสูงกว่าและอาจพลาด state changes ระหว่างการ poll; WebSocket จะ push event ออกมาทันทีที่ matcher ยืนยัน

Parsing bids/asks/depth

Order book คือรายการของ price levels ที่รวม size แล้ว มี conventions ในการ parsing สองข้อที่ต้องทำให้ถูก

ทิศทางของ order: bids คือ buy orders (มีคนต้องการ BUY ที่ราคานี้) เมื่อ bot ของคุณขาย คุณจะไป hit bid เมื่อ bot ของคุณซื้อ คุณจะ lift ask Polymarket UI แสดงทิศทางเดียวกัน; แต่อีกหลาย exchange จะกลับด้าน

การเรียงลำดับ: bids จะมาเรียงจากมากไปน้อย (best bid มาก่อน) asks จะมาเรียงจากน้อยไปมาก (best ask มาก่อน) best bid คือ bids[0]; best ask คือ asks[0] ระวัง: public WebSocket บางครั้งส่ง partial book updates ที่ยังไม่ถูก pre-sort-ให้ re-sort ทุกครั้งอย่างระมัดระวังหลัง merge

Depth ในแต่ละระดับคือมูลค่าเป็นดอลลาร์ที่สามารถเทรดได้: price * size Top-5-level depth เป็น metric ด้านสภาพคล่องที่ใช้บ่อย: sum(b.price * b.size for b in bids[:5]) หาก top-5 depth ต่ำกว่า $100 book นี้ถือว่า illiquid และสมมติฐานของกลยุทธ์ส่วนใหญ่จะเริ่มใช้ไม่ได้

Computing mid-price and best-bid/ask

มี price points ที่ bot ของคุณต้องคำนวณเพิ่มอีก 3 ค่า

  • Best bid / best ask: bids[0].price และ asks[0].price คือราคาที่คุณเทรดได้จริงสำหรับ 1 share
  • Mid-price: (best_bid + best_ask) / 2 จุดกึ่งกลางทางคณิตศาสตร์ของ spread ใช้สำหรับ valuation; คุณไม่ได้เทรดที่ mid จริง ๆ
  • VWAP price สำหรับ size N: เดินไปตาม book จน cumulative size ถึง N แล้วคืนค่า average price แบบถ่วงน้ำหนักด้วย size นี่คือ cost จริงในการ BUY N shares ตอนนี้ โดยคิดรวมการ sweep ไปยังระดับที่ลึกกว่า

กรณีขอบเขต: ถ้าฝั่ง bid หรือ ask ว่างเปล่า (ไม่มีคนขาย หรือไม่มีคนซื้อ) แปลว่า book เป็น one-sided ในโครงสร้างตลาดของ Polymarket สิ่งนี้เกิดกับตลาดที่ปิดแล้วหรือใกล้ปิด โดยฝั่งหนึ่งอยู่ที่ 0.999 และไม่มีใครเสนอ liquidity ฝั่งผู้แพ้ ให้ถือว่า best-bid = 0 หรือ best-ask = 1 เป็นสัญญาณ "do not trade"

Maker fees, taker fees, rebates

ตลอดประวัติศาสตร์ส่วนใหญ่ของมัน Polymarket ไม่ได้เก็บ fee การเทรดเลย สิ่งนี้เปลี่ยนไปในปี 2026: fee ถูกนำมาใช้ช่วงต้นปีในตลาด crypto แบบ 15 นาที ขยายไปยัง Sports เมื่อวันที่ 30 มีนาคม 2026 และนับแต่นั้นก็ทยอยใช้กับหมวดส่วนใหญ่ คู่มือใด ๆ ที่ยังอ้างว่า Polymarket ไม่มี fee ถือว่าล้าสมัย-และใครที่มองข้ามเรื่องนี้ในกลยุทธ์ความถี่สูงจะถูกกัดกินอย่างเงียบ ๆ นี่คือวิธีที่โมเดลทำงานจริง ณ กลางปี 2026

ก่อนอื่นคือสองฝั่งของทุกการเทรด maker คือผู้ที่วาง limit order ค้างไว้ใน book และรออยู่ตรงนั้น; taker คือผู้ที่ส่ง order ที่ถูก execute ทันทีกับ liquidity ที่มีอยู่ maker ยังคงจ่าย fee ศูนย์ และยังได้ rebate เพิ่มอีก; มีเพียง taker เท่านั้นที่จ่าย fee

taker fee ไม่ใช่เปอร์เซ็นต์คงที่ มันเป็นไปตามเส้นโค้งที่ขึ้นกับทั้งขนาดของ order และราคา:

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

พจน์ price × (1 - price) มีค่าสูงสุดที่ราคา 0.50 (ตลาด "โยนเหรียญ" ที่แท้จริง) และหดเข้าหา 0 หรือ 1 กล่าวอีกนัยหนึ่ง ในตลาดที่ไม่แน่นอนที่สุดคุณจ่าย 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 ที่ราคา 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 บทเรียนนั้นชัดเจน: การรับ liquidity ในตลาด crypto และ sports ที่ผันผวนและเกือบสมดุลเสีย fee มากที่สุด-ดังนั้นตรงนั้นเองที่คุ้มที่สุดที่จะ quote เป็น maker และเก็บ rebate แทนที่จะจ่าย fee

นอกเหนือจาก fee ที่ระบุชัดเจน ทุกครั้งที่คุณข้าม bid-ask spread คุณก็จ่าย spread นั้นด้วย spread คือช่องว่างระหว่างราคาซื้อที่ดีที่สุดและราคาขายที่ดีที่สุด และสำหรับกลยุทธ์ที่เข้าออกแบบ taker ช่องว่างนั้นคือต้นทุนจริงที่เพิ่มจาก fee ให้สมมติราว 1-3 เซนต์ต่อ round-trip สำหรับ book ทั่วไป และมากกว่านั้นในตลาดที่ illiquid ตลาด NegRisk (multi-outcome exchange) ใช้โมเดล fee เดียวกันแต่ settle บน contract แยกต่างหาก ดังนั้น rewards ของมันจึงสะสมแยกกัน Chapter 19 ครอบคลุม liquidity-rewards farming ที่ซึ่งการเก็บ maker rebate คือกลยุทธ์ในตัวเอง ไม่ใช่เพียงผลพลอยได้

Code: connect WS and process price-change events

ตัวอย่าง Node แบบขั้นต่ำ: connect, subscribe, log ทุก price-change event สำหรับ 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));

Subscribe ได้สบาย ๆ ถึงประมาณ 30 token ต่อ WebSocket connection หากมากกว่านั้น ให้แยกไปหลาย connections-เซิร์ฟเวอร์บางครั้งจะหล่น subscriptions ขนาดใหญ่ออกไปโดยไม่แจ้ง error ซึ่งทำให้เกิด stale book reads แบบเงียบ ๆ

Reconnect and gap-handling

WebSocket connection ที่รันยาว ๆ จะหลุดได้ Cloudflare มีการ cycle connections ทุกไม่กี่ชั่วโมง; เครือข่ายอาจสะดุด; Polymarket ก็มีช่วง deploy ได้เช่นกัน ต้องเตรียมรับมือไว้

กลยุทธ์ reconnect: เมื่อเกิด close หรือ error ให้รอ min(2^attempt, 30) วินาทีพร้อม jitter แล้วค่อย re-subscribe รีเซ็ตตัวนับ attempt เมื่อได้รับข้อความสำเร็จครั้งแรกหลัง reconnect

การจัดการ gap สำคัญยิ่งกว่าความเร็วในการ reconnect ระหว่างที่ WebSocket หลุด book อาจเคลื่อนไหวไปแล้ว เมื่อ reconnect ทุกครั้ง ให้ดึง REST snapshot ของ token ที่ subscribe ไว้อีกครั้งและ reconcile: open positions ใด ๆ ที่ book เคลื่อนไหวอย่างมีนัยสำคัญต้องตรวจ state ใหม่, exits อาจต้อง fire, alarms อาจล้าสมัย เคส "ฉันพลาด book updates ไป 30 วินาที" คือฆาตกรเงียบของ bot ที่รันยาว-มันจะยังทำงานต่อไปบน state เก่า และวาง orders ที่ราคาไม่มีอยู่จริงแล้ว

แพตเทิร์นแบบ defensive: snapshot ของ book ที่ subscribe ไว้ทุกนาทีไม่ว่า WebSocket จะอยู่ในสถานะใด และถือว่า WS เป็นการ optimize แบบ fast-path ที่ทำงานอยู่บน snapshot poll อีกชั้นหนึ่ง

คำถามที่พบบ่อย

Polymarket CLOB API endpoint คืออะไร?
Base CLOB endpoint คือ https://clob.polymarket.com (REST) และ wss://ws-subscriptions-clob.polymarket.com/ws/market (WebSocket) นี่คือ V2 endpoints ที่ใช้โดย @polymarket/clob-client-v2 และ py-clob-client
ต้องใช้ API key เพื่ออ่าน order book ไหม?
ไม่ต้อง Order book reads (snapshots และ WebSocket subscriptions) เป็น public และไม่ต้อง authenticate คุณต้องใช้ API key เฉพาะตอนวาง/cancel orders และอ่านข้อมูลเฉพาะบัญชี (positions, fills)
CLOB WebSocket push price updates เร็วแค่ไหน?
เร็วเท่ากับความเร็วที่ orders match กันได้ ตลาดที่ active จะเห็น updates ทุกไม่กี่ร้อยมิลลิวินาที; ตลาดบางจะอัปเดตเฉพาะเมื่อมี orders จริงเท่านั้น ทั้ง depth changes และ trade events ไหลผ่าน WS channel เดียวกัน - ให้ parse event type เพื่อจัดการแต่ละแบบให้ถูกต้อง
จะคำนวณ mid-price ของ Polymarket order book ได้อย่างไร?
mid = (best_bid + best_ask) / 2 ถ้ามีทั้งสองฝั่ง; ถ้าไม่มีก็ใช้ last_trade_price เป็นตัวสำรอง ระวัง book บาง ๆ ที่ best_bid ต่ำกว่า best_ask มาก-mid อาจไม่มีความหมาย ควรพิจารณา spread เสมอก่อนจะมอง mid เป็น fair price
maker fee บน Polymarket ในปี 2026 คือเท่าไร?
maker จ่าย 0% และได้ rebate (20% สำหรับ crypto, 25% สำหรับหมวดอื่นส่วนใหญ่) มีเพียง taker เท่านั้นที่จ่าย fee และมันไม่คงที่: เป็นไปตาม fee = shares × feeRate × price × (1 - price) จึงสูงสุดในตลาด "โยนเหรียญ" แถว 0.50 และหดลงเข้าหาขอบ feeRate ตามหมวดมีตั้งแต่ 0.03 (Sports, จุดสูงสุดที่แท้จริงราว 0.75%) ถึง 0.07 (Crypto, จุดสูงสุดที่แท้จริงราว 1.8%) โดย Geopolitics ยังคงไม่มี fee ความไม่สมดุล maker-taker นี่เองที่ทำให้ bots ที่ active มักจะ quote ด้วย limit orders ที่ค้างไว้แทนที่จะข้าม spread ด้วย market orders
จะจัดการ WebSocket disconnects อย่างไร?
Reconnect ด้วย exponential backoff (1s, 2s, 4s, สูงสุด 30s), re-subscribe ไปยัง markets เดิม และ re-fetch REST snapshot เพื่อเติมช่องว่าง ห้ามเชื่อ order book ที่ stale-หากหลุดการเชื่อมต่อเกิน 5 วินาที ให้ขอ fresh snapshot ก่อนวาง orders