预测市场看起来像是世界上最容易回测的东西。价格在 [0, 1] 之间。收益要么是1美元,要么一无所获。没有杠杆、没有资金费率、没有基差,也没有 UTC 00:00 时永续合约那一堆怪事。我们已经有一套能处理加密永续合约费用、资金费率和冲击成本的回测器,原计划花两天改造一下,然后就开始生成策略。
结果花了八天。下面是我们的记录,大致按时间排序,也包括权益曲线看起来惊人、而实际上也确实惊人的那一天。
第1天:本该轻而易举的适配器
最初的映射是一一对应的,看起来很顺畅。一个市场就是一个标的。YES 价格就是价格。买入 YES 就是做多,买入 NO 就是做空。结算时强制以1.00或0.00平仓。把小时价格序列输入同一个事件循环,就搞定了。
这个映射经真实数据检验,大约九十分钟就撑不住了。首先出问题的是收益率序列。我们的整套风险层——头寸规模、波动率目标、Sharpe 报告——都假设标的是持续交易的,并使用对数收益率。一个从0.04跌到0.00的市场,对数收益率未定义,但亏损总额明确。并且,结算前三天价格为0.04的市场,与结算前四分钟价格为0.04的市场,性质截然不同,即使两行都写着 0.040.04
最后我们把整个序列改为以距结算时间为参数,而非挂钟时间,并将 PnL 视为终值,而不是盯市值。这个决定是对的,但也让下游所有报告代码都失效了。
第2天:费用公式就是策略本身
Kalshi 的交易费不是按基点收取的折扣,而是关于价格的二次函数:大致为 0.07 × contracts × P × (1−P)0.07 × C × P × (1 − P)
| 价格 | 每份合约费用 | 所需优势(概率百分点) | 费用占本金比例 |
|---|---|---|---|
| 5¢ | 0.33¢ | 0.33 | 6.7% |
| 10¢ | 0.63¢ | 0.63 | 6.3% |
| 25¢ | 1.31¢ | 1.31 | 5.3% |
| 50¢ | 1.75¢ | 1.75 | 3.5% |
| 75¢ | 1.31¢ | 1.31 | 1.8% |
| 90¢ | 0.63¢ | 0.63 | 0.7% |
| 95¢ | 0.33¢ | 0.33 | 0.35% |
第三列才是关键:如果以50¢买入并持有到结算,要达到盈亏平衡,你的概率估计必须比市场高1.75个百分点。不是相对高1.75%。而是绝对高1.75个百分点。如果不持有到结算,而是在之前退出,就要付两次费用,外加价差。
Polymarket 的 CLOB 历来采用截然不同的费用模式,交易本身的费用接近于零,成本主要体现在价差和深度上。因此,同一套策略逻辑在不同平台上的经济效果完全不同,使用同一套费用模型比较不同平台纯属虚构。现在我们为每个平台使用各自的费用函数,而不是一个固定数值。
如果你只看预测市场回测报告中的一行,就看以概率百分点表示的费用。一个声称在50¢市场上有1.2个百分点预测优势的策略,在 Kalshi 上连其他成本都还没算,就已经是亏损策略。这一点应该在报告第一页清楚呈现,而不该让读者自己推算。
第3天:订单簿像阶梯,而且很浅
我们的冲击模型是根据 BTC 永续合约订单簿校准的平方根函数,形状完全不对。在一个价值1美元、最小变动价位为1¢的标的上,整个订单簿只有99个可能的价格档位;中等流动性的市场,最优价位上可能挂着几百份合约,然后就是一道空档。
这是我们采样的一处经济数据市场的快照,YES 侧,距结算约30小时:
| 档位 | 规模(合约数) | 累计买入成本 |
|---|---|---|
| 42¢ | 310 | 310份,均价42.0¢ |
| 43¢ | 85 | 395份,均价42.2¢ |
| 45¢ | 140 | 535份,均价42.9¢ |
| 49¢ | 600 | 1,135份,均价46.1¢ |
一笔1,000份的订单——五百美元风险敞口,在加密货币市场里不值一提——就会让有效价格移动四美分。四美分是费用的两倍多。这里没有可拟合的平滑函数;你得逐档遍历订单簿,否则就是凭空编数字。我们删掉了适用于这类资产的 sqrt 模型,改写成直接逐档遍历订单簿的模型,速度慢一些,但结果准确。
就在这段时间,我发现自己竟然在抱怨这些市场“小得无关紧要”。它们规模小,是因为定价对象是一个有截止日期的现实问题,而关注周三美国首次申请失业救济人数的人就那么多。规模就是市场本身。抱怨市场规模小,就像抱怨一张扑克桌只能坐九个人。
第4天:权益曲线美得不真实的那一天
1,400个市场,Sharpe 4.1。曲线平滑。每个研究员看到这个都该心里一沉,我后来确实沉了——只不过是在已经截图之后大约四十分钟。
问题出在重采样器。流动性差的市场返回的历史价格时间戳不规则,所以我们把数据重新索引到统一的小时网格上。每条归档序列的最后一个点都是结算值,1.00或0.00。我们的重新索引用了最近邻填充,却没有限制填充方向,于是对于最后一笔交易发生在结算数小时前的市场,结算值就沿着缺口向过去传播。模型在今天这一行读到了明天的答案,而这恰好发生在那些流动性差、可以在不受深度限制的情况下加大头寸的市场里。
对连续交易的标的来说,最近邻填充没问题。但对最后一条观测就是真实结果的标的来说,它就是一台前视机器。我们现在会断言,所有填充都只能向前进行,并且结算行必须打上标记,在任何特征计算中排除。这条断言有九行代码,是我们这周写出的最有价值的代码。
第5–6天:1,412个市场,大约180笔投注
预测市场的样本量陷阱看起来很有迷惑性。结算了1,412个市场,你觉得自己有1,412个观测值,于是据此计算 t 统计量。但同一个星期日的十四场 NFL 比赛,共享同一套天气状况、一份伤病报告发布时间和共同的投注者资金流。五十一个州级选举市场,共享同一场全国性选情变化。“X会在3月31日前发生吗”和“X会在6月30日前发生吗”是同一笔投注,只是到期日不同,而且会一起结算。
我们按底层事件来源和结算日期对市场聚类,得到的有效独立样本数大约是180。以我们研究的效应大小,这不足以区分真实优势和噪声;无论按单个市场做多少次自助抽样都解决不了这个问题,因为抽样单位必须是簇,而不是行。弄错这一点,会悄无声息地把显著性夸大两到三倍。
第7天:结算本身也是概率分布
这些是我们完全没有建模、后来吃了苦头才发现的因素:
- 提前结算。“在12月31日前发生”的市场,可能在事件于8月4日真正发生时就结算。你的资金更早回来,而实际持有期从来就不像合约名称暗示的那样。
- 争议。由预言机结算的市场设有质疑期。事件发生后,头寸可能还要搁置数天;少数情况下,结果甚至会从显而易见的解读翻转。
- 作废。来源数据有歧义时,市场会取消,所有人获得退款。在我们的样本里,这类市场不到1%,但它们恰好集中在模型会判断为定价错误的那些模糊问题上。
- 资金占用。90天实现的3个百分点优势,与6小时实现的3个百分点优势无法相提并论。如果回测只报告总优势,却没有计入资金占用时间,就总会偏向持有时间长的策略。
我们在模拟器中加入了持有成本项和结算日期抖动。两者都不精确,但都胜过默认假设结算一定恰好发生在指定日期。
第8天:哪些步骤可以跳过,哪些应该先做
- 完全跳过基于收益率的框架。别把波动率目标风险层改造成适用于终值型收益的工具。直接写一个用概率和投注额来表达的仓位规模层。我们花了两天试图让旧框架适配。
- 先建立费用函数,再做策略。把每个计划交易的平台的盈亏平衡优势曲线打印出来,贴在交易台上。这样能在想法还没耗费一次回测运行之前,就筛掉大约一半。
- 从第一天起就逐档遍历订单簿。从连续市场搬来的任何参数化冲击模型都会出错,而且会让结果对你有利。
- 先聚类,再计数。确定市场范围时,就决定好有效样本量的聚类依据,不要等到得到喜欢的 Sharpe 后才考虑。
- 断言填充方向。每次数据合并都加一行断言。如果序列以真实结果结尾,就从一开始把反向填充当作漏洞,而不是等代码审查时才发现。
这八天很值得,主要是因为预测市场剥掉了那些让加密货币回测特别容易自欺欺人的模糊地带。没有可以敷衍过去的资金费率,也没有需要争论的标记价格惯例。只有一个问题、一个截止日期和一个答案。回测在这里出错时,你能明确指出错在哪里。
← 全部文章


