本目录可直接
git init后发布到 GitHub —— 里面只有源码、示例与文档, 不含任何密钥、凭证或私有地址,也不依赖本机环境。
一句话:这是 FinHub API 提供的 Polymarket 5 分钟涨跌盘(up/down)结算输入数据的官方 Python 客户端 —— 给你官方 Chainlink TWAP60 + 多家现货报价 + CLOB 盘口,约 2 秒一条, 覆盖 7 个市场,零第三方依赖。
接口文档:https://api.wanminguo.top/quant/polymarket/docs.php · 端点清单:https://api.wanminguo.top/quant/polymarket/endpoints.php
- 这是什么
- 7 个市场
- 快速开始
- 为什么
price_to_beat必须用官方 Chainlink 读数 - 端点表
- 响应信封
- 错误码表
- 套餐与限额
- 客户端用法
- 信号订阅(另一条产品线)
- 因子与信号目录
- 示例脚本
- 常见问题
- English
Polymarket 上的 5 分钟涨跌盘结算时,看的是 「Chainlink 提供的 TWAP 与区间起点价格的比较」,不看任何现货市场。 所以做这类盘,你真正需要的输入只有三件事:
- 区间起点价格(price to beat)—— 而且必须是官方 Chainlink 读数;
- 区间内的官方 TWAP60 现值 —— 它相对起点的 bp 就是结算方向的直接输入;
- 多家现货报价与盘口 —— 用来判断「官方会不会跟过来」以及市场怎么定价。
自己凑这三件事很痛苦:要分别对接 Chainlink 数据流、六家交易所的行情、 Polymarket 的 CLOB,还要把观测时刻对齐到同一拍上。
本 API 把这些直接给你,以约 2 秒一条的节奏,7 个市场全覆盖:
| 你需要的 | 接口字段 |
|---|---|
| 区间起点(官方) | price_to_beat + beat_source + beat_trusted |
| 官方现值距起点的 bp | official_bp |
| 现货中位距起点的 bp | spot_bp |
| 60 秒动量距起点的 bp | momentum_bp |
| 各家现货分别的 bp | venue_bp(spot_n_sources 是这一拍实际取到的家数) |
| 盘口与隐含概率 | implied_up / yes_bid / yes_ask / no_bid / no_ask / liquidity |
| 现货与官方 TWAP60 之间的基差 | spot_basis_bp |
| 结算核对 | /v1/settle.php 的 outcome + 四条规则 rules.so/sc/to/tc(可自己复算) |
数据是结算输入,不是交易建议;接口不托管资金、不需要下单权限。
?market= 短名,默认 btc:
?market= |
slug 前缀 | 现货源数 | 官方 Chainlink TWAP60 |
|---|---|---|---|
btc(默认) |
btc-updown-5m |
6 | ✅ |
eth |
eth-updown-5m |
6 | ✅ |
sol |
sol-updown-5m |
6 | ✅ |
xrp |
xrp-updown-5m |
6 | ✅ |
doge |
doge-updown-5m |
5 | ✅ |
bnb |
bnb-updown-5m |
5 | ✅ |
hype |
hype-updown-5m |
4(无 coinbase / kraken) | ✅ |
market 参数认四种写法,都归一化到同一个前缀:
?market=eth ?market=ETH ?market=ETH-5m ?market=eth-updown-5m
非法值返回 400 bad_market,响应体里带 markets 数组列出全部可用短名。
现货源数不是恒定的 6。 真实家数看响应里的
spot_n_sources;venue_bp里出现的键才是这一拍真正取到的源。HYPE 只有 4 家 —— 做「多源中位」类信号时请按实际家数判断,不要写死 6。官方读数 7 个市场都有,所以
price_to_beat的beat_source正常都是official;少数窗口官方流没进来时会退化成computed,用beat_trusted判断。
打开 https://api.wanminguo.top/me/register.php,
免费档(free,0 元)当场就能取当前窗口的完整数据(含 2 秒序列),
不需要付费就能验证这份数据是否对你有用。
# Linux / macOS
export PM_API_KEY=pm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Windows (cmd)
set PM_API_KEY=pm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Windows (PowerShell)
$env:PM_API_KEY = "pm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"本仓库不含任何 key,也请不要把你的 key 提交进 Git。
curl -H "X-Api-Key: $PM_API_KEY" \
'https://api.wanminguo.top/quant/polymarket/v1/window.php?market=btc'# 直接跑仓库里的示例,不需要 pip install 任何东西
python examples/current_window.pyfrom pm_api_client import PmApi
api = PmApi() # 读环境变量 PM_API_KEY
data, meta = api.window(market='btc') # 当前窗口快照
print(data.price_to_beat) # 区间起点(官方 Chainlink 读数)
print(data.official_bp) # 官方 TWAP60 距起点多少 bp —— 结算方向的直接输入
print(data.spot_bp) # 六家现货中位距起点多少 bp
print(data.leading_side) # UP / DOWN / FLAT
# ★ 必须检查这个标记
if data.beat_trusted is False:
# 该窗口官方读数没进来,price_to_beat 退化成自算值(系统性偏约 3bp)
# —— 此时不要用它判方向
pass
print(meta.remaining) # 剩余配额,低于 10% 就该降频或者用 curl / 任意语言直接打 HTTP —— 这就是个普通的 JSON API。
这是本 API 最核心的卖点,请务必读完这一节。
Polymarket 的市场规则原文写的是:
the TWAP of Bitcoin, generated by Chainlink, of the time range specified in the title … greater than or equal to the price at the beginning of that range … not according to any other sources or spot markets
也就是说:锚点必须是官方读数,现货市场的价格不算数。
于是问题来了 —— 数据里存在两个看起来都像「起点值」的数,语义却完全不同:
| 字段 | 语义 | 能当 beat 吗 |
|---|---|---|
twap60_off_start |
开盘那一刻的官方 Chainlink 流读数 | ✅ 就是它 |
twap60_at_start |
多家平台中位价自算的 TWAP60 起点值 | ❌ |
自算值带来的是系统性偏差,不是随机噪声。 实测 60 个窗口:
- 官方 − 自算 = 中位 −2.72 bp(区间 −3.37 ~ −1.26 bp)
- 而 60 秒位移的中位数只有 1.83 bp
- 有 3% 的窗口,用官方 beat 与用自算 beat 得出的方向是相反的
偏差的量级(2.72 bp)已经超过了信号本身的量级(1.83 bp)。 这意味着:如果你用自算值当锚点,你不是在「稍微不准」, 而是在系统性地把一部分窗口判反。
本 API 优先使用官方值,并把来源如实返回:
| 字段 | 含义 |
|---|---|
price_to_beat |
锚点值 |
beat_source |
official / computed / none |
beat_trusted |
false = 该窗口官方读数没进来,price_to_beat 退化成自算值(偏约 3bp,判方向不可信) |
给调用方的建议:beat_trusted == false 时不要用它判方向。
线上实测官方值覆盖率约 98%,退化是低频事件 —— 但低频不等于可以忽略,
因为它的后果是「方向判反」而不是「数值略偏」。
本客户端如实透传
beat_source/beat_trusted,绝不吞掉或平滑这两个标记。data.beat_trusted is False时请自行跳过该窗口。
另外,接口把「官方量」与「自算量」分开存放,不会混淆:
- 官方组:
official/official_bp/official_ts/official_source - 自算组:
computed_twap60/computed_at_start/computed_bp
想把「官方 vs 自算」的差异本身做信号?直接用 computed_* 那几个字段就行。
注意 spot_at_start 是「边界点假设」的锚点,不是 price to beat,别混用。
基线:https://api.wanminguo.top/quant/polymarket/v1/
鉴权:每个请求都要带 X-Api-Key: <你的 key>(也支持 Authorization: Bearer <key>;
?api_key= 能用但不推荐 —— 会进 access log)
⚠️ 端点路径都带.php(如/v1/window.php)。本站没有配置 URL rewrite, 写成/v1/window会直接 404。
| 端点 | 参数 | 说明 |
|---|---|---|
GET /v1/window.php ★ |
market, slug, series, n, raw |
主力端点。当前(或指定)窗口的快照:price to beat、距 beat 的 bp、剩余时间、隐含概率。series=1 附带时间序列(当前窗口任何套餐可用;历史窗口需样本权限),n 为序列点数 1~2000(超了等距抽样,末点必留),raw=1 带原始盘口字段(需 samples_raw 功能) |
GET /v1/settle.php |
market, slug 或 last |
结算核对:官方 outcome + 四条规则读数 rules.A/B/C/D(含 so/sc/to/tc,可自己复算)。last 默认 20,上限 500;market 默认 all |
GET /v1/history.php |
market, from, to, limit, offset, compact |
历史窗口列表,新的在前。from/to 接受 unix 秒或 2026-09-22;limit 上限 = 套餐 max_windows_per_call;compact=1 只返 slug/start/outcome/n_samples;older_available 便于翻页。范围超限整体 403,不做静默截断 |
GET /v1/samples.php |
slug(必填), limit, offset, tail, raw |
原始 2 秒样本。需套餐 max_samples_per_call > 0(免费档为 0)。tail=1 取最后 N 条;raw=1 返回采集器原始 JSONL 字段 |
GET /v1/stats.php |
— | 预聚合统计 + 完整健康度。免费档可用,用来快速判断数据质量与规则表现 |
GET /v1/index.php |
— | 端点清单 / 市场清单 / 套餐表(客户端自举用,建议启动时读一次而不是硬编码路径)。★ 免鉴权,但响应是扁平结构(无 data/meta) |
GET /v1/signals.php † |
since, market, wait, limit |
信号订阅线(按成交次数计费,不占每日请求配额):长轮询取实时信号,带 token_id。详见 信号订阅 |
POST /v1/receipt.php † |
signal_id, filled_shares, … |
信号订阅线:下单回执上报 —— 唯一扣次的地方 |
GET /v1/credits.php † |
ledger |
信号订阅线:次数余额 / 流水 / 价目表 / 当前计次口径 |
† 这三个属于信号订阅那条产品线:鉴权方式相同(同一个
X-Api-Key), 但计费单位是成交次数而不是请求数,且不消耗daily_quota。
补充细节:
- 约 2 秒一条。轮询
window.php的合理频率是 1~2 秒 —— 数据本身就是 2 秒一条,更密没有意义。 - 一个 5 分钟窗口大约 150 条样本。
/v1/settle.php的数据源已按 slug 去重(采集器重启会重复结算同一窗口,接口保留最后一条)。/v1/history.php与/v1/settle.php的market默认是all(全部 7 个市场);/v1/window.php的market默认是btc。- 传了
slug时,market参数不起作用(按 slug 取数)。 /v1/index.php是唯一免鉴权的端点(不用带X-Api-Key), 也是唯一不使用标准信封的端点 —— 见 响应信封 末尾那一节。- 其它所有
/v1/*端点都必须带 key,没有第二个免鉴权的公开端点。 - 上表里标 † 的三个是信号订阅端点:按成交次数计费, 不消耗本节的每日请求配额(两套计量单位互不影响)。
成功(ok: true):
{
"ok": true,
"data": { "...": "具体载荷,见端点表" },
"meta": {
"endpoint": "window",
"plan": "pro",
"key_prefix": "pm_live_ab12",
"quota": { "daily_limit": 500000, "used_today": 8123,
"remaining": 491877, "reset_at": "2026-09-27T00:00:00Z" },
"limits": { "qps": 60, "daily_quota": 500000, "history_days": 90,
"max_windows_per_call": 2000, "max_samples_per_call": 2000,
"features": { "samples_raw": 1, "stream": 1, "bulk": 0 } },
"feed": { "ok": true, "latest_slug": "btc-updown-5m-1790389200",
"latest_age_sec": 43 },
"server_ts": 1790389243
}
}失败(ok: false):
{ "ok": false, "error": "daily_quota_exceeded",
"message": "今日配额已用完(500/500),UTC 零点重置",
"quota": { "daily_limit": 500, "used_today": 500,
"reset_at": "2026-09-27T00:00:00Z" } }几个要点:
meta.quota每次都会返回 —— 客户端据此提前退避,不用等撞 429。 (唯一例外是/v1/index.php,它没有meta,见下一节。)meta.limits告诉你当前套餐的真实上限,客户端可以据此自适配(本仓库的iter_history就是这么做的:先读max_windows_per_call再决定页大小)。meta.feed是采集侧的新鲜度(ok/latest_slug/latest_age_sec), 用来自查「我拿到的是不是陈数据」。注意它不含任何服务器路径。meta.limits.features是功能开关:samples_raw/stream/bulk。
限速相关响应头:X-Quota-Limit / X-Quota-Used / X-Quota-Remaining / Retry-After。
全站只有 /v1/index.php 不走上面这个信封 —— 它既没有 data 也没有 meta,
所有字段都在顶层。真实顶层键清单:
ok, service, note, auth, endpoints, markets, market_codes,
response_envelope, error_codes, plans, health, server_ts
所以就这个端点而言:meta.quota / meta.limits 不存在(读不到配额与限额),
endpoints / markets / market_codes / plans / error_codes 直接从顶层取。
本客户端对两种形状都能解析 —— 按「响应里有没有 data 层」判断形状,
而不是给 index.php 打特例补丁,所以将来站点把它统一成标准信封也不会坏:
data, meta = api.index()
data["markets"] # ['btc-updown-5m', 'eth-updown-5m', ...]
data["market_codes"] # ['btc', 'eth', 'sol', 'xrp', 'doge', 'hype', 'bnb']
data["plans"] # 套餐表/v1/index.php 也是唯一免鉴权的端点(不需要 X-Api-Key),
所以还没注册 key 也能先自举看一眼这个 API 有什么:
from pm_api_client import PmApi
api = PmApi.public() # 不带 key 的实例
data, _ = api.index() # 免鉴权,能拿到端点清单与套餐表客户端遇到「扁平形状」时会往 stderr 打一次提示(提醒你
meta是空的), 设PM_QUIET_DEVIATION=1可关掉。
| HTTP | error |
含义 |
|---|---|---|
| 400 | missing_param / bad_slug / bad_param |
参数缺失或非法 |
| 400 | bad_market |
?market= 认不出来(响应带 markets 数组给出可用值) |
| 401 | missing_api_key |
没带 key |
| 401 | invalid_api_key |
key 不存在 |
| 403 | key_revoked / key_expired / customer_inactive / plan_retired |
key 或套餐已停用 |
| 403 | history_not_in_plan |
套餐不含历史(history_days = 0,当前窗口仍可访问) |
| 403 | history_depth_exceeded |
请求的窗口超出套餐可回溯天数 |
| 403 | samples_not_in_plan |
套餐不含原始样本(max_samples_per_call = 0;历史窗口的 /v1/window.php?series=1 同门槛) |
| 403 | plan_feature_denied |
套餐不含该功能开关(如 samples_raw) |
| 404 | window_not_found / settle_not_found |
没有该窗口 |
| 429 | rate_limited |
超过 QPS(带 Retry-After: 1)—— 退避后重试 |
| 429 | daily_quota_exceeded |
当日配额用完 —— 不要按 Retry-After 重试,等到 quota.reset_at(UTC 零点) |
| 503 | feed_root_missing / no_window / billing_db_unavailable |
服务端数据/计费库不可用 |
这是最容易写错的地方:
from pm_api_client import PmApi, PmError
api = PmApi()
try:
data, meta = api.window()
except PmError as e:
if e.is_rate_limited:
import time; time.sleep(e.retry_after or 1.0) # 退避后重试,有意义
elif e.is_quota_exceeded:
print("等 %s 再跑" % e.reset_at) # 重试没有意义!
else:
raise本客户端只对 rate_limited 自动重试(默认 2 次),
daily_quota_exceeded 永远不会被自动重试 —— 这是刻意的设计。
| code | U/月 | QPS | 日配额 | 历史 | 样本/次 | 说明 |
|---|---|---|---|---|---|---|
free |
0 | 1 | 500 | 1 天 | —(0) | 当前窗口 + 1 天历史 + 汇总统计 |
basic |
29 | 5 | 20,000 | 7 天 | 200 | 加原始样本 |
pro |
129 | 60 | 500,000 | 90 天 | 2,000 | 全量 |
internal |
— | 500 | 100,000,000(实际等于无限) | 3650 天 | 100,000 | 自用 |
配额按 UTC 天计,UTC 零点重置。
字段语义(别猜):
daily_quota = 0⇒ 不限量(不是禁用)history_days = 0⇒ 只能访问当前窗口,历史端点 403max_samples_per_call = 0⇒ 禁用原始样本(/v1/samples.php与历史窗口的/v1/window.php?series=1都拒)
免费档能做什么:free 档就能取当前窗口的完整数据(含 2 秒序列) ——
这是设计上的漏斗口,不受 history_days / max_samples_per_call 限制。
受限的是历史。
★ 但注意免费档的日配额只有 500 次/天:按 2 秒轮询,约 17 分钟就打光
(1 秒轮询还会撞 qps = 1)。免费档适合「看一眼当前窗口」;
要持续轮询请上 basic 以上。套餐详情见
https://api.wanminguo.top/quant/polymarket/endpoints.php。
上表是「数据 API」的套餐(按月 + 按请求数)。 信号订阅那条线不看这张表:它按成交次数计费 (注册送 10 次 / 9.9U=300 次 / 19.9U=800 次),并且信号端点不消耗这里的
daily_quota。
不需要安装,也不需要 pip install —— 只用标准库。
# 把 pm_api_client.py 拷到你的项目里就能用
cp finhub-api-client/pm_api_client.py your_project/或者让它可被导入:
export PYTHONPATH="$PYTHONPATH:$(pwd)/finhub-api-client"requirements.txt 是空的(只有注释)—— 这就是真实状态:零依赖。
from pm_api_client import PmApi, PmError, MARKETS
api = PmApi() # key 从环境变量 PM_API_KEY 读
# api = PmApi(api_key="pm_live_...") # 显式传
# api = PmApi(timeout=5, max_retries=0) # 5 秒超时,关掉自动重试
# api = PmApi.public() # 不带 key(只能打 index.php)所有方法都返回 (data, meta):
| 方法 | 对应端点 |
|---|---|
api.window(market='btc', slug=None, series=False, n=None, raw=False) |
/v1/window.php |
api.settle(market=None, slug=None, last=None) |
/v1/settle.php |
api.history(market=None, frm=None, to=None, limit=None, offset=None, compact=False) |
/v1/history.php |
api.samples(slug, limit=None, offset=None, tail=False, raw=False) |
/v1/samples.php |
api.stats() |
/v1/stats.php |
api.index() |
/v1/index.php(★ 扁平结构,无 data/meta,免鉴权) |
api.iter_history(...) |
/v1/history.php 自动翻页(生成器) |
data 是一个 dict 子类,既支持下标也支持属性访问:
data['official_bp'] # dict 风格(字段名是 Python 保留字时必须用这种,
data.official_bp # 属性风格
data.get('beat_trusted')
'series' in datameta 是一个 Meta 对象:
meta.plan # 'free' / 'basic' / 'pro' / 'internal'
meta.quota.remaining # 剩余配额
meta.quota.remaining_ratio # 剩余比例(0.0 ~ 1.0)
meta.should_back_off() # 剩余 < 10% 时为 True —— 该降频了
meta.history_days # 套餐可回溯天数
meta.max_samples_per_call # 单次样本上限
meta.feed.ok # 采集侧是否健康
meta.feed.latest_age_sec # 最新数据多少秒前from pm_api_client import PmApi, PmError, PmQuotaExceeded, PmRateLimited
api = PmApi()
try:
data, meta = api.window(market='sol')
except PmRateLimited as e: # 429 rate_limited
print("超 QPS,%.1fs 后重试" % (e.retry_after or 1.0))
except PmQuotaExceeded as e: # 429 daily_quota_exceeded
print("今日配额用完,%s 重置" % e.reset_at)
except PmError as e:
print("HTTP %s %s %s" % (e.status, e.code, e.message))
if e.available_markets:
print("可用市场:", e.available_markets)PmError 上可用的信息:status / code / message / retry_after / quota /
reset_at / available_markets / payload(完整原始 body)。
data, meta = api.window(market='eth')
if data.beat_trusted is False:
# 官方 Chainlink 读数缺失,price_to_beat 是自算退化值(偏约 3bp)
# 偏差量级 > 60 秒真实位移的中位数 —— 用它判方向会被系统性地判反
skip_this_window()
else:
direction_hint = data.leading_side # UP / DOWN / FLAT
strength_bp = data.official_bp # 距 beat 多少 bpdata.beat_trusted 在载荷没有该字段时返回 None;
严格判断请用 data.is_trusted_beat(只有明确 True 才算可信)。
import time
from pm_api_client import PmApi, PmError, SAMPLE_INTERVAL_SEC
api = PmApi()
last_slug = None
while True:
try:
data, meta = api.window(market='btc')
except PmError as e:
if e.is_quota_exceeded:
print("配额用完,等到 %s" % e.reset_at)
break # ★ 不要重试
raise
if data.get('slug') != last_slug:
last_slug = data.get('slug')
print("=== 新窗口 %s ===" % last_slug)
print("%s 官方 %+.2f bp 现货 %+.2f bp 领先 %s 可信=%s"
% (last_slug, data.get('official_bp') or 0.0,
data.get('spot_bp') or 0.0, data.get('leading_side'),
data.beat_trusted))
# 配额低于 10% 就降频
time.sleep(SAMPLE_INTERVAL_SEC * (8 if meta.should_back_off() else 1))本仓库的根目录(
pm_api_client.py/examples/)是「数据 API」那条线; 这一节讲的是同站的另一条产品线 —— 实时信号订阅。 两者鉴权方式相同(同一个 key), 但计费口径完全不同:数据 API 按月 + 按请求数,信号订阅按成交次数。 客户端源码在本仓库的signal-client/,成品安装包在 https://api.wanminguo.top/download/。
订阅 BTC-5m 的实时进场信号:平台把信号推送到你自己电脑上的客户端, 客户端在本地下单(私钥只在你机器上,平台不代持、不代下单), 成交后把回执回传给平台,平台按回执扣「次数」。
| 端点 | 参数 | 说明 |
|---|---|---|
GET /v1/signals.php |
since, market, wait(0~15, 默认 10), limit(≤50) |
长轮询取新信号。每条带 signal_id(slug:trade_seq)、方向、信号价、limit_price/limit_capped/over_cap、condition_id + Up/Down 的 token_id(实盘下单必需)、ask_sz(那一刻最优档挂单量) |
POST /v1/receipt.php |
signal_id, filled_shares(必填), requested_shares, avg_price, status, order_id |
唯一扣次的地方。只接受平台确实投递过的 signal_id;没成交(filled_shares=0)不扣 |
GET /v1/credits.php |
ledger(≤200) |
次数余额 + 流水 + 订阅市场 + 价目表 + 当前计次口径(credit_rule) |
计费与执行口径(写死在这里,避免猜):
- 1 次 = 一个成功下单回执(不看份数:×1 和 ×5 都只扣 1 次);没成交不扣;
status=paper/dry之类的纸面回执不计次(服务端按 0 成交处理)。 - 同一个
(signal_id, key_id)只会扣一次 —— 重发、并发、分笔成交都幂等。 - 一把 key 只绑一个市场(当前只开放
BTC-5m),绑定后不可改。 - 客户端默认按「信号价 + 0.05、硬上限 0.85」下 FAK 限价单:挂不上就不成交,不追高。
- 次数用光时
/v1/signals.php返回 402no_credits;信号端点不消耗每日请求配额 (那是数据 API 的计量单位),但仍有秒级限速。 - 价目:注册送 10 次、9.9U / 300 次、19.9U / 800 次。
★ 按当前口径,套餐寿命与倍数无关(300 次 ≈ 4.4 天,实测约 68 条信号/天);
平台另留了「按份数」口径(1 次 = 10 份成交、×5 一单扣 5 次)但未启用 ——
以接口返回的
charge_mode/credit_rule为准。
平台不承诺任何收益。用真实历史(93 单 / 1.36 天)按客户端口径回测的结果是:
- 胜率 78.5%(95% 置信区间 69.1%~85.6%),而盈亏平衡胜率 = 平均成交价 74.8% → 置信区间跨过平衡点,这段历史分不出正负;
- 滑点从 0 加到 0.05,每单少赚 0.500U;
- 盘口深度会吃掉倍数:×5 时近 40% 的信号那一档挂单量不够,每单收益从 1.855U 掉到 0.961U,却要多付 5 倍次数费。
完整报告(含分档表格与读取方法):https://api.wanminguo.top/download/backtest-report.txt;
客户端的安装、参数、隧道与常见问题见 signal-client/README.md。
国内客户下单需要出网隧道(平台提供,按 SNI 白名单转发、不解密,私钥不会经过平台), 细节同样写在客户端的 README 里。
本站采集器在采数时评估了一批信号(多源基差、盘口滞后、动量、深度、边界点等族), 这些评估出来的因子不在这条数据 API 的响应里、也不是本站推荐的下单依据 —— 它们只是让你知道「这份数据被从哪些角度检查过」;完整清单、每族的判定口径与实测表现见 https://api.wanminguo.top/quant/polymarket/factors.php。
⚠️ 别把两件事搞混:上面那些是数据侧评估的因子(不对外下单); 而 信号订阅 是另一条产品线,有自己的端点与客户端, 按成交次数计费。
本数据 API 卖的是原始结算输入数据,不是策略或建议;因子页上的数字 是研究者自己的信号口径,请结合
?号口径自行复算。
四个都能直接跑,都支持 --help,都会在没设 key 时给出清晰提示:
| 文件 | 演示什么 |
|---|---|
examples/current_window.py |
取当前窗口快照;打印三个 bp 与 beat_trusted;--series 拉 2 秒序列并用纯标准库画 ASCII 走势图 |
examples/settle_recent.py |
取最近 N 个已结算窗口;--verify 用 rules.so/sc/to/tc 在本地自己复算四条规则并与官方 outcome 对比一致率 |
examples/history.py |
按时间范围拉历史列表;手动 limit/offset 翻页 + iter_history 自动翻页;正确处理 history_depth_exceeded |
examples/samples.py |
拉某窗口的原始 2 秒样本;--tail 取尾部;--raw 原始字段;--csv 导出 |
python examples/current_window.py --help
python examples/current_window.py --market eth --series --n 60
python examples/settle_recent.py -n 30 --market btc --verify
python examples/history.py --days 1 --pages 3
python examples/samples.py --slug btc-updown-5m-1790389200 --tail 30 --csv out.csvQ:为什么路径要写 .php?
本站没有配置 URL rewrite,/v1/window 会 404。所有端点路径都带 .php。
Q:没有 key 能试吗?
只能试一个端点 —— /v1/index.php 免鉴权,能拿到端点清单、市场清单与套餐表
(PmApi.public() 就是给这一步用的)。除此之外所有 /v1/* 都要 key。
数据本身仍然要注册(free 档 0 元),注册当场就能取当前窗口的完整数据
(含 2 秒序列),这是唯一能证明数据对你有用的方式:
https://api.wanminguo.top/me/register.php
Q:为什么 api.index() 拿不到 meta.quota?
因为 /v1/index.php 是全站唯一扁平的端点 —— 它没有 data 也没有 meta,
字段全在顶层。本客户端两种形状都能解析(按有没有 data 层判断),
见 响应信封 末尾那一节。
Q:meta.quota.remaining 是 0 是不是就彻底不能用了?
看 daily_limit:daily_quota = 0 的语义是不限量,不是禁用。
remaining 才是真实剩余次数。
Q:有 WebSocket / 推送吗? 目前是轮询制。要做实时推送请单独联系。轮询间隔 1~2 秒即可, 更密没有意义(数据本身就是 2 秒一条)。
Q:为什么现货源数有时不是 6?
spot_n_sources 是这一拍实际取到的家数(HYPE 只有 4 家)。
做「多源中位」类信号时请按实际家数判断,不要写死 6。
Q:spot_at_start 能当 price to beat 用吗?
不能。 它是「边界点假设」的锚点,不是 price to beat。
price to beat 只有一个正确来源:官方 Chainlink 读数,
见 上面那一节。
Q:遇到 429 就重试,可以吗?
分两种。rate_limited 可以按 Retry-After 退避重试;
daily_quota_exceeded 重试没有意义,只会白烧请求,请等到 quota.reset_at。
Q:数据能拿去做什么? 做结算方向的判读、做「官方会不会跟过来」的判断、做市场隐含概率与你的 模型隐含概率之间的偏差分析、做策略回测。数据是结算输入, 不构成任何投资建议。
FinHub API · Polymarket 5-minute up/down market data API — official Python client. Zero third-party dependencies (standard library only).
This API provides the settlement inputs for Polymarket's 5-minute up/down markets: the official Chainlink TWAP60, multi-venue spot quotes, and CLOB order-book data, at ~1 sample per 2 seconds, across 7 markets (BTC / ETH / SOL / XRP / DOGE / HYPE / BNB).
Besides the data API, the same site sells a real-time signal subscription for BTC-5m: the platform pushes signals to a client running on your machine, the client places the order locally (private keys never leave your machine), and posts a fill receipt back — billing is per filled order, not per request.
| Endpoint | Purpose |
|---|---|
GET /v1/signals.php |
long-poll for new signals (signal_id, side, prices, condition_id, Up/Down token_id, book depth) |
POST /v1/receipt.php |
report a fill — the only place credits are charged (no fill ⇒ no charge) |
GET /v1/credits.php |
credit balance, ledger, subscription, price table, current credit_rule |
1 credit = one successful fill receipt (independent of order size: ×1 and ×5 both cost 1 credit); no fill ⇒ no charge; paper/dry receipts are never charged.
Source of the client: signal-client/ in this repo;
ready-to-run packages: https://api.wanminguo.top/download/.
No profit is promised. A backtest over real history (93 trades / 1.36 days, same rules the client uses) gives a 78.5% win rate against a 74.8% break-even (average fill price) — the 95% confidence interval straddles break-even, i.e. this sample cannot tell profit from loss. Slippage and thin books matter: at ×5, ~40% of signals were depth-limited and per-trade P&L roughly halved. Full report: https://api.wanminguo.top/download/backtest-report.txt.
Polymarket's market rules state that settlement uses "the TWAP of Bitcoin, generated by Chainlink … greater than or equal to the price at the beginning of that range … not according to any other sources or spot markets." So the anchor must be the official reading.
There are two fields that look like "the starting price", with completely
different meanings: twap60_off_start (official Chainlink stream reading — ✅ the
real price to beat) and twap60_at_start (self-computed median across venues — ❌).
Measured over 60 windows, official − computed has a median of −2.72 bp,
while the median 60-second move is only 1.83 bp — the bias is larger than the
signal, and in 3% of windows the two anchors imply opposite directions.
This API prefers the official value and reports provenance honestly:
| Field | Meaning |
|---|---|
price_to_beat |
the anchor |
beat_source |
official / computed / none |
beat_trusted |
false = official reading missing, price_to_beat degraded to a computed value (~3 bp bias, unreliable for direction) |
Do not use price_to_beat for direction when beat_trusted == false.
This client never swallows that flag.
export PM_API_KEY=pm_live_xxxx # get a free key first
python examples/current_window.py # no pip install neededGet a free key (the free plan costs nothing and already returns the complete
current window including the 2-second series):
https://api.wanminguo.top/me/register.php
Base: https://api.wanminguo.top/quant/polymarket/v1/ · Auth header: X-Api-Key
⚠️ All paths end with.php(e.g./v1/window.php). No URL rewriting is configured —/v1/windowreturns 404.
| Endpoint | Params | Description |
|---|---|---|
GET /v1/window.php ★ |
market, slug, series, n, raw |
Current/specified window snapshot: price to beat, bp vs beat, remaining time, implied probability |
GET /v1/settle.php |
market, slug or last |
Settlement check: official outcome + four rule readings you can recompute yourself |
GET /v1/history.php |
market, from, to, limit, offset, compact |
Historical window list, newest first |
GET /v1/samples.php |
slug(required), limit, offset, tail, raw |
Raw 2-second samples |
GET /v1/stats.php |
— | Pre-aggregated stats (available on the free plan) |
GET /v1/index.php |
— | Endpoint/market/plan self-description. ★ No auth required, but flat shape (no data/meta) |
Success: {"ok": true, "data": {...}, "meta": {"quota": {...}, "limits": {...}, "feed": {...}}}.
Failure: {"ok": false, "error": "code", "message": "..."}.
meta.quota is returned on every response so clients can back off before
hitting a 429.
One exception: /v1/index.php is flat — it has no data and no meta;
every field sits at the top level (ok, service, note, auth, endpoints,
markets, market_codes, response_envelope, error_codes, plans,
health, server_ts). It is also the only endpoint that needs no API key.
This client parses both shapes (it keys off whether a data member exists),
so it will keep working if the endpoint is ever normalised.
bad_market · missing_api_key · invalid_api_key · key_revoked ·
history_not_in_plan · history_depth_exceeded · samples_not_in_plan ·
plan_feature_denied · window_not_found · settle_not_found ·
rate_limited (back off and retry) · daily_quota_exceeded (do not retry —
wait for quota.reset_at) · no_window
| code | USD/month | QPS | Daily quota | History | Max samples/call |
|---|---|---|---|---|---|
free |
0 | 1 | 500 | 1 day | — |
basic |
29 | 5 | 20,000 | 7 days | 200 |
pro |
129 | 60 | 500,000 | 90 days | 2,000 |
Quotas reset at 00:00 UTC. Note the free plan's 500/day is exhausted in ~17 minutes at a 2-second polling interval.
from pm_api_client import PmApi, PmError
api = PmApi() # reads PM_API_KEY
data, meta = api.window(market='btc')
print(data.price_to_beat, data.official_bp, data.leading_side)
if data.beat_trusted is False:
pass # unreliable anchor — skip this window
print(meta.quota.remaining) # back off when this gets low
# No key yet? /v1/index.php needs none (and is flat: no data/meta layer)
pub = PmApi.public()
info, _ = pub.index()
print(info['market_codes']) # ['btc', 'eth', 'sol', 'xrp', 'doge', 'hype', 'bnb']Docs: https://api.wanminguo.top/quant/polymarket/docs.php · Endpoints & plans: https://api.wanminguo.top/quant/polymarket/endpoints.php
MIT © FinHub API
- 零第三方依赖:只用标准库(
urllib.request/json/argparse/dataclasses/typing),和站点采集器的风格保持一致。 - 不含任何密钥:key 只从环境变量
PM_API_KEY或命令行参数读, 代码里没有任何硬编码凭证、私有地址或内网信息。 (signal-client/同理:API key 由--key传入,实盘私钥只在客户本机、 既不落盘也不上报。) - 目录两条线:
- 根目录 —— 数据 API 客户端(
pm_api_client.py+examples/,本节以上全部内容); signal-client/—— 信号订阅客户端(实时信号 → 本地下单 → 回执计次), 成品安装包在 https://api.wanminguo.top/download/,源码在此可直接运行/打包。 两者互不依赖,可分别使用。
- 根目录 —— 数据 API 客户端(
- 可直接发布:本目录就是一个完整的、可
git init后推上 GitHub 的仓库 (发布步骤与 push 前的泄密自查见PUBLISHING.md, 版本记录见CHANGELOG.md)。