
这不仅仅是一份技术清单,而是一个层层递进的故事。跳过任何一环都可能导致数学逻辑断裂。请跟随指引,到最后你将获得构建一个机构级预测市场模拟引擎每一层的可运行代码。
免责声明:本文内容不构成任何财务建议,请自行研究。
第一部分:颠覆一切的抛硬币游戏
想象你正盯着一份 Polymarket 合约。“美联储会在3月降息吗?”“是”的交易价格是 $0.60。
我们本能地会想:这代表一个 60% 的概率。也许你认为真实概率应该是 70%,于是你选择买入。
恭喜,你刚刚做了每个散户交易员都会做的事:你把一个复杂的预测市场合约,简化成了一个已知偏差的抛硬币游戏,估算了自己的偏差,并押注于差额。但问题来了:
- 你对自己估算的 70% 有多大信心?
- 明天的就业报告发布后,这个概率应该如何变化?
- 它如何与 Polymarket 上其他六份与美联储相关的合约相关联?
- 从现在到结算之间的价格路径,是否允许你在最终判断正确的情况下仍能获利退出?
抛硬币只有一个参数:$p$。
而一个嵌入在相关事件投资组合中、伴有动态信息流、订单簿变化和执行风险的预测市场合约,则拥有几十个参数。我们需要更强大的工具。
第二部分:蒙特卡洛——一个未被充分重视的基石
本文中的所有模拟,最终都归结为蒙特卡洛方法:从分布中抽取样本,计算统计量,然后不断重复。
对于一个事件,其概率 $p$ 的估计量就是样本均值:

中心极限定理给出了收敛速率:$O(1/\sqrt{N})$,方差为 $p(1-p)/N$。
当 $p=0.5$ 时,方差最大。这意味着,平台上交易价格在 50 美分(即最不确定、交易最活跃)的合约,恰恰是你的蒙特卡洛估计最不精确的地方。
当 $p=0.5$ 时,若想在 95% 的置信度下达到 ±1% 的精度:

这个样本量还算可控。但当你需要模拟整条路径,而不仅仅是终点时,情况会迅速恶化。

你的第一个可运行模拟
目标:估计一个与资产挂钩的二元合约的支付概率(例如,“苹果股价在3月15日收盘时会高于 $200 吗?”)。
import numpy as np
def simulate_binary_option(S0=100, K=200, T=1.0, mu=0.05, sigma=0.2, r=0.03, n_paths=10000):
"""
模拟 Black-Scholes 框架下的二元期权(数字期权)支付概率。
S0: 初始资产价格
K: 行权价
T: 到期时间(年)
mu: 资产期望收益率
sigma: 资产波动率
r: 无风险利率
n_paths: 模拟路径数
"""
np.random.seed(42)
# 生成对数正态分布的终端价格
Z = np.random.randn(n_paths)
ST = S0 * np.exp((mu - 0.5 * sigma**2) * T + sigma * np.sqrt(T) * Z)
# 计算支付:到期价格 > K 则支付 1,否则为 0
payoffs = (ST > K).astype(float)
# 估计风险中性概率并折现
estimated_prob = np.mean(payoffs)
discounted_price = np.exp(-r * T) * estimated_prob
return discounted_price, estimated_prob
price, prob = simulate_binary_option()
print(f"估算的风险中性价格: {price:.4f}")
print(f"估算的物理概率: {prob:.4f}")
这代码有效,但它只针对一份合约、一个标的资产,并假设了几何布朗运动。真实的预测市场打破了所有这些假设。
评估你的模拟
在我们改进模拟之前,需要一种方法来衡量它的好坏。Brier Score 是标准的概率校准指标:

