执行(execution)是把「投资决策」变成「实际持仓」的最后一公里。一个年化 20% 的选股信号,如果执行环节多付出 30 bp 的冲击成本、每年换手 50 次,那么光是执行就会吃掉约 15% 的收益。研究做得再好,执行不行,收益就蒸发在盘口里。
工程上真正难的地方在于:执行面对的是一个会对你的行为做出反应的市场。你买得越多越快,价格就被你推得越高;你买得越慢,价格漂移的风险就越大。这个「冲击成本 vs 时间风险」的对抗,是执行算法的核心矛盾,也是 Almgren-Chriss 这类模型要解决的问题。
与 OMS 关注「订单状态是否正确」不同,本文关注的是「成交价格是否划算」。订单管理与执行算法 已经介绍了 TWAP/VWAP/POV 的基本调度与 OMS 状态机,本文不重复这些基础,而是深入最优执行理论、冲击成本建模、暗池与智能路由,以及执行质量的归因方法。
本文按「基准 → 冲击 → 容量 → 最优轨迹 → 自适应 → 路由 → 归因 → 调优 → 工程」的顺序展开,重点放在「为什么」而不是「调用哪个库」。
目录
- 执行的三个基准与实现落差
- 冲击成本的两种形态
- 平方根冲击律与策略容量
- Almgren-Chriss 最优执行轨迹
- 自适应执行与成交量预测
- 暗池、内部化与智能路由
- 执行质量的归因分解
- 执行参数的在线调优
- 执行系统的工程结构
1. 执行的三个基准与实现落差
评价一笔执行的好坏,必须先选一个基准。基准选错,结论会完全相反:
| 基准 | 定义 | 适用场景 | 偏差方向 |
|---|---|---|---|
| 到达价(Arrival) | 决策时刻的中间价 | 主动型、alpha 衰减快 | 对慢执行不利 |
| VWAP | 区间市场成交量加权均价 | 被动型、跟踪指数 | 对大单有利 |
| 收盘价(Close) | 收盘时刻价格 | 被动指数复制 | 对流动性差标的有利 |
实现落差(Implementation Shortfall, IS) 以到达价为基准,是最全面的指标,因为它把延迟、冲击、择时全部算进去:
def implementation_shortfall(fills, arrival_mid, side):
qty = sum(f.qty for f in fills)
vwap = sum(f.price * f.qty for f in fills) / qty
sign = 1 if side == 'BUY' else -1
# 买入成交价高于到达价为负贡献,单位 bp
return sign * (vwap - arrival_mid) / arrival_mid * 10000
「实现落差」这个词容易被误用:它既指上面这个成本指标,也指「计划调度与实际成交之间的偏差」。工程上要把两者分开看——前者衡量结果,后者衡量执行过程。一个 IS 为 5 bp 的执行,可能是「计划 5 bp、偏差 0」,也可能是「计划 0、偏差 5」,改进方向完全不同。
VWAP 基准适合被动型委托,但它有个隐藏缺陷:市场 VWAP 包含了你自己的成交量。如果你占当日成交的 20%,那么你的成交价会被自己的冲击推高,VWAP 基准反而显得「达标」。严肃的评估要用「剔除自身成交的 VWAP」作为基准。
2. 冲击成本的两种形态
市场冲击必须拆成两部分,因为它们的性质完全不同:
| 形态 | 特征 | 恢复 | 建模 |
|---|---|---|---|
| 临时冲击 | 执行期间的价格压力 | 执行后部分回落 | 与执行速率相关 |
| 永久冲击 | 信息被市场消化 | 不恢复 | 与总成交量相关 |
临时冲击来自流动性消耗:你把盘口的挂单吃掉了,价格临时偏离,等新挂单补上就回落。永久冲击来自信息泄露:市场从你的持续买入中推断出「有资金在建仓」,于是重估价格,这部分不会回来。
def temporary_impact(rate, eta):
# rate: 单位时间的执行量占比;eta: 流动性系数
return eta * rate # 线性临时冲击
def permanent_impact(total_qty, adv, gamma, sigma):
# 永久冲击通常与参与率成正比,并随波动率放大
return gamma * sigma * (total_qty / adv)
这个区分直接决定执行策略:如果冲击主要是临时的,那么「慢慢执行」几乎无成本,TWAP 就是最优;如果冲击有永久成分,那么拖延执行等于让永久冲击持续作用于更大的剩余持仓,应该更快执行。
滑点(slippage)是冲击在单笔订单上的微观表现:挂单价与实际成交价之差。回测里常用固定滑点(如 10 bp),但真实滑点随订单大小、标的流动性、时段剧烈变化。开盘后 5 分钟的滑点是午盘的 35 倍,小盘股的滑点是大盘蓝筹的 510 倍。用单一固定值建模,会系统性地低估小盘策略成本。
3. 平方根冲击律与策略容量
大量实证研究发现,冲击成本对订单规模呈次线性——不是线性的,而是接近平方根:
冲击成本 ≈ Y · σ · sqrt(Q / V)
σ:标的日波动率
Q:订单量
V:日均成交量(ADV)
Y:经验系数,股票市场约 0.3~1.0
这个「平方根律」是执行领域最重要的经验规律之一。它的含义是:规模翻四倍,冲击只翻一倍。这解释了为什么大资金仍然可以交易——如果冲击是线性的,规模一大成本就爆炸。
import math
def sqrt_impact(qty, adv, daily_vol, y=0.5):
"""返回以 bp 为单位的冲击成本估计"""
return y * daily_vol * math.sqrt(qty / adv) * 10000
print(f"{sqrt_impact(0.10 * 1e8, 1e8, 0.02):.1f} bp") # 买入 10% ADV,约 31.6 bp
print(f"{sqrt_impact(0.40 * 1e8, 1e8, 0.02):.1f} bp") # 买入 40% ADV,约 63.2 bp
策略容量(capacity) 由此推导:当冲击成本逼近策略的预期 alpha 时,策略就到达了容量上限。一个日频 alpha 为 50 bp 的策略,如果单日下单量达到 25% ADV 时冲击是 50 bp,那么它的容量就是「每天不超过 25% ADV」。
容量估算要保守,因为平方根律的系数 Y 在市场压力下会显著上升。2018 年、2020 年 3 月的极端行情里,同样的订单量冲击成本是平时的 3~5 倍。按常态系数算出的容量,在危机中会瞬间失效。
4. Almgren-Chriss 最优执行轨迹
Almgren-Chriss(AC)框架把执行形式化为一个随机最优控制问题:在「冲击成本(随执行速率增加)」和「时间风险(随执行时间增加)」之间求最优折中。
目标:min E[成本] + λ · Var[成本]
成本 = 临时冲击(与速率 v 成正比)+ 永久冲击(与累计量成正比)
风险 = 剩余持仓暴露于价格波动 σ
最优解:x(t) = X · sinh(κ(T − t)) / sinh(κT)
κ = sqrt(λ σ² / η) λ 风险厌恶,η 流动性
import numpy as np
def ac_schedule(X, T, kappa, n=10):
t = np.linspace(0, T, n + 1)
remaining = X * np.sinh(kappa * (T - t)) / np.sinh(kappa * T)
return np.diff(-remaining) # 每段应执行的量
print(ac_schedule(1e6, 1.0, kappa=5.0, n=5).round(0)) # κ 大:前重后轻,快速执行
print(ac_schedule(1e6, 1.0, kappa=0.5, n=5).round(0)) # κ 小:接近均匀,类似 TWAP
AC 的两个极限很有启发:
- κ → 0(低风险厌恶或高流动性):解退化为均匀分布,即 TWAP。
- κ → ∞(高风险厌恶或低流动性):解退化为「立即全部执行」,即市价单。
所以 TWAP 不是「拍脑袋的简单算法」,而是 AC 在特定参数下的最优解。真正需要调的是 κ,而 κ 由alpha 的衰减速度决定:alpha 衰减越快,κ 越大,越应该快速执行。
AC 的局限也要清楚:它假设冲击函数已知且稳定、价格服从算术布朗运动、没有成交量约束。真实市场里这三个假设都不成立。工程实践中 AC 更多是提供直觉和参数框架,而不是直接照搬轨迹。
5. 自适应执行与成交量预测
静态调度(TWAP/VWAP)的问题是不响应市场变化:如果中途成交量突然枯竭,VWAP 会为了追赶进度而激进下单,推高成本。
自适应执行(adaptive execution)动态调整参与率,目标是最小化实现落差而非贴合 VWAP:
class AdaptiveExecutor:
def __init__(self, total, alpha_decay, risk_aversion=1.0):
self.remaining = total
self.alpha_decay = alpha_decay # alpha 每秒衰减率
self.lam = risk_aversion
def target_rate(self, elapsed, horizon, volatility, adv):
# 剩余时间越少、alpha 衰减越快 → 速率越高
urgency = self.alpha_decay * (1 - elapsed / horizon)
base = self.remaining / max(horizon - elapsed, 1e-9)
return base * (1 + self.lam * urgency / max(volatility, 1e-6))
成交量预测是自适应执行的关键输入。A 股日内成交量呈 U 形:开盘 15 分钟和收盘 30 分钟成交量最大,午间最低。用历史同期的平均分布作为先验,再用当日实时成交量做贝叶斯更新:
def predict_volume_profile(hist_profiles, realized_ratio):
# hist_profiles: 历史各时段成交量占比矩阵 (days × bins)
prior = hist_profiles.mean(axis=0) # 先验 U 形分布
remaining_bins = len(prior) - len(realized_ratio)
# 已实现部分修正剩余部分的预测
scale = realized_ratio.sum() / prior[:len(realized_ratio)].sum()
return prior * scale
成交量预测的误差会直接转化为执行误差。经验上,开盘前的预测误差约 15~25%,到收盘前会收敛到 5% 以内。因此自适应执行应该越往后越激进,把预测不确定性留给后半段。
6. 暗池、内部化与智能路由
暗池(dark pool)是不显示报价、只在成交后披露的流动性池。它的价值是降低冲击:在暗池里,你的大单不会出现在公开盘口上,不会被其他参与者察觉。
| 场所 | 报价可见 | 价格改善 | 主要风险 |
|---|---|---|---|
| 公开交易所 | 是 | 无 | 冲击成本高 |
| 暗池 | 否 | 通常取中间价 | 逆向选择 |
| 内部化 | 否 | 券商自定 | 利益冲突 |
暗池的核心风险是逆向选择:愿意在暗池和你成交的对手,往往掌握你不知道的信息(比如即将发布的利好/利空)。你的成交价看似是「中间价」,但成交本身就说明价格即将朝不利方向变动。
def dark_pool_adverse_selection(fills, mid_after_5min):
# 成交后 5 分钟中间价相对成交价的变化,正值表示被逆向选择
return np.mean([
(mid_after_5min[f.id] - f.price) / f.price * f.sign
for f in fills
]) * 10000 # bp
智能路由(SOR, Smart Order Router) 的目标是在多个场所间分配订单,使总成本最小。它要同时考虑:各场所的报价、深度、历史成交率、手续费差异、以及延迟。
def route(order, venues):
# 按「有效价格 = 报价 + 预期冲击 - 手续费返还」排序
ranked = sorted(venues, key=lambda v: v.price + v.impact - v.rebate)
alloc, left = {}, order.qty
for v in ranked:
take = min(left, v.display_qty)
alloc[v.id] = take
left -= take
if left == 0:
break
return alloc
A 股目前是单交易所集中撮合,没有真正意义上的暗池与多场所路由;但在港股、美股、期货跨市场场景里,SOR 是执行系统的核心模块。理解它也有助于理解为什么海外执行成本结构的差异那么大。
7. 执行质量的归因分解
一笔执行的实现落差可以分解成几个来源,分解清楚才能知道该优化哪一环:
IS = 延迟成本 + 冲击成本 + 择时成本 + 机会成本
延迟成本:决策时刻 → 订单到达市场
冲击成本:成交对价格的推动
择时成本:执行期间价格自然漂移
机会成本:未成交部分的机会损失
def decompose_is(fills, decision_px, arrival_px, final_px, target_qty):
filled = sum(f.qty for f in fills)
vwap = sum(f.price * f.qty for f in fills) / filled
delay = (arrival_px - decision_px) / decision_px # 延迟
impact = (vwap - arrival_px) / arrival_px # 冲击
timing = (final_px - vwap) / final_px # 执行后漂移
unfilled = (target_qty - filled) / target_qty
return {
'delay_bp': delay * 10000,
'impact_bp': impact * 10000,
'timing_bp': timing * 10000,
'opportunity_bp': unfilled * (final_px - arrival_px) / arrival_px * 10000,
}
机会成本最容易被忽略。如果一笔买入委托因为限价太保守只成交了 60%,剩余 40% 的仓位没建上,而价格随后涨了 3%,这 40% 的机会损失(约 120 bp)比整个冲击成本还大。很多执行系统的限价逻辑只优化「成交价」,结果系统性地牺牲了成交率。
归因结果可以直接映射到改进动作:
| 主导成本 | 典型原因 | 改进方向 |
|---|---|---|
| 延迟成本 | 决策到报单链路慢 | 优化信号到 EMS 的路径 |
| 冲击成本 | 参与率过高 | 降速、拆更细、用暗池 |
| 择时成本 | 执行窗口太长 | 缩短窗口或提高激进度 |
| 机会成本 | 限价太保守 | 放宽限价、提高参与率 |
归因的价值在于指导改进:如果延迟成本占比高,要优化的是链路和决策到报单的时间;如果冲击成本占比高,要放慢节奏或换用暗池;如果机会成本占比高,要放宽限价或提高参与率。
8. 执行参数的在线调优
执行算法的参数(参与率、限价偏移、切片间隔)不能一次调好就固定。市场流动性是变化的,参数需要在线自适应:
| 参数 | 影响 | 调优信号 |
|---|---|---|
| 参与率上限 | 冲击 vs 完成度 | 成交率与冲击的边际变化 |
| 限价偏移 | 成交率 vs 价格 | 被动成交占比 |
| 切片间隔 | 隐蔽性 vs 择时 | 与同期市场 VWAP 的偏离 |
| 激进度 | 完成速度 | alpha 衰减实测值 |
调优的方法是控制变量 + A/B:同一类委托随机分配到不同参数组,比较实现落差。这本质上是在线实验设计,与 可观测性 里的实验度量是同一种方法论。
def tune_participation(records, current_rate, step=0.01):
# 找到「边际冲击 < 边际 alpha 收益」的参与率
lo, hi = current_rate, min(current_rate + 0.05, 0.30)
impact_lo = mean_impact(records, rate=lo)
impact_hi = mean_impact(records, rate=hi)
marginal_impact = (impact_hi - impact_lo) / (hi - lo)
if marginal_impact > marginal_alpha(records):
return max(lo - step, 0.02) # 冲击上升太快,降速
return hi
要注意不要过拟合执行参数。执行样本的信噪比很低(每笔成交的成本受当天行情影响极大),用几十笔成交就调参,很容易把当天的运气当成参数效果。至少要有几百笔同类型委托、跨越不同市场状态,参数调整才有统计意义。
9. 执行系统的工程结构
执行系统(Execution Management System, EMS)在 OMS 与柜台之间,负责调度、路由与监控:
策略/OMS ──Parent Order──► EMS ──Child Orders──► 柜台/交易所
│
├─ 调度器(何时下多少)
├─ 路由器(下到哪里)
├─ 限价器(什么价格)
└─ 监控器(进度与偏差)
延迟敏感的环节(限价计算、路由决策)应该放在低延迟路径上,与 低延迟交易系统架构 里的无锁与预分配模式一致;而调度与预测这类计算密集但非实时的部分,可以放在独立的进程里。
执行系统必须记录决策时刻的市场快照(到达价、盘口、成交量)。没有这些数据,事后无法计算实现落差,也无法归因。执行日志的完整性比执行算法的高深更重要——一个平庸但可观测的算法,比一个精妙但黑盒的算法更容易持续改进。
与交易所的交互细节(订单类型、撤改时序)在撮合引擎设计与订单管理里讨论,EMS 只需保证发出的子单符合场所规则。
权衡取舍
| 维度 | 激进执行 | 被动执行 | 暗池/内部化 |
|---|---|---|---|
| 冲击成本 | 高 | 低 | 最低 |
| 时间风险 | 低 | 高 | 中 |
| 完成确定性 | 高 | 低 | 中 |
| 逆向选择 | 低 | 低 | 高 |
| 适合 alpha | 衰减快 | 衰减慢 | 中性 |
执行速度的选择本质上是在「冲击」和「时间风险」之间移动。判断依据是 alpha 的半衰期:半衰期 10 分钟的信号,执行窗口不应超过 30 分钟;半衰期一天的信号,可以用一整天慢慢做。把执行速度和 alpha 衰减匹配起来,是执行系统最重要的一个决策。
基准的选择也有取舍:IS 基准对主动型策略公平,但波动大(受市场漂移影响);VWAP 基准稳定,但可能掩盖真实成本。实践中两个都算,IS 用于内部改进,VWAP 用于对客户汇报。
常见坑清单
- 用到达价还是收盘价不分:主动策略用收盘价基准会低估延迟成本,必须用 IS。
- 固定滑点建模:忽略滑点随规模、流动性、时段的变化,系统性低估小盘成本。
- 忽略永久冲击:以为慢执行零成本,实际永久冲击随总成交量累积。
- VWAP 含自身成交:基准被自己的冲击抬高,评估失真。
- 容量按常态系数算:危机中冲击系数放大数倍,容量瞬间失效。
- 机会成本不计入:只看成交价不看成交率,限价太保守导致建仓不足。
- 暗池不看逆向选择:中间价成交看似便宜,成交后价格反向变动。
- 用少量样本调参:几十笔成交调执行参数,把运气当规律。
- 不记录到达价快照:事后无法计算 IS,执行质量无从评估。
- 自适应执行越走越保守:把预测不确定性全留给尾盘,尾盘被迫激进。
小结
执行的核心矛盾是冲击成本与时间风险的对抗,所有执行算法都是这个矛盾的某种折中:TWAP 是极端被动,市价单是极端激进,Almgren-Chriss 给出了参数化的最优路径。理解平方根冲击律,就理解了为什么大单要拆、为什么策略有容量上限、为什么危机中执行成本会暴涨。
判断一个执行系统是否成熟,看三件事:有没有用 IS 而非 VWAP 做内部评估?有没有把机会成本算进去?执行参数有没有基于足够样本在线更新? 三件事都做到了,执行才是一个可优化的工程问题,而不是一个玄学。
下一步建议阅读 撮合引擎设计 ,理解订单在交易所侧如何被撮合与排队,这决定了你的限价单能否成交、排在什么位置;如果你关心执行链路本身的延迟,可以回顾低延迟交易系统架构里的无锁与预分配模式。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。