halo 的技术博客

返回

有一个残酷的事实,散户永远体会不到,但每一个管着几亿资金的交易员每天都在跟它搏斗:你想买的价格,会因为你想买而消失。

散户下单,脑子里只有一个问题——买还是不买。因为你那 1000 股扔进市场,连一个水花都溅不起来,成交价就是你看到的价。但当你要买的是 10 万股、100 万股,占了这只票当天成交量的一大块时,第二个问题就冒出来了:这单怎么下,才不会自己把价格推上去?

结论先放这:把大单一次性砸进市场是最蠢的做法。 本文用 4000 条蒙特卡洛路径实测——同样一笔 10 万股的买单,一次性激进执行的平均滑点是 98.7bp,拆成一整天均匀下单(TWAP)降到 43.0bp,按日内成交量分布下单(VWAP)再降到 39.6bp。一次性冲击的成本是 VWAP 的 2.5 倍。这多出来的 60bp,对一笔千万级的单子就是几万块凭空蒸发。最优执行(Optimal Execution)研究的就是:怎么把一头大象,切成没人注意的小块,在时间轴上悄悄搬完。

为什么一次性下单这么贵:市场冲击的凸性#

要理解拆单为什么省钱,先得理解市场冲击(Market Impact)长什么样。

当你发出一个买单,它会吃掉订单簿上现有的卖单。你要的量越大,就得往上吃越多档,成交均价就越高。这个”因为我下单而导致的价格不利移动”,就是市场冲击。它分两层:

  • 临时冲击(Temporary Impact):只影响你这一笔的成交价。你这笔单吃穿了几档,但订单簿会被其他人的挂单迅速补回来,价格弹回去。它跟你这一瞬间的下单强度有关。
  • 永久冲击(Permanent Impact):你的成交向市场泄露了信息(“有人在大举买入”),把中间价永久性地推高了,不会弹回来。它跟你累计买了多少有关。

关键在临时冲击的凸性。一个简化但抓住本质的模型是:某一分钟的成交价偏移 ∝ 你这一分钟的参与率(你的下单量 ÷ 市场这一分钟的成交量)。也就是说,你下单越猛,单位成本越高——成本相对下单速度是**超线性(凸)**的。

凸性一旦成立,一个纯数学结论就把拆单的收益锁死了。假设你要在 N 段时间里买完总量 Q,第 i 段买 q_i、市场成交量 v_i,总临时冲击成本约正比于:

Costiqi2vi\text{Cost} \propto \sum_i \frac{q_i^2}{v_i}

要最小化这个和(约束 Σq_i = Q),用 Cauchy-Schwarz 不等式,最优解是:

qiviq_i^* \propto v_i

翻译成人话:每一段时间应该买的量,正比于那段时间市场自己的成交量。 这正是 VWAP(Volume Weighted Average Price,成交量加权平均价)策略的数学根据。而 TWAP(Time Weighted Average Price,时间加权平均价)——不管市场成交量、每段时间买一样多——是当你假设成交量在全天均匀分布时的特例。现实中成交量不均匀,所以 VWAP 通常比 TWAP 更省。

日内成交量的 U 型:VWAP 拆单的地图#

VWAP 要跟着成交量走,那成交量长什么样?几乎所有流动性正常的股票,日内成交量都呈现一条稳定的 U 型曲线:开盘时集合竞价和隔夜信息释放带来放量,午间交投清淡跌到谷底,收盘前因为要对齐收盘价、指数基金调仓、日内头寸了结再次放量。

我用一个双指数模型复现这条曲线,并对比 TWAP 与 VWAP 两种拆法:

import numpy as np

M = 390  # 美股一个交易日的分钟数(6.5 小时)
t = np.arange(M)
# U 型:开盘、收盘放量,午间清淡
u_shape = 1.0 + 1.6*np.exp(-t/40.0) + 1.9*np.exp(-(M-1-t)/45.0)
volume_profile = u_shape / u_shape.sum()   # 归一化成每分钟成交量占比

Q = 100_000.0                    # 总下单量(股)
twap_slices = np.full(M, Q/M)    # TWAP:每分钟买一样多
vwap_slices = Q * volume_profile # VWAP:按成交量占比切
python

日内成交量 U 型曲线与 TWAP/VWAP 拆单对比

左图是那条经典的 U 型:开盘、收盘两头翘,中间塌陷。右图是同样 10 万股的两种拆法——TWAP 是一条笔直的橙线(每分钟 256 股,雷打不动),VWAP 是一条跟着成交量起伏的绿线(开盘时一分钟下上千股,午间只下一两百股)。