Brier Score 低于 0.1 算不错,低于 0.05 则非常出色。顶尖的选举预测机构(如 538、经济学人)在总统竞选中历史上的 Brier Score 大约在 0.03 左右。如果你的模拟能超越这个水平,你就具备了优势。
第三部分:当10,000个样本还不够时
故事现在升级了。Polymarket 上充斥着极端事件合约。“标普500指数会在一周内下跌 20% 吗?”交易价格可能是 $0.01。使用 10,000 个样本的朴素蒙特卡洛模拟,你可能只看到零次或一次“命中”。
你的估计要么是 0,要么是 0.0001——两者都毫无用处。这不是理论问题,而是大多数散户交易者无法正确评估尾部风险合约的原因。
让稀有事件变得“常见”
重要性采样通过用一个过度采样稀有区域的概率测度替换原始测度,然后用似然比(Radon-Nikodym导数)校正偏差来解决这个问题。
虽然定义有些抽象,但它指明了方向。实践中,指数倾斜是一种常用方法。
如果你的标的资产遵循随机游走,其增量 $X_i$ 的矩母函数为 $M(θ) = E[e^{θX_i}]$,那么你可以对分布进行“倾斜”:

选择合适的 $γ$,使得稀有事件变得典型。对于当总和超过一个大阈值时才支付的合约,$γ$ 可以通过解 Lundberg 方程 $M(γ)=1$ 得到。
重要性采样对于尾部风险合约的意义:在极端事件合约上,重要性采样可以将方差降低数个数量级。这意味着 1,000 个重要性采样样本可能比 1,000,000 个朴素样本提供更好的精度。这不是微小的改进,而是“我们无法定价”和“我们正在交易”之间的区别。
第四部分:用于实时更新的序列蒙特卡洛(粒子滤波)
但是,当我们需要从静态估计转向动态模拟时,该怎么办?
想象一下:选举之夜,晚上8点,佛罗里达州投票站关闭。初步结果显示某候选人领先5个百分点。你的模型需要立即更新,将这个新数据点纳入对全国各州胜率的估计中。
这就是状态空间滤波问题,而工具是序列蒙特卡洛,也就是粒子滤波器。
状态空间模型
定义:
- 隐藏状态 $x_t$:事件的“真实”概率(不可直接观测)。
- 观测 $y_t$:市场价格、民调结果、计票数、新闻信号。
状态通过 Logit 随机游走演化(以保持概率在[0,1]区间内):

观测值是真实状态带有噪声的读数:

自助粒子滤波器
该算法维护 $N$ 个“粒子”——每个粒子都是对真实概率的一个假设。随着新数据的到来,粒子会根据其与观测值的似然程度被重新赋权。
import numpy as np
from scipy.stats import norm
class ParticleFilter:
def __init__(self, n_particles=1000, process_noise=0.1, obs_noise=0.05):
self.n_particles = n_particles
self.process_noise = process_noise
self.obs_noise = obs_noise
# 初始化粒子:从均匀分布中抽取概率,并转换为logit空间
self.particles = np.random.uniform(0.01, 0.99, n_particles)
self.weights = np.ones(n_particles) / n_particles
self.logit_particles = np.log(self.particles / (1 - self.particles))
def predict(self):
# 在logit空间进行随机游走
epsilon = np.random.randn(self.n_particles) * self.process_noise
self.logit_particles += epsilon
# 转换回概率空间
self.particles = 1 / (1 + np.exp(-self.logit_particles))
def update(self, observation):
# 计算每个粒子的似然:观测到的价格围绕粒子概率的正态分布
likelihood = norm.pdf(observation, loc=self.particles, scale=self.obs_noise)
# 更新权重
self.weights *= likelihood
self.weights += 1e-12 # 防止归零
self.weights /= np.sum(self.weights)
# 重采样
indices = np.random.choice(range(self.n_particles), size=self.n_particles, p=self.weights)
self.particles = self.particles[indices]
self.logit_particles = self.logit_particles[indices]
self.weights = np.ones(self.n_particles) / self.n_particles
def estimate(self):
return np.mean(self.particles)
# 使用示例
pf = ParticleFilter(n_particles=5000)
observations = [0.55, 0.58, 0.62, 0.60, 0.65] # 模拟观测到的市场价格序列
for obs in observations:
pf.predict()
pf.update(obs)
print(f"观测后估计概率: {pf.estimate():.4f}")
为什么这比直接使用市场价格更好?因为粒子滤波器可以平滑噪声并传递不确定性。当市场因一笔大单使价格从 $0.60 飙升至$0.70 时,滤波器会识别出真实概率可能并未发生如此剧烈的变化,它会根据观测噪声的设定来调整更新幅度。
第五部分:三种可叠加的方差缩减技巧
在我们离开蒙特卡洛领域之前,介绍三种可以乘法级提升效率的技巧。
1. 对偶变量法
当支付函数是单调的(二元合约总是如此——价格越高,超过行权价的概率越大),使用对偶变量可以保证方差缩减:

