389 lines
16 KiB
Markdown
389 lines
16 KiB
Markdown
# Polymarket API 完整调研报告
|
||
|
||
> 数据来源:官方文档 https://docs.polymarket.com/、Polymarket/agent-skills GitHub、polymarkets.co.il 教程 (2026-07)
|
||
> 调研日期:2026-07-13
|
||
|
||
---
|
||
|
||
## 0. 总体架构
|
||
|
||
Polymarket 将数据拆分为 **4 个 REST API + 3 个 WebSocket 频道**,前 3 个完全公开(无需 API Key):
|
||
|
||
| API | Base URL | 鉴权 | 用途 |
|
||
|---|---|---|---|
|
||
| **Gamma API** | `https://gamma-api.polymarket.com` | 公开 | 市场元数据、发现、搜索、标签 |
|
||
| **CLOB API** | `https://clob.polymarket.com` | 读公开 / 写需 L2 | 订单簿、价格、深度、历史价 |
|
||
| **Data API** | `https://data-api.polymarket.com` | 公开 | 用户持仓、交易、活跃度、Holder、OI |
|
||
| **Bridge API** | `https://bridge.polymarket.com` | 钱包 | 充提(fun.xyz 代理) |
|
||
| **RTDS WebSocket** | `wss://ws-subscriptions-clob.polymarket.com/ws/{market,user}` + `wss://sports-api.polymarket.com/ws` | 公开 / 鉴权 | 实时订单簿、成交、用户事件 |
|
||
|
||
官方 SDK:**Python (`py-clob-client`)**、**TypeScript (`@polymarket/clob-client`)**、**Rust (`rs-clob-client`)**。第三方封装很多(`polymarket-pandas`、`polybricks-sdk`、`almanak-sdk` 等)。
|
||
|
||
---
|
||
|
||
## 1. 数据模型(核心概念)
|
||
|
||
```
|
||
Event(事件) ──┬── Market A ── Yes token / No token (条件 ID = conditionId)
|
||
├── Market B ── Yes token / No token
|
||
└── Market N
|
||
```
|
||
|
||
- **Event**:顶级问题(如 "2024 大选谁赢?")。每个 event 含 ≥1 个 market。
|
||
- **Market**:实际可交易的二元/多元合约,对应一对 ERC-1155 `token_id`。
|
||
- **核心 ID**:
|
||
- `conditionId` — 市场唯一条件 ID(0x + 64 hex)
|
||
- `questionID` — UMA 解析用的哈希
|
||
- `tokenID / asset_id` — Yes/No 各自的 ERC-1155 token ID(**下单/查订单簿都用它**)
|
||
- `slug` — URL 友好标识
|
||
- **价格含义**:`outcomePrices` 是隐含概率,二元市场 `[Yes, No]` 数值相加 ≈ 1。
|
||
- **市场类型**:`negRisk=true` → 多结果互斥事件;`enableOrderBook=true` → 可在 CLOB 交易。
|
||
|
||
---
|
||
|
||
## 2. Gamma API — 市场发现与元数据
|
||
|
||
> 无需鉴权,REST。
|
||
|
||
### 2.1 端点列表
|
||
|
||
| 端点 | 用途 |
|
||
|---|---|
|
||
| `GET /events` | 列出 events(带过滤、分页) |
|
||
| `GET /events/{id}` | 单个 event |
|
||
| `GET /events/keyset` | keyset 分页版本(用于游标分页) |
|
||
| `GET /markets` | 列出 markets |
|
||
| `GET /markets/{id}` | 单个 market |
|
||
| `GET /public-search` | 全文搜索 events/markets/profiles |
|
||
| `GET /tags` | 标签(分类)列表 |
|
||
| `GET /series` | 系列(分组 event) |
|
||
| `GET /sports` | 体育元数据 |
|
||
| `GET /teams` | 球队 |
|
||
| `GET /comments` | 评论(部分端点) |
|
||
|
||
### 2.2 `/events` 关键过滤参数
|
||
|
||
| 参数 | 说明 |
|
||
|---|---|
|
||
| `slug` | URL slug 精确匹配 |
|
||
| `id` | event id |
|
||
| `tag_id` | 标签 ID(可多次传入,支持 `related_tags=true`) |
|
||
| `tag_slug` | slug 形式过滤 |
|
||
| `exclude_tag_id` | 排除的标签 |
|
||
| `series_id` | 系列 ID(体育赛事分组) |
|
||
| `active` | 是否上架 |
|
||
| `closed` | 是否已结算(默认 `false`) |
|
||
| `archived` | 是否归档 |
|
||
| `new`, `featured`, `restricted` | 状态布尔 |
|
||
| `live` | 是否 live |
|
||
| `order` | `volume_24hr` / `volume` / `liquidity` / `start_date` / `end_date` / `competitive` / `closed_time` |
|
||
| `ascending` | `true` / `false` |
|
||
| `limit` | 1–500,默认 20 |
|
||
| `offset` | 分页偏移 |
|
||
| `include_chat`, `include_template` | 是否包含聊天/模板字段 |
|
||
|
||
示例:
|
||
```bash
|
||
# 24h 成交量最高的活跃事件
|
||
curl "https://gamma-api.polymarket.com/events?active=true&closed=false&order=volume24hr&ascending=false&limit=100"
|
||
|
||
# NBA 标签下的活动
|
||
curl "https://gamma-api.polymarket.com/events?tag_id=745&active=true&closed=false"
|
||
```
|
||
|
||
### 2.3 `/markets` 关键过滤参数
|
||
|
||
`/markets` 支持几乎所有 `/events` 的参数,外加:
|
||
|
||
| 参数 | 说明 |
|
||
|---|---|
|
||
| `condition_ids` / `clob_token_ids` / `question_ids` | 用 ID 精确匹配 |
|
||
| `slug` | URL slug |
|
||
| `archived`, `featured`, `new`, `restricted`, `closed`, `active` | 状态 |
|
||
| `order` / `ascending` | 排序 |
|
||
| `liquidity_num_min`, `volume_num_min` | 数值范围过滤 |
|
||
| `end_date_min`, `end_date_max` | 截止日期范围 |
|
||
| `limit` (≤500) / `offset` | 分页 |
|
||
|
||
### 2.4 `/tags` 常用分类与 ID
|
||
|
||
| Tag | ID |
|
||
|---|---|
|
||
| Sports(运动) | 1 |
|
||
| Politics(政治) | 2 |
|
||
| Election(选举) | 3 |
|
||
| Crypto(加密货币) | 21 |
|
||
| Bitcoin(比特币) | 100196 |
|
||
| Ethereum(以太坊) | 100383 |
|
||
| NFL | 450 |
|
||
| NBA | 745 |
|
||
| Tennis | 864 |
|
||
| Esports | 702 |
|
||
| Soccer(足球) | 1059 |
|
||
| EPL | 739 |
|
||
| UCL | 2186 |
|
||
| Middle East | 1432 |
|
||
|
||
⚠️ 教程建议生产环境只用数值 `tag_id`,`tag_slug` 和 `?q=...` 不稳定。
|
||
|
||
---
|
||
|
||
## 3. CLOB API — 价格与订单簿
|
||
|
||
> 读端点公开,写端点(挂单/撤单)需 L2 钱包签名鉴权。
|
||
|
||
### 3.1 公共只读端点
|
||
|
||
| 端点 | 说明 |
|
||
|---|---|
|
||
| `GET /markets` | 列出 CLOB markets(**注意与 Gamma 的 `/markets` 不同**,是简化版) |
|
||
| `GET /markets/{conditionId}` | 单个 market 详情 |
|
||
| `GET /book?token_id=...` | 某 token 的订单簿快照 |
|
||
| `POST /books` | 批量订单簿(≤500 token) |
|
||
| `GET /price?token_id=...&side=BUY\|SELL` | 买/卖最优价 |
|
||
| `POST /prices` | 批量最优价 |
|
||
| `GET /midpoint?token_id=...` | 中间价 |
|
||
| `POST /midpoints` | 批量中间价 |
|
||
| `GET /spread?token_id=...` | 价差 |
|
||
| `POST /spreads` | 批量价差 |
|
||
| `GET /last-trade-price?token_id=...` | 最近成交价 |
|
||
| `POST /last-trades-prices` | 批量最近成交 |
|
||
| `GET /prices-history?market=...` | **历史价格** |
|
||
| `POST /prices-history` | 批量历史价格 |
|
||
|
||
### 3.2 历史价格 `/prices-history` 参数
|
||
|
||
| 参数 | 必填 | 说明 |
|
||
|---|---|---|
|
||
| `market` | ✅ | token_id(asset_id) |
|
||
| `startTs` | ❌ | Unix 秒(与 `interval` 互斥) |
|
||
| `endTs` | ❌ | Unix 秒 |
|
||
| `interval` | ❌ | `max` / `all` / `1m` / `1w` / `1d` / `6h` / `1h` |
|
||
| `fidelity` | ❌ | 精度(分钟),默认 1 |
|
||
|
||
返回 `[{ "t": timestamp, "p": price }, ...]`。
|
||
|
||
### 3.3 `/markets` (CLOB) 参数
|
||
|
||
- 支持 `next_cursor` 游标分页(新版上限 limit=100,需用 `after_cursor`)
|
||
- 过滤字段:`active`, `closed`, `archived`, `condition_ids`, `clob_token_ids`, `question_ids`, `slug`, `tags`, `order`, `ascending`
|
||
|
||
### 3.4 鉴权写入端点(L2)
|
||
|
||
挂单、撤单、查用户订单、心跳、API key 创建等。需用 `POLY_API_KEY / POLY_ADDRESS / POLY_SIGNATURE / POLY_PASSPHRASE / POLY_TIMESTAMP` 头部 HMAC 签名。
|
||
|
||
---
|
||
|
||
## 4. Data API — 持仓、交易、Holder、OI
|
||
|
||
> 全部公开。
|
||
|
||
### 4.1 端点列表
|
||
|
||
| 端点 | 关键参数 |
|
||
|---|---|
|
||
| `GET /positions` | `user` (0x 地址, 必填), `market` (conditionId 数组), `eventId`, `sizeThreshold`, `redeemable`, `mergeable`, `limit` (≤500), `offset` (≤10000), `sortBy` (`CURRENT`/`INITIAL`/`TOKENS`/`CASHPNL`/`PERCENTPNL`/`TITLE`/`RESOLVING`/`PRICE`/`AVGPRICE`), `sortDirection` (`ASC`/`DESC`), `title` |
|
||
| `GET /closed-positions` | `user` (必填), `market[]`, `eventId[]`, `title`, `limit` (≤50), `offset` (≤100000), `sortBy` (`REALIZEDPNL`/`TITLE`/`PRICE`/`AVGPRICE`/`TIMESTAMP`), `sortDirection` |
|
||
| `GET /activity` | `user` (必填), `market[]`, `eventId[]`, `type` (`TRADE`/`SPLIT`/`MERGE`/`REDEEM`/`REWARD`/`CONVERSION`/`DEPOSIT`/`WITHDRAWAL`/`YIELD`/`MAKER_REBATE`/`TAKER_REBATE`/`REFERRAL_REWARD`), `side` (`BUY`/`SELL`), `start`/`end` Unix 秒, `sortBy` (`TIMESTAMP`/`TOKENS`/`CASH`), `sortDirection`, `limit` (≤500), `offset` |
|
||
| `GET /v1/market-positions` | **按市场查 top 持仓人**:`market` (conditionId, 必填), `user`, `status` (`OPEN`/`CLOSED`/`ALL`), `sortBy` (`TOKENS`/`CASH_PNL`/`REALIZED_PNL`/`TOTAL_PNL`), `sortDirection`, `limit` (≤500), `offset` |
|
||
| `GET /value?user=...` | 用户所有持仓的总价值(美元) |
|
||
| `GET /oi` | 全市场未平仓量(按市场聚合) |
|
||
| `GET /holders?market={conditionId}` | 某市场的 top 持仓人 |
|
||
| `GET /trades` | 全市场成交流(见下) |
|
||
|
||
> **⚠️ 踩坑笔记(`/closed-positions`)**
|
||
>
|
||
> 1. **`limit` 上限是 50,不是 100**。服务端会忽略超过 50 的请求或截断,导致 PnL/信誉分计算漏数据。`data_api.py:get_closed_positions` 已用 `min(limit, 50)` 防御性 clamp。
|
||
> 2. **默认 `sortBy=REALIZEDPNL` 是个坑**:返回的是"最赚/最亏的 50 条",而不是"最近的 50 条"。如果直接按 `timestamp >= cutoff` 在 Python 端过滤,窗口内的平仓可能根本不在前 50 盈亏里 → 静默漏数。正确做法:用 `sortBy=TIMESTAMP` + `sortDirection=DESC` + `offset` 翻页,直到拿到 `ts < cutoff` 的记录就停。封装见 `data_api.py:get_closed_positions_since`。
|
||
>
|
||
> 本项目曾因这两个问题导致 PnL 和信誉分偏小,已在 `wallet_pool.py` / `bayesian.py` 修复。
|
||
|
||
> **📝 `/markets` 端点用法(信号结果回填)**
|
||
>
|
||
> 信号回填服务(`outcome_resolver.py`)调 Gamma `/markets?condition_id=...` 拉市场结算结果。关键字段(按官方 `/markets/{id}` schema):
|
||
>
|
||
> - `closed` (bool):市场是否已关闭/结算
|
||
> - `closedTime` (str):结算时间
|
||
> - `outcomes` (str):JSON 数组,如 `'["Yes", "No"]'`
|
||
> - `outcomePrices` (str):JSON 数组,结算后赢家价格 = 1,输家 = 0
|
||
>
|
||
> 判定逻辑:`closed=true` 且 `outcomePrices` 中某 outcome 价格 ≥ 0.99 即为赢家。PnL 模型:win = `S*(1-p)/p`,loss = `-S`(理论全仓执行,不建模滑点)。
|
||
|
||
### 4.2 `/trades`(成交历史)参数
|
||
|
||
| 参数 | 说明 |
|
||
|---|---|
|
||
| `market` | conditionId |
|
||
| `asset_id` | token ID |
|
||
| `maker_address` | 必填,按 maker 过滤 |
|
||
| `before`, `after` | Unix 秒时间窗 |
|
||
| `next_cursor` | base64 游标分页 |
|
||
| `id` | 单笔 trade ID |
|
||
|
||
鉴权:需 L2 API key 头。返回字段包括 `taker_order_id`、`asset_id`、`side`、`size`、`fee_rate_bps`、`price`、`status`、`match_time`、`outcome`、`owner`、`maker_address`、`transaction_hash`、`trader_side`、`maker_orders[]`。
|
||
|
||
### 4.3 返回字段(持仓示例)
|
||
|
||
```
|
||
proxyWallet, asset, conditionId, size, avgPrice, initialValue, currentValue,
|
||
cashPnl, percentPnl, totalBought, realizedPnl, percentRealizedPnl, curPrice,
|
||
redeemable, mergeable, title, slug, icon, eventSlug, outcome, outcomeIndex,
|
||
oppositeOutcome, oppositeAsset, endDate, negativeRisk
|
||
```
|
||
|
||
### 4.4 其他 Data 端点
|
||
|
||
- `GET /leaderboard` — 排行榜
|
||
- `GET /profiles/{address}` — 公开档案
|
||
- `GET /comments/by-user/{address}` — 用户评论
|
||
- `GET /builder-leaderboard` — builder 排行榜
|
||
- `GET /v1/accounting/snapshot` — 会计快照下载(ZIP/CSV)
|
||
- `GET /v1/positions/combos`, `/v1/activity/combos` — 组合仓
|
||
|
||
---
|
||
|
||
## 5. WebSocket — 实时数据
|
||
|
||
| 频道 | Endpoint | 鉴权 | 订阅方式 |
|
||
|---|---|---|---|
|
||
| Market | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | 否 | 按 `assets_ids[]`(token_id) |
|
||
| User | `wss://ws-subscriptions-clob.polymarket.com/ws/user` | **是** | 按 `markets[]`(conditionId),省略=全部 |
|
||
| Sports | `wss://sports-api.polymarket.com/ws` | 否 | 无需订阅 |
|
||
|
||
### 5.1 Market 频道事件类型
|
||
|
||
| 事件 | 触发 |
|
||
|---|---|
|
||
| `book` | 订阅时 + 影响订单簿的成交 |
|
||
| `price_change` | 挂单/撤单 |
|
||
| `last_trade_price` | 成交 |
|
||
| `tick_size_change` | 价格越过 0.96 / 0.04(**做市必听**) |
|
||
| `best_bid_ask` | 需 `custom_feature_enabled=true` |
|
||
| `new_market` | 新市场创建 |
|
||
| `market_resolved` | 结算 |
|
||
|
||
订阅消息:
|
||
```json
|
||
{"assets_ids":["TOKEN_ID"], "type":"market", "custom_feature_enabled":true}
|
||
```
|
||
|
||
### 5.2 User 频道事件
|
||
|
||
- `trade` — 成交生命周期(MATCHED / MINED / CONFIRMED / RETRYING / FAILED)
|
||
- `order` — 订单生命周期(PLACEMENT / UPDATE / CANCELLATION)
|
||
|
||
### 5.3 心跳
|
||
|
||
- Market/User:**客户端每 10 秒发 `PING`**
|
||
- Sports:**服务端每 5 秒发 `ping`,客户端必须在 10 秒内回 `pong`**,否则断连
|
||
|
||
---
|
||
|
||
## 6. 子图 / 链上数据(Goldsky Subgraph)
|
||
|
||
通过 GraphQL 查询 Polymarket on-chain 数据:
|
||
|
||
| 子图 | 内容 |
|
||
|---|---|
|
||
| Positions | 用户 token 余额 |
|
||
| Orders | 订单簿和成交事件 |
|
||
| Activity | Split / Merge / Redeem |
|
||
| OpenInterest | 市场 OI |
|
||
| PNL | 用户盈亏 |
|
||
|
||
端点(Goldsky 托管):
|
||
```
|
||
https://api.goldsky.com/api/public/project_cl6mb8i9h0003e201j6li0diw/subgraphs/orderbook-subgraph/0.0.1/gn
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 速率限制 & 缓存建议
|
||
|
||
- Gamma / Data API(Cloudflare 前置):
|
||
- 同一 IP **≤30 req/s** 持续 → 安全
|
||
- **100+ req/s** 突发 → 偶发 429(重试可恢复)
|
||
- **500 req/s** 持续 → 限流 / 封禁 10–60s
|
||
- 响应头 `Cache-Control` 通常 30–60s
|
||
- 推荐:单 fetcher 进程 + 本地 LRU 缓存(30s TTL),多个消费者读取本地副本
|
||
- 大量拉取时建议加 1 fetcher × N consumers,避免多 fetcher 抢配额
|
||
|
||
---
|
||
|
||
## 8. 常用查询模式速查
|
||
|
||
### 8.1 找"现在最热"的市场
|
||
```bash
|
||
curl "https://gamma-api.polymarket.com/events?active=true&closed=false&order=volume24hr&ascending=false&limit=100"
|
||
```
|
||
|
||
### 8.2 查某个分类所有活跃市场
|
||
```bash
|
||
# 政治类(Politics tag = 2)
|
||
curl "https://gamma-api.polymarket.com/events?tag_id=2&active=true&closed=false&order=volume&ascending=false"
|
||
```
|
||
|
||
### 8.3 拿单个市场的实时报价
|
||
```bash
|
||
# 1. 用 slug 查 conditionId / clobTokenIds
|
||
curl "https://gamma-api.polymarket.com/events?slug=fed-decision-in-october"
|
||
# 2. 用 token_id 查订单簿
|
||
curl "https://clob.polymarket.com/book?token_id=<TOKEN_ID>"
|
||
# 3. 查历史价
|
||
curl "https://clob.polymarket.com/prices-history?market=<TOKEN_ID>&interval=1d&fidelity=60"
|
||
```
|
||
|
||
### 8.4 监控一个鲸鱼地址
|
||
```bash
|
||
# 当前持仓
|
||
curl "https://data-api.polymarket.com/positions?user=0x...&sizeThreshold=1&limit=500&sortBy=CURRENT"
|
||
# 历史成交
|
||
curl "https://data-api.polymarket.com/activity?user=0x...&type=TRADE&limit=500"
|
||
# 订阅 User WebSocket 实时 trade/order
|
||
```
|
||
|
||
### 8.5 找热门市场的 top 持有人
|
||
```bash
|
||
curl "https://data-api.polymarket.com/v1/market-positions?market=<CONDITION_ID>&status=OPEN&sortBy=TOTAL_PNL&sortDirection=DESC&limit=50"
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 注意事项 / 坑
|
||
|
||
1. **Gamma 的 `outcomes` / `outcomePrices` / `clobTokenIds` 字段是 JSON 字符串而非数组**,需 `json.loads()` 再用。
|
||
2. **不要用 `?q=...` 搜索**:`/events` 不支持自由文本,文档显式说"silently returns default ordering",请用 `/public-search` 或 slug。
|
||
3. **`tag_slug` 不稳定**:教程建议生产环境只用数值 `tag_id`。
|
||
4. **排序与标签同时过滤可能返回空**:tag 下市场太少 + `volume24hr desc` 时会空,需要 over-fetch 客户端再过滤。
|
||
5. **WebSocket 必须立刻发订阅**:连接后未立刻发订阅消息会被立刻断开。
|
||
6. **CLOB 价格用 token_id 不是 slug**:先 Gamma 拿 `clobTokenIds` 再查 CLOB。
|
||
7. **WSS 心跳**:Market/User 是**客户端发 PING**;Sports 是**服务端发 ping**。两者别搞反。
|
||
8. **`tick_size_change` 是做市关键事件**:tick 改了还用旧值会被拒单。
|
||
9. **`/markets` 在 CLOB 与 Gamma 两个端点都有**,含义不同:
|
||
- `clob.polymarket.com/markets`:交易用的简化市场(limit 上限 100,需游标)
|
||
- `gamma-api.polymarket.com/markets`:元数据 + 内嵌 markets(limit 上限 500,offset)
|
||
10. **地理限制**:某些地区会被 Cloudflare 屏蔽,必要时用代理。
|
||
|
||
---
|
||
|
||
## 10. 常用分类/标签 ID 速查
|
||
|
||
| 分类 | Tag ID |
|
||
|---|---|
|
||
| Sports | 1 |
|
||
| Politics | 2 |
|
||
| Election | 3 |
|
||
| Crypto | 21 |
|
||
| Bitcoin | 100196 |
|
||
| Ethereum | 100383 |
|
||
| Pop Culture / Entertainment | (通过 `/tags` 动态查询) |
|
||
| Middle East | 1432 |
|
||
| NBA | 745 |
|
||
| NFL | 450 |
|
||
| Tennis | 864 |
|
||
| Esports | 702 |
|
||
| Soccer | 1059 |
|
||
| EPL | 739 |
|
||
| UCL | 2186 |
|
||
|
||
完整列表通过 `GET https://gamma-api.polymarket.com/tags?limit=500` 获取。 |