每个量化团队都经历过这段对话。回测显示每月 +2.1%,Sharpe 干净,回撤可控。策略上线。一个月后,实盘账户在同样的月度节奏下收益只有 +1.4%。有人问:“alpha 是不是衰减了?”答案几乎总是:不是。
问题不在信号。问题在成本模型。
这篇文章讲的是:为什么回测到实盘的落差,在实践里其实是一个成本核算问题——以及在 Dnalyaw 这样的混合交易场地布局上,要关掉这个落差需要什么。具体细节带着交易味道。但底层教训可以推广到任何依赖模拟去预测现实的系统:你的 replay 必须知道实盘执行真正支付的每一项成本。
简单的部分:买卖价差滑点
大多数量化框架会停在买卖价差滑点。你跨过 spread 成交;回测用“买入总是在 ask 成交,卖出总是在 bid 成交”来建模。如果你使用 Almgren-Chriss 平方根冲击模型,还会加入一部分 σ × √(Q/V) 来表示自己订单带来的价格移动。这些是每本教材都会讲、每个成熟回测引擎都会包含的成本。
但这些成本也很少解释真正有意义的回测-实盘落差。落差不在 mid 到 fill 的 spread 里。落差在下面三层会叠加的结构里——每一层孤立看都容易漏掉,叠在一起却足以摧毁收益。
困难的部分:三层叠加
形状比具体百分比更重要。这个落差的结构——小但通常被建模的滑点,大且经常被算错的佣金时序,大且经常被忽略的 FX,小但真实存在的微观结构成本——才是多数团队共同面对的问题。只对冲其中一层(买卖价差滑点),却忽略其他层,就会得到经典结果:“回测说赚钱,实盘只是不亏。”
三个红色层,每一层都是自己的工程问题。三者还会叠加。
第一层:佣金时序
券商费用模型在纸面上看起来很简单:每股 X bps,或者每笔固定费用。 运营现实并不简单。
在 Interactive Brokers——多数系统化基金用于美股的量化原生场地——成交和对应佣金会从不同 callback 到达。执行回报先到,用 execId 标识。佣金报告随后到达,也用 execId 标识。两者必须事后关联。如果你的系统读取 fill 后立刻关闭交易循环——计算 P&L、更新持仓追踪、喂给风险系统——那它就是在完全没有佣金信息的情况下做这些事。佣金会在之后异步入账。
这种不对称不是“callback 晚到”这样的小麻烦。它是会计状态上的竞态条件:fill 事件触发 P&L pipeline,commission 事件稍后改变这个 pipeline 的输入,而任何读取中间状态的消费者都会看到结构性错误的数字。风险系统看到的是已实现成本被低估的持仓。下游策略看到的是带系统性正偏的 Sharpe。同步入账的回测看不到这些风险,于是它的 P&L 每天都在悄悄偏离券商 statement。
天真的实现会把佣金归到错误的 fill 上,或者在 callback 于重连期间抵达时完全漏掉。我们曾用验证层需要显式状态模型,而不是 timeout这个框架写过同类问题——佣金绑定也是同一种形状:一个隐含的“它总会自己出现”的假设,在负载下破裂;而基于状态模型的修复,会把这种迟到绑定变成显式、可审计的过程。
两秒听起来不长。在高频里,它乘上整个行业每年数十亿次成交;而佣金数字对每一次成交都重要。以我们的交易频率看,它决定的是:P&L 对账能不能每天关上,还是漂移几个星期之后才被人发现。
Futu 的等价 callback 有不同的时序和不同的 key 结构,但不对称性完全一样:fill 很快,费用异步到达。任何假设费用同步入账的成本模型,到月底都会和券商 statement 对不上——而正确的一方是券商。
第二层:FX 归一化
只跑一个场地、一种货币,你可以忽略 FX。跑两个场地、两种货币,就不行。
Dnalyaw 的混合布局天然带着这个问题:IB US 的成交以 USD 计价,Futu HK 的成交以 HKD 计价;当标的是港股但账户映射到 JPY 清算时,FX 还需要跨两条汇率才能结算到组合基准货币。一个假设“一切都是 USD”的回测,在你的持仓刚好全是美股时是误打误撞地正确;其他时候都会错。
不明显的部分是:需要转换的不只是 P&L。每个消费者——计算敞口限制的风险引擎、计算仓位预算的订单管理器、计算已实现收益的回测 replay——都必须归一化。在一个地方做对很容易。在每个地方都做对,才是工作量。Rust 风险引擎、Go OMS、Python 研究 pipeline 各自有自己的 FX 路径,都必须被审计并统一。
漏掉一个地方时的失败模式是:风险引擎把一个 5% 的 HKD 持仓看成 USD 组合的 5%,让它通过限额检查;然后 P&L 以 USD 实现,显示出不同结果。回测从未看到这次违规,因为它的 FX 假设是统一的。实盘账户会用更疼的方式发现它。
第三层:现金初始注入与费率表
最安静的一层,也是事后复盘很少谈的一层,是每家券商注入初始资金的方式都不同。
模拟账户经常以错误的初始余额、非标准 rounding、或者缺失的货币子账户开始。实盘账户更干净,但带着券商特有的费率表,而你的回测很可能从来没加载过这些表。IB US 的佣金是阶梯制;Futu HK 同时有按笔和按成交额收费;两边还都有额外监管费用(美国的 SEC、TAF、FINRA;香港的交易征费等),单看很小,累计起来很要命。
这些都不神秘。也都很容易忘。失败模式是:上线头几周,实盘 P&L 每天比回测漂移几个基点;每天你都安慰自己说“还在预期噪声内”;然后月底对账时发现,“预期噪声”其实是你漏掉的系统性偏差。
修这个问题很乏味,不聪明:按场地加载真实费率表,根据已知券商约定归一化现金初始值,并且——关键点——每天把券商 statement 喂回对账报告,而不是每个月才看一次。
混合场地的放大器
这三层成本,在混合交易场地布局上都会变得更难,也更值得做对。
只跑 IB 的策略有一套佣金表、一组 FX 对、一套监管费用要加载。跑 IB 和 Futu HK 的策略,两边各有一套,而且 callback 时序不同、货币子账户需要对账、费率表以不同节奏更新。成本模型必须是一套统一抽象——加入 Futu 不应该要求重写 replay 引擎,shadow tracker 也应该用同样的会计纪律处理 USD 和 HKD 的 P&L。
好处是:一旦做对,同一个系统可以用同一组不变量服务两个场地。这不只是工程上的优雅——在量化业务里,多场地接入本身就是战略能力。IB 是机构流动性所在;Futu HK 是亚洲散户和自营流量所在。一个能在两边一致运行、并保持同等成本模型正确性的系统,可以表达任何单一场地都无法支持的策略。
这也正是为什么 calibration pipeline 不可协商。没有 drift tracker 就构建跨两个场地的统一成本抽象,本质上是在闭眼做抽象——你无法知道它到底对不对,直到月底对账失败。pipeline 是让这个抽象证明自己值得存在的机制。
修复方式:影子执行
关闭这个落差是 pipeline 问题,不是建模问题。你需要两条 P&L trace 并行运行——一条来自实盘账户,一条来自使用同一份实盘系统看到的数据的回测 replay——再用一个 drift tracker 实时标记分歧。
具体来说:建模 P&L 使用实盘执行支付的同一组佣金数字、实盘报价的同一组 FX 汇率、实盘观察到的同一组成交质量。当回测和实盘在容忍范围内一致时,策略才赢得更多资本的资格。当二者不一致,说明模型有 bug,必须在加大资本之前找到它。
这就是为什么 Dnalyaw 的资本配置是分阶段的:validation → calibration → limited → full。每个阶段都有四个维度上的显式量化毕业标准——日 P&L、Sharpe、成交率、平均滑点。策略不会因为回测好看就晋级。它们晋级,是因为实时执行在一个持续窗口内、在容忍范围内与建模执行匹配。
shadow replay 是让这个循环成为可能的东西。没有它,你只能拿上个季度的回测去对比这个月的实盘 statement,然后祈祷中间市场 regime 没变。有了它,你是在同一条 tape 上,把今天的实盘 fills 和今天的 shadow 做比较。
这解决不了什么
诚实地说,仍然存在剩余落差:
- 市场冲击仍然是模型。 Almgren-Chriss 框架——或者任何你使用的冲击模型——都只是近似现实。真实市场冲击依赖路径,依赖隐藏订单簿状态,也对同一时刻还有谁在交易高度敏感。replay 用的是我们拥有的最好模型;它不知道 ground truth,因为没人知道。
- 费率表会变。 IB、Futu,以及我们接入的每个场地,都会按自己的节奏更新定价。归一化 pipeline 必须追踪这些更新;漏掉一次,几周内就会悄悄引入偏差。
- 税务和交割会计在下游。 券商佣金是成本模型的一部分;盈亏税务处理不是——那是组合会计层。两者到年底必须对得上,但它们由不同系统解决。
- 市场 regime 变化会破坏比较。 shadow replay 假设结构性成本模型成立。当一个 regime 里 bid-ask spread 变宽或佣金上升,实盘和 shadow 都会被同样比例咬掉收益——P&L 差异保持很小,但两边一起变差。shadow tracking 是校准工具,不是 regime detector。
把这些写清楚,而不是把它们藏在“现实假设”里面,是为了避免 shadow replay 悄悄变成另一个掩盖落差的东西。
教训
研究层是可见的;所有人都看得到策略。执行层才是正确性被赚出来的地方——一层成本一层成本地赚,大多发生在视野之外。
如果你的回测能在每月 10 个基点内预测实盘,信号没问题,成本模型也很扎实。如果能在 50 个基点内预测,信号可能没问题,但成本模型还要干活。如果差到 200 个基点甚至更糟,先不要碰信号——先修成本模型。
具体修复——IB 上的 execId late-binding、跨 Rust/Go/Python 消费者的 FX 归一化、现金初始值归一化、按场地加载费率表——都是 Dnalyaw 特有的。可推广的直觉是:如果你的问题是回测-实盘漂移,成本模型应该是第一个检查的地方。
更具体一点,如果你在审计一个现有平台的 calibration pipeline:第一个问题应该是,回测 replay 是否知道佣金是在 fill 之后 2.3 秒才到的,还是它把所有东西都同步入账了。 如果是后者,你已经找到一个会持续数月的隐性偏差来源。在它修好之前,其他都排在后面。
这是关于研究与执行垂直整合背后工程纪律的四篇系列最后一篇——从 flatten verifiers 到 prompt-cache stability,再到 agent checkpoint design,最后到这里的成本模型 pipeline。系统不同,模式相同:边际优势活在你强制执行的不变量里,而不是你训练的模型里。
Flatten、cache、turn、cost。每一个都是一条普通 PR 会悄悄把好系统变坏系统的地方。每一个也是少量纪律会复利成正确性的地方,而这种正确性最终会出现在月度对账上。