执行算法与最优执行

执行算法与最优执行的深度拆解:实现落差与 VWAP 基准的取舍、临时与永久冲击成本的建模、平方根冲击律与策略容量估算、Almgren-Chriss 轨迹求解、暗池撮合与智能路由、执行质量的归因分解,回答大单如何在不推高价格的前提下完成成交并客观度量执行优劣。

执行(execution)是把「投资决策」变成「实际持仓」的最后一公里。一个年化 20% 的选股信号,如果执行环节多付出 30 bp 的冲击成本、每年换手 50 次,那么光是执行就会吃掉约 15% 的收益。研究做得再好,执行不行,收益就蒸发在盘口里。

工程上真正难的地方在于:执行面对的是一个会对你的行为做出反应的市场。你买得越多越快,价格就被你推得越高;你买得越慢,价格漂移的风险就越大。这个「冲击成本 vs 时间风险」的对抗,是执行算法的核心矛盾,也是 Almgren-Chriss 这类模型要解决的问题。

与 OMS 关注「订单状态是否正确」不同,本文关注的是「成交价格是否划算」。订单管理与执行算法 已经介绍了 TWAP/VWAP/POV 的基本调度与 OMS 状态机,本文不重复这些基础,而是深入最优执行理论、冲击成本建模、暗池与智能路由,以及执行质量的归因方法。

本文按「基准 → 冲击 → 容量 → 最优轨迹 → 自适应 → 路由 → 归因 → 调优 → 工程」的顺序展开,重点放在「为什么」而不是「调用哪个库」。

目录

  1. 执行的三个基准与实现落差
  2. 冲击成本的两种形态
  3. 平方根冲击律与策略容量
  4. Almgren-Chriss 最优执行轨迹
  5. 自适应执行与成交量预测
  6. 暗池、内部化与智能路由
  7. 执行质量的归因分解
  8. 执行参数的在线调优
  9. 执行系统的工程结构

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 用于对客户汇报。

常见坑清单

  1. 用到达价还是收盘价不分:主动策略用收盘价基准会低估延迟成本,必须用 IS。
  2. 固定滑点建模:忽略滑点随规模、流动性、时段的变化,系统性低估小盘成本。
  3. 忽略永久冲击:以为慢执行零成本,实际永久冲击随总成交量累积。
  4. VWAP 含自身成交:基准被自己的冲击抬高,评估失真。
  5. 容量按常态系数算:危机中冲击系数放大数倍,容量瞬间失效。
  6. 机会成本不计入:只看成交价不看成交率,限价太保守导致建仓不足。
  7. 暗池不看逆向选择:中间价成交看似便宜,成交后价格反向变动。
  8. 用少量样本调参:几十笔成交调执行参数,把运气当规律。
  9. 不记录到达价快照:事后无法计算 IS,执行质量无从评估。
  10. 自适应执行越走越保守:把预测不确定性全留给尾盘,尾盘被迫激进。

小结

执行的核心矛盾是冲击成本与时间风险的对抗,所有执行算法都是这个矛盾的某种折中:TWAP 是极端被动,市价单是极端激进,Almgren-Chriss 给出了参数化的最优路径。理解平方根冲击律,就理解了为什么大单要拆、为什么策略有容量上限、为什么危机中执行成本会暴涨。

判断一个执行系统是否成熟,看三件事:有没有用 IS 而非 VWAP 做内部评估?有没有把机会成本算进去?执行参数有没有基于足够样本在线更新? 三件事都做到了,执行才是一个可优化的工程问题,而不是一个玄学。

下一步建议阅读 撮合引擎设计 ,理解订单在交易所侧如何被撮合与排队,这决定了你的限价单能否成交、排在什么位置;如果你关心执行链路本身的延迟,可以回顾低延迟交易系统架构里的无锁与预分配模式。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「量化交易」更多文章

  1. 风险模型与因子归因
  2. 回测偏差与过拟合防范
  3. 市场微结构与流动性