Obizhaeva-Wang 执行模型:把限价簿弹性写进最优拆单
Almgren-Chriss 假设冲击成本只跟'交易多快'有关,却漏掉了限价簿的一个关键性质——它会恢复。你砸下一笔单,价格被临时推走,但只要停手,挂单会重新涌入,把冲击慢慢填平。Obizhaeva-Wang (2013) 把这个'弹性'写进模型,得出一个 AC 里没有的结论:最优执行不是一条平滑衰减曲线,而是'两端各下一个离散大块 + 中间恒定速率连续吃单'的 U 型轨迹。本文从限价簿动力学出发推导 OW 最优解,用蒙特卡洛证明它在期望成本(43.4bp vs TWAP 45.0bp)与成本方差上双双改善,弹性越低优势越大,并诚实拆解连续模型在离散撮合、日内弹性时变、被猎杀四个方向上的失真(中阶)。
Almgren-Chriss(AC,2000)是最优执行的教科书起点,但它有一个被反复忽略的隐含假设:限价簿没有记忆。在 AC 里,你这一秒吃掉的流动性,下一秒对市场毫无影响——临时冲击是瞬时的,你停手它立刻归零。这个假设让数学变得漂亮(成本只跟”交易速度”有关,最优解是一条平滑衰减曲线),但它和真实的限价簿对不上。
真实的簿是这样的:你砸下一笔市价单,吃穿几档挂单,价格被临时推高;但只要你停手几秒到几分钟,新的限价单会重新涌进被吃空的档位,价格慢慢回落。这个”被打乱后自我修复”的性质,市场微观结构里叫弹性(resilience)。
结论先放这:Obizhaeva-Wang(OW,正式发表于 2013,工作稿始于 2005)把弹性写进模型后,最优执行的形状发生了质变——它不再是 AC 的平滑衰减曲线,而是”期初下一个离散大块、期末再下一个离散大块、中间用恒定速率连续吃单”的 U 型轨迹。 直觉是:期初那一块打出的临时冲击,在随后的连续吃单期间会自己恢复掉,等于”免费”用掉了一部分深度;期末那一块则因为后面没有交易了,不用担心自己推高自己的后续成交价。本文用一个自洽的限价簿动力学模型复现这套逻辑,蒙特卡洛显示 OW 最优执行的期望缺口 43.4bp、优于 TWAP 的 45.0bp,且成本方差更小;弹性越低(簿越薄、恢复越慢),OW 相对 TWAP 的优势越大。附完整 Python 与四类真实陷阱。

一、AC 漏掉了什么:从”深度”到”弹性”#
AC 模型里只有两个成本来源:
- 永久冲击:交易改变了市场对价值的看法,价格永久移动。与你怎么拆单无关(拆多细,永久冲击的总量都一样),所以在优化里其实是个常数,不影响最优形状。
- 临时冲击:吃簿深度的瞬时代价,只跟”这一刻交易多快”有关,交易结束立刻消失。
OW 的关键补充是:临时冲击不是瞬时消失的,而是按一个有限速率恢复。用一个状态变量 表示”当前尚未恢复的临时冲击”,它的动力学是:
- 是这一刻的交易量, 是吃簿系数( 越大簿越深,同样交易量推动越小);
- 是恢复项, 就是弹性——恢复速率。 时临时冲击瞬间归零,OW 退化回 AC; 时临时冲击永不恢复,等价于所有冲击都是永久的。
成交价 = 到达价 + 累计永久冲击 + 当前未恢复的临时失衡 。你每一笔单付的成本,不只是它自己打出的冲击,还包括之前那些单留下的、还没恢复完的残余失衡。这就是 AC 里完全不存在的项:交易之间通过 产生了时间耦合。
上面那张图画的就是这件事:在 和 各下一笔单,恢复慢()的簿在第二笔单下手时,第一笔的冲击还没消完,两者叠加, 被顶得更高;恢复快()的簿则几乎每次都从零开始。弹性决定了”两笔单该隔多远”——恢复越慢,越该把单摊开等它恢复;恢复越快,越可以密集连续吃。
二、OW 最优解:为什么是 U 型而不是平滑曲线#
把执行成本写成拆单向量 (每步占总量的比例,)的二次型:
其中 的每个元素 ()= 第 步交易对第 步成交价的影响,由两部分组成:
- 临时部分:(第 步的冲击到第 步已经恢复了一部分);
- 永久部分:(不恢复,对所有后续成交价一视同仁)。
在 、 约束下最小化这个二次型,就是 OW 的离散版最优执行。下面这段代码构造 并用 SLSQP 求解——不写死”两端块+中间恒速”的形状,让优化器自己找出来:
import numpy as np
from scipy.optimize import minimize
TEMP, PERM, P0 = 1.20, 0.15, 100.0 # 临时/永久冲击系数、到达价
def cost_matrix(N, rho, dt):
"""H[i,j] = 第 i 步交易对第 j 步(j>=i)成交价的影响。"""
idx = np.arange(N)
diff = idx[None, :] - idx[:, None] # j - i
lower = diff >= 0 # 只有 i<=j 才有影响(因果)
temp = np.where(lower, TEMP * np.exp(-rho * diff * dt), 0.0)
perm = np.where(lower, PERM, 0.0)
return temp + perm
def ow_trajectory(X, T, N, rho):
"""在 sum(f)=1, f>=0 下最小化 f^T H f,求真正最优的执行占比。"""
dt = T / N
H = cost_matrix(N, rho, dt)
Hs = 0.5 * (H + H.T) # 对称化
obj = lambda f: f @ Hs @ f
grad = lambda f: 2 * Hs @ f
cons = {"type": "eq", "fun": lambda f: f.sum() - 1.0}
res = minimize(obj, np.full(N, 1/N), jac=grad,
bounds=[(0, 1)] * N, constraints=cons,
method="SLSQP", options={"maxiter": 500, "ftol": 1e-12})
f = np.clip(res.x, 0, None)
return f / f.sum() * Xpython求解结果如下图(左):弹性越弱(,红线),最优轨迹越呈明显的 U 型——期初、期末各堆一个大块,中间被压成一条低平的恒速带;弹性越强(,绿线),U 型越平,逐渐逼近 TWAP 的水平直线。这正是 OW 理论解的核心特征,也是它区别于 AC 平滑衰减曲线的地方。

