Type/to search
2
Follow
510
Followers
自适应条件概率建模:二元期权市场的浅思考和实现
Discussions
Created 2026-08-21 14:29:10  Updated 2026-08-24 08:58:02
 0
 3

img

其实一直想对二元期权市场做一次相对完整的建模。

这类市场看上去很简单。以 Polymarket 的 BTC 15 分钟 Up/Down 为例,一轮只有两个结果:到期价格高于基准价则 Up 胜出,否则 Down 胜出。没有复杂的收益曲线,也没有传统期权中那么多执行价与期限组合。

但真正要把它做成量化系统,却很难找到一个合适的切入点。

如果只是预测 BTC 接下来涨还是跌,问题很容易退化成均线、动量、突破或者订单流策略;如果直接把 Polymarket 的合约价格当作概率,又很难回答市场价格是否已经充分反映了当前信息。

前段时间重新翻到贝叶斯理论时,我才想到:二元市场真正需要建模的,或许从来不是“方向”,而是条件概率。

img

这里的 Y是最终结算结果,X_t 是当前能够观察到的信息,包括 BTC 相对本轮基准价的位置、剩余时间、不同周期的波动率、短期漂移、价格路径以及当前信息的不确定程度。

于是问题从“BTC 会不会涨”,变成了:

在当前这些条件已经发生的情况下,BTC 最终收在基准价上方的概率是多少?

这个变化看似只是换了一种表述,实际上决定了整个系统的结构。

条件概率为什么适合二元市场

假设当前 Up 合约可以用 0.58 买到。如果暂时忽略交易摩擦,可以把它理解为市场对 Up 给出了大约 58% 的价格。

如果我们的模型也估计为 58%,即使方向判断是 Up,也不存在交易价值。只有当模型条件概率与真实可成交价格之间出现足够大的差异,交易才可能成立。

因此,需要分别得到两个东西:

img

这里的 q_t 不能简单使用页面上的卖一价。真实下单会穿过订单簿,还要考虑滑点和费用,所以代码按计划购买数量遍历 Ask 深度,先计算完整 VWAP,再计算每份 token 的绝对预期收益:

img

rust
fn expected_value(prob: f64, buy_vwap: f64) -> f64 { prob * (1.0 - FEE_RATE) - (buy_vwap + SLIPPAGE) * (1.0 + FEE_RATE) }

如果一份二元合约最终获胜,它兑付 1;失败则兑付 0。因此上式得到的是每份 token 预计能赚多少美元,而不是一个抽象的收益率。当前入场门槛为 0.025,意味着扣除费用和滑点后,每份 token 至少还要留下 2.5 美分的预期优势。

这也是条件概率模型和普通方向指标最重要的区别。

方向指标告诉我们市场偏多还是偏空;概率模型试图回答,在当前状态下,这个结果发生的可能性究竟有多大;执行层再判断,这个概率能否在真实盘口中兑现。

Polymarket 的价格当然包含大量信息,但这里有一个容易忽略的问题:如果把盘口价格作为模型输入,再拿模型输出与同一个盘口计算 EV,就可能形成循环论证——市场价格参与产生预测,预测再回来证明市场定价存在错误。

所以我把两条链路分开:Binance 行情和官方基准价负责产生独立终局概率;Polymarket 订单簿只负责提供交易价格、深度、点差和执行成本。

找到 OpenMarket:一个足够诚实的研究框架

img

沿着这个思路寻找资料时,我看到了 Gregory Young 开源的 OpenMarket 项目,以及论文 OpenMarket: A Synchronized Polymarket-Binance Dataset for High-Frequency Prediction-Market Research

这个项目研究的正是 Binance BTC/USDT 行情与 Polymarket BTC 15 分钟二元市场之间的关系。

作者搭建了一套相当完整的研究框架:采集 Binance 毫秒级成交和 Polymarket 订单簿,对两个市场进行时间配对,构建 43 个时间、收益、波动率、订单流和盘口特征,再使用 Logistic、Platt Scaling 与 Walk-forward 方式检验终局概率。

更难得的是,作者没有把结果包装成一个“发现了稳定套利”的故事。

论文给出的结论很诚实:公开的 43 特征模型在样本外并没有稳定超过 Polymarket 盘口自身隐含的概率;加入手续费、点差和滑点后,也没有形成可以直接宣称有效的交易优势。

但这并不妨碍它成为一个很好的实践框架。对于量化研究而言,一个公开特征、数据、模型参数、校准方法和样本外结果的负面结论,往往比一个只展示收益曲线、却无法复现的“成功策略”更有价值。

