第三节:时间加权平均价格算法(TWAP)

TWAP,全称 Time-Weighted Average Price。说白了,就是把一个大单子,均匀地切成很多小份,然后按时间一点点扔到市场里去。

我刚开始做算法交易时,第一个接触的就是 TWAP。当时觉得这玩意儿太简单了,不就是定时定量嘛。后来在实际项目中踩了不少坑,才发现越简单的东西,细节越要命。

TWAP 的核心原理

TWAP 的逻辑非常直观:

  • 目标:让成交价格尽量接近这段时间的市场平均价
  • 方法:把总订单量按时间均匀分配
  • 假设:市场在短时间内是随机的,均匀下单能降低冲击

举个例子。你要在 10 分钟内买入 100 万股。TWAP 的做法是:每分钟下 10 万股,不管价格怎么变。就这么简单。

核心公式

每期下单量 = 总订单量 / 总期数

每期时间间隔 = 总执行时间 / 总期数

嗯,这里要注意。实际交易中,期数不能设得太细。我见过有人把 1 小时切成 3600 秒,每秒下一小单。结果呢?交易所的流控直接把他拒了。所以一般建议每期至少 3-5 秒以上。

实现步骤

TWAP 的实现,我习惯分三步走。你想想看,其实跟做饭差不多:备料、下锅、出锅。

第一步:参数设定

  • 总数量:比如 100 万股
  • 总时间:比如 600 秒(10 分钟)
  • 切片数:比如 60 片(每 10 秒下一单)
  • 每片数量:1000000 / 60 ≈ 16667 股

第二步:定时执行

用一个定时器,每 10 秒触发一次。每次触发时,检查当前持仓和剩余时间,动态调整下单量。

为什么要动态调整?因为前面的单子可能没完全成交。我曾经在实盘中遇到过连续 3 秒都没吃到货的情况,如果不调整,最后肯定完不成任务。

第三步:异常处理

  • 如果市场剧烈波动,暂停执行
  • 如果某次下单失败,重试 3 次
  • 如果剩余时间不够,加速下单

下面是一个简化版的 Python 实现:

import time
import threading

class TWAPExecutor:
    def __init__(self, total_qty, total_seconds, slices):
        self.total_qty = total_qty
        self.total_seconds = total_seconds
        self.slices = slices
        self.qty_per_slice = total_qty / slices
        self.interval = total_seconds / slices
        self.executed_qty = 0
        self.start_time = None
        
    def execute_slice(self):
        # 计算剩余需要执行的量
        remaining_qty = self.total_qty - self.executed_qty
        remaining_time = self.total_seconds - (time.time() - self.start_time)
        
        # 如果剩余时间不够,加速
        if remaining_time < self.interval:
            qty = remaining_qty
        else:
            qty = min(self.qty_per_slice, remaining_qty)
            
        # 这里调用实际的交易接口
        # self.broker.place_order(qty)
        print(f"下单 {qty} 股,剩余 {remaining_qty - qty} 股")
        
        self.executed_qty += qty
        
    def run(self):
        self.start_time = time.time()
        for i in range(self.slices):
            if self.executed_qty >= self.total_qty:
                break
            self.execute_slice()
            time.sleep(self.interval)

# 使用示例
twap = TWAPExecutor(total_qty=1000000, total_seconds=600, slices=60)
twap.run()

避坑指南:我曾经在回测时发现,如果切片数设得太多(比如 1000 片),TWAP 的成交价格反而比市场平均价差很多。原因是每单太小,容易被市场噪音吃掉。建议切片数控制在 10-50 之间。

优缺点分析

TWAP 的优点很明显,缺点也很致命。咱们一个一个说。

优点

  • 简单可靠:逻辑清晰,不容易出 bug。我团队的新人,培训 10 分钟就能上手写 TWAP。
  • 可预测性强:你知道什么时候下多少单,风控好做。
  • 对市场冲击小:均匀分散,不会引起价格剧烈波动。
  • 适合大单:特别是那些不着急的订单,比如指数基金的调仓。

缺点

  • 不灵活:市场明明在涨,它还是按部就班地买。说白了,它不会「追涨杀跌」。
  • 容易被狙击:如果对手盘知道你在跑 TWAP,他可以在你下单前拉高价格,等你买完再砸下来。
  • 无法应对极端行情:遇到闪崩,TWAP 还在傻傻地买,结果全买在高位。
  • 执行成本可能偏高:在趋势行情中,TWAP 的成交价往往比 VWAP 差。

重要提醒:TWAP 不是万能的。我见过有人用 TWAP 做日内高频交易,结果亏得一塌糊涂。TWAP 适合的是分钟级以上的大单执行,不是秒级的抢单。

TWAP 的适用场景

场景 是否适合 TWAP 原因
指数基金调仓 ✅ 非常适合 时间充裕,量大,不需要抢速度
大宗交易拆单 ✅ 适合 避免引起市场注意
日内高频交易 ❌ 不适合 速度太慢,跟不上市场节奏
趋势行情 ⚠️ 谨慎使用 容易买在高点,卖在低点
流动性差的股票 ⚠️ 谨慎使用 每单量太小,容易被吃单

TWAP 的核心逻辑图

下面这张图,是我自己画的一个 TWAP 执行流程。你看一眼就能明白整个逻辑:

开始 设定参数 总量、时间、切片数 还有剩余? 计算下单量 剩余量 / 剩余时间 执行下单 发送到交易所 结束

这张图的核心逻辑是:先设定参数,然后循环判断是否还有剩余订单。如果有,就计算当前应该下多少,然后执行。执行完再回来判断。直到所有订单都完成,结束。

个人经验:我在做 A 股市场的 TWAP 时,发现一个规律。如果股票流动性好(比如茅台),切片数可以设多一点,比如 30 片。如果流动性差(比如一些小盘股),切片数最好控制在 10 片以内。否则你的单子会被市场吃掉,成交价会偏离很多。

好了,TWAP 的核心内容就这些。说白了,它就是最基础的拆单算法。但基础不代表简单。我见过很多量化团队,连 TWAP 都写不好,最后实盘亏钱。所以,把基础打扎实了,后面的 VWAP、POV 才能学得明白。


公众号:蓝海资料掘金营,微信deep3321