通知Polymarket 5 分钟与 15 分钟涨跌市场已全部改用 60 秒 TWAP 结算,30 秒 TWAP 流(twap30s)将于 2026-10-20(UTC)起停止采集与发布,已发布的文件不受影响。
教程

两个 Polymarket 策略:加上手续费和排队之前,看起来都还行

只要按中间价成交、不算手续费,给 Polymarket 短周期策略跑出一个「看起来赚钱」的回测挺容易的。我们想看看两个简单思路在回测里加上三样东西之后会差多少:付 taker 手续费、按盘口深度逐档成交、让挂单在队列里排队。

两个测试都跑在 Polymarket BTC 5 分钟涨跌市场的一天(2026-09-08)上。数据包括 UP 和 DOWN 两个 token 的盘口快照与增量、成交记录,以及市场结算用的 Chainlink 价格流。盘口是用快照和按采集频率抽稀过的增量重建的,近似每一时刻的深度,不是对交易所的精确重放。

胜率 82%,还是亏钱

第一个策略在每个市场的最后 60 秒,买入价格更高的那一边,只在 55¢ 到 95¢ 之间才买:

from outcometick import Strategy, Order
from outcometick.backtest import run, load_sample


class Favourite(Strategy):
    def on_market_open(self, ctx, market):
        self.close, self.done = market.close_ts_ms, False

    def on_tick(self, ctx, tick):
        if self.done or self.close - ctx.now > 60_000:
            return None
        book = ctx.book()
        up, down = book.best("UP"), book.best("DOWN")
        if up is None or down is None:
            return None
        self.done = True
        side, price = ("UP", up) if up >= down else ("DOWN", down)
        if not 0.55 <= price <= 0.95:
            return None
        return Order(side=side, notional=50, limit=0.95)


result = run(Favourite, load_sample("polymarket"))
print(result.summary())
  market-days          1
  net pnl              -$15.62
  trades               108
  win rate             82.4%
  brier                0.1397
  edge/contract        0.7¢
  max drawdown         -$284.51
  fees                 -$52.90  (venue schedule, estimated per book level)
  lost to slippage     -$4.22

108 笔交易赢了 82.4%,扣费前每份合约的优势是 0.7¢。Polymarket 按 C × rate × p × (1 − p) 向 taker 收手续费,费率每个市场各自设定。这一天手续费合计 $52.90,比策略扣费前赚的还多。在 80–95¢ 买入时,高胜率基本就是你花钱买来的。这次 82.4% 的胜率,最后仍然净亏 $15.62。

做市挂单,总在不该成交的时候成交

第二个测试里,我们改用挂单来避开 taker 手续费。这个策略在最优买价挂 25 份 UP,在「1 − 最优卖价」挂 25 份 DOWN。它每秒最多重新报价一次,持仓用开仓价上方一个 tick 的被动挂单平掉。下单延迟 150ms,撤单延迟 100ms。它就是回测工具示例里的 maker_quotes.py。

挂单要成交,得等成交把排在它前面的同价位挂单量吃完。两本盘口互为镜像:价格 a 上的 UP 卖单和价格 1 − a 上的 DOWN 买单是同一个队列,只是发布了两次,所以这笔 DOWN 挂单排在最优卖价上那些 UP 卖单的后面。成交之后,markout 衡量的是 1 秒、10 秒、60 秒后以及结算时,中间价朝不利方向移动了多少。

  trades               1105
  net pnl              -$26,133.42
rested 117061 orders, maker fill rate 0.0377 by size
median queue ahead at join 94.57, at fill 0.0
markout     1s: -0.0463 per share over 44594 fills
markout    10s: -0.0408 per share over 43760 fills
markout    60s: -0.0397 per share over 33437 fills
markout settle: -0.0462 per share over 44606 fills

这里一笔 trade 指从开仓到平仓的一个持仓,所以 1,105 笔持仓由约 44,600 次挂单成交累积而成。1 秒 markout 平均每份 −4.6¢,到结算时仍约 −4.6¢。这么简单的报价,往往在对手知道点什么的时候才被成交,比如 BTC 现货刚动、5 分钟市场还没重新定价。这些成交亏掉的,远比赚到的价差多。

挂出去的量只有 3.8% 成交了。按中位数算,一笔成交对应的挂单在加入队列时,前面排着约 95 份。如果回测在价格一碰到报价就让它成交,这看起来就会是一个每笔都赚价差的策略。

任何 Polymarket 回测都该过一遍的清单

我们看一个 Polymarket 回测时,会检查这些:

  • 逐市场的手续费。 taker 手续费取决于价格和每个市场自己的费率。按固定费率估算,或者干脆不算,都会高估吃单策略的收益。
  • 盘口深度。 订单要按当时的盘口逐档成交;盘口上没有的量要如实报告,不能都按最优价成交。
  • 延迟。 订单要和它实际到达时的盘口撮合,而不是信号触发时的盘口。
  • 挂单要排队。 价格碰到不等于成交:前面排着的量得先被成交吃完。跳过这一步,做市策略会显得比实际好得多。
  • Markout。 对挂单,还要看成交之后中间价怎么走。1 秒 markout 能解释盈亏:它显示价格是不是几乎立刻就朝不利方向走了。
  • 按官方结果结算。 这些市场按某条 Chainlink 价格流结算,具体是哪条写在市场自己的配置里。用自己挑的现货价格结算,可能把结果很接近的市场判错。
  • 样本外的日子。 一天的结果不足以下结论。先在较早的日子上给参数排名,再拿排名靠前的组合去没参与排名的后续日子上检验。

怎么复现

这两个测试的代码是 polymarket-backtest 里的 quickstart.py 和 maker_quotes.py。用 pip install outcometick 安装,load_sample() 会下载当前的免费样本日,上面这些数字来自 2026-09-08 那一天。样本日会不定期更新,换了新的一天,跑出来的数字也会不同。在 8 核机器上每次运行大约两分钟。

回测工具是 MIT 许可,另有 JS 版。样本数据按 CC BY 4.0 免费提供,不需要 key 也不用注册,Kaggle 和 Hugging Face 上也有。样本外检验可以用 sweep(..., holdout=0.3);想不下载任何数据、直接在最近几周上跑同一个策略类,用托管回测。

如果你在别的日子上找到了 1 秒 markout 不为负的做市变体,欢迎到 Discord 发给我们看看。

相关页面