它至少回答了三个问题:应该收集什么数据,如何把短周期市场状态转换为条件概率,以及如何区分概率拟合能力与真实可交易能力。

我保留了这套研究框架的数据采集部分。43 个阶段特征每 5 秒写入数据库,市场结算后补充最终标签,供后续按剩余期限分桶训练和样本外验证。但这些固定权重暂时不直接参与交易概率。

原因很具体:第 60 秒时,180 秒收益率还不存在,所谓的 180 秒成交量实际上只有 60 秒;到了第 10 分钟,同名特征才真正拥有完整窗口。如果用一套未经阶段校准的权重,从开盘十几秒一直解释到第 14 分钟,特征名称相同,统计含义却已经变了。

所以这一次实现没有急着把复杂模型推上生产链路,而是先从终局事件本身出发,构建一条透明、可检查的概率基线。

img

从终局距离开始计算概率

在第 1 分钟预测第 15 分钟结果,并不是提前知道未来,而是在计算当前条件下的终局概率:

img

其中,K是本轮官方 Price-to-Beat,S_t是 BTC 当前参考价格,tau是剩余时间,sigma_t 是对未来波动的估计,mathcal I_t 是当前已经得到的信息。

最基本的标准化距离写成:

img

再通过标准正态分布的近似把 z_t 转成 Up 的中心概率。

这个公式看起来不复杂,真正困难的是波动率和漂移怎么估计。

如果一轮只开始了 15 秒,直接用这 15 秒的波动外推剩余 885 秒,概率很容易失真。最近十几秒恰好安静,分母就会过小;最近十几秒恰好单边,漂移又会被过度放大。两者叠加,很容易制造出开盘不久的伪确定性。

所以波动率不只看本轮阶段内数据。系统从 Binance 实时成交构造 1 秒收盘序列,再组合 60 秒、5 分钟、15 分钟和 30 分钟的实现波动率:

rust
let windows = [ (60usize, 0.15), (300usize, 0.25), (900usize, 0.30), (1800usize, 0.30), ]; for &(window, weight) in windows.iter() { if available >= window * 3 / 4 { let vol = realized_vol(&close, window); variance += weight * vol * vol; total_weight += weight; } }

这样即使一轮刚开始,也可以利用开盘前的价格路径估计未来剩余波动。系统还设置了保守的每秒波动率下限,避免短样本的偶然低波动把概率推向极端。

漂移的处理更加克制。最近 60 秒平均收益率不会直接乘以全部剩余时间,而是按照有效样本数向零收缩:

img

并且漂移对最终分布的贡献最多只能达到剩余波动的 0.25 倍:

rust
let reliability = n as f64 / (n as f64 + 300.0); let drift_total = drift_per_sec * remaining * reliability; let drift = clamp( drift_total, -0.25 * remaining_sigma, 0.25 * remaining_sigma, );

短期趋势可以修正概率,但不能单独把一笔交易推成接近必胜。

此外,开盘前 5 分钟还会对概率的 log-odds 降低置信度。它不是武断地规定“开盘几分钟内不能交易”,也不是把概率强制固定在 50%,而是承认相同的价格距离在不同阶段拥有不同的信息质量。阶段数据逐渐完整后,置信系数才恢复到 1。

中心概率不等于可以拿去下单的概率

这个地方是整套逻辑中最关键的一步。

模型输出的中心概率只表示当前估计落在哪里,并不表示估计误差为零。尤其在市场刚开始、波动历史不足或概率本身快速变化时,直接用中心概率计算 EV,会把“不知道”误认为“确定是 50%”。

因此,对于准备买入的方向,系统使用一个经过不确定度折价的概率:

img

当前 z_u=1。不确定度 u_t 由三部分组成:近期概率序列自身的波动、开盘阶段的信息不足,以及可用波动历史长度。

这不是增加一个“预热多少次才能开仓”的硬限制。第一份有效概率依然会被计算,只是信息越不足,可交易概率越低。只有在偏保守的概率下 EV 仍然为正,这笔交易才值得继续检查。

有一笔模拟日志很能说明这个区别。开盘第 15 秒,Down 的中心概率约为 49.75%,合约卖价为 0.43。只看中心概率,系统似乎拥有 6 个多点的空间;但成交均价加滑点后约为 0.435,而整仓可卖价格只有 0.42,刚成交的实际浮亏已经达到 5.36%。