直觉上就能看出问题:TWAP 在午间清淡时段,下单量和开盘时一样多,但那时市场没人跟你交易,你的参与率飙升——同样 256 股,占午间成交量的比例远高于占开盘成交量的比例。凸性惩罚就砸在这里。VWAP 则聪明地在市场活跃时多买、清淡时少买,把参与率压平,从而压低总冲击。

蒙特卡洛实测:三种执行方式的滑点分布#

光有理论不够,我把市场冲击模型写进蒙特卡洛,跑 4000 条价格路径,直接量出三种执行方式的滑点分布。价格模型 = 随机游走 + 永久冲击(累积、不消散)+ 临时冲击(线性于参与率、当笔消散):

三种拆法喂进去:VWAP(跟量)、TWAP(均匀)、激进执行(整单集中在开盘前 30 分钟砸完)。结果:

三种执行方式的滑点分布

三个分布拉开明显的档次:

  • 激进执行(红):均值 98.7bp,分布又宽又靠右。把整单压在开盘 30 分钟,参与率爆表,凸性惩罚全额兑现,而且方差还大——因为你把执行风险全押在了一小段时间窗上。
  • TWAP(橙):均值 43.0bp。摊到一整天,参与率大幅下降,成本降到激进执行的 44%。
  • VWAP(绿):均值 39.6bp,分布最靠左最集中。在 TWAP 基础上再省 8%,靠的就是把参与率在时间轴上进一步拉平。

注意 VWAP 相对 TWAP 的优势(40 vs 43bp)远小于两者相对激进执行的优势(40/43 vs 99bp)。这是最优执行里最重要的一课:第一大收益来自”拆单”本身(不要一次性砸),VWAP 相对 TWAP 的精细优化是second-order 的锦上添花。 很多人一上来就纠结 VWAP 曲线拟合的精度,却忘了只要你没在拆单,怎么拆都是省的。

Almgren-Chriss:成本与风险的有效前沿#

到这里似乎结论很清楚:拆得越细、摊得越久越省。但这里藏着一个致命的反作用力——时间就是风险

你把 10 万股摊到一整天甚至几天来买,冲击成本是低了,但在你还没买完的这段时间里,价格可能因为和你完全无关的原因(大盘暴跌、行业利空)走掉。你想买的票在你慢吞吞建仓的过程中涨了 5%,省下的那几十个 bp 冲击成本,在这 5% 面前不值一提。这就是时序风险(Timing Risk):持仓暴露于市场波动的时间越长,风险越大。

Almgren 和 Chriss 在 2000 年把这对矛盾写成了一个优雅的最优化问题:在给定风险厌恶程度下,最小化”预期执行成本 + λ × 执行风险”。 解出来是一族清仓轨迹:

def ac_trajectory(kappa):
    # kappa 由风险厌恶隐含:越大代表越急着清仓
    if kappa < 1e-9:
        return X * (1 - np.arange(Nsteps+1)/Nsteps)   # 线性清仓 = TWAP
    j = np.arange(Nsteps+1)
    return X * np.sinh(kappa*(Nsteps-j)*tau) / np.sinh(kappa*Nsteps*tau)
python

Almgren-Chriss 最优清仓轨迹与有效前沿

左图是三条代表性的清仓轨迹:

  • κ=0(橙):风险中性,只想省成本,得到一条直线——就是 TWAP,把量匀速摊满全程。成本最低,但持仓暴露时间最长。
  • κ=9(红):高度风险厌恶,急着把仓位清掉——曲线陡降,前期就卖掉大半。执行快、时序风险低,但因为前期下单猛、参与率高,冲击成本高。
  • κ=3(蓝):介于两者之间的平衡解。

右图是这族解扫出来的有效前沿:横轴是时序风险,纵轴是预期执行成本。它和马科维茨的均值-方差前沿是同一个哲学——没有免费午餐,只有取舍。你想省成本就得慢慢来、承担更多价格漂移风险;你想快速锁定就得付更高的冲击。落在前沿上的每一点都是”给定风险下成本最低”的最优解,具体选哪一点,取决于你有多急、这只票的波动有多大。

TWAP/VWAP 其实是这条前沿上的两个特例(对应风险中性、只顾省成本的角落)。当你的交易信号有明确时效性(比如基于一个很快会失效的 alpha)时,你会主动往前沿的右下角移动——牺牲一点冲击成本,换取更快的执行、更少的时序风险。

VWAP 的死穴:你在跟踪一个还不存在的基准#

VWAP 听起来很美,但它有一个结构性的软肋,很多人上手才踩到:VWAP 是一个事后才能算出来的基准。