为什么是 U 型?拆开看两端:
- 期初大块:它打出的临时冲击,会在随后漫长的连续吃单期间被弹性慢慢恢复掉。等于说,期初这一块”借用”了后面的恢复时间,它的临时成本被摊薄了。
- 期末大块:它后面没有任何交易了,所以不必担心”自己推高自己的后续成交价”。前面各步累积的 已经恢复得差不多,这一块可以趁着簿重新变厚一次性打掉。
- 中间恒速:连续、均匀地吃,让 维持在一个稳态水平,既不让残余失衡堆积,也持续利用恢复。
三、蒙特卡洛:OW 到底省了多少#
光有形状不够,得验证它在含随机价格波动的环境里真的更省。下面模拟执行成本:临时冲击按 恢复,叠加永久冲击和一条独立的价格随机游走。
def simulate_cost(traj, rho, dt, sigma=0.0, seed=None):
X, N = traj.sum(), len(traj)
D = perm = rw = cash = 0.0
rng = np.random.default_rng(seed)
for i in range(N):
f = traj[i] / X
rw += sigma * np.sqrt(dt) * (rng.standard_normal() if sigma > 0 else 0.0)
D += TEMP * f # 吃簿抬升临时失衡
perm += PERM * f # 永久冲击累积
cash += traj[i] * (P0 + perm + D + rw) # 成交价 = 到达价+永久+临时+游走
D *= np.exp(-rho * dt) # 交易后临时冲击恢复
return cash / X # 平均成交价
# 参数:卖 100 万股,20 步,弹性 rho=2,波动 sigma=2%
X, T, N, rho, sigma = 1_000_000, 1.0, 20, 2.0, 0.02
dt = T / N
ow = ow_trajectory(X, T, N, rho)
twap = np.full(N, X / N)
cost_ow = np.array([simulate_cost(ow, rho, dt, sigma, s) for s in range(4000)])
cost_tw = np.array([simulate_cost(twap, rho, dt, sigma, s) for s in range(4000)])
bps = lambda c: (c - P0) / P0 * 1e4
print(f"OW : {bps(cost_ow).mean():.1f}bp ± {bps(cost_ow).std():.1f}")
print(f"TWAP: {bps(cost_tw).mean():.1f}bp ± {bps(cost_tw).std():.1f}")
# OW : 43.4bp ± 1.1
# TWAP: 45.0bp ± 1.2python结果是 4000 次蒙特卡洛下 OW 期望缺口 43.4bp、TWAP 45.0bp——省了约 1.6bp。对单笔 5000 万名义的执行,1.6bp 是 8000 元,一年跑几千笔就是七位数的差别。更关键的是下图揭示的第二层优势:OW 不仅期望成本更低,成本分布也更窄(标差 1.1 vs 1.2)——它同时改善了成本的均值和方差,这是 AC 有效前沿思想的自然延伸。

