Polymarket 预测市场 API 总览
托管者通过统一的 exchange.* 接口为策略提供 Polymarket 预测市场(Polygon 链,CLOB 撮合)的行情、账户、下单与链上赎回能力。通用方法(Buy、GetTicker 等)与其他交易所共用同一份文档,Polymarket 下的差异写在各方法文档末尾的「Polymarket 说明」中;本目录只收录 Polymarket 专属的扩展命令。内容基于以下代码整理:
internal/exchange/polymarket/prediction/prediction.go:Polymarket 交易所适配(实现exchange.Exchange接口)internal/exchange/polymarket/prediction/converter.go:标的解析、订单状态映射、去重规则internal/exchange/polymarket/prediction/redeem_dw.go:Deposit Wallet 无 Gas 赎回internal/grpc/exchange_server.go:策略调用的 gRPC 服务(字段映射)pkg/embedded/python_sdk/sdk/exchange/exchange.py:Python 策略端封装
与 Binance / Gate 等交易所相比,Polymarket 的"交易对"是某个预测市场的某个结果(Outcome),价格是 0~1 之间的概率价格(单位 USDC),数量是结果份额(shares)。
1. 接入配置
1.1 交易所编码
| 字段 | 取值 | 说明 |
|---|---|---|
exchange_code | POLYMARKET | 交易所编码 |
market_type | FUTURES | 工厂注册键为 POLYMARKET:FUTURES(见 prediction/init.go)。注意:实例内部 MarketType() 返回 PREDICTION,但创建实盘时必须传 FUTURES |
private_key | 以太坊私钥 | 必填。支持带/不带 0x 前缀;若传入 128 位 hex,只取前 64 位 |
api_key / secret_key | 留空 | 不需要。CLOB L2 凭证由私钥自动派生(CreateOrDeriveAPIKey) |
symbol | 任意 | 当前不会生效,策略启动后必须自行调用 SetCurrency() 或 IO("SetTokenID", ...)(见 1.3) |
# 管理端创建实盘示例(ExchangeConfig)
exchange_pb2.ExchangeConfig(
exchange_code="POLYMARKET",
market_type="FUTURES",
private_key="0x你的私钥",
)1.2 钱包自动探测
创建实例时托管者会自动确定下单使用的钱包(funder)与签名类型,策略无需关心:
- 用私钥得到 EOA 地址,本地推算 Gnosis Safe 与 Proxy 钱包地址;
- 调用 Gamma
public-profile接口获取 Polymarket 实际分配的钱包地址; - 按以下顺序探测,第一个 USDC 余额 > 0 的候选被选中:
| 顺序 | 签名类型 | 钱包地址来源 |
|---|---|---|
| 1 | POLY_1271(Deposit Wallet) | Gamma Profile 钱包 |
| 2 | POLY_1271(Deposit Wallet) | EOA |
| 3 | POLY_PROXY | Gamma Profile 钱包 |
| 4 | POLY_GNOSIS_SAFE | Gamma Profile 钱包 |
| 5 | POLY_GNOSIS_SAFE | 本地推算 Safe |
| 6 | POLY_PROXY | 本地推算 Proxy |
| 7 | EOA | EOA |
全部候选余额都为 0 时,使用第 1 个候选。持仓查询(Data API)优先使用 Gamma Profile 钱包地址,查不到仓位时依次回退到 Proxy → Safe → EOA,并记住命中的地址。
APIConfig.WalletAddress虽然支持显式指定 Deposit Wallet,但当前实盘管理器(internal/manager/real_trade.go)创建交易所时没有传递该字段,因此实际总是走自动探测。
初始化时还会创建 Builder API Key,用于链上赎回的无 Gas 中继提交;创建失败时 IO("Redeem") 不可用,其余功能不受影响。
1.3 交易标的(Currency)格式
{market_slug}_USDC.{outcome}| 示例 | 含义 |
|---|---|
btc-updown-5m-1778612700_USDC.Up | BTC 5 分钟涨跌市场的 "Up" 结果 |
btc-updown-5m-1778612700_USDC.Down | 同一市场的 "Down" 结果 |
will-bitcoin-hit-150k_USDC.Yes | 二元市场的 "Yes" 结果 |
market_slug:Polymarket 市场 slug(市场页面 URL 中的那一段)outcome:结果名称,大小写不敏感(Yes/yes均可),必须是该市场outcomes中的一个- 分隔符固定为
_USDC.,缺少会报错invalid currency: missing '_USDC.' separator
GetMarkets() 返回的字典 key 就是这种格式,可直接传给 SetCurrency()。设置规则详见 exchange.SetCurrency()。
1.4 Token ID 解析优先级
所有需要具体标的的接口(行情、下单、挂单查询)都通过以下规则得到 CLOB Token ID:
1. IO("SetTokenID", id) 设置的全局 Token ID ← 最高优先级,0 额外请求
2. SetCurrency(currency) 设置的标的 ← 每次调用都会请求一次 Gamma API 解析
3. 都没有 ← 报错:未设置 token ID 或 currencyGetTicker / GetDepth / GetTrades传入symbol参数时,直接按 symbol 解析,不受上述优先级影响。Buy / Sell / GetOrders / GetPendingOrders / CancelAllOrders不接受 symbol,只走上述优先级。- Token ID 是十进制大整数字符串(Gamma 市场数据中的
clobTokenIds),不是0x开头的地址。
2. 方法支持矩阵
2.1 通用方法
以下方法与其他交易所共用文档,Polymarket 下的差异见各文档的「Polymarket 说明」。
| 分类 | 方法 | 支持 | Polymarket 下的要点 |
|---|---|---|---|
| 设置 | exchange.GetCurrency() | ✅ | 未设置时显示 "BTC_USDT"(无效值) |
| 设置 | exchange.GetName() | ✅ | 固定返回 "Polymarket" |
| 行情 | exchange.GetMarkets() | ✅ | 按分类拉取活跃市场,每个结果一条 |
| 行情 | exchange.GetTicker() | ✅ | 概率价格;High=Sell、Low=Buy |
| 行情 | exchange.GetDepth() | ✅ | CLOB 订单簿 |
| 行情 | exchange.GetTrades() | ⚠️ | 返回本账户成交,不是全市场成交 |
| 行情 | exchange.GetRecords() | ❌ | 不支持,返回 None |
| 行情 | exchange.GetCurrenciesPrecision() | ❌ | 不支持,精度用 GetMarkets() |
| 账户 | exchange.GetAccount() | ✅ | USDC 余额;Stocks 当前总是 0 |
| 账户 | exchange.GetPosition() | ✅ | 所有市场的结果份额持仓 |
| 账户 | exchange.GetTradingFee() | ✅ | 固定 Maker 0、Taker 0.001 |
| 交易 | exchange.Buy() | ✅ | GTC 限价单,amount 为份数,-1 以 0.99 提交 |
| 交易 | exchange.Sell() | ✅ | 只能卖出已持有份额,-1 以 0.01 提交 |
| 交易 | exchange.GetOrder() | ✅ | 大写状态,支持成交兜底 |
| 交易 | exchange.GetOrders() | ✅ | 只返回当前标的的挂单 |
| 交易 | exchange.GetPendingOrders() | ✅ | 同上,含 Time |
| 交易 | exchange.GetHistoryOrders() | ⚠️ | 实现受限,通常返回空列表 |
| 交易 | exchange.CancelOrder() | ✅ | 支持复合订单 ID |
| 交易 | exchange.CancelAllOrders() | ✅ | 只撤当前标的的挂单 |
| 扩展 | exchange.IO("api", ...) | ❌ | 直接返回空 {},不发请求 |
| 合约 | SetContractType / SetDirection / SetMarginLevel / SetPositionMode / SetDualMode | ❌ | 不支持,Python 端返回 False |
| 合约 | GetContractType() / GetMarginLevel() | - | 分别返回 "" 与 0 |
| 合约 | GetFundings() / GetFundingHistory() | ❌ | 不支持 |
2.2 Polymarket 专属方法与独立文档
| 方法 | 说明 | 文档 |
|---|---|---|
exchange.SetCurrency(currency) | 设置标的 {slug}_USDC.{outcome}(与其他交易所不同,单独成文) | 查看 |
exchange.IO("SetTokenID", token_id) | 设置全局 Token ID | 查看 |
exchange.IO("GetTokenID") | 查询全局 Token ID | 查看 |
exchange.IO("ClearTokenID") | 清除全局 Token ID | 查看 |
exchange.IO("SetCategory", category) | 设置 GetMarkets() 的分类 | 查看 |
exchange.IO("GetCategory") | 查询当前分类 | 查看 |
exchange.IO("GetCategories") | 内置分类列表 | 查看 |
exchange.IO("GetActivity", address, opts) | 查询任意地址的活动流 | 查看 |
exchange.IO("Redeem", condition_ids) | 赎回已结算仓位(无 Gas) | 查看 |
不支持的方法在 Go 端返回
unsupported feature错误。Python SDK 会把错误写入策略日志,并对查询/交易类方法返回None、对配置类方法返回False,不会抛出异常,策略需自行检查返回值。
3. 数值与单位
| 项 | 单位 / 规则 |
|---|---|
价格 price | USDC / 份,取值 (0, 1)。必须在 [tick_size, 1 - tick_size] 内,最多 3 位小数,会按 tick 四舍五入 |
数量 amount | 结果份额(shares)。买入与卖出都按份数计,不是 USDC 金额 |
| 买入花费 | 约 price × amount USDC |
| 结算 | 市场结算后胜出结果每份价值 1 USDC,失败结果价值 0,需调用 IO("Redeem") 兑回 USDC |
| 最小下单量 | 以 GetMarkets() 中的 min_qty(Gamma orderMinSize,缺省 5 份)为准 |
| 余额 | GetAccount()["Balance"],已按 6 位小数换算为 USDC |
| 订单 ID | "{token_id},{polymarket_order_id}" 复合格式,原样传回 GetOrder / CancelOrder 即可 |
| 订单状态 | PENDING(挂单中)/ FILLED(已成交)/ CANCELLED(已撤销)/ EXPIRED(已过期),大写,与其他交易所的小写状态不同 |
4. 完整工作流程示例
def main():
# 1. 选择市场(默认 crypto 分类)
exchange.IO("SetCategory", "crypto")
markets = exchange.GetMarkets()
# 找到当前一期的 BTC 5 分钟涨跌市场
symbol = None
for key in markets:
if key.startswith("btc-updown-5m-") and key.endswith("_USDC.Up"):
symbol = key
break
if not symbol:
Log("未找到目标市场")
return
market = markets[symbol]
Log(f"标的: {symbol}, tick={market['tick_size']}, 最小下单={market['min_qty']}")
# 2. 设置标的;高频场景可额外设置 Token ID 省去每次解析
exchange.SetCurrency(symbol)
info = market["info"]
# outcomes / clobTokenIds 可能是 JSON 字符串,也可能已是列表
outcomes = info["outcomes"] if isinstance(info["outcomes"], list) else json.loads(info["outcomes"])
token_ids = info["clobTokenIds"] if isinstance(info["clobTokenIds"], list) else json.loads(info["clobTokenIds"])
token_id = token_ids[[o.lower() for o in outcomes].index("up")]
exchange.IO("SetTokenID", token_id)
# 3. 查看行情与余额
ticker = exchange.GetTicker(symbol) # 传 symbol 才能拿到 24h 成交量
account = exchange.GetAccount()
Log(f"买一 {ticker['Buy']} 卖一 {ticker['Sell']} 最新 {ticker['Last']}, USDC 余额 {account['Balance']}")
# 4. 下单:以 0.45 买入 10 份 Up
order_id = exchange.Buy(0.45, 10, "BTC 5m 看涨")
if not order_id:
Log("下单失败,详见错误日志")
return
# 5. 等待成交或撤单
for _ in range(30):
order = exchange.GetOrder(order_id)
if order and order["Status"] == "FILLED":
Log(f"已成交 {order['DealAmount']} 份,均价 {order['Price']}")
break
Sleep(1000)
else:
exchange.CancelOrder(order_id)
Log("超时未成交,已撤单")
# 6. 市场结算后赎回
# result = exchange.IO("Redeem")
# Log("赎回结果:", result)5. 注意事项与当前实现限制
- 启动后必须设置标的:实盘配置中的
symbol当前不会传入 Polymarket 实例。Python 端GetCurrency()在服务端标的为空时会显示默认值"BTC_USDT",这不是有效的 Polymarket 标的,请在main()开头调用SetCurrency()或IO("SetTokenID", ...)。 - SetCurrency 不做校验:格式或 slug 错误要到第一次调用行情/下单时才报错。
- 解析开销:仅用
SetCurrency时,每次GetTicker / Buy / Sell / GetOrders都会请求一次 Gamma API(约 100~300ms)。高频策略请配合IO("SetTokenID", ...)。 - Token ID 与 GetTicker:设置了全局 Token ID 后,不带参数的
GetTicker()拿不到 Gamma 市场数据,Volume为 0;需要成交量时请传GetTicker(symbol)。 - 市价单是激进限价单:
price=-1时买单以 0.99、卖单以 0.01 提交 GTC 限价单,未成交部分会继续挂在盘口,请注意检查并撤单。 - GetAccount 的 Stocks:当前实现中
Stocks总是 0(代码从持仓 Info 中读取raw字段,但该字段已被展开),持仓市值请用GetPosition()的Info["currentValue"]自行累加。 - 固定使用第一个交易所的方法:
IO()、GetMarkets()、GetPendingOrders()、CancelAllOrders()在 gRPC/SDK 层固定作用于exchanges[0]。多交易所实盘中请把 Polymarket 配置为第一个交易所,或避免在exchanges[1..]上调用这些方法。 - Post-only 模式:CLOB 处于 post-only 维护模式时,下单会按服务端返回的
retry_after_seconds等待后重试,最多 3 次,期间该调用会阻塞。 - 自定义请求头不生效:配置中的
headers会被忽略(底层 SDK 不支持)。