halo 的技术博客

返回

问题:那份 TCA 报表在考核谁#

买方的交易成本分析(TCA)报表长得都差不多。一笔母单从 PM 按下按钮到最后一股成交,中间的价格损耗叫执行落差(Implementation Shortfall,IS),然后被拆成两块:

  • 延迟成本:决策价 → 到达交易台时的价格。这段归”流程”。
  • 交易成本:到达价 → 实际成交均价。这段归交易台

第二个数字被印在交易员的季度考核表上。

这个拆法源自 Perold 1988,逻辑无懈可击:交易台只能对它拿到单子之后的事负责。问题在于,“到达之后发生的价格变化”和”交易台造成的价格变化”根本不是一回事。

单子到达之后,价格会动的原因至少有四个:

  1. 你自己的买盘把价格推高了(这才是交易台的责任)
  2. 当初让 PM 下单的那个 alpha 正在兑现(不交易也会发生)
  3. 随机波动
  4. 别人的单子

标准归因把这四样东西打包塞进一个桶,贴上”交易成本”的标签,然后拿去发奖金。

本文用一个真值已知的受控模拟量化这个混淆到底有多贵。

实验设计:让每一个 bp 都可追溯#

要判断归因准不准,得先有个”准”的定义。真实市场里你永远不知道那 20 个 bp 里有多少是自己推的—所以我造一个知道的世界。

价格路径由四个独立生成、单独记账的分量叠加:

BAR_VOL   = 0.0016      # 每 bar 噪声 sigma(1 bar = 5 分钟)
ALPHA_HL  = 26.0        # alpha 半衰期(bar)
GAMMA     = 0.00030     # 永久冲击系数
ETA       = 0.00055     # 临时冲击系数
T_REF     = 200         # alpha 归一化的固定参考窗
python
  • alpha 漂移:前重后轻、指数衰减。这是当初下单的理由,也是延迟为什么贵的原因。
  • 噪声:零均值随机游走。
  • 永久冲击:自己的脚印,不会回来。
  • 临时冲击:自己的脚印,停手后按 3 bar 半衰期衰减。

一笔母单的生命周期:t=0 PM 决策 → t=L 到达交易台 → t=L..L+H 逐 bar 均匀成交 → 结束。

这里有个必须踩对的坑。alpha 的归一化窗口不能用订单自己的时长 T,必须用一个固定的参考窗 T_REF:

t_grid = np.arange(T)
ref = np.exp(-lam * np.arange(T_REF))
incr = np.exp(-lam * t_grid) / ref.sum() * (alpha_bp / BP)
alpha_path = np.cumsum(incr) * side
python

如果按每笔订单自己的 T 归一化,短单子就等于活在一个”alpha 衰减更快”的宇宙里—参与率扫描会得到完全错误的最优点。这个 bug 我在第一版里犯了,扫描结果把最优参与率从 12% 错报成 5%,改掉之后 U 形曲线才对上。

因为每个分量是分开生成的,成交均价里各自贡献了多少 bp 可以精确算出来:

def avg_component(arr):
    return float(np.mean(arr[exec_bars]) - arr[0])
python

这就是标准归因要被拿去对照的真值。

结果一:交易成本桶里,一半以上不是交易台的锅#

4000 笔母单,参数随机(alpha 均值 45 bp、延迟泊松均值 8 bar、时长均值 24 bar、参与率均值 12%),买卖各半:

标准归因报出来的样子 vs 真值分解

口径数值
总执行落差23.48 bp
报告的「延迟成本」7.91 bp
报告的「交易成本」15.57 bp
真实 alpha 漂移17.18 bp
真实自身冲击(永久+临时)7.09 bp
├ 永久4.44 bp
└ 临时2.65 bp
噪声(应≈0)-0.78 bp

交易台被记账 15.57 bp,真实市场脚印只有 7.09 bp。54.5% 的账单不是它造成的。

差额去哪了?在执行的那 24 根 bar 里,alpha 还在继续兑现。价格朝着 PM 预期的方向走,而交易台正好在这段时间买入—于是 alpha 的兑现被记成了交易台的成本。

单笔订单的解剖看得更清楚:

一笔买单的解剖

黑线是交易台唯一能看到的东西。红色虚线(alpha)和绿色虚线(自身冲击)在真实世界里不可分离—但它们的斜率完全不同:alpha 在整个窗口持续推高价格,自身冲击只在执行期间累积。

结果二:把冲击彻底关掉,账单还在#

