做预测市场聚合器之前,我以为难点会在交易链路上——签名、下单、撮合、结算,这些是我熟的东西。
真正把我卡住的,是一个听起来很蠢的问题:
怎么确认 Polymarket 上的这个市场,和 predict.fun 上的那个市场,说的是同一件事?
这个问题没解决,后面所有功能都是空中楼阁。因为聚合器的全部价值——比价、找最优执行、发现跨平台价差——都建立在”这两个市场可以拿来比”这个前提上。前提错了,你不是在帮用户省钱,是在帮用户亏钱。
这篇讲清楚这个问题为什么难,以及我最后是怎么拆的。
背景:预测市场本身是什么、为什么 2025 年爆发,我写在《预测市场的 2026:从”赌博网站”到 $1275 亿的全球概率基础设施》。这篇假设你已经知道”份额价格 = 隐含概率”这件事。
为什么没有一个共同 ID
如果两个平台共享某种全局事件 ID,这篇文章就不用写了。现实是没有。
Polymarket 和 predict.fun 在架构上其实高度同构——都用 Gnosis 的条件代币框架(CTF)、都是 ERC-1155、都是链下 CLOB 撮合 + 链上原子结算。你可能会以为,同构就意味着标识符可以对齐。
恰恰相反。 看一眼 CTF 的 ID 派生链路:
questionId = keccak256(问题元数据)
conditionId = keccak256(oracle 地址, questionId, outcomeSlotCount)
tokenId = keccak256(抵押品合约地址, collectionId)
三层里每一层都带着平台自己的东西:
questionId取决于这个平台怎么写这段问题元数据(文案、结算规则、数据源,一个字不一样,哈希就完全不同)conditionId里混进了这个平台用的 oracle 地址tokenId里混进了这个平台的抵押品合约地址——Polymarket 是 Polygon 上的 USDC.e,predict.fun 是 BNB Chain 上的稳定币
所以结论很硬:
哪怕两个平台对同一个现实事件,写了一模一样的问题文案、设了一模一样的截止时间,它们的 questionId / conditionId / tokenId 也必然完全不同。
链上标识符在这里帮不了你。不要试图用 tokenId 做跨平台的 key,这是我最早走的弯路。
那用什么?只剩下问题本身的语义。而语义是脏的。
脏在哪:四种会咬人的差异
我把踩过的坑归成四类,从”容易发现”到”最阴”排序。
1. 文案差异(最容易,也最容易让人放松警惕)
同一件事,两个平台的写法可能是:
Will Bitcoin reach $120,000 by September 30, 2026?BTC above $120K on Sep 30 2026?
大小写、缩写(BTC / Bitcoin)、数字格式($120,000 / $120K)、日期格式、有没有问号。这一层用规范化 + 模糊匹配基本能搞定,是最不值得担心的一层。
但它危险的地方在于:它会让你产生”这个问题就是个字符串匹配问题”的错觉,然后你就会栽在下面三层。
2. 阈值与比较符差异(开始要命)
看这两个:
- A 平台:
BTC ≥ $120,000 - B 平台:
BTC > $120,000
文案上差一个字符,实际是两个不同的市场。如果 BTC 最终精确收在 $120,000.00,一个赔付 YES,一个赔付 NO。
同类的还有:
| 差异点 | 例子 |
|---|---|
| 开闭区间 | ≥ vs >、between 3-4% 含不含端点 |
| 单位口径 | $120K vs 120,000 USD vs 以某交易所报价计 |
| 精度 | 保留几位小数、四舍五入还是截断 |
这一层已经不能靠文本相似度判断了,必须把阈值和比较符抽成结构化字段单独比。
3. 时间口径差异(很阴)
这一层我吃过亏。两个市场都写着”9 月 30 日”,但:
- 时区:UTC 的 9/30 24:00 和 UTC+8 的 9/30 24:00,差 8 小时。在这 8 小时里价格剧烈波动是完全可能的
- 判定时点 vs 截止时点:市场停止交易的时间,和判定结果的时间,是两件事。有的平台在事件发生后立刻停止交易,有的会挂到数据源正式发布
- 数据源发布延迟:宏观数据类市场(GDP、CPI)判定依赖官方发布,而官方发布时间本身可能推迟
结果就是:两个”同一个事件”的市场,可能在不同的时刻停止交易、在不同的时刻结算。 对聚合器意味着什么?意味着你以为可以两边同时平掉的仓位,实际上有一段时间是单边裸敞口。
4. 结算规则与数据源差异(最阴,也是真正的身份)
这是我最后想明白的一层,也是整篇文章我最想说的一句话:
一个预测市场的真正身份,不是它的问题文案,而是它的结算规则。
同样问”BTC 是否突破 $120K”,两个平台可能:
- 取的价格源不同(某个交易所现货 / 某个指数 / 某个预言机 feed)
- 判定方式不同(收盘价 / 期间最高价 / 连续 N 分钟均价)
- 争议机制不同(乐观预言机的争议窗口 vs 价格 feed 自动结算)
我在整理两个平台的资料时注意到一个结构性差异:Polymarket 的长期事件走 UMA 乐观预言机,有提议 + 争议窗口;而 predict.fun 除了乐观预言机路径,还对短期价格市场用 Chainlink 做自动结算(据其 2026 年 4 月的公开公告,从 BTC 的 15 分钟市场开始)。
这意味着同一个”BTC 涨跌”的问题,在两个平台可能走完全不同的裁定路径——一个可能因为争议拖上数天,一个到点即结。
如果你把这两个市场当成同一个来做对冲,你对冲的不是同一个风险。
所以我不敢只用 LLM
一开始我很自然地想:这不就是语义匹配吗,扔给模型不就完了。
试完的结论是:LLM 适合做召回,绝对不能单独做终判。
原因很具体:
- 它对文案敏感,对规则不敏感。 两个市场文案几乎一样但结算数据源不同,模型大概率给你一个很高的相似度分
≥和>这种细节,恰恰是模型最容易抹平的——它倾向于认为这是”表达差异”- 它会幻觉出自信。 你要的是”我不确定”,它给你的是一个 0.93
- 错误代价不对称。 漏掉一个可匹配的市场,损失是少一个机会;错配一对市场,损失是用户真金白银的裸敞口
第 4 点是关键。在这个场景里,假阳性(错配)的代价远高于假阴性(漏配)。 任何一个倾向于”给出答案”的方案,方向上就是错的。
我最后的做法:三层漏斗 + 置信度
我把它做成一条从宽到严的漏斗,每一层负责不同的事。
候选池(两个平台的全部活跃市场)
│
▼
① 粗召回:规范化文本 + 关键实体 ← 要「宽」,宁可多召
│ 标的(BTC/SOL/选举/球队)、方向、数字、日期
▼
② 结构化硬校验:把市场解析成一个规范对象 ← 要「严」,一票否决
│ { 标的, 比较符, 阈值, 单位, 截止时间(UTC), 判定时点, 数据源, 结果数 }
▼
③ 结算规则比对 + 置信度分级 ← 要「诚实」,允许说不确定
│
▼
高置信 → 自动配对
中置信 → 只展示行情,不允许跨平台对冲操作
低置信 → 进人工复核队列,默认不配对
几个我认为关键的设计决定:
第一,把市场解析成一个”规范对象”,而不是比字符串。
核心字段大概是这样:
{
"subject": "BTC", // 标的
"metric": "spot_price", // 判定的量
"comparator": "gte", // 比较符:gt / gte / lt / lte / between
"threshold": 120000,
"unit": "USD",
"closeAt": "2026-09-30T23:59:59Z", // 停止交易,统一转 UTC
"resolveAt": "2026-09-30T23:59:59Z", // 判定时点,可能 ≠ closeAt
"resolutionSource": "...", // 数据源
"outcomeCount": 2 // 2 = 二元;>2 走多结果(negRisk)路径
}
能不能解析出这个对象,本身就是一道闸。 解析不出来(文案太自由、规则写在一大段散文里)就直接进人工队列,不猜。
第二,硬字段一票否决。
comparator、threshold、unit、outcomeCount 只要有一个不一致,无论文本多像、模型多自信,直接判不匹配。这几个字段没有”差不多”这回事。
第三,closeAt 和 resolveAt 分开存,并且允许有容差但要显式声明。
两个平台的 closeAt 差 8 小时,不代表不是同一件事,但必须把这个差值带到下游——下游做跨平台对冲时要知道”这里有 8 小时的单边敞口窗口”。把差值吞掉是最危险的做法。
第四,置信度必须落到”能不能下单”这个动作上,而不只是一个展示用的分数。
这是我觉得最重要的一条。置信度如果只是 UI 上一个小标签,它就没有意义。它必须真的卡住操作权限:
| 置信度 | 允许做什么 |
|---|---|
| 高 | 聚合行情 + 跨平台比价 + 允许对冲/套利操作 |
| 中 | 聚合行情 + 比价,禁止一键跨平台操作 |
| 低 | 两个市场分开展示,不声称它们相关 |
宁可少给一个功能,不要给一个错的对冲。
第五,多结果市场(negRisk)单独处理。
两个平台都有多结果市场的机制(一个事件下多个互斥选项,比如多候选人选举)。这时匹配不是”一对一”,而是要匹配整个结果集合:选项数量、每个选项的语义、以及”是否互斥且完备”都要对上。
一个很实际的坑:A 平台把选举拆成”候选人 A / 候选人 B / 其他”,B 平台只有”候选人 A / 候选人 B”。这两个事件不能整体对齐——虽然”候选人 A 获胜”这个子市场看起来能对上,但两边的概率归一化基数不同。
这件事的工程量,比它听起来大得多
写到这我想说个可能反直觉的判断:
事件对齐不是聚合器的”数据预处理步骤”,它就是聚合器的核心资产。
理由:
- 交易链路是可复制的。两个平台都是 CTF + CLOB + EIP-712,把 adapter 写完就是写完了,竞争对手也能写
- UI 是可抄的
- 而一个高正确率、覆盖面广、且知道自己哪里不确定的事件对齐系统,是需要持续投入人工校准和规则积累的——它随时间变厚
这和我之前拆 Meme 交易平台时的结论是同一个形状:头部平台真正的护城河不是前端,是 Indexer。预测市场聚合器的护城河,也不在下单按钮上,在事件对齐的正确率和它的诚实度上。
几条我会给自己的提醒
如果你也在做类似的东西:
- 别用链上 ID 做跨平台 key。
tokenId里混着抵押品地址,conditionId里混着 oracle 地址,同一个事件在两个平台必然不同 - 把结算规则当成市场的真正身份,文案只是它的显示名
≥和>要单独存字段,这不是文案细节,这是两个不同的赔付函数- 时间统一转 UTC,
closeAt和resolveAt分开,差值要传给下游而不是吞掉 - LLM 只做召回,硬字段做终判,并且允许系统输出”我不确定”
- 置信度要卡住操作权限,不能只是个展示标签
- 假阳性远比假阴性贵。 在这个场景里,保守是正确的工程价值观
小结
聚合器看起来是个”接两个 API 然后并排展示”的活。真做下去会发现,最难的部分不在任何一个平台的 API 文档里——它在两个平台对同一个现实事件的不同描述之间的缝隙里。
而这个缝隙,是没有 API 能帮你填的。
下一篇写另一面:既然两个平台架构如此同构(都是 CTF + CLOB),为什么”一套代码跑两个平台”依然会到处漏——以及那些藏在常量里的坑。
延伸阅读
- 预测市场的 2026:从”赌博网站”到 $1275 亿的全球概率基础设施 —— 这个赛道的全景与 Polymarket 架构概览
- 头部 Meme 平台月烧 $700K,钱花在什么上 —— “真正的护城河在数据层”的同形结论
- 「不就是加个 adapter 吗?」——多链扩展的三个血泪教训 —— 多平台抽象为什么总是泄漏