WSS Overview
一、核心定位:Websocket 是做什么的?
Polymarket 提供的 Websocket(WSS)通道,核心作用是:
让客户端(你的程序)实时接收推送更新 ,而非主动轮询接口,以此实现「几乎实时」的订单、交易、市场数据同步。
对比:普通 HTTP 接口是「你问我答」(比如每10秒查一次订单),Websocket 是「服务器主动通知」(订单有变化立刻告诉你),效率和实时性更高。
二、核心概念:两个核心通道(Channel)
Polymarket WSS 只开放了两种类型的通道,对应不同的推送内容:
| 通道类型(type) | 作用 | 关注的标识 |
|---|---|---|
USER(用户通道) |
接收当前用户的订单、交易相关实时事件(比如你的订单被成交、取消、挂单) | 用 markets(市场ID/condition IDs)指定要监听的市场 |
MARKET(市场通道) |
接收全网市场的实时数据(比如某个标的的价格变动、成交量变化) | 用 assets_ids(资产ID/token IDs)指定要监听的资产 |
简单说:
- 想监控「自己的订单状态」→ 订阅
USER通道 + 传入关注的markets; - 想监控「某个标的的市场行情」→ 订阅
MARKET通道 + 传入关注的assets_ids。
三、第一步:初始订阅(连接时的首次配置)
打开 Websocket 连接后,必须先发送「订阅消息」才能接收推送,消息需包含以下核心字段:
| 字段 | 类型 | 必选 | 作用 |
|---|---|---|---|
auth |
Auth 类型 | ✅ | 身份认证信息(下文会单独讲 WSS 认证规则),没有认证会被拒绝连接 |
markets |
字符串数组 | 仅 USER 通道 ✅ | 要监听的市场ID列表(比如 ["12345", "67890"]) |
assets_ids |
字符串数组 | 仅 MARKET 通道 ✅ | 要监听的资产ID/代币ID列表(比如 ["token123", "token456"]) |
type |
字符串 | ✅ | 订阅的通道类型,只能是 USER 或 MARKET |
custom_feature_enabled |
布尔值 | ❌ | 开启/关闭自定义功能(一般默认 false 即可) |
核心逻辑:连接建立后,先传「认证+要监听的内容+通道类型」,服务器验证通过后,才会开始推送对应事件。
四、第二步:动态订阅/取消订阅(连接后调整)
连接建立后,不需要断开重连,就能动态新增/取消监听的资产/市场,只需发送包含以下字段的消息:
| 字段 | 类型 | 必选 | 作用 |
|---|---|---|---|
assets_ids |
字符串数组 | 仅 MARKET 通道 ✅ | 要新增/取消的资产ID列表 |
markets |
字符串数组 | 仅 USER 通道 ✅ | 要新增/取消的市场ID列表 |
operation |
字符串 | ✅ | 操作类型:subscribe(订阅)或 unsubscribe(取消订阅) |
custom_feature_enabled |
布尔值 | ❌ | 同上,自定义功能开关 |
举例:
- 已订阅 MARKET 通道监听资产 A,想新增监听资产 B → 发送
operation: "subscribe"+assets_ids: ["B的ID"]; - 不想监听 USER 通道的市场 C → 发送
operation: "unsubscribe"+markets: ["C的ID"]。
WSS Quickstart
🔑 第一部分:获取 API Keys
你需要一个以太坊私钥(Private Key)来初始化客户端。如果你使用的是邮箱登录(通过 Magic.link),可以去 https://reveal.magic.link/polymarket 导出私钥;否则从你的 Web3 钱包(如 MetaMask)导出。
✅ 安装依赖
bash
npm install @polymarket/clob-client
npm install ethers
📜 JavaScript 代码(用于生成 API Key)
js
import { ClobClient } from "@polymarket/clob-client";
import { Wallet } from "@ethersproject/wallet";
const host = 'https://clob.polymarket.com';
const signer = new Wallet("YourPrivateKey"); // 替换为你的私钥
// 初始化 CLOB 客户端(链 ID 137 是 Polygon)
const clobClient = new ClobClient(host, 137, signer);
(async () => {
const apiKey = await clobClient.deriveApiKey();
console.log("Generated API Key:", apiKey);
})();
运行这段代码后,你会得到一个包含 key、secret 和 passphrase 的对象,用于后续的 WebSocket 认证。
🌐 第二部分:连接 WebSocket(Market / User 频道)
📜 Python 代码(WebSocket 订阅示例)
python
from websocket import WebSocketApp
import json
import time
import threading
MARKET_CHANNEL = "market"
USER_CHANNEL = "user"
class WebSocketOrderBook:
def __init__(self, channel_type, url, data, auth, message_callback, verbose):
self.channel_type = channel_type
self.url = url
self.data = data
self.auth = auth
self.message_callback = message_callback
self.verbose = verbose
furl = url + "/ws/" + channel_type
self.ws = WebSocketApp(
furl,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
on_open=self.on_open,
)
self.orderbooks = {}
def on_message(self, ws, message):
print("Received message:", message)
def on_error(self, ws, error):
print("Error:", error)
exit(1)
def on_close(self, ws, close_status_code, close_msg):
print("Connection closed")
exit(0)
def on_open(self, ws):
if self.channel_type == MARKET_CHANNEL:
ws.send(json.dumps({"assets_ids": self.data, "type": MARKET_CHANNEL}))
elif self.channel_type == USER_CHANNEL and self.auth:
ws.send(
json.dumps({
"markets": self.data,
"type": USER_CHANNEL,
"auth": self.auth
})
)
else:
exit(1)
# 启动心跳线程(每10秒发一次 PING)
thr = threading.Thread(target=self.ping, args=(ws,))
thr.start()
def subscribe_to_tokens_ids(self, assets_ids):
if self.channel_type == MARKET_CHANNEL:
self.ws.send(json.dumps({"assets_ids": assets_ids, "operation": "subscribe"}))
def unsubscribe_to_tokens_ids(self, assets_ids):
if self.channel_type == MARKET_CHANNEL:
self.ws.send(json.dumps({"assets_ids": assets_ids, "operation": "unsubscribe"}))
def ping(self, ws):
while True:
ws.send("PING")
time.sleep(10)
def run(self):
self.ws.run_forever()
if __name__ == "__main__":
url = "wss://ws-subscriptions-clob.polymarket.com"
# 🔑 从第一部分 JS 脚本中获取这三个值
api_key = "" # e.g., "abc123..."
api_secret = "" # e.g., "def456..."
api_passphrase = "" # e.g., "ghi789..."
asset_ids = [
"109681959945973300464568698402968596289258214226684818748321941747028805721376",
]
condition_ids = [] # 用户频道可能需要 condition_id,但通常可为空
auth = {
"apiKey": api_key,
"secret": api_secret,
"passphrase": api_passphrase
}
# 创建 Market 频道连接(订阅特定资产)
market_connection = WebSocketOrderBook(
MARKET_CHANNEL, url, asset_ids, auth, None, True
)
# 可选:动态订阅/取消订阅其他 asset_id
market_connection.subscribe_to_tokens_ids(["123"])
# 启动 Market 连接(阻塞主线程)
market_connection.run()
# 如果需要监听自己的订单,取消注释以下两行:
# user_connection = WebSocketOrderBook(USER_CHANNEL, url, condition_ids, auth, None, True)
# user_connection.run()
WSS Authentication
✅ 核心要点:
-
只有连接到
user频道时才需要认证 。连接
market频道是公开的,不需要提供 API 凭据。 -
如果你要订阅 用户私有数据 (比如自己的订单、持仓、成交等),就必须在连接
user频道时提供有效的认证信息。
🔐 认证所需的三个字段(全部必填,尽管表格写"Optional: yes",但实际使用中是必需的):
| 字段名 | 是否可选 | 说明 |
|---|---|---|
apikey |
否(实际必需) | 你在 Polymarket CLOB 上为 Polygon 账户生成的 API Key |
secret |
否(实际必需) | 对应的 API Secret(用于签名) |
passphrase |
否(实际必需) | API Passphrase(通常是一个 UUID,用于增强安全性) |
⚠️ 注意:虽然表格中标注为 "Optional: yes",但这很可能是文档笔误。在连接
user频道时,这三个字段是必须提供的,否则服务器会拒绝连接或返回认证错误。
User Channel
由于 user 频道是经过认证的私有频道 ,它只会推送与当前 API Key 所属用户相关的活动(如自己下的单、自己的成交等)。
✅ 核心内容总结
1. 频道类型
- 频道名称 :
user - 认证要求 :必须提供有效的
apikey、secret、passphrase - 数据范围 :仅限当前认证用户的订单和交易活动
📥 一、Trade 消息(成交事件)
🔔 触发时机
- 用户的 市价单被撮合(MATCHED)
- 用户的 限价单作为 Maker 被撮合(MATCHED)
- 成交状态后续更新:
MINED(上链)、CONFIRMED(确认)、RETRYING(重试)、FAILED(失败)
换句话说:只要与你相关的交易发生或状态变化,就会收到此消息。
🧱 消息结构关键字段
| 字段 | 说明 |
|---|---|
type: "TRADE" |
消息类型标识 |
event_type: "trade" |
事件类型 |
id |
成交唯一 ID(UUID) |
status |
当前成交状态(如 "MATCHED") |
side |
"BUY" 或 "SELL" |
price / size |
成交价格和数量 |
asset_id |
成交的 token ID(对应某个市场结果,如 "YES") |
market |
市场标识(即 condition ID) |
maker_orders |
包含所有参与此成交的 挂单方(Maker)订单详情 |
taker_order_id |
吃单方(Taker)的订单 ID |
owner / trade_owner |
通常是你自己的 API Key(UUID 格式) |
💡 注意:即使你是 Taker(吃单方),也会收到这条消息,因为成交涉及你的订单。
📥 二、Order 消息(订单事件)
🔔 触发时机
- 下单成功 →
type: "PLACEMENT" - 部分成交或更新 →
type: "UPDATE" - 取消订单 →
type: "CANCELLATION"
🧱 消息结构关键字段
| 字段 | 说明 |
|---|---|
type |
"PLACEMENT" / "UPDATE" / "CANCELLATION" |
event_type: "order" |
事件类型 |
id |
订单 ID(通常是交易哈希) |
asset_id |
订单对应的 token ID |
market |
所属市场的 condition ID |
side / price / original_size |
订单方向、价格、原始数量 |
size_matched |
已成交数量(UPDATE 时会增加) |
associate_trades |
关联的成交 ID 列表(可能为 null) |
owner / order_owner |
你的 API Key(表示这是你的订单) |
🔄 举例:
- 初始下单:
type=PLACEMENT,size_matched="0"- 部分成交后:收到
type=UPDATE,size_matched="5"- 全部成交或取消:可能再收到一次
UPDATE或CANCELLATION
Market Channel
该频道无需认证,任何人都可以订阅,适用于行情监控、做市策略、数据分析等场景。
✅ 核心要点总结
- 频道名称 :
market - 用途:接收实时市场深度(订单簿)、价格变动、成交价、新市场上线、市场结算等公开信息
- 订阅方式 :通过 WebSocket 发送包含
assets_ids的订阅消息 - 所有消息都与特定
asset_id(即某个结果 token,如 "YES")或market(condition ID)关联
📡 消息类型详解
1. book 消息 --- 完整订单簿快照
- 触发时机 :
- 首次订阅某个
asset_id - 有交易发生导致订单簿变化
- 首次订阅某个
- 内容:当前买卖盘的聚合 Level 2 数据(价格 + 总量)
- 字段说明 :
buys→ 实际应为bids(文档结构写的是buys,但示例用bids,可能是笔误)sells→ 实际应为askshash:订单簿内容的摘要哈希,可用于检测是否重复或完整timestamp:毫秒级 Unix 时间戳
2. price_change 消息 --- 增量订单簿更新(高效更新)
- 触发时机 :
- 新订单挂单
- 订单被取消
- 内容 :仅发送发生变化的价格档位(非全量)
- 每个
PriceChange包含 :side(BUY/SELL)price:价格档size:该档位新的总挂单量(若为 0 表示该档消失)best_bid/best_ask:当前最优买卖价(方便快速获取)
- ⚠️ 重要提示:2025 年 9 月 15 日将有 Breaking Change(需关注迁移指南)
💡 适合高频更新场景,减少带宽消耗。
3. tick_size_change 消息 --- 最小价格变动单位变更
- 触发时机 :
- 当市场价格接近极端值( 0.96)时,系统自动缩小 tick size(如从 0.01 → 0.001)
- 用途:提高价格精度,防止"卡死"在 0.01 步长
- 字段 :
old_tick_size→new_tick_size
4. last_trade_price 消息 --- 最新成交价格
- 触发时机:每当有 Maker 和 Taker 成交
- 内容 :成交的
price、size、side、asset_id等 - 注意:不包含订单细节,仅成交快照
5. best_bid_ask 消息(需开启 feature flag)
- 触发时机:最优买卖价发生变化
- 内容 :
best_bid/best_askspread(价差)
- 优势 :比解析完整
book更轻量,适合只关心 top-of-book 的应用
6. new_market 消息(需开启 feature flag)
- 触发时机:平台上线一个新预测市场
- 内容 :
- 市场问题(
question) - condition ID(
market) - 所有 outcome 的
asset_ids和outcomes(如 ["Yes", "No"]) - 描述、slug、事件元数据等
- 市场问题(
- 用途:自动化发现新市场,用于爬虫或监控系统
7. market_resolved 消息(需开启 feature flag)
- 触发时机:市场已结算(结果揭晓)
- 内容 :
winning_asset_idwinning_outcome(如 "Yes")- 其余字段同
new_market
- 用途:自动跟踪市场结果,用于结算、回测或通知
Sports Websocket
Overview
📡 1. 用途
- 提供 实时体育赛事更新 ,包括:
- 比分(scores)
- 节/局/半场等阶段信息(periods)
- 比赛状态(如进行中、暂停、结束等)
🔌 2. 连接方式
- WebSocket 地址 :
wss://sports-api.polymarket.com/ws - 无需认证 :这是一个公开广播频道,任何人都可以连接。
- 无需订阅消息 :
只要建立连接,就会自动开始推送所有正在进行的体育赛事更新,不需要发送额外的订阅请求。
❤️ 3. 连接管理(心跳机制)
为保持长连接稳定,服务器和客户端需配合完成 Ping/Pong 心跳:
| 参数 | 默认值 | 说明 |
|---|---|---|
| PING 间隔 | 5 秒 | 服务器每隔 5 秒向客户端发送一次 PING |
| PONG 超时 | 10 秒 | 客户端必须在 10 秒内回复 PONG,否则连接会被强制关闭 |
✅ 客户端必须实现 PONG 响应逻辑,否则连接会频繁断开。
🔁 4. 健康与重连建议
- 如果未及时响应 PING → 连接被服务器终止
- 建议客户端实现 :
- 自动重连机制
- 指数退避(exponential backoff) 策略(例如:1s、2s、4s、8s... 逐步增加重试间隔),避免频繁重连导致服务压力
🍪 5. 会话亲和性(Session Affinity)
- 服务器使用名为
sports-results的 Cookie 来确保: 同一个客户端始终连接到同一个后端实例 - 对浏览器用户透明:浏览器会自动处理 Cookie,无需额外操作
- 非浏览器客户端(如 Python、Node.js)注意 :
需要保存并携带服务器返回的 Cookie,否则可能因负载均衡切换后端而丢失上下文或触发限流
Message Format
✅ 核心内容总结
1. 消息类型
- 唯一消息类型 :
sport_result(虽然 JSON 中未显式包含event_type字段,但所有推送都属于此类) - 触发时机 :
- 比赛开始(上线)
- 比分变化
- 节/局/半场等阶段变更(如"Q4"、"HT")
- 比赛结束
- NFL 和大学橄榄球(CFB):控球权(possession)切换
所有消息自动广播给所有连接客户端,无需订阅。
🧱 2. 消息结构(关键字段)
| 字段 | 类型 | 说明 |
|---|---|---|
gameId |
number | 唯一标识符,用于区分不同比赛(核心 ID) |
leagueAbbreviation |
string | 联赛缩写,如 "nfl", "nba", "cs2" |
homeTeam / awayTeam |
string | 主队 / 客队名称或简称 |
status |
string | 比赛状态,如 "InProgress", "finished" |
live |
boolean | 是否正在进行中 |
ended |
boolean | 是否已结束 |
score |
string | 当前比分(格式因运动而异) |
period |
string | 当前阶段(如 "Q4", "2H", "2/3") |
elapsed |
string | 当前节已过时间(如 "5:18") |
finishedTimestamp |
string | 比赛结束的 Unix 时间戳(仅在 ended: true 时存在) |
turn |
string | 仅 NFL/CFB :当前控球方(小写队名缩写,如 "lac") |
📌 3. 特殊说明
🔹 比分格式(score)因项目而异
- 传统体育 (NFL/NBA):简单数字,如
"3-16" - 电竞(CS2) :复杂结构,如
"000-000|2-0|Bo3"
→ 可能表示:小分 0-0,大分 2-0,赛制为 Best of 3
🔹 period 值对照表
| 值 | 含义 |
|---|---|
1H / 2H |
上半场 / 下半场(足球、篮球等) |
1Q--4Q |
第1--4节(NFL、NBA) |
HT |
中场休息 |
FT |
常规时间结束 |
FT OT |
加时赛后结束 |
1/3, 2/3 |
Bo3 赛制中的第1、2图(电竞) |
End 1 |
MLB 第1局结束 |
🔹 Slug 命名规范(虽未在消息中出现,但供参考)
格式:{league}-{team1}-{team2}-{date}
示例:
nfl-buf-kc-2025-01-26nba-lal-bos-2025-02-15
可用于构建市场 URL 或唯一赛事标识(但消息本身用
gameId)
💡 4. 客户端处理建议
-
以
gameId作为唯一键 来更新本地状态 -
示例逻辑(JavaScript):
jssetSportsData(prev => { const existing = prev.find(item => item.gameId === data.gameId); if (existing) { // 更新已有比赛 return prev.map(item => item.gameId === data.gameId ? data : item ); } // 新增比赛 return [...prev, data]; });
这确保即使收到多条同一比赛的更新,也能正确合并状态。
是构建体育赛事监控面板、预测市场联动或实时提醒系统的基础数据源。
Quickstart
✅ 核心内容总结
1. 基本连接方式
- 端点(Endpoint) :
wss://sports-api.polymarket.com/ws - 无需认证:公开服务,直接连接即可
- 自动广播 :连接后立即开始推送所有活跃赛事的更新,无需发送订阅消息
2. JavaScript 示例(含关键逻辑)
提供了一个完整的浏览器端 WebSocket 连接示例,包含:
onopen:连接成功日志onmessage:核心!必须处理 PING/PONGonclose:简单重连(通过页面刷新)onerror:错误捕获
3. ⚠️ 关键要求:正确处理 PING/PONG 心跳
这是最容易出错也最关键的点:
- 服务器行为 :每 5 秒发送一次纯文本
"ping" - 客户端必须 :收到
"ping"后立即回复"pong" - 错误示例 :直接对所有消息执行
JSON.parse()→ 遇到"ping"字符串会抛出异常,导致连接断开 - 后果:10 秒内未回复 PONG → 服务器主动关闭连接
✅ 正确做法:
js
if (event.data === 'ping') {
ws.send('pong');
return;
}
// 再处理 JSON 消息
4. 连接状态管理建议
- 发送前检查
ws.readyState === WebSocket.OPEN - 避免在非 OPEN 状态下发送数据(如 PONG),防止报错
5. 浏览器环境特殊问题:Tab 不可见时连接可能中断
- 浏览器为省电,可能暂停后台 Tab 的 WebSocket 或定时器
- 解决方案 :监听
visibilitychange事件- 当用户切回 Tab 且连接已断 → 自动重连
6. 常见问题与调试技巧
| 问题 | 可能原因 | 调试建议 |
|---|---|---|
| 连接 10 秒后断开 | 未正确响应 PING | 检查是否处理了 "ping" 字符串 |
| 频繁掉线 | 未实现重连 / 心跳失败 | 添加指数退避重连逻辑 |
| UI 未更新 | 消息解析错误 / 状态未合并 | 打印原始消息,确认 gameId 更新逻辑 |
| 内存泄漏 | 组件卸载后未关闭 WebSocket | 在 React 中用 useEffect 清理连接 |
调试工具建议:
- 开启详细日志(记录 open/close/error/message)
- 定时打印
readyState监控连接状态
Real Time Data Stream
Real Time Data Socket
📡 1. 服务概述
- 名称:Real-Time Data Socket(RTDS)
- 用途 :提供两类实时数据:
- Crypto Prices:加密货币的实时价格更新
- Comments:评论相关事件(如新评论、点赞/反应等)
- 官方客户端 :提供 TypeScript SDK(
real-time-data-client)
🔌 2. 连接信息
- WebSocket 地址 :
wss://ws-live-data.polymarket.com - 协议:标准 WebSocket
- 数据格式:JSON
🔐 3. 认证机制
-
大多数数据公开 ,但部分用户专属流 (如个人评论通知)可能需要认证:
json"gamma_auth": { "address": "0xYourWalletAddress" } -
仅在订阅需要身份验证的 topic 时才需提供
gamma_auth
⚙️ 4. 连接管理特性
- 动态订阅 :
连接建立后,可随时通过发送消息添加、移除或修改订阅 ,无需断开重连 - 心跳保活 :
客户端应每 5 秒主动发送一次"PING"(注意:这里是客户端发 PING,与 Sports API 不同),以维持连接
💡 注意:此处是 客户端主动发 PING,而 Sports API 是服务器发 PING、客户端回 PONG。两者机制相反!
📥 5. 消息统一结构
所有推送消息遵循固定格式:
json
{
"topic": "crypto_prices", // 订阅主题
"type": "update", // 事件类型
"timestamp": 1712345678901, // 毫秒级 Unix 时间戳
"payload": { ... } // 具体数据(结构依 topic/type 而定)
}
🔁 6. 订阅管理(核心交互方式)
✅ 订阅示例
json
{
"action": "subscribe",
"subscriptions": [
{
"topic": "crypto_prices",
"type": "update",
"filters": "BTC,ETH" // 可选:过滤特定币种
},
{
"topic": "comments",
"type": "reaction_created",
"gamma_auth": {
"address": "0x123..."
}
}
]
}
❌ 取消订阅
将 "action" 改为 "unsubscribe",其余结构相同。
⚠️ 重要:
- 仅支持文档中列出的
topic和type- 发送无效订阅可能导致连接被关闭
🛑 7. 错误处理
- 网络错误:客户端应自动重连
- 无效订阅消息:可能导致服务器主动断开连接
- 认证失败:无法订阅受保护的用户数据流(但不影响其他公开订阅)
RTDS Crypto Prices
✅ 核心内容总结
1. 两种价格数据源
| 特性 | Binance 源 (crypto_prices) |
Chainlink 源 (crypto_prices_chainlink) |
|---|---|---|
| 数据来源 | Binance 交易所实时行情 | Chainlink 去中心化预言机喂价 |
| 认证要求 | 无需认证 | 无需认证 |
| 符号格式 | 小写拼接(如 btcusdt) |
斜杠分隔(如 btc/usd) |
| 订阅类型 | type: "update" |
type: "*"(通配所有事件类型) |
| 过滤方式 | 字符串逗号分隔(如 "btcusdt,ethusdt") |
JSON 字符串(如 "{\"symbol\":\"eth/usd\"}") |
💡 两者提供相似的价格信息,但格式、语义和底层来源不同,适用于不同场景:
- Binance:适合交易策略、短期波动监控
- Chainlink:适合 DeFi 应用、链上结算参考价
📥 2. 消息统一结构(含 payload 细节)
所有价格更新消息都遵循 RTDS 的通用格式:
json
{
"topic": "...",
"type": "update",
"timestamp": 1753314064237, // 消息发送时间(毫秒)
"payload": {
"symbol": "xxx", // 交易对标识
"timestamp": 1753314064213, // 价格生成/记录时间(毫秒)
"value": 67234.50 // 当前价格(单位:报价货币,如 USD/USDT)
}
}
payload.timestamp:价格实际产生的时间(来自数据源)- 外层
timestamp:Polymarket 服务器推送该消息的时间
🔧 3. 订阅方式示例
✅ 订阅全部 Binance 价格
json
{
"action": "subscribe",
"subscriptions": [{
"topic": "crypto_prices",
"type": "update"
}]
}
✅ 只订阅 BTC 和 ETH(Binance)
json
{
"action": "subscribe",
"subscriptions": [{
"topic": "crypto_prices",
"type": "update",
"filters": "btcusdt,ethusdt"
}]
}
✅ 订阅 Chainlink 的 ETH/USD
json
{
"action": "subscribe",
"subscriptions": [{
"topic": "crypto_prices_chainlink",
"type": "*",
"filters": "{\"symbol\":\"eth/usd\"}"
}]
}
⚠️ 注意:Chainlink 的
filters是 JSON 字符串,不是普通字符串!
📊 4. 支持的交易对示例
| 资产 | Binance 符号 | Chainlink 符号 |
|---|---|---|
| Bitcoin | btcusdt |
btc/usd |
| Ethereum | ethusdt |
eth/usd |
| Solana | solusdt |
sol/usd |
| XRP | xrpusdt |
xrp/usd |
所有报价货币均为 USD 或 USDT(稳定币等价)
📌 5. 关键注意事项
- 无认证要求:所有价格流都是公开的
- 实时推送:价格变动时立即推送,非轮询
- 时间戳双重含义:区分"价格生成时间"和"消息推送时间"
- 官方 SDK 支持 :推荐使用
real-time-data-client(TypeScript)简化接入
RTDS Comments
✅ 核心内容总结
1. 功能概述
- 用途:实时推送 Polymarket 平台上的用户评论活动
- 支持的事件类型 :
comment_created:新评论发布(包括顶层评论和回复)comment_removed:评论被删除reaction_created/reaction_removed:对评论的点赞/取消点赞
- 适用场景 :
- 实时评论流展示
- 讨论线程监控
- 社区情绪分析
🔌 2. 订阅方式
- Topic :
comments - Type :指定具体事件类型(如
"comment_created") - 认证 :
- 公开评论流通常无需认证
- 若需接收用户私有数据 (如"我收到的回复"),可能需要提供
gamma_auth(含钱包地址)
- 过滤(可选):可按评论 ID、用户地址、实体类型等筛选
订阅示例:
json
{
"action": "subscribe",
"subscriptions": [{
"topic": "comments",
"type": "comment_created"
}]
}
📥 3. 消息结构
所有评论事件遵循 RTDS 统一格式:
json
{
"topic": "comments",
"type": "comment_created",
"timestamp": 1753454975808, // 消息推送时间(毫秒 Unix 时间戳)
"payload": { ... } // 具体事件数据
}
🧾 4. Payload 字段详解(以 comment_created 为例)
| 字段 | 类型 | 说明 |
|---|---|---|
body |
string | 评论正文内容 |
createdAt |
string | 评论创建时间(ISO 8601 格式 ,如 "2025-07-25T14:49:35.801298Z") |
id |
string | 评论唯一 ID |
parentCommentID |
string | 父评论 ID(若为顶层评论则为 null 或空) |
parentEntityID |
number | 所属实体 ID(如某个 Event 或 Market 的 ID) |
parentEntityType |
string | 实体类型:"Event"、"Market" 等 |
userAddress |
string | 发帖用户的 Polygon 钱包地址 |
replyAddress |
string | 用于接收回复的地址(可能与 userAddress 不同) |
reactionCount |
number | 当前点赞数 |
reportCount |
number | 被举报次数 |
profile |
object | 用户公开资料 |
👤 Profile 对象字段
baseAddress:用户主地址name:显示名称(如"salted.caramel")pseudonym:系统生成的匿名昵称(如"Adored-Disparity")displayUsernamePublic:是否公开用户名proxyWallet:用于交易的代理钱包地址
🌲 5. 评论层级结构(嵌套回复)
- 顶层评论 :
parentCommentID为空或null - 回复评论 :
parentCommentID指向被回复的评论 ID - 所有评论都关联到一个父实体(如某个预测事件或市场)
示例:A 评论 → B 回复 A → C 回复 B,形成讨论链
⏱️ 6. 时间戳说明
payload.createdAt:评论实际创建时间(ISO 8601)- 外层
timestamp:WebSocket 消息发送时间(毫秒级 Unix 时间戳)
两者可能略有差异,反映网络传输延迟。
🛠️ 7. 使用建议与注意事项
- 地址多样性 :注意区分
userAddress、replyAddress、proxyWallet和baseAddress - 隐私控制 :用户名是否显示由
displayUsernamePublic控制 - 扩展性 :未来可能支持更多
parentEntityType(如文章、用户页等) - 官方 SDK :推荐使用
real-time-data-client(TypeScript)简化开发
Gamma Structure
Overview
✅ 核心要点总结
1. 背景:市场数据的两种来源
- 链上数据(权威来源) :
所有用于市场结算(resolution) 的关键数据都已写入区块链(例如 UMA 协议中的ancillaryData),这是最终、不可篡改的依据。 - Gamma(托管索引服务) :
Polymarket 提供的一个中心化托管服务 ,用于:- 索引链上市场数据
- 补充额外元数据(如分类、交易量统计、标签等)
💡 换句话说:结算靠链上,体验靠 Gamma。
2. Gamma REST API 的定位
- 只读公开接口:面向公众开放,无需认证(对普通用户)
- 用途广泛 ,适用于:
- 非营利性研究项目
- 第三方交易平台 / 替代前端
- 自动化交易机器人
- 数据分析与可视化工具
3. API 端点
- 基础 URL:
https://gamma-api.polymarket.com
Gamma Structure
✅ 核心内容总结
1. 基本组成单元:Market(市场)
- Market 是最基础、最核心的元素。
- 每个 Market 代表一个可交易的预测问题,例如:"Will Barron attend Harvard?"
- 技术上,一个 Market 对应:
- 一对 CLOB(中央限价订单簿)的 token IDs
- 一个市场合约地址(market address)
- 一个问题 ID(question ID)
- 一个条件 ID(condition ID,通常来自预言机如 UMA)
简言之:每个 Market = 一个具体的"是/否"或"多选"预测合约。
2. 组织容器:Event(事件)
- Event 是一组相关 Markets 的集合,用于逻辑归类。
- 所有 Market 都属于某个 Event,但 Event 本身不直接交易。
- 两种常见变体 :
- 单市场事件(SMP, Single-Market Prediction) :
一个 Event 只包含 1 个 Market(例如一个简单的二元问题)。 - 多市场事件(GMP, Grouped-Market Prediction) :
一个 Event 包含 ≥2 个互斥或相关的 Markets(通常构成一个完整预测集)。
- 单市场事件(SMP, Single-Market Prediction) :
📌 示例说明
以问题 "Where will Barron Trump attend College?" 为例:
- 这是一个 Event
- 它包含多个互斥的 Markets :
- "Will Barron attend Georgetown?"
- "Will Barron attend NYU?"
- "Will Barron attend UPenn?"
- "Will Barron attend Harvard?"
- "Will Barron attend another college?"
这些 Markets 共同覆盖所有可能结果,构成一个典型的 GMP(多市场事件)。
🔑 关键要点
- Market 是交易和结算的基本单位。
- Event 是 UI/UX 和数据组织的逻辑分组,帮助用户理解上下文。
- Gamma 通过这种层级结构,使复杂预测市场更易浏览、分析和集成。
How to Fetch Markets
✅ 核心内容总结
🎯 三大获取策略(按使用场景划分)
| 策略 | 适用场景 | 推荐接口 | 优势 |
|---|---|---|---|
| 1. 按 Slug 获取 | 已知具体市场或事件(如从 URL 得到) | GET /events/slug/{slug}``GET /markets/slug/{slug} |
最精准、高效,直接命中目标 |
| 2. 按标签(Tags)过滤 | 按类别、体育项目、主题筛选(如"NFL"、"政治") | GET /markets?tag_id=xxx``GET /events?tag_id=xxx |
灵活分类浏览,支持多维筛选 |
| 3. 获取所有活跃市场 | 全量市场发现、分析、监控 | GET /events?closed=false&order=id... |
最完整覆盖,通过 Event 关联 Market |
🔍 详细说明
1. 按 Slug 获取(精准查询)
- Slug 来源 :Polymarket 前端 URL 中
/event/或/market/后的路径
例:https://polymarket.com/event/fed-decision-in-october→ slug =fed-decision-in-october - 用途 :当你已经知道某个市场的唯一标识时(如用户分享链接),这是最快、最可靠的方式
2. 按 Tags 过滤(分类查询)
-
先发现标签 :
- 通用标签:
GET /tags - 体育标签及元数据:
GET /sports(含图片、赛程、解析源等)
- 通用标签:
-
再用标签查询 :
bashcurl "https://gamma-api.polymarket.com/events?tag_id=100381&closed=false" -
高级过滤 :
related_tags=true:包含相关标签的市场exclude_tag_id=xxx:排除某些标签
3. 获取所有活跃市场(全量抓取)
- 为什么用
/events而不是/markets?
因为 Event 是 Market 的容器,一个 Event 可包含多个 Markets(如多选题),通过 Event 能更完整地组织数据。 - 关键参数组合 :
closed=false:只获取未结算 的活跃市场(强烈建议默认加上)order=id&ascending=false:按 ID 降序 → 最新市场优先limit+offset:分页控制
📖 分页机制(Pagination)
-
所有列表接口(
/events,/markets)均支持分页 -
使用
limit(每页数量)和offset(起始偏移)实现翻页 -
示例 :
bash# 第1页(0-49) ?limit=50&offset=0 # 第2页(50-99) ?limit=50&offset=50
⚠️ 注意:不要一次性请求大量数据,应循环分页获取。
🛠️ 最佳实践(Best Practices)
| 场景 | 建议 |
|---|---|
| 获取单个市场 | ✅ 优先用 slug |
| 浏览某类市场 | ✅ 用 tag_id 过滤 |
| 全量市场同步 | ✅ 用 /events + 分页 + closed=false |
| 避免无效数据 | ✅ 默认加 closed=false(除非需要历史数据) |
| 生产环境 | ✅ 实现 速率限制(rate limiting),避免被限流 |
🔗 补充资源
/tags:获取所有可用标签/sports:获取体育赛事专属标签与元数据- 搜索功能 :另有
Search Markets接口(未展开)
Subgraph
Polymarket 将链上数据通过开源的 The Graph 子图 进行索引和聚合,并通过 GraphQL 接口提供结构化、实时的市场与用户数据,供开发者查询使用。
✅ 核心要点总结
1. 什么是 Subgraph?
- 是基于 The Graph 协议构建的数据索引服务。
- 从 Polymarket 的智能合约中实时监听并处理链上事件(如订单、交易、持仓变化等)。
- 将原始链上数据转化为可查询的结构化模型(如用户持仓、市场成交量、活动历史等)。
2. 功能与用途
- 提供 聚合计算 和 历史事件索引 ,包括:
- 市场交易量(Volume)
- 用户持仓(Positions)
- 订单簿数据(Orders)
- 活动记录(Activity)
- 未平仓合约(Open Interest)
- 盈亏(PNL, Profit and Loss)
- 数据实时更新,与 Polymarket 主站前端展示的数据保持一致。
- 可用于构建:
- 第三方分析仪表盘
- 交易机器人
- 钱包集成
- 研究工具
3. 开源与托管
- 完全开源 :代码托管在 Polymarket GitHub,数据模型定义在
schema.graphql文件中。 - 公共托管 :由第三方服务商 Goldsky 提供免费、公开的 GraphQL 查询端点(无需自建节点)。
4. 可用的子图列表(Hosted on Goldsky)
每个子图聚焦不同维度的数据:
| 子图名称 | 功能 | 公共查询地址 |
|---|---|---|
| Orderbook Subgraph | 订单簿、挂单数据 | .../orderbook-subgraph/0.0.1/gn |
| Positions Subgraph | 用户当前持仓 | .../positions-subgraph/0.0.7/gn |
| Activity Subgraph | 用户/市场活动历史(交易、创建等) | .../activity-subgraph/0.0.4/gn |
| Open Interest (OI) Subgraph | 市场未平仓量 | .../oi-subgraph/0.0.6/gn |
| PNL Subgraph | 用户盈亏计算 | .../pnl-subgraph/0.0.14/gn |
所有链接均附带 GraphQL Playground,可直接在浏览器中编写和测试查询。
Resolution
✅ 核心内容总结
1. 结算机制基础:UMA Optimistic Oracle(OO)
- Polymarket 使用 UMA 的 Optimistic Oracle 作为其市场结果的去中心化裁决系统。
- OO 的核心逻辑是 "乐观假设 + 争议挑战" :
- 任何人都可对某个问题提出一个结果(称为"报价",price proposal),并质押一笔保证金(
proposalBond)。 - 如果 无人在"存活期"(liveness period)内提出异议,该结果自动生效。
- 如果 有人质疑 ,则触发 争议(Dispute) ,问题被提交给 UMA 的 DVM(Data Verification Mechanism) ------ 由 UMA 代币持有者投票决定最终答案。
- DVM 投票通常需要 几天时间,但作为最终仲裁保障。
- 任何人都可对某个问题提出一个结果(称为"报价",price proposal),并质押一笔保证金(
💡 这是一种 低成本、高效率、抗女巫攻击 的预言机设计:大多数情况无需投票,仅在有争议时才启动昂贵的链上治理。
2. Polymarket 的适配器:UmaCtfAdapter
- 由于 Polymarket 的 CTF 市场与 UMA OO 的接口不直接兼容,Polymarket 开发了 自定义智能合约
UmaCtfAdapter作为桥梁。 - 该适配器负责:
- 初始化市场(调用
initialize()) - 向 OO 提交问题(含
ancillaryData) - 处理奖励、保证金、存活期等参数
- 在 OO 返回结果后,更新 Polymarket 市场状态
- 初始化市场(调用
3. 关键概念:Ancillary Data 与 Clarifications(澄清)
- 每个市场的问题文本和上下文存储在
ancillaryData中。 - v2+ 版本的适配器支持"公告板"功能 :
- 市场创建者可在结算前发布澄清说明(clarifications),例如修正歧义或提供额外上下文。
- 如果允许澄清,
ancillaryData会包含类似语句: "Updates made by the question creator via the bulletin board on [地址] should be considered..." - 澄清不能改变问题原意,仅用于消除模糊性。
4. 结算流程(Resolution Flow)
整个过程由以下动作组成:
| 步骤 | 说明 |
|---|---|
| Initialize | 创建市场,设置问题、奖励、保证金、存活期等,获得 questionID |
| Propose Price | 任何人可提交结果(如 "1" 表示"是"),并质押 proposalBond |
| Dispute(可选) | 若有人反对,可发起争议,需质押反向保证金 |
🔄 三种可能的流程路径:
-
无争议快速结算
Initialize → Propose → Resolve(最常见,高效)
-
一次争议后结算
Initialize → Propose → Dispute → Propose → Resolve(第一次提案被挑战,第二次提案无人反对)
-
两次争议,进入 DVM 投票
Initialize → Propose → Dispute → Propose → Dispute → Resolve (via DVM)(最终由 UMA 持币者投票裁决)
🔒 设计优势:
- 防止"垃圾提案"拖延结算(第一次离谱提案可被挑战,不影响整体进度)
- 提高恶意攻击成本(需连续两次发起争议才能触发 DVM)
5. 已部署合约地址(Polygon 主网)
| 版本 | 地址 |
|---|---|
| v3.0 | 0x157Ce2d672854c848c9b79C49a8Cc6cc89176a49 |
| v2.0 | 0x6A9D0222186C0FceA7547534cC13c3CFd9b7b6A4 |
| v1.0 | 0xC8B122858a4EF82C2d4eE2E6A276C719e6929951 |
⚠️ 注意:v2+ 支持澄清功能,v1 不支持。
6. 附加资源
- 审计报告(Audit)
- 智能合约源代码(Source Code)
- UMA 官方文档与 Oracle Portal(用于查询提案/争议状态)