把阶段不确定度加入后,如果机器人拥有完整的 30 分钟波动历史,可交易概率约为 44.81%,费用后净 EV 只剩 0.0043,低于 0.025 的门槛;如果机器人刚启动、只有 15 秒波动数据,可交易概率约为 41.96%,净 EV 已经为负。这笔交易在概率层就不会通过。

所以自适应的重点不是模型不停修改参数,而是让概率的可信程度随信息质量变化。

恰好 FMZ 上线了 Rust

完成建模思路之后,还需要选择一个适合长期运行的工程环境。

恰好发明者量化平台上线了 Rust 支持,这让整个框架的实现顺畅了很多。Rust 很适合实时数据、固定维度计算和复杂状态管理,编译器也能提前发现不少字段、类型和所有权问题。

当前框架只需要在 FMZ 配置一个 Polymarket 交易所对象。Binance BTC/USDT 行情直接使用公共 WebSocket:

rust
const BINANCE_WS_URL: &str = "wss://stream.binance.com:9443/ws/btcusdt@trade"; let mut binance_ws = Dial(BINANCE_WS_URL);

Polymarket BTC 15 分钟市场根据 UTC 时间确定性组装:

rust
let start = (now_ms() / 900_000) * 900_000; let slug = format!("btc-updown-15m-{}", start / 1000);

系统通过 Gamma Market API 验证市场并取得 Token 信息,同时订阅 Polymarket RTDS 中的 Chainlink 与 TWAP60 数据,区分 Binance 实时成交价、Chainlink 参考价、当前 60 秒 TWAP 和本轮固定的 Price-to-Beat。

这几个价格不能混为一谈。实时价描述当前状态,Price-to-Beat 定义最终结算事件:

img

如果基准价 K 错了,后面所有概率计算都没有意义。因此交易链路要求取得官方 openPrice;研究数据可以记录其他来源作对照,但不能拿临近收盘的当前价格冒充开盘基准价。

多周期波动率每秒更新一次并缓存,终局概率约每 200 毫秒更新一次,主循环以约 50 毫秒频率轮询数据和管理状态。这里没有把“每 5 秒记录一次特征”和“每 5 秒才能决策”混为一谈:5 秒是研究快照频率,不是交易响应速度。

概率通过之后,仍然不一定下单

即使不确定度后的 EV 达到 0.025,系统也不会看到第一帧优势就马上成交。

新市场刚打开时,订单簿经常还在组装,双方报价、深度和做市库存都可能快速变化。单次 EV 达标只能说明这一刻出现了候选,不能证明优势持续存在。

系统维护一个很短的入场证据状态:候选方向必须保持一致,净 EV 持续达标至少 1.2 秒,并累计 5 次有效更新。如果方向改变、EV 消失或盘口检查失败,证据立即清零。

rust
let ready = evidence_count >= 5 && now_ms - evidence_first >= 1_200; if !ready { signal.decision = "EV持续确认"; return; }

1.2 秒并不是慢速预热,而是用来区分持续优势和盘口瞬时错位。

准备提交订单前,系统重新读取 Up、Down 两边订单簿,检查数据时间是否同步,并验证二元盘口的互补关系:

img

随后还会计算“现在买入,再立即整仓卖出”的费用后往返损失。这个检查同时考虑 Ask、Bid、滑点和双边费用,当前最大允许损失为 4%。前面那笔开仓即浮亏 5.36% 的交易,即使概率 EV 勉强通过,也会在执行层被拒绝。

仓位使用五分之一 Kelly:

img

最终资金比例限制在账户权益的 0.1% 到 3% 之间。因此模拟账户有 100 USDC 时,单笔最多投入 3 USDC。这个限制不是余额读取错误,而是单笔风险上限主动截断。

概率模型不直接负责下单。它提出一个可检验的概率观点;订单簿决定成交条件;持续证据、往返成本和仓位管理决定这个观点是否值得拿真钱表达。

持仓之后:预测负责判断,价格负责兜底

开仓以后,系统实时计算整仓能够卖出的 VWAP,而不是只看盘口第一档。实际盈亏按照当前可卖净值与真实持仓成本计算:

img

预测侧持续计算持仓 EV:

img

正常的预测退出不是“概率下降几个点就卖”,而是要求当前持仓 EV 已经跌到 -0.005 以下,同时相对历史水平骤降至少 0.025,并连续确认 3 次、持续 600 毫秒。满足条件后,以 PREDICTED_EV_SHOCK_NEGATIVE 平仓。

这意味着概率下降但持仓 EV 仍然为正时,系统继续持有;反过来,概率变化看起来不大,但它已经让原始交易假设转为负期望,系统也能识别。