典型的缩减幅度约为 $1+ρ$ 因子($ρ$为负)。除了函数评估次数翻倍(这通常是计划内的),几乎没有额外计算成本。
2. 控制变量法——利用已知信息
如果你正在模拟随机波动率下的二元合约 $1_{S_T > K}$(无解析解),可以使用具有解析解的 Black-Scholes 数字期权价格 $p_{BS}$ 作为控制变量:

3. 分层抽样法
将概率空间划分为 $K$ 个层,在每个层内分别抽样,然后合并。其方差永远不大于朴素蒙特卡洛(根据全方差定律)。最大收益来自于 Neyman 分配:根据每层的方差分配样本数,对高方差层过度抽样。
叠加所有三种方法:在每层内部使用对偶变量,再进行控制变量校正,通常可以实现相比朴素蒙特卡洛高达 $10-100$ 倍的方差缩减。在生产环境中,这不再是可选项,而是基本要求。
第六部分:相关矩阵无法捕捉的尾部依赖
分层贝叶斯模型通过共享的“全国性摇摆”参数隐式地编码了相关性。但尾部依赖呢?即极端事件同步发生的趋势,而这在线性相关中无法体现。
2008年,高斯 Copula 在建模尾部依赖上的失败加剧了全球金融危机。预测市场中也存在同样问题:当一个关键摇摆州出现意外结果时,所有摇摆州同时翻转的概率,远高于高斯 Copula 所预测的。
Sklar 定理

其中 $C$ 是 Copula(纯粹的依赖结构),$F_i$ 是边缘累积分布函数。这允许你单独为每个市场建模其边缘行为,然后用一个捕获了依赖关系(包括尾部依赖)的 Copula 将它们“粘合”起来。
尾部依赖问题
- 高斯 Copula:尾部依赖系数为 $0$。极端同步运动被建模为零概率。对于相关的预测市场,这是一个灾难性的错误。
- 学生 t Copula:

