Files
my-polymarket-copytrader/polymarket_api_research.md

389 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` — 市场唯一条件 ID0x + 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` | 1500,默认 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_idasset_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 APICloudflare 前置):
- 同一 IP **≤30 req/s** 持续 → 安全
- **100+ req/s** 突发 → 偶发 429(重试可恢复)
- **500 req/s** 持续 → 限流 / 封禁 1060s
- 响应头 `Cache-Control` 通常 3060s
- 推荐:单 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`:元数据 + 内嵌 marketslimit 上限 500offset
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` 获取。