价格侧只保留一条在建仓时确定、之后不移动的固定止损线:

img

rust
let fixed_stop_price = (position.vwap - 0.50).max(0.35);

它不使用入场成本百分比,也不跟随持仓期间出现过的最高 Bid,因此不会因为盘口短暂冲高后回落而触发移动止损。系统使用整仓穿透买盘后的可卖 VWAP 判断是否真正跌破止损价。此外保留 0.95 的固定止盈。

这条止损更像灾难保护,而不是日常的预测管理。入场价低于 0.85 时,止损线都是 0.35;入场价为 0.915 时,止损线为 0.415。它可能对应很大的 token 百分比亏损,所以正常退出仍然依靠预测 EV 转负。固定止损是否足够合理,还需要用真实持仓路径和最大不利变动继续验证。

数据库让研究具备时间连续性

这类模型如果不能持久化,机器人一旦重启,概率历史、持仓和模拟账户状态都会消失。

系统使用 FMZ 的 DBExec 保存每轮市场、官方基准价、阶段特征快照、概率序列、订单、持仓、退出原因、结算结果、模拟余额和风险状态。机器人重启后可以恢复未结算仓位,而不是把已经存在的风险当作不存在。

模拟盘与实盘使用同一套状态机。区别只是模拟成交不会发送真实订单,而是按照完整 VWAP、滑点和费用更新内部账本。实时日志明确显示模拟买入、模拟卖出、持仓方向、数量、成本、可卖净值、实际盈亏、中心概率、可交易概率、预测 EV、固定止损线与退出原因。

状态栏则持续展示:

  • Binance、Chainlink、TWAP 与官方基准价;
  • Up/Down 中心概率、阶段不确定度和可交易概率;
  • 双边真实 Ask、完整 VWAP 与费用后净 EV;
  • 入场证据累计状态与未下单原因;
  • 当前持仓成本、整仓可卖价值和实际盈亏;
  • 持仓 EV、EV 骤降幅度、退出确认次数和固定止损价;
  • 最近交易、模拟余额和累计已实现盈亏。

记录这些数据不是为了让状态表看起来复杂,而是为了回答一个关键问题:策略到底因为预测错误亏损,还是因为盘口、滑点、流动性和执行成本亏损?如果这两类问题不能分开,后面的模型优化很容易走错方向。

这仍然只是一次实践探索

我更愿意把这套系统理解为一个自适应条件概率实验平台,而不是已经验证完成的交易策略。

这里的“自适应”不是每得到一个结果就修改模型,也不是把市场价格不断混入预测。真正需要适应的是信息质量:同样的价格距离,在第 30 秒和第 13 分钟应该拥有不同的可信度;同样的 50% 中心概率,在拥有 30 分钟波动历史和只有 15 秒样本时,也不应该对应相同的可交易概率和仓位。

当前实现选择先用透明的终局距离模型交易,同时继续保存 43 个阶段特征和最终标签。等数据量足够后,可以按 0—1 分钟、1—3 分钟、3—6 分钟、6—10 分钟和 10—14 分钟分别建立样本外数据集,观察校准曲线、Brier Score、Log Loss 和真实可成交收益。

只有当某组特征在对应阶段持续提供独立于 Polymarket 盘口的增量信息,才值得让它进入生产概率。否则,复杂模型只是给一个简单假设增加了更多参数。

后续还可以继续研究不同时段的条件分布、宏观数据与突发消息造成的跳跃风险、First-Passage Probability、分状态 Logistic、动态波动率与非线性模型。但比增加复杂度更重要的,是持续检验:概率是否校准,优势是否发生在真实可成交盘口,收益能否覆盖费用、点差、滑点和执行延迟。

对于二元市场而言,真正困难的从来不是判断 Up 或 Down,而是持续回答三个问题:

img

把它放回交易链路,大致就是:

img

预测模型提出观点,市场价格给出交易条件,执行层决定观点能否兑现,风险层负责在判断错误时限制损失。

现阶段,这套系统仍然应该以模拟盘和数据采集为主。逻辑完整不等于统计有效,最终能不能形成稳定优势,只能由分阶段样本外结果和真实可成交收益回答。

风险提示:本文讨论的是量化研究与工程实现,不构成投资建议。二元合约可能归零,流动性、盘口跳变、模型失效和结算规则都可能造成显著损失。

策略源码:发明者量化 Rust:OpenMarket BTC 15 分钟逻辑回归预测策略

Related Recommendations
Comment
All comments (0)
No data
No data
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)