上面那个 54.5% 也许是我模型参数没调好?做个安慰剂:把 gammaeta 都设成 0。这个世界里,交易台在物理上不可能对价格产生任何影响

对抗式检验:把机制逐个关掉

真实自身冲击        : 0.0000 bp   ← 构造上精确为零
报告的「交易成本」  : 9.51 bp     ← 纯粹的幽灵
plaintext

一个毫无市场足迹的交易台,被开出 9.51 bp 的账单。 这不是估计误差,是归因框架的结构性产物。

反方向的安慰剂同样重要—把 alpha 设成 0,冲击照常开:

报告的「延迟成本」  : 1.90 bp   ← 塌回接近零(本该如此,没有 alpha 就没有延迟代价)
报告的「交易成本」  : 5.36 bp
真实自身冲击        : 7.03 bp   ← 误差只剩 1.67 bp
plaintext

alpha 一关,交易桶立刻变诚实。这条对照锁死了污染源:不是模型有 bug,就是 alpha 本身。

结果三:同一批成交,三个基准三个故事#

换个基准,同一批一模一样的成交能讲出完全不同的故事:

三个基准 / 时间戳游戏

基准成本谁爱用
决策价23.48 bpPM
到达价15.57 bp交易台
区间 VWAP7.09 bp经纪商

跨度 16.39 bp,成交记录一个字没改。

有意思的是区间 VWAP 那行—它给出的 7.09 bp 恰好精确等于真实自身冲击。这不是巧合:区间 VWAP 剔除了执行窗口内的市场整体漂移,剩下的正好是你自己推出来的那部分。如果你只想考核交易台的市场脚印,VWAP 反而是三者中最诚实的(代价是它对”该不该慢慢做”这个问题完全失明)。

更难看的是时间戳。不改任何一笔成交,只把”到达时间”往后登记:

到达时间戳后移交易台被记账成本
+0 bar15.57 bp
+3 bar9.95 bp
+6 bar5.24 bp
+9 bar1.33 bp
+12 bar-1.42 bp

17.0 bp 的纯文书改善。 挪一小时的时间戳,交易台从”成本 15.6 bp”变成”给公司赚了 1.4 bp”。任何把到达时间戳交给被考核方自己填的 TCA 系统,考核的都是填表能力。

结果四:最优参与率是个 U 形,两头都贵#

既然延迟贵是因为 alpha 跑掉,那全速买不就行了?扫一遍参与率(参与率越高、执行窗口越短):

执行落差的 U 形

参与率执行窗口总 ISalpha 漂移自身冲击
3%96 bar35.73 bp30.965.13
5%58 bar28.68 bp25.545.67
8%36 bar25.56 bp20.816.34
12%24 bar22.51 bp17.157.19
18%16 bar23.06 bp14.658.26
25%12 bar23.40 bp13.249.54
50%6 bar24.59 bp10.8012.17

两条曲线方向相反:慢了 alpha 跑掉,快了自己踩自己。最优在 12%,最差角落 35.73 bp—13.23 bp 是纯粹可避免的成本

注意这个最优点完全取决于 alpha 半衰期。对照 A3 的扫描:

alpha 半衰期延迟成本
8 bar20.32 bp
26 bar8.13 bp
120 bar2.62 bp

信号衰减越快,拖延越致命。一个日内反转信号和一个季度价值信号,不该用同一套执行算法—这是本文唯一可以直接落地的结论。

结果五:你的 TCA 报表可能在读噪声#

单笔执行落差的分布:

单笔噪声与样本量

  • 均值 23.48 bp,标准差 63.98 bp
  • 35.9% 的单子成本为负(价格朝你有利的方向动了)

信噪比低到什么程度?要让 t 统计量达到 2,需要 30 笔订单:

R["n_for_t2"] = (2.0 * per_order_std / per_order_mean) ** 2
python
样本笔数90% 置信区间
5[-19.62, 70.92] bp
25[2.14, 44.05] bp
100[13.22, 33.92] bp
1000[20.22, 26.68] bp

30 笔是总体均值的门槛。要比较两个交易台、或者比较两个算法,方差翻倍,门槛还要往上抬。而现实中,一个中型基金某个交易台某个策略某个月的样本量,经常就是十几笔。

在这个样本量下讨论”A 算法比 B 算法好 3 bp”,是在给随机数排名。

结果六:被动挂单看起来更便宜,只要你不数漏掉的单#

TCA 报表最常见的漏洞是只统计已成交部分:

成交率陷阱