四、弹性扫描:什么时候最该用 OW#
省 1.6bp 是在 这个特定弹性下的结果。OW 的价值高度依赖弹性——弹性越低,AC/TWAP 越吃亏,OW 的相对优势越大。下面扫描 从 0.5 到 40:
rhos = np.linspace(0.5, 40, 30)
for rho in rhos:
ow = ow_trajectory(X, T, N, rho)
twap = np.full(N, X / N)
c_ow = (simulate_cost(ow, rho, dt) - P0) / P0 * 1e4
c_tw = (simulate_cost(twap, rho, dt) - P0) / P0 * 1e4
# 弹性低时 c_tw - c_ow 最大;弹性高时两者收敛python结果见下图:弹性 很低(簿很薄、恢复很慢)时,TWAP 的成本曲线明显高于 OW,两者之间的绿色阴影就是 OW 省下的钱;随着 升高,簿恢复越来越快,冲击很快消散,怎么拆都差不多,两条线收敛。 这给出一个非常实用的判断:流动性差、盘口薄、恢复慢的标的(小盘股、非活跃时段、大宗),OW 才值得上;对流动性极好、簿几秒就填满的大盘蓝筹主力时段,OW 相对 TWAP 的边际收益很小,简单 TWAP/VWAP 就够了。

A. 实现细节#
- 信号与成本口径:成交价 = 到达价 P0 + 累计永久冲击 + 当前未恢复临时失衡 + 独立价格随机游走。永久冲击对拆单形状不敏感(是常数项),真正被优化的是临时冲击的时间耦合。
- 最优解求法:不预设”两端块+中间恒速”形状,而是把成本写成二次型 ,在 下用 SLSQP 数值最小化,让 U 型自然涌现——这样避免了硬编码解析近似带来的形状误差。
- 弹性建模:临时冲击按 离散恢复, 是唯一决定形状的关键参数。 退化为 AC/TWAP, 退化为全永久冲击。
- 成本单位:以到达价 P0=100 为基准,最终换算成 bps(相对缺口),跨标的可比。
- 随机游走独立于轨迹:价格随机游走用固定 seed 保证 OW 与 TWAP 在同一条价格路径上对比,隔离掉”运气”,只比较执行策略本身的优劣。
B. 已知偏差#
- 连续时间模型 vs 离散撮合:OW 的”离散大块”在真实市场里不可能瞬时成交,一个大块本身就会吃穿多档、触发它自己的临时冲击链。真实执行需要把两端块再拆成子序列,U 型会被”抹圆”,理论节省会打折。
- 弹性时变且难估: 在日内不是常数——开盘、收盘、消息时点弹性骤降。用单一 建模是粗近似,实盘要滚动估计弹性并动态调整,否则用错 可能反而比 TWAP 更差。
- 合成成本系数:TEMP/PERM 是为了让机制清晰而设的自洽参数,绝对 bps 数值(43 vs 45)不代表任何真实标的。真实市场的临时/永久冲击比例需要用自己的成交回补数据(trade + book replay)标定。
- 两端块会被猎杀:期初、期末的离散大块是可预测的下单指纹,高频对手方一旦识别出”这家在用 OW”,会在两端块前抢跑。实盘必须对块大小和时点加随机化扰动,牺牲一点理论最优换取抗猎杀。
C. 结果解读#
- 核心增量在”弹性”这一个维度:OW 相对 AC 的全部价值,来自承认限价簿会恢复。如果你的标的簿恢复极快(大盘主力时段),OW 和 TWAP 几乎无差,别为了用而用;只有簿薄、恢复慢的场景(小盘、大宗、清淡时段),OW 的 U 型才真正省钱。
- 形状是可解释的,不是拟合出来的:期初块借用后续恢复时间、期末块无后续成交价顾虑、中间恒速维持稳态失衡——每一段都有清晰的经济含义,这让 OW 比黑箱执行算法更可信、更好调试。
- 同时改善均值和方差:OW 不只是期望成本低 1.6bp,成本分布还更窄。对一个每天要执行成百上千笔的机构台子,成本方差的收窄意味着更稳定的可预测执行质量,这在业绩归因里和期望成本一样重要。
- 落地优先级:真实部署 OW 的收益排序应该是——先把弹性 估准(错的 比不用 OW 更糟),再把两端块拆细并加随机化,最后才是微调 TEMP/PERM 比例。把连续解直接照搬成两个巨型冰山单,是最典型的”理论正确、实盘挨打”。
- 和 AC 的关系:OW 不是推翻 AC,而是 AC 的严格推广——当弹性趋于无穷,OW 的 U 型压平成 AC 的平滑曲线。理解这一点能帮你判断在你的市场里到底该用哪个:先测弹性,弹性高用 AC/TWAP,弹性低才上 OW。