一家上海私募的交易员盯着一张母单:沪深300 ETF(510300.SH)800,000 股买入,VWAP 基准,窗口设到全天。基金经理只关心基点数字,对收盘集合竞价并不挑剔。交易员调出 20 日滚动中位的盘中成交量曲线,看到 A 股典型的「双峰带午休」形状——上午 9:30 开盘一峰,11:30 前再起一峰,13:00 复盘后又有一峰,14:55 收盘集合竞价之前最后一拉——然后开始切片。本课要搭的,就是驱动这些切片时点与切片大小的算法本身:从定义到可工作伪代码,覆盖三个时间表家族——VWAP、TWAP、POV。
学完本课你会拥有:每个算法的母单到子单切片器、关于「何时 TWAP 战胜 VWAP」的量化答案、POV 残量风险的显式规则、三种失败模式的信号。本课不挑选时间表 vs. IS——那是第 4 课;也不估市场冲击系数——那是模块 4.5.2。
VWAP(成交量加权平均价):用成交量曲线搭时间表
VWAP 按区间 期望 成交量比例切片。令 为区间 占当日总成交量的期望比例()。则区间 的子单大小为
基准——区间内公开盘面的成交量加权平均价(VWAP)——为
其中 是该区间实际公开成交量, 是该区间实际公开均价。如果你的时间表匹配 ,且子单成交跟得上 (除去价差),算法的平均成交价按构造等于 VWAP 基准,滑点只来自半买卖价差 与你自身订单流(order flow)带来的市场冲击 。偏离 会产生 VWAP 滑点期望为零、但方差被放大——这正是到达价基准会暴露的时间风险。
估计 ——5 分钟桶配方(executable Python snippet)
市场冲击(market impact)与订单流(order flow)分析共享同一分桶口径,下面的可工作伪代码也用于这些模块。
A 股的工业做法用 9:30–11:30、13:00–15:00 连续竞价时段的 20 日滚动中位、5 分钟桶。注意 11:30–13:00 的 午休——A 股的 是 带间断 的两段,不是单段 U 形;这是把美股 VWAP 算法直接搬到 A 股最常见的错误。510300.SH 在 5 分钟桶下覆盖 48 桶(上午 24 桶 + 下午 24 桶)加上收盘集合竞价。伪代码:
import numpy as np
# trades: DataFrame,列 [date, bucket_idx, shares]
# bucket_idx: 0..23 上午, 24..47 下午, 48 收盘集合竞价
daily_total = trades.groupby('date')['shares'].sum()
per_bucket = trades.groupby(['date', 'bucket_idx'])['shares'].sum() \
.div(daily_total, level='date')
U = per_bucket.unstack('bucket_idx').rolling(20).median().iloc[-1]
U = U / U.sum() # 归一化使权重和为 1
对 510300.SH,这通常重现「双峰 + 午休 + 收盘小拉」的形状:9:30 桶约占 3.5%,11:25 桶约占 2.5%,13:00 桶约占 2.0%,14:55 收盘集合竞价桶约占 1.5%–2.5%。一个忽略午休的曲线会把 13:00 复盘后的「热身」成交分散到一个不存在的连续区间里,造成系统性误调度。
TWAP(时间加权平均价):什么时候均匀打败曲线
TWAP 取 , 个等长区间——一条平直的时间表。子单大小是
TWAP 看上去比 VWAP 朴素,但它在三种场合占优:
- 平直成交量曲线的合约。 单段连续竞价合约——CFFEX 指数期货(IF、IC、IH)——的 几乎平直。从平直过程里估出一条弯曲的 只是把样本噪声注入时间表;TWAP 干脆不估。
- 非平稳成交量曲线。 当当日实现的成交量分布漂移(财报季单只股票、央行降息日 510300.SH),20 日中位的 相对当日真曲线是有偏的。TWAP 的「平直」假设错得比 VWAP 的「陈旧曲线」少。
- 无集合竞价的单段合约。 没有开盘 / 收盘集合竞价,VWAP 的集合竞价偏差问题就不存在;跟曲线的边际价值也降下来。
一个有用的诊断指标:如果 20 日滚动 MAD(中位绝对偏差)除以桶中位超过 0.4,曲线已不稳定到可下注的程度——这只名字 / 这周切换 TWAP。
POV:参与,而不是调度
POV 不承诺轨迹。它承诺一个 参与率 ——比如 5%——并让实际公开成交量驱动子单大小。区间 实际公开成交量为 时,子单为
POV 的强项是自适应:流动性强时多打,流动性枯时少打。它的弱点是 残量风险。若当日实际成交量低于预期,,算法会在日终留下未完成残量。基金经理这时面临选择——把残量推进收盘集合竞价(510300.SH 上代价低,单只小盘 A 股上代价高),或者隔夜留仓承担跳空风险,外加 T+1 结算的二级约束(见术语表 t-plus-1-settlement,A 股 T 日买入 T+1 才能卖出)。盘前分析通常在 时预警——5% POV 想清掉一笔母单,至少需要两个正常交易日的流动性。
A 股私募桌的参与率上限通常压在 5%–10% ADV 以下,避开涨跌停(limit-up / limit-down,见术语表)触发——这是 A 股市场结构对 POV 配置的硬约束,与美股 10% 的经验上限不同。
子单切片器:可工作伪代码
生产环境的切片器不止 q_t = Q * U(t)。它加入 抖动(jitter)——每个桶内随机化下单时点——以打破对周期性 VWAP 印迹的预测。完整骨架:
import numpy as np
def schedule_vwap(Q, U, time_grid, jitter_sec=15, seed=None):
"""返回 [(send_time, child_qty)] 形式的 VWAP 时间表."""
rng = np.random.default_rng(seed)
schedule = []
cum = 0
for t, frac in zip(time_grid, U):
target_cum = Q * (cum + frac)
child_qty = int(round(target_cum - cum * Q))
cum += frac
offset = rng.uniform(-jitter_sec, jitter_sec)
schedule.append((t + offset, child_qty))
# 取整修正:剩余股数分配到最大桶
total = sum(qty for _, qty in schedule)
if total != Q:
diff = Q - total
i_max = int(np.argmax([qty for _, qty in schedule]))
send_time, qty = schedule[i_max]
schedule[i_max] = (send_time, qty + diff)
return schedule
TWAP 是同一函数取 。POV 把静态 U 换成回调,每个区间读取实际 后重新计算 ,并受剩余母单约束。
这条把三个失败模式都连起来的核心公式:
Formula Explorer
q_t = Q * U_t三种经典失败模式
每个时间表算法都有一种正典的「亏钱方式」,且每一种都映射到一个可测量的盘后 TCA 信号。
-
VWAP 在开盘附近被反向利用。 反向流动性算法识别出 VWAP 节奏,把买侧子单挤到 9:30 开盘集合竞价的印迹里——此处明面流动性最薄。VWAP 基准本身被推得不利于你(开盘价差远宽于 9:35 之后的稳态价差)。TCA 信号:第一桶滑点持续为负,即使全窗口滑点接近零。
-
TWAP 错过成交量集群。 央行降息公告在 14:30 引爆 5 倍成交量;TWAP 的平直时间表继续发 ,错过明面流动性。滑点表现为错过桶的价差走宽以及相对新 VWAP 的机会成本。TCA 信号:事件日实际 VWAP 滑点显著上行,即使子单大小均匀。
-
POV 追着自己的脚印跑。 母单相对 ADV 大时,算法自身印迹支配了区间成交量度量。POV 看到高 ,发送 的子单;该子单印迹又抬高下一区间 ;反馈环让实际参与率超出名义 。TCA 信号:实际参与率超出配置 一个可测量边距( 时常见 1.2 倍–1.5 倍)。
A 股的额外注脚:印花税(stamp-duty)只压卖侧,是固定楔形成本,会出现在 TCA 中但不进入时间表调度。第 4 课会展开。
区域锚定算例(region-anchored example):510300.SH,800k VWAP 买入
母单 800,000 股全天 VWAP。 在 9:30 桶 3.5%、14:55 收盘集合竞价桶 2.0%。切片:、。、到达中价 ¥3.850,全笔交易成本约 4–6 bp。
模块 4.5.1 负责撮合规则把时间表映射为 ;模块 4.5.2 负责 的线性冲击模型。
练习
Exercise
你拿到沪深300 ETF(510300.SH)300,000 股买单,VWAP 基准,窗口 10:00–11:00。该窗口 20 日滚动中位 大致平直,每 5 分钟桶约 1%。10:30–10:35 桶实际公开成交量飙到中位的 4 倍(一份提前流出的研报)。写下:(a) VWAP 时间表在该桶的 ,(b) TWAP 时间表在该桶的 ,(c) 哪个时间表产出更低的 VWAP 基准滑点、为什么,(d) 5% POV 会不会比两者更好。
提示
提示
提示
与第 3 课的衔接
至此你能端到端搭一个时间表型算法:估 、切片、加抖动、识别失败模式。下一课从时间表迈向最优化。我们把静态 换成 Almgren–Chriss 最优轨迹——那个 在到达中价基准下最小化预期成本加上风险厌恶加权方差的时间表——并把单一场所执行换成跨 SSE、SZSE 主板与私募内部撮合网络的智能订单路由。交易成本会开始呈现时间表算法做不出来的形状。把本课的切片器骨架带进第 3 课;下一课要做的,是把 U 参数替换成 Almgren–Chriss 的输出。