策略只算已成交加上机会成本
市价单 100% 成交24.22 bp24.22 bp
被动 85% 成交19.69 bp24.66 bp
更被动 65% 成交14.14 bp24.96 bp
极被动 45% 成交9.15 bp24.95 bp

按”已成交成本”排名,极被动策略以 9.15 bp 完胜—成本砍掉六成。按总落差排名,它是最差的之一。

这个陷阱特别阴险,因为它不留伤口。没成交的单子不会出现在成交回报里、不会进 P&L、不会有人投诉。它只是让某些本该发生的交易从未发生,而这部分损失从不出现在任何报表上。

结果七:考核排名会精确反转#

把前面的东西合起来,看真实后果。三个交易台,风格不同:

三个交易台的考核

交易台报告的「交易成本」真实市场脚印
A 台(激进,流程快)11.49 bp9.50 bp
B 台(温和,流程慢)14.08 bp6.81 bp
C 台(被动,流程中)21.67 bp5.67 bp

按报告成本排名:A < B < C(A 最好) 按真实脚印排名:C < B < A(C 最好)

精确反转。 被表扬的 A 台恰恰是市场足迹最大的那个—它做得快,所以逃过了 alpha 漂移的记账,但它对市场的实际冲击是 C 台的 1.7 倍。而 C 台因为慢慢做、承受了更多 alpha 漂移,被记了最高的账单。

如果奖金挂钩报告数字,理性的交易员会学会:做快、抢流动性、把 alpha 兑现的时间压缩到记账窗口之外。这恰好是冲击成本最高的做法。

对抗式检验#

八项,全过:

#检验结果
A1买卖对称性买 23.37 / 卖 23.59 bp,差 0.22 bp,符号约定无误
A2分解恒等式残差 0.00e+00 bp,三个桶严格加总到总数
A3alpha 半衰期扫描8→120 bar,延迟成本 20.32→2.62 bp 单调下降
A412 个宇宙种子稳定性均值 23.43 bp,标准差 2.48 bp,CV 0.106
A5零延迟对照delay=0 时延迟成本 精确 0.00 bp
A6考核排名反转报告 A<B<C,真值 C<B<A,完全反转
A7零冲击安慰剂真值 0,报表仍开出 9.51 bp
A8零 alpha 安慰剂交易桶误差降到 1.67 bp,锁定污染源

A2 值得单说:残差精确为 0 说明这个分解在算术上是严丝合缝的。这正是它危险的地方—一个恒等式永远成立,但它成立不代表每个桶的标签是对的。很多人看到”分解能对上”就以为验证过了,其实什么都没验证。

实操建议#

  1. 不要用「到达价 → 成交均价」考核交易台。 它系统性地把 alpha 漂移记在交易台头上,且高 alpha 策略的交易台受罚最重。
  2. 同时报三个基准。 决策价看 PM 的决策质量+流程效率,区间 VWAP 看交易台的市场脚印,到达价看两者的混合。单报一个必然误导。
  3. 到达时间戳必须由系统自动打,不能由被考核方填。 17 bp 的操纵空间。
  4. 机会成本必须入表。 否则被动算法会在报表上无限占优。
  5. 样本量低于 30 笔的 TCA 结论,直接扔掉。
  6. 按 alpha 半衰期分组考核。 快信号和慢信号的最优参与率不是一个数量级的问题,是方向相反的问题。

局限#

  • 模拟是线性冲击模型(冲击 ∝ 参与率)。真实市场普遍认为是平方根律,这会让高参与率区间的成本被本文低估,最优参与率的真实位置应该比 12% 更靠左。
  • 假设匀速执行。真实算法会择时、会跟随成交量曲线,这本身就是”择时”那一块,本文没有单独拆出来。
  • 没有建模流动性枯竭和信息泄漏。大单被察觉之后的抢跑,在这个框架里完全不存在。
  • alpha 路径是确定性衰减的,真实 alpha 有相当大的不确定性,这会进一步压低单笔的信噪比—也就是说,那个”30 笔”的门槛在现实中只会更高。

最后一点值得强调:本文所有结论都建立在”alpha 存在且会兑现”这个前提上。如果一个策略的 alpha 是假的,那么”延迟成本”这个桶测到的东西就只是噪声,整套归因讨论的前提本身就不成立。

执行落差归因:把 Implementation Shortfall 拆成延迟、冲击与择时
https://blog.halo26812.eu.org/blog/implementation-shortfall-attribution
Author halo
Published at 2026年8月4日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