拆单算法核心原理:时间加权平均价格(TWAP)算法详解
TWAP,全称 Time-Weighted Average Price,时间加权平均价格。这名字听着挺唬人,说白了就是——把一个大单子,切成很多小单子,然后均匀地撒到一段时间里。
我刚开始做量化交易那会儿,第一次接触 TWAP 就觉得:这不就是个定时器加循环吗?后来真上了生产环境才发现,坑比想象的多得多。今天咱们就把这个算法掰开揉碎了讲清楚。
TWAP 的核心思想
TWAP 的目标很简单:让成交价格尽可能接近这段时间内的平均价格。
你想想看,如果你要买 100 万股某只股票,一次性挂单,市场直接给你拉涨停。但如果你分成 100 次,每次 1 万股,每隔 1 分钟发一次,市场根本感觉不到你的存在。
这就是 TWAP 的哲学:用时间换流动性,用分散换隐蔽。
数学公式:其实没那么复杂
TWAP 的计算公式长这样:
TWAP = (P₁ + P₂ + P₃ + ... + Pₙ) / N
其中 P₁ 到 Pₙ 是每个时间点的成交价格,N 是时间点的数量。
嗯,就这么简单。但实际执行的时候,我们要解决的是:如何让我的成交价格,尽可能接近这个理论值。
标准 TWAP 算法流程
我一般把 TWAP 拆成三个步骤:
- 切分阶段:把总订单量按时间均匀切分
- 执行阶段:在每个时间切片内,把对应的子单发出去
- 调整阶段:根据市场情况微调剩余订单的节奏
来看一个最基础的实现:
class TWAPExecutor:
def __init__(self, total_quantity, start_time, end_time, slices=20):
self.total_qty = total_quantity
self.start = start_time
self.end = end_time
self.slices = slices
# 每片的时间间隔(秒)
self.interval = (end_time - start_time) / slices
# 每片的数量
self.slice_qty = total_quantity / slices
def generate_schedule(self):
schedule = []
for i in range(self.slices):
slice_time = self.start + i * self.interval
schedule.append({
'time': slice_time,
'quantity': self.slice_qty
})
return schedule
def execute(self, market_api):
schedule = self.generate_schedule()
for slice_info in schedule:
# 等待到指定时间
wait_until(slice_info['time'])
# 发送子单
market_api.send_order(
quantity=slice_info['quantity'],
order_type='LIMIT',
price=get_current_price()
)
关键点:这个代码只是教学演示。生产环境里,你还需要考虑滑点、延迟、订单簿深度等因素。
TWAP 的变体:不是所有场景都适合硬切
我在项目中遇到过一个问题:市场突然剧烈波动,但 TWAP 还在按部就班地发单。结果就是,明明可以等一等再买,却买在了高点。
所以后来我做了几个改进:
| 变体名称 | 核心改进 | 适用场景 |
|---|---|---|
| Volume-Weighted TWAP | 根据历史成交量分布调整切片大小 | 开盘/收盘时段流动性不均 |
| Adaptive TWAP | 根据实时市场波动动态调整切片间隔 | 高波动行情 |
| Residual TWAP | 根据已成交情况重新计算剩余切片 | 部分成交或撤单频繁的场景 |
说白了,标准的 TWAP 是个「死」算法,而实战中我们需要「活」的 TWAP。
避坑指南:我踩过的几个坑
我曾经犯过一个低级错误:把切片数量设得太多,比如 1000 片。结果每片只有几百股,频繁发单导致交易所把我当成了高频交易者,直接限制了接口权限。
这里有几个经验:
- 切片数量不要超过 50,除非你有特殊需求
- 每片数量不要低于最小交易单位,否则会报错
- 留出缓冲时间,最后一片不要卡在结束时间点上
TWAP 与暗池的配合
TWAP 本身不依赖暗池,但两者结合效果更好。我习惯的做法是:
- 用 TWAP 生成时间切片
- 每个切片优先发到暗池
- 如果暗池没有成交,再发到公开市场
这样做的好处是:暗池吃掉了一部分流动性,公开市场的冲击就小了。
性能考量:别让算法拖慢系统
TWAP 算法本身计算量很小,但执行时的延迟很关键。我建议:
- 预计算时间表:在算法启动前就把所有切片算好
- 使用异步 I/O:不要阻塞主线程等待成交回报
- 本地时钟同步:确保你的服务器时间和交易所时间一致
一个小技巧:我习惯在算法启动前先发一个测试单,检查网络延迟。如果延迟超过 50ms,我会调整切片间隔,把延迟考虑进去。
TWAP 的局限性
说实话,TWAP 不是万能的。它有几个硬伤:
- 不感知市场深度:它不管当前买一卖一有多少,只管按时间发单
- 不预测价格走势:如果价格单边上涨,TWAP 会越买越贵
- 对极端行情无效:熔断、停牌等情况,TWAP 直接失效
所以,TWAP 适合的是「正常市场、中等规模、不着急」的场景。如果你要抢涨停板,或者要砸盘出货,TWAP 不是好选择。
SVG 流程图:TWAP 执行流程
总结一下
TWAP 是拆单算法里最基础、最常用的一种。它不复杂,但要做好不容易。我个人的建议是:
- 先从标准 TWAP 开始,跑通流程
- 然后加 Adaptive 逻辑,应对市场变化
- 最后对接暗池,降低冲击成本
嗯,今天就聊到这儿。TWAP 虽然基础,但它是理解更复杂算法(比如 VWAP、Implementation Shortfall)的基石。把这个吃透了,后面的路就好走了。