2. TWAP核心数学原理:时间加权平均价格公式推导、切片逻辑与执行周期设计
好,咱们直接进入正题。TWAP,全称 Time-Weighted Average Price,时间加权平均价格。名字听着挺唬人,其实核心思想特别朴素——就是把一个大订单,拆成很多小单,均匀地撒在交易时间段里。
为什么要这么做?说白了,就是为了不引起市场注意。你想想看,如果你一口气砸进去100万股,盘口瞬间就被你打穿好几个价位,滑点成本高得吓人。TWAP就是帮你「润物细无声」地把单子做完。
2.1 时间加权平均价格的数学本质
先看公式。TWAP的定义其实很简单:
TWAP = (P₁ + P₂ + ... + Pₙ) / N
其中 P₁ 到 Pₙ 是每个时间切片上的成交价格,N 是切片数量。注意,这里每个价格的权重是一样的——这就是「时间加权」的含义。每个时间点,权重相同。
我刚开始做量化的时候,经常把TWAP和VWAP搞混。VWAP是按成交量加权的,哪个时间点成交量大,那个价格权重就高。TWAP不管成交量,只看时间。这个区别很关键,后面讲实战的时候你会体会到。
2.2 切片逻辑:把时间切成几块?
好,公式看完了。接下来是实操问题:怎么切?
假设你需要在1小时内买入100万股。你打算切成10份,每份10万股,每6分钟发一个单子。这就是最朴素的切片逻辑。
但问题来了——切多少份合适?
我个人习惯,切片数量取决于三个因素:
- 订单规模:订单越大,切片越多。我做过一个极端案例,单笔订单占日成交量的5%以上,我切了200多片。
- 市场流动性:流动性好的股票,切片可以少一些。流动性差的,必须多切,不然一单下去价格就飞了。
- 执行时间窗口:时间越短,切片越少。时间充裕,可以切得细一些。
这里有个经验公式,我用了好几年:
切片数量 = min( max( 订单量 / 预期每片成交量, 10 ), 200 )
预期每片成交量,我一般取过去5分钟平均成交量的1/10。这样既能保证每单不会太大,又不会切得太碎导致延迟成本上升。
2.3 执行周期设计:什么时候发单?
切片逻辑搞定了,接下来是执行周期。说白了,就是每个切片之间的时间间隔怎么定。
最简单的做法:均匀分布。比如1小时切20片,每3分钟发一单。但这样做有个问题——如果市场在某个时间点突然波动很大,你的单子刚好撞在枪口上。
我建议的做法是:引入随机偏移。
具体来说,每个切片的时间点不是固定的,而是在基准时间点附近加一个随机偏移量。比如基准是每3分钟发一单,实际发单时间可以是 2分50秒、3分05秒、2分55秒……这样随机化。
为什么要这么做?
嗯,这里要注意。如果你每次都在整点发单,其他算法交易者很容易识别出你的模式。他们可能会在你前面抢跑,导致你的成交价格变差。随机化之后,你的交易模式就难以预测了。
我曾经在实盘中吃过这个亏。一开始用固定间隔,结果被一个做市商盯上了,每次我发单前价格就往上跳一跳。后来改成随机偏移,情况立刻好转。
2.4 核心逻辑流程图
下面这张图,是我自己总结的TWAP执行流程。你看一遍,基本就明白整个链条了。
2.5 执行偏差与修正机制
理论讲完了,咱们聊聊现实。TWAP在实际执行中,一定会出现偏差。原因很简单——市场不是均匀的。
偏差主要来自两个方面:
- 时间偏差:你的单子发出去,可能因为网络延迟、交易所排队等原因,没有在预定时间成交。
- 价格偏差:你发单时的市场价格,和切片基准价格之间可能有差距。
怎么处理?我一般用动态调整。
具体做法是:每执行完一个切片,就计算一下当前的实际成交均价和理论TWAP之间的差距。如果偏差超过阈值(比如0.1%),就在下一个切片中微调订单量,把偏差拉回来。
2.6 一个简单的代码示例
最后,给一个Python伪代码。你拿去改改就能用。
def twap_executor(symbol, total_quantity, time_window_sec, num_slices):
"""
TWAP执行器
:param symbol: 股票代码
:param total_quantity: 总订单量
:param time_window_sec: 执行时间窗口(秒)
:param num_slices: 切片数量
"""
slice_qty = total_quantity / num_slices
interval = time_window_sec / num_slices
executed_qty = 0
total_cost = 0
for i in range(num_slices):
# 生成随机偏移(±10%的间隔时间)
offset = random.uniform(-interval * 0.1, interval * 0.1)
sleep_time = interval + offset
# 等待到预定时间
time.sleep(sleep_time)
# 发单(这里用市价单示例)
order = market_order(symbol, slice_qty)
# 记录成交
executed_qty += order.filled_qty
total_cost += order.filled_qty * order.avg_price
# 动态调整:如果偏差过大,修正下一片
actual_avg = total_cost / executed_qty
theoretical_avg = get_current_market_price(symbol)
deviation = abs(actual_avg - theoretical_avg) / theoretical_avg
if deviation > 0.001: # 偏差超过0.1%
# 修正下一片的订单量
slice_qty *= (1 + (theoretical_avg - actual_avg) / theoretical_avg * 0.6)
return executed_qty, total_cost / executed_qty
这段代码我简化了很多,但核心逻辑都在里面了。你实际用的时候,记得加上风控、日志、异常处理这些东西。
好了,TWAP的核心数学原理和切片逻辑就讲到这里。说白了,就是「均匀拆分 + 随机执行 + 动态修正」这三板斧。你把这个框架搭好了,剩下的就是根据具体场景调参数。