Polymarket PnL精准计算:为什么你算的盈亏可能是错的?

Bitsfull2026/05/02 11:0015335

摘要:

做 Polymarket 量化,第一步不是找策略,是先确认自己的收益算没算对。


我在 Polymarket 自研自动化交易半年,踩过的最大坑不是策略失灵,是连自己赚了多少钱都算不对。


不是我菜。是 PM 的 PnL 计算本身就是雷区。官方 API 给你的数字是错的,第三方分析网站展示的排名也是错的。你自己写脚本算?大概率还是错的。


偏差有多离谱?排行榜第 3 名 kch123,用错误方法算出来亏 $350 万,实际盈利 $1140 万。不是差几个百分点——是盈亏符号都反了。


这篇把我踩过的每个坑拆开讲。做交易的、写工具的、看排行榜的,迟早会遇到。


坑 1:cashPnl 不包含已结算的盈利


最直觉的做法:拉 /positions 接口,求和 cashPnl(现金盈亏)字段。


拿排行榜前 15 的三个地址实测:


swisstony:cashPnl 求和 +$3.5 万,排行榜实际 +$560 万,差 158 倍


kch123:cashPnl 求和 -$352 万,排行榜实际 +$1140 万,符号反转


gmanas:cashPnl 求和 -$264 万,排行榜实际 +$502 万,符号反转


三个地址,两个盈亏符号直接反了。


原因:/positions 接口返回的 cashPnl 不包含已 closed/redeemed 的 realized PnL。赢了的仓位被自动赎回成 USDC 后,这个 position 就从 API 响应里消失了。留下来的是未结算的持仓——往往以浮亏为主。


你以为在算全部盈亏,其实只拿到了未结算的部分。


坑 2:makerPnl 字段与链上现金流不一致


交易数据 JSONL 里有个 makerPnl(做市盈亏)字段,看名字就是给你算 PnL 用的。别信。


我在做市数据中观察到,SUM(makerPnl) 算出来的数字跟链上现金流核算结果差了一个数量级。具体倍数可能因场景不同而变化,但方向一致:makerPnl 的内部计算逻辑跟实际 USDC 流动对不上。


不管偏差多大,结论一样:不要用这个字段算 PnL。


坑 3:不能按 txHash 单独去重


这个最反直觉。


同一个 txHash(交易哈希)出现了多条记录,正常人第一反应:重复数据,去重。


不能这么做。PM 的 CLOB(链上限价订单簿)在一笔链上交易里可以撮合多个 maker 订单,同一个 txHash 下的多条记录是真实的独立 fill。


我之前按 txHash + asset 去重,BUY 侧少算了 $133。上 Polygon 链验证,一个交易哈希里确实有多个独立的 USDC Transfer event,每条都对应一笔真实成交。


结论:不能按 txHash alone 去重。要算 PnL,直接对 /activity 原始数据求和。


坑 4:offset 翻页有天花板


/activity 接口翻页,用 offset(偏移量)?超过 3000 条直接 400 报错。文档里没写。


上面三个地址全部验证过:GET /activity?offset=3100 返回 HTTP 400,错误信息 max historical activity offset of 3000 exceeded。头部玩家动辄上万笔交易,3000 条根本不够。


用 end 参数(传上一页最后一条的时间戳 - 1)做游标翻页,没有上限。


坑 5:排行榜 PnL 口径差异


你算完一个地址的 PnL,去排行榜一对比,差了一点。


大多数情况下差距在 $10 以内(来自持仓市值的实时波动)。但如果差距明显更大,可能的原因包括:排行榜的聚合窗口、缓存刷新延迟、或者用户绑定了多个 proxy wallet。


实测中,用现金流法算出的单地址 PnL 跟 lb-api 返回值高度一致。如果你的结果差距较大,先检查翻页是否完整(坑 4)、是否用了错误字段(坑 1-2)。


正确做法


试了各种歪路之后,我验证下来最可靠的方法是 Data API 现金流汇总。不用任何预计算字段,直接从原始交易记录算资金进出。


公式:


PnL = SUM(TRADE where side=SELL) + SUM(REDEEM) + SUM(MERGE) + SUM(MAKER_REBATE) + SUM(REWARD) - SUM(TRADE where side=BUY) - SUM(SPLIT) + 持仓市值


· TRADE BUY:花 USDC 买 token(支出)

· TRADE SELL:卖 token 回收 USDC(收入)

· REDEEM:赢的仓位赎回 USDC(收入)

· SPLIT:USDC 铸造成 token 对(支出)

· MERGE:token 对合并回 USDC(收入)

· MAKER_REBATE:Maker 返佣(收入)

· REWARD:奖励/空投(收入)


· 数据来源:

GET /activity?user=<address>&limit=500,用 end 翻页,全量拉取后按类型求和。


· 持仓市值:

GET /positions?user=<address>,size × curPrice。


· 交叉验证:

拿计算结果跟 Polymarket 排行榜 API(lb-api.polymarket.com/profit?window=all&address=X)对比,差 <$10 就算过。差距来自持仓市值的实时波动。


验证:排行榜前 15 实测


用现金流法算完后,拿排行榜 API 交叉验证:


swisstony:现金流法 +$560.1 万,排行榜 +$560.1 万,差距 < $10


kch123:现金流法 +$1139.6 万,排行榜 +$1139.6 万,差距 < $10


gmanas:现金流法 +$502.4 万,排行榜 +$502.4 万,差距 < $10


三个地址误差均在 $10 以内,差距来自持仓市值的实时波动。


方法跑通之后,我拿它分析了上百个头部地址的真实盈亏。那是另一回事了。


汇总


SUM(cashPnl) from /positions → 不行,不含已结算盈利,符号可能反转


makerPnl 字段求和 → 不行,与链上现金流不一致


按 txHash 去重后计算 → 不行,$100+,删了真实 fill


offset 翻页 + 求和 → 不行,数据截断,>3000 报错


Data API 现金流法 → 目前最可靠,<$10


做量化的第一步不是找 alpha。是先确认你算得对。


以上全部来自实盘踩坑,不是理论推导。PM 的 API 随时可能调整行为,建议定期用排行榜 API 交叉验证你的计算结果。