市场当天的真实 VWAP,要等收盘、所有成交都发生完了才知道。但你从开盘就得开始拆单执行。也就是说,你在用一条历史平均的成交量曲线来切单,赌今天的成交量分布和历史相似。一旦今天出了大新闻、成交量分布整体偏移或形状扭曲,你按历史曲线切出来的单就会偏离真实的市场 VWAP——这就是 VWAP 策略的跟踪误差(Tracking Error)

我模拟 500 个交易日,每天的真实成交量 = 历史 U 型 × 当日随机扰动(整体量偏移 ±35% + 分钟级噪声 ±25%),比较”用静态历史曲线”和”部分动态更新”两种拆单方式相对真实市场 VWAP 的偏离:

VWAP 跟踪误差分布

红色是纯用静态历史曲线(跟踪误差标准差 0.40),绿色是能部分观测到当日实际成交量、动态修正拆单(标准差 0.28)。两个分布都以 0 为中心——VWAP 策略在期望上能贴住基准,但每一天都会有随机偏离,有时跑赢、有时跑输,取决于你的成交量预测那天准不准。

这就解释了机构里为什么有专门的团队在做日内成交量预测:VWAP 执行的好坏,一半取决于拆单算法,另一半取决于你对”今天成交量会怎么分布”预测得有多准。预测越准,跟踪误差越小,你的执行就越稳定地贴住基准。绿色分布比红色窄,量化的就是”更好的成交量预测”值多少钱。

已知偏差(一定要说清楚)#

  1. 冲击模型是简化的。真实市场冲击是非线性的(著名的”平方根律”:冲击 ∝ √参与率,而非线性),且与波动率、买卖价差、订单簿深度耦合。本文用线性临时冲击是为了让 Cauchy-Schwarz 的凸性推导干净、结论清晰,真实标定需要用自家成交回补数据拟合。绝对 bp 数值是示意性的,相对排序(激进 > TWAP > VWAP)才是稳健结论
  2. 忽略了对手方和博弈。本文假设市场对我们的拆单没有针对性反应。现实中如果你的拆单模式太规律(尤其是纯 TWAP 的等间隔等量),会被其他算法(乃至掠夺性 HFT)识别并前置交易(front-run),这也是实盘要引入随机化拆单、暗池的原因。
  3. 成交量分布假设静态且外生。有效前沿和 VWAP 拆单都假设市场成交量不受我们影响。大单本身会改变成交量结构,日内还可能出现结构性事件(停牌、熔断、突发新闻),历史 U 型完全失效。
  4. 没有建模隔夜/多日执行。Almgren-Chriss 的多日版本要处理隔夜跳空风险,那是另一个量级的时序风险,本文只覆盖日内。

结果解读#

第一收益来自”拆”,不是来自”怎么拆”。 激进执行 98.7bp、TWAP 43bp——光是把单子摊开就省掉一半以上的滑点。VWAP 相对 TWAP 再省 8%(40 vs 43bp)是精细优化,重要但次要。如果你资源有限,先保证”绝不一次性砸单”,再去抠 VWAP 曲线的精度。

省成本和控风险是一对反向的力。 Almgren-Chriss 有效前沿说得很清楚:拆得越久、摊得越平,冲击成本越低,但你承担的价格漂移风险越大。最优执行不是”把成本降到最低”,而是”在你能承受的风险下把成本降到最低”。一个快要失效的 alpha 信号,宁可多付点冲击也要快速建仓;一个长期价值仓位,则可以慢慢磨、把成本榨干。

VWAP 的稳定性取决于成交量预测。 它在期望上贴住基准,但每天都有跟踪误差,误差大小直接由你的日内成交量预测精度决定。这解释了为什么”更好的成交量预测模型”在机构里是真金白银的资产——它不产生 alpha,但它实实在在地降低执行的方差。

这套逻辑对散户也不是完全无用。 如果你要买一只流动性差的小盘股、或者要一次性调仓一大笔,不要用市价单一把梭。把单子拆成几笔、用限价单、避开午间清淡时段、尽量在成交活跃时下单——这就是穷人版的 VWAP。你占市场成交量的比例越大,这套纪律就越值钱。

最优执行是量化世界里少数”不产生 alpha 却直接决定盈亏”的领域。一个再好的策略,如果执行时把 alpha 全喂给了市场冲击,纸面收益和实盘收益之间就会裂开一道无法弥合的鸿沟。好的策略负责找到钱,好的执行负责把钱真正拿到手。

TWAP/VWAP 最优执行:把大单拆细在时间轴上隐身
https://blog.halo26812.eu.org/blog/optimal-execution-twap-vwap
Author halo
Published at 2026年7月25日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