Polymarket

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_codePOLYMARKET交易所编码
market_typeFUTURES工厂注册键为 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)
python
# 管理端创建实盘示例(ExchangeConfig)
exchange_pb2.ExchangeConfig(
    exchange_code="POLYMARKET",
    market_type="FUTURES",
    private_key="0x你的私钥",
)

1.2 钱包自动探测

创建实例时托管者会自动确定下单使用的钱包(funder)与签名类型,策略无需关心:

  1. 用私钥得到 EOA 地址,本地推算 Gnosis Safe 与 Proxy 钱包地址;
  2. 调用 Gamma public-profile 接口获取 Polymarket 实际分配的钱包地址;
  3. 按以下顺序探测,第一个 USDC 余额 > 0 的候选被选中:
顺序签名类型钱包地址来源
1POLY_1271(Deposit Wallet)Gamma Profile 钱包
2POLY_1271(Deposit Wallet)EOA
3POLY_PROXYGamma Profile 钱包
4POLY_GNOSIS_SAFEGamma Profile 钱包
5POLY_GNOSIS_SAFE本地推算 Safe
6POLY_PROXY本地推算 Proxy
7EOAEOA

全部候选余额都为 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)格式

plaintext
{market_slug}_USDC.{outcome}
示例含义
btc-updown-5m-1778612700_USDC.UpBTC 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:

plaintext
1. IO("SetTokenID", id) 设置的全局 Token ID     ← 最高优先级,0 额外请求
2. SetCurrency(currency) 设置的标的             ← 每次调用都会请求一次 Gamma API 解析
3. 都没有                                        ← 报错:未设置 token ID 或 currency
  • GetTicker / 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. 数值与单位

项单位 / 规则
价格 priceUSDC / 份,取值 (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. 完整工作流程示例

python
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. 注意事项与当前实现限制

  1. 启动后必须设置标的:实盘配置中的 symbol 当前不会传入 Polymarket 实例。Python 端 GetCurrency() 在服务端标的为空时会显示默认值 "BTC_USDT",这不是有效的 Polymarket 标的,请在 main() 开头调用 SetCurrency() 或 IO("SetTokenID", ...)。
  2. SetCurrency 不做校验:格式或 slug 错误要到第一次调用行情/下单时才报错。
  3. 解析开销:仅用 SetCurrency 时,每次 GetTicker / Buy / Sell / GetOrders 都会请求一次 Gamma API(约 100~300ms)。高频策略请配合 IO("SetTokenID", ...)。
  4. Token ID 与 GetTicker:设置了全局 Token ID 后,不带参数的 GetTicker() 拿不到 Gamma 市场数据,Volume 为 0;需要成交量时请传 GetTicker(symbol)。
  5. 市价单是激进限价单:price=-1 时买单以 0.99、卖单以 0.01 提交 GTC 限价单,未成交部分会继续挂在盘口,请注意检查并撤单。
  6. GetAccount 的 Stocks:当前实现中 Stocks 总是 0(代码从持仓 Info 中读取 raw 字段,但该字段已被展开),持仓市值请用 GetPosition() 的 Info["currentValue"] 自行累加。
  7. 固定使用第一个交易所的方法:IO()、GetMarkets()、GetPendingOrders()、CancelAllOrders() 在 gRPC/SDK 层固定作用于 exchanges[0]。多交易所实盘中请把 Polymarket 配置为第一个交易所,或避免在 exchanges[1..] 上调用这些方法。
  8. Post-only 模式:CLOB 处于 post-only 维护模式时,下单会按服务端返回的 retry_after_seconds 等待后重试,最多 3 次,期间该调用会阻塞。
  9. 自定义请求头不生效:配置中的 headers 会被忽略(底层 SDK 不支持)。

相关文档