ตลาดการทำนายเป็นหนึ่งในโดเมนที่ท้าทายที่สุดสำหรับการออกแบบ API เพราะต้องรองรับตราสารทางการเงินที่มีวันหมดอายุ ราคาความน่าจะเป็นแบบเรียลไทม์ เหตุการณ์หลายผลลัพธ์ที่มีความสัมพันธ์ด้านเงินทุนซับซ้อน รวมถึงผู้ใช้ทั้งมนุษย์และบอต arbitrage ทุกการตัดสินใจด้านสถาปัตยกรรมจึงถูกทดสอบทันทีภายใต้โหลดและความเสี่ยงจริง
Polymarket ซึ่งเป็นแพลตฟอร์มตลาดการทำนายขนาดใหญ่ตามปริมาณการซื้อขาย มีระบบนิเวศ API ที่น่าสนใจสำหรับนักพัฒนา ไม่ใช่เพียง CRUD API บนฐานข้อมูล แต่เป็นการออกแบบที่จัดการความตึงเครียดระหว่างข้อมูลเปิดกับความปลอดภัย ข้อมูลเรียลไทม์กับข้อมูลย้อนหลัง และโมเดลการเงินดั้งเดิมกับพื้นฐานของคริปโต
บทความนี้สรุป 8 รูปแบบที่นำไปใช้ได้เมื่อคุณกำลังออกแบบ API สำหรับระบบซื้อขาย ตลาด หรือแพลตฟอร์มที่มีข้อมูลเรียลไทม์
รูปแบบที่ 1: แบ่งชั้น API ตามโดเมน
Polymarket แยก API ออกเป็น 3 ชุด โดยแต่ละชุดมีขอบเขตโดเมนชัดเจน:
-
Gamma API (
gamma-api.polymarket.com) — ค้นหาตลาด เหตุการณ์ แท็ก และข้อมูลสำหรับการค้นหา -
CLOB API (
clob.polymarket.com) — สมุดคำสั่งซื้อ ราคา และการส่งคำสั่งซื้อ -
Data API (
data-api.polymarket.com) — ตำแหน่งผู้ใช้ การซื้อขาย การวิเคราะห์ และลีดเดอร์บอร์ด
การแบ่งนี้ไม่ใช่แค่เรื่องการตั้งชื่อ แต่แต่ละ API มีรูปแบบการใช้งานต่างกัน:
| API | ผู้ใช้หลัก | การยืนยันตัวตน | ลักษณะข้อมูล |
|---|---|---|---|
| Gamma | UI, ระบบค้นหา, หน้ารวมตลาด | สาธารณะ | อ่านเป็นหลัก |
| CLOB | เทรดเดอร์, market maker, บอต | อ่านสาธารณะ / เทรดต้องยืนยันตัวตน | เวลาแฝงต่ำ |
| Data | Dashboard, analytics, portfolio tracker | สาธารณะตามที่อยู่กระเป๋าเงิน | ข้อมูลย้อนหลังและการวิเคราะห์ |
วิธีนำไปใช้
อย่าเริ่มจากการรวมทุก resource ไว้ใต้ API เดียว เช่น:
/markets
/orders
/users
/trades
ให้เริ่มจากคำถามว่า API แต่ละส่วนมีไว้เพื่ออะไร แล้วกำหนด base URL หรือ service boundary ตามหน้าที่:
/discovery/* # ค้นหาและเรียกดูข้อมูล
/trading/* # สร้าง ยกเลิก และติดตามคำสั่งซื้อ
/analytics/* # ประวัติ สถิติ และพอร์ตโฟลิโอ
ผลลัพธ์คือคุณสามารถปรับขนาด กำหนด rate limit และใช้ authentication ที่เหมาะกับแต่ละโดเมนได้อิสระ
รูปแบบที่ 2: เปิดอ่านข้อมูลเป็นอันดับแรก
ข้อมูลตลาด เช่น ราคา สมุดคำสั่งซื้อ เมตาเดตาเหตุการณ์ และประวัติการซื้อขาย เปิดให้อ่านได้แบบสาธารณะ:
curl "https://gamma-api.polymarket.com/events?limit=5"
ไม่ต้องใช้ API key หรือ OAuth เพื่อเริ่มดึงข้อมูลตลาด
แนวทางนี้ลด friction สำหรับนักพัฒนา ผู้สร้าง dashboard นักวิเคราะห์ และผู้ที่กำลังประเมินแพลตฟอร์ม โดยให้การยืนยันตัวตนเกิดขึ้นเฉพาะเมื่อการกระทำนั้นมีความเสี่ยง เช่น การส่งคำสั่งซื้อ
วิธีนำไปใช้
แยกสิทธิ์ read และ write ให้ชัดเจน:
GET /markets # public
GET /markets/:id/orderbook # public
GET /events # public
POST /orders # authenticated
DELETE /orders/:id # authenticated
POST /withdrawals # authenticated + stronger verification
สำหรับ API ของคุณ ให้ถามคำถามนี้ก่อนเพิ่ม authentication:
การป้องกัน endpoint นี้ช่วยลดความเสี่ยงจริง หรือเพียงเพิ่มอุปสรรคให้ผู้ใช้?
หากข้อมูลมีเป้าหมายเพื่อให้คนค้นพบหรือสร้างต่อได้ การเปิด read API อาจช่วยเพิ่ม adoption ได้มากกว่าการล็อกไว้หลัง API key
รูปแบบที่ 3: ใช้การยืนยันตัวตนสองระดับตามระดับความไว้วางใจ
การซื้อขายต้องมีการยืนยันตัวตน แต่ Polymarket แยกขั้นตอนออกเป็นสองระดับ
L1: พิสูจน์ความเป็นเจ้าของกระเป๋าเงิน
L1 ใช้ลายเซ็น EIP-712 จาก private key เพื่อสร้างหรือดึง API credentials:
// L1: ใช้ private key เพื่อสร้าง API credentials
const credentials = await client.createOrDeriveApiKey();
// { key: "...", secret: "...", passphrase: "..." }
ขั้นตอนนี้เกิดขึ้นไม่บ่อย เพราะเป็นการพิสูจน์ตัวตนที่มีความสำคัญสูง
L2: ยืนยันคำขอซื้อขายแต่ละครั้ง
L2 ใช้ HMAC-SHA256 พร้อม credentials ที่ได้จาก L1:
{
"POLY_ADDRESS": "0x...",
"POLY_SIGNATURE": "<hmac-sha256>",
"POLY_TIMESTAMP": "1716000000",
"POLY_API_KEY": "550e8400-...",
"POLY_PASSPHRASE": "..."
}
วิธีนำไปใช้
นำแนวคิดนี้ไปใช้ได้แม้ไม่ใช่ระบบคริปโต:
- ใช้ credential ที่แข็งแรงสำหรับการสร้าง session หรือ API key
- ใช้ credential ที่จำกัดขอบเขตและหมุนเวียนได้สำหรับคำขอประจำ
- กำหนดอายุของ session token หรือ signed request
- รองรับการเพิกถอน key โดยไม่ต้องเปลี่ยนข้อมูลยืนยันตัวตนหลัก
ตัวอย่าง flow สำหรับระบบทั่วไป:
1. ผู้ใช้ยืนยันตัวตนด้วย passkey, MFA หรือ hardware key
2. ระบบออก API key แบบมี scope
3. ไคลเอนต์ลงนามคำขอด้วย HMAC หรือ short-lived token
4. ระบบตรวจ timestamp, signature และ scope ทุกครั้ง
แนวคิดสำคัญคือแยก:
- พิสูจน์ว่าคุณเป็นใคร
- พิสูจน์ว่าคำขอนี้ได้รับอนุญาตจากคุณ
รูปแบบที่ 4: มองคำสั่งซื้อเป็นข้อความที่ลงนาม ไม่ใช่เพียง API request
เมื่อส่งคำสั่งซื้อบน Polymarket คุณไม่ได้เพียงส่ง JSON ไปให้เซิร์ฟเวอร์ แต่กำลังสร้างข้อความที่ลงนามด้วยคริปโตกราฟี ซึ่งเป็นข้อผูกพันทางการเงินที่ตรวจสอบได้
const response = await client.createAndPostOrder(
{
tokenID: "71321045679...",
price: 0.65,
size: 100,
side: Side.BUY,
},
{
tickSize: "0.01",
negRisk: false,
},
OrderType.GTC
);
ภายใน SDK จะสร้างข้อมูลแบบ EIP-712 ลงนามด้วย private key และส่งลายเซ็นพร้อมคำสั่งซื้อ การจับคู่คำสั่งเกิดขึ้นแบบ off-chain แต่การชำระบัญชีบน Polygon อาศัยข้อความที่ลงนามเหล่านี้
วิธีนำไปใช้
สำหรับการดำเนินการที่มีความเสี่ยงสูง ให้พิจารณาลงนาม payload แทนการพึ่งพา transport-level authentication เพียงอย่างเดียว
ตัวอย่าง payload ที่ออกแบบให้ลงนามได้:
{
"action": "TRANSFER",
"from": "account_123",
"to": "account_456",
"amount": "100.00",
"currency": "USD",
"nonce": "a5ec7d8d-1ed9-4e07-b06d-bf978ddfa241",
"expiresAt": "2026-05-18T12:00:00Z"
}
องค์ประกอบที่ควรมี:
-
actionเพื่อป้องกันการนำลายเซ็นไปใช้กับ operation อื่น -
nonceเพื่อป้องกัน replay attack -
expiresAtเพื่อจำกัดอายุของคำสั่ง - ข้อมูลธุรกรรมครบถ้วน เพื่อให้ลายเซ็นผูกกับเจตนาที่ชัดเจน
รูปแบบนี้มีประโยชน์กับระบบการเงิน เอกสารกฎหมาย การอนุมัติรายการสำคัญ และ workflow ที่ต้องการ audit trail
รูปแบบที่ 5: ทำให้ออนโทโลยีของโดเมนชัดเจนในโมเดลข้อมูล
Polymarket แยกวัตถุหลักเป็น เหตุการณ์ (Event) และ ตลาด (Market)
- Event คือคำถามระดับสูง เช่น “ใครจะชนะการเลือกตั้งวุฒิสภาสหรัฐฯ ปี 2026 ในรัฐเพนซิลเวเนีย?”
- Market คือผลลัพธ์ที่ซื้อขายได้ภายในเหตุการณ์ เช่น “Bob Casey จะชนะหรือไม่?”
หนึ่ง Event จึงมีได้หลาย Market:
{
"id": "501",
"title": "2026 Pennsylvania Senate Race",
"negRisk": true,
"markets": [
{
"id": "2301",
"question": "Will Bob Casey win?",
"outcomePrices": "[\"0.42\", \"0.58\"]"
},
{
"id": "2302",
"question": "Will Dave McCormick win?",
"outcomePrices": "[\"0.35\", \"0.65\"]"
},
{
"id": "2303",
"question": "Will a third candidate win?",
"outcomePrices": "[\"0.23\", \"0.77\"]"
}
]
}
โมเดลนี้ไม่ได้เก็บเพียงข้อมูล แต่บอกความสัมพันธ์เชิงแนวคิดของโดเมนด้วย
วิธีนำไปใช้
เมื่อออกแบบ resource ให้หลีกเลี่ยงการ flatten ความสัมพันธ์ที่สำคัญจนผู้ใช้ API ต้องเดาเอง
แทนที่จะใช้:
{
"marketId": "2301",
"eventName": "2026 Pennsylvania Senate Race",
"candidate": "Bob Casey",
"price": "0.42"
}
ให้รักษาความสัมพันธ์ไว้:
{
"event": {
"id": "501",
"title": "2026 Pennsylvania Senate Race",
"markets": []
}
}
หาก API มี array ที่สัมพันธ์กันตาม index ให้ระบุสัญญานี้ใน schema และ SDK อย่างชัดเจน เช่น outcomes[0] ต้องตรงกับ outcomePrices[0]
รูปแบบที่ 6: ทำให้ข้อจำกัดด้านเงินทุนเป็นส่วนหนึ่งของ API
แฟล็ก negRisk แสดงว่าตลาดใน Event เดียวกันมีความสัมพันธ์ด้านเงินทุน
สำหรับเหตุการณ์ NegRisk ซึ่งมีผลลัพธ์ชนะได้เพียงหนึ่งรายการ:
1 โทเค็น “ไม่” สำหรับผลลัพธ์ A ≡ 1 โทเค็น “ใช่” สำหรับผลลัพธ์อื่นทั้งหมด
ตัวอย่างการแปลงตำแหน่ง:
| ก่อน | หลัง |
|---|---|
| 1× ไม่ (อื่นๆ) | 1× ใช่ (Casey) + 1× ใช่ (McCormick) |
API เปิดเผยข้อจำกัดนี้ผ่าน negRisk: true ทั้งในข้อมูลตลาดและตัวเลือกการส่งคำสั่งซื้อ
const options = {
tickSize: "0.01",
negRisk: true
};
หากส่งค่าผิด คำสั่งซื้ออาจถูกปฏิเสธหรือชำระไม่ถูกต้อง
วิธีนำไปใช้
หากโดเมนของคุณมี invariant ที่สำคัญ อย่าซ่อนไว้แค่ในเอกสาร ให้แสดงเป็น field ที่ตรวจสอบได้
ตัวอย่าง:
{
"order": {
"amount": 100,
"currency": "USD",
"settlementMode": "NETTED",
"requiresCollateral": true
}
}
หรือใช้ enum เพื่อป้องกันค่าที่ไม่ถูกต้อง:
type SettlementMode = "GROSS" | "NETTED";
หลักการคือ:
- ข้อจำกัดที่ละเว้นแล้วทำให้ระบบผิดพลาด ควรอยู่ใน schema
- ค่าที่เปลี่ยนพฤติกรรมของระบบ ควรถูก validate โดย server
- SDK ควรบังคับให้ผู้เรียกตัดสินใจอย่างชัดเจน
รูปแบบที่ 7: ปฏิบัติต่อขนาด Tick เป็นสถานะตลาดแบบไดนามิก
API การเงินจำนวนมากมอง tick size เป็นค่าคงที่ แต่ Polymarket เปลี่ยน tick size ตามราคาตลาด
เมื่อราคาสูงกว่า 0.96 หรือต่ำกว่า 0.04 ขนาด tick ขั้นต่ำจะลดจาก 0.01 เป็น 0.001:
{
"event_type": "tick_size_change",
"asset_id": "65818619657...",
"old_tick_size": "0.01",
"new_tick_size": "0.001",
"timestamp": "100000000"
}
เหตุผลคือใกล้ขอบเขตราคา การขยับทีละ 0.01 มีผลมากเกินไป ตัวอย่างเช่น จาก 0.04 เป็น 0.03 คือการเปลี่ยนแปลงสัมพัทธ์ 25% การใช้ tick ที่ละเอียดขึ้นทำให้ราคาแสดงความน่าจะเป็นได้แม่นยำกว่า เช่น 0.973 แทนการปัดเป็น 0.97
วิธีนำไปใช้
อย่า hard-code parameter ที่เปลี่ยนแปลงได้ในระบบการเงินหรือระบบเรียลไทม์
ให้ไคลเอนต์ทำงานตามขั้นตอนนี้:
- ดึงค่าตั้งต้นของตลาดเมื่อเริ่มต้น
- Subscribe event ที่บอกการเปลี่ยนแปลงสถานะ
- อัปเดต cache ในหน่วยความจำ
- ตรวจสอบคำสั่งก่อนส่ง
- รีเฟรชสถานะเมื่อได้รับ validation error จาก server
ตัวอย่าง pseudo-code:
let tickSize = "0.01";
socket.on("tick_size_change", (event) => {
if (event.asset_id === assetId) {
tickSize = event.new_tick_size;
}
});
function validatePrice(price: number) {
const tick = Number(tickSize);
return Number.isInteger(price / tick);
}
หลักการที่นำไปใช้ได้กว้างกว่าตลาดการเงินคือ: หากค่าใดส่งผลต่อความถูกต้องของ request และเปลี่ยนได้ตามสถานะระบบ ให้เผยแพร่การเปลี่ยนแปลงนั้นเป็น event
รูปแบบที่ 8: แยก WebSocket ตามโปรไฟล์ผู้ใช้
Polymarket มี WebSocket แยกกันสองระบบ ซึ่งตอบโจทย์ผู้ใช้คนละประเภท
Market Channel
wss://ws-subscriptions-clob.polymarket.com/ws/market
ช่องทางนี้ออกแบบสำหรับผู้ที่ต้องซื้อขาย โดยส่งข้อมูลสมุดคำสั่งซื้อ การเปลี่ยนแปลงราคา การจับคู่การซื้อขาย และการเปลี่ยน tick size
{
"assets_ids": [
"65818619657568813474341868652308942079804919287380422192892211131408793125422"
],
"type": "market"
}
Real-Time Data Socket
wss://ws-live-data.polymarket.com
ช่องทางนี้รองรับข้อมูลแบบอื่น เช่น ความคิดเห็น ราคาคริปโตจาก Binance และ Chainlink ราคาหุ้น และกิจกรรมทางสังคม
{
"action": "subscribe",
"subscriptions": [
{
"topic": "crypto_prices",
"type": "update",
"filters": "btcusdt,ethusd"
}
]
}
วิธีนำไปใช้
อย่าพยายามทำ WebSocket เดียวให้รองรับทุก use case หากผู้ใช้มีความต้องการด้าน latency ปริมาณข้อมูล และความน่าเชื่อถือที่ต่างกัน
แยก channel ตามลักษณะ workload:
wss://api.example.com/ws/trading
wss://api.example.com/ws/market-data
wss://api.example.com/ws/notifications
wss://api.example.com/ws/social
ตัวอย่างการแบ่งความรับผิดชอบ:
| Channel | ความสำคัญ | รูปแบบข้อมูล |
|---|---|---|
| Trading | latency ต่ำมาก | order update, fill, rejection |
| Market data | throughput สูง | order book, price ticks |
| Notifications | รับประกันการส่งระดับหนึ่ง | account alert, system event |
| Social | ยอมรับความหน่วงได้มากกว่า | comments, reactions, feed |
การแยก infrastructure ทำให้คุณกำหนด retry policy, retention, backpressure และ SLA ให้เหมาะกับแต่ละประเภทข้อมูลได้
สิ่งที่รูปแบบเหล่านี้มีร่วมกัน
การออกแบบ API ของ Polymarket สะท้อนแนวคิดเดียวกัน: API ควรเปิดเผยโครงสร้างจริงของโดเมน แทนที่จะซ่อนความซับซ้อนไว้หลัง endpoint ที่ดูเรียบง่าย
สรุปเป็น checklist สำหรับนำไปใช้:
- แยก API ตามโดเมนและรูปแบบ workload ไม่ใช่แค่ตาม entity
- เปิดข้อมูลอ่านที่ปลอดภัยต่อสาธารณะเพื่อลด friction
- แยกการพิสูจน์ตัวตนออกจากการยืนยันคำขอประจำ
- ใช้ signed payload สำหรับการดำเนินการที่มีความเสี่ยงสูง
- ทำให้ความสัมพันธ์ของ resource ปรากฏใน schema
- เข้ารหัส domain constraint เป็น field หรือ type ที่ตรวจสอบได้
- ส่ง state change ผ่าน event แทนการบังคับให้ client เดา
- แยก real-time infrastructure ตามความต้องการของผู้ใช้
คำแนะนำ API ทั่วไปมักเน้นความง่ายในการเรียกใช้ ความสม่ำเสมอของชื่อ และการจัดการข้อผิดพลาดที่คาดการณ์ได้ ซึ่งทั้งหมดสำคัญ แต่ระบบที่ซับซ้อนต้องการมากกว่านั้น: ความซื่อสัตย์ต่อโดเมน
เมื่อโดเมนมีข้อจำกัด API ควรบังคับใช้ เมื่อโดเมนมีสถานะที่เปลี่ยนแปลง API ควรแจ้งให้ทราบ และเมื่อโดเมนมีความสัมพันธ์ที่มีผลต่อความถูกต้อง API ควรทำให้ความสัมพันธ์นั้นมองเห็นได้ชัดเจน
Top comments (0)