当 $ν=3$ 且 $ρ=0.8$ 时,尾部依赖系数约为 $0.4$——这意味着当一个合约达到极端值时,另一个合约也同步达到极端值的概率是 40%。而高斯 Copula 会给出 0%。
- Clayton Copula:仅具有下尾部依赖。一个市场崩溃时,其他市场也更可能崩溃。没有上尾部依赖。
- Gumbel Copula:仅具有上尾部依赖。适用于关联的积极结果同时发生的情景。
模拟相关的预测市场结果时,选择正确的 Copula 至关重要。这正是高斯 Copula 在2008年失败的原因,也将在预测市场投资组合中重演。使用学生 t Copula($ν=3$)通常能将极端联合结果的概率提高 10 倍以上。如果你在交易相关的预测市场合约时没有建模尾部依赖,你的投资组合将在最关键的情景下崩溃。
Vine Copula
对于超过5个合约的情况,二元 Copula 已不足够。Vine Copula 将 $d$ 维依赖分解为 $d(d-1)/2$ 个二元条件 Copula,并将其排列成树状结构:
- C-vine(星形):一个中心事件驱动一切(例如,总统获胜结果 -> 所有政策市场)。
- D-vine(路径形):顺序依赖(例如,初选结果流向大选结果)。
- R-vine(一般图):最大灵活性。
构建过程涉及通过最大生成树对成对关系排序,根据 AIC 等准则为每对选择 Copula 族,并进行顺序估计。实现库包括 pyvinecopulib (Python) 和 VineCopula (R)。
第七部分:基于代理的模拟
目前为止的一切都假设你了解数据生成过程,只需要模拟它。但预测市场由异质代理(知情交易者、噪声交易者、做市商、机器人)组成,它们的互动产生了内生动态,这是任何封闭形式的随机微分方程都无法捕捉的。
零智能的启示
市场即使由完全非理性的交易者组成,也可以是有效的。Gode & Sunder (1993) 表明,零智能代理——仅受预算约束提交随机订单的交易者——在连续双向拍卖中实现了接近 100% 的配置效率。Farmer, Patelli & Zovko (2005) 将此扩展至限价订单簿。这可以解释伦敦证券交易所高达 96% 的横截面价差变动。仅用一个参数。
基于代理的预测市场模拟器
通过模拟不同策略的代理(如趋势跟随者、价值投资者、做市商)在虚拟订单簿中的互动,可以研究价格发现、流动性形成和泡沫产生等微观结构现象。这种模拟可以帮助理解价格收敛速度如何受知情交易者比例、做市商利差对信息流的响应等因素影响。
第八部分:生产级系统架构
以下是完整的系统堆栈,从市场数据到交易执行:
-
数据摄入层
- Polymarket CLOB API 的 WebSocket 数据流(实时价格、交易量)。
- 新闻/民调数据流(经 NLP 处理为概率信号)。
- 链上事件数据(Polygon)。
-
概率引擎层
- 分层贝叶斯模型(使用 Stan/PyMC)用于州级后验估计。
- 粒子滤波器用于实时更新新观测。
- 用于风险管理的跳跃扩散 SDE 路径模拟。
- 模型集成:对不同模型的输出进行加权平均。
-
依赖建模层
- 使用 Vine Copula 对合约间的成对依赖性建模。
- 因子模型用于捕获共享的国家/全球风险因子。
- 通过 t-Copula 进行尾部依赖性估计。
-
风险管理层
- 基于极值理论的 VaR 和预期短缺计算。
- 逆向压力测试以识别最坏情况。
- 相关性压力测试:如果州之间的相关性飙升会怎样?
- 流动性风险:订单簿深度监控。
-
监控层
- Brier Score 跟踪(我们的预测校准了吗?)。
- 盈亏归因(哪个模型组件增加了价值?)。
- 回撤警报。
- 模型漂移检测。
参考文献
- Dalen (2024). "Toward Black-Scholes for Prediction Markets." arXiv
- Saguillo et al. (2024). "Unravelling the Probabilistic Forest: Arbitrage in Prediction Markets." arXiv
- Madrigal-Cianci et al. (2024). "Prediction Markets as Bayesian Inverse Problems." arXiv
- Farmer, Patelli & Zovko (2005). "The Predictive Power of Zero Intelligence." PNAS
- Gode & Sunder (1993). "Allocative Efficiency of Markets with Zero-Intelligence Traders." JPE
- Kyle (1985). "Continuous Auctions and Insider Trading." Econometrica
- Glosten & Milgrom (1985). "Bid, Ask, and Transaction Prices." JFE
- Hoffman & Gelman (2014). "The No-U-Turn Sampler." JMLR
- Merton (1976). "Option Pricing When Underlying Stock Returns Are Discontinuous." JFE
- Linzer (2013). "Dynamic Bayesian Forecasting of Presidential Elections." JASA
- Gelman et al. (2024). "Updated Dynamic Bayesian Forecasting Model." HDSR
- Aas, Czado, Frigessi & Bakken (2009). "Pair-Copula Constructions of Multiple Dependence." Insurance: Mathematics and Economics
- Wiese et al. (2020). "Quant GANs: Deep Generation of Financial Time Series." Quantitative Finance
- Kidger et al. (2021). "Neural SDEs as Infinite-Dimensional GANs." ICML
原文链接:https://x.com/gemchange_ltd/status/2027744530124951831
希望这篇从基础到进阶的指南,能为你构建自己的预测市场分析工具提供扎实的起点。更多关于量化金融与复杂系统建模的深度讨论,欢迎在云栈社区交流分享。