第四节:成交量加权平均价格算法(VWAP)

VWAP,全称 Volume-Weighted Average Price。说白了,就是按成交量来算平均成交价。

我刚开始做算法交易时,总觉得这玩意儿不就是个均线吗?后来真上了实盘才发现,VWAP 和普通均线完全是两码事。普通均线看的是时间,VWAP 看的是钱——哪里的钱多,哪里的价格权重就大。

4.1 VWAP 的核心原理

VWAP 的计算公式其实很简单:

VWAP = Σ(价格 × 成交量) / Σ(成交量)

嗯,就是每一笔成交的价格乘以该笔的成交量,累加起来,再除以总成交量。

举个例子:

  • 第一笔:10元成交100股 → 贡献值 10×100 = 1000
  • 第二笔:11元成交200股 → 贡献值 11×200 = 2200
  • 第三笔:9元成交50股 → 贡献值 9×50 = 450

总贡献值 = 1000 + 2200 + 450 = 3650,总成交量 = 100 + 200 + 50 = 350,VWAP = 3650 / 350 ≈ 10.43元。

你看,第二笔成交量最大,所以它的价格11元对最终结果影响最大。这就是VWAP的核心理念——谁量大,谁说了算。

关键理解:VWAP 不是预测价格,而是描述市场真实的平均成交成本。你的订单如果能围绕 VWAP 成交,说明你没有给市场造成额外的冲击。

4.2 VWAP 的实现步骤

实现 VWAP 算法,我一般分三步走:

  1. 获取历史成交量分布——知道什么时间段流动性好
  2. 计算目标成交量曲线——把总订单按历史比例分配到每个时间片
  3. 执行并动态调整——根据实时成交情况修正后续下单量

下面是我常用的一个简化版实现:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

class VWAPExecutor:
    def __init__(self, total_shares, start_time, end_time, history_data):
        """
        total_shares: 总委托股数
        start_time, end_time: 交易时间段
        history_data: DataFrame,包含历史每分钟的成交量
        """
        self.total_shares = total_shares
        self.start_time = start_time
        self.end_time = end_time
        self.history_data = history_data
        
        # 已成交量和已花费时间
        self.executed_shares = 0
        self.elapsed_minutes = 0
        
    def _calculate_volume_profile(self):
        """计算历史成交量分布比例"""
        total_volume = self.history_data['volume'].sum()
        self.volume_profile = self.history_data['volume'] / total_volume
        return self.volume_profile
    
    def _get_target_shares(self, minute_index):
        """获取当前分钟应该下单的目标股数"""
        if minute_index >= len(self.volume_profile):
            return 0
        
        # 理论目标 = 总股数 × 该分钟历史占比
        target = self.total_shares * self.volume_profile.iloc[minute_index]
        
        # 动态调整:如果之前落后了,这里补上
        expected_executed = self.total_shares * (
            self.volume_profile.iloc[:minute_index].sum()
        )
        deviation = expected_executed - self.executed_shares
        
        # 调整后的目标
        adjusted_target = target + deviation * 0.3  # 0.3是调整系数
        return max(0, int(adjusted_target))
    
    def execute(self, market_data_stream):
        """执行VWAP算法"""
        self._calculate_volume_profile()
        
        for minute_index, market_data in enumerate(market_data_stream):
            target = self._get_target_shares(minute_index)
            
            if target <= 0:
                continue
            
            # 这里用拆单逻辑把target拆成更小的订单
            # 实际项目中我会用TWAP再拆一次
            actual_executed = self._send_order(target, market_data)
            
            self.executed_shares += actual_executed
            self.elapsed_minutes += 1
            
            # 检查是否完成
            if self.executed_shares >= self.total_shares:
                break
    
    def _send_order(self, shares, market_data):
        """发送订单(简化版)"""
        # 实际项目中这里会对接交易接口
        # 返回实际成交的股数
        return shares  # 假设全部成交

个人经验:我在做A股VWAP时,发现上午10:00-10:30和下午14:00-14:30的成交量占比特别大。如果你在这两个时段下单不足,后面很难追回来。所以我习惯在算法里加一个「最低执行比例」的约束,防止前松后紧。

4.3 VWAP 与 TWAP 的对比

TWAP 是 Time-Weighted Average Price,按时间平均分配。VWAP 是按成交量分配。两者有什么区别?我画个图你就明白了。

VWAP vs TWAP 下单量对比 TWAP(时间均匀) 恒定 VWAP(成交量跟随) 时间(分钟) 成交量 TWAP下单量 VWAP下单量 市场成交量

从图上你能看到:TWAP 像一条直线,每个时间点下单量都一样。VWAP 则跟着市场成交量走——市场活跃时多下,市场冷清时少下。

我把两者的核心区别整理成了一张表:

对比维度 VWAP TWAP
分配依据 历史成交量分布 时间均匀分配
市场冲击 较低(跟随流动性) 较高(可能逆流动性)
执行成本 接近市场均价 可能偏离均价
适用场景 大单、流动性好的股票 小单、流动性差的股票
实现复杂度 较高(需要历史数据) 较低(只需时间计算)
对市场变化的适应性 强(可动态调整) 弱(固定节奏)

避坑指南:我曾经在创业板一只小盘股上用了纯VWAP算法,结果当天该股突然放量,算法跟着追量下单,直接把价格打飞了。后来我加了两个保护:一是单笔最大下单量限制,二是实时偏离度监控——如果当前价格偏离VWAP超过0.5%,就暂停下单。

4.4 什么时候用 VWAP,什么时候用 TWAP?

我个人的经验是:

  • 大盘蓝筹股、成交活跃的品种——用 VWAP。流动性好,历史数据有参考价值,VWAP 能帮你吃到流动性的红利。
  • 小盘股、流动性差的品种——用 TWAP 或者干脆手动拆单。因为历史成交量分布可能不稳定,VWAP 反而会误导你。
  • 市场剧烈波动时——两个都不好用。这时候我倾向于暂停算法,等市场稳定了再继续。

其实很多机构是把两者结合用的。比如用 VWAP 决定每个小时的下单量,再用 TWAP 把每个小时内的订单均匀打出去。这样既跟上了流动性,又避免了在某一分钟内集中下单。

一句话总结:VWAP 是「跟着钱走」,TWAP 是「跟着表走」。前者更聪明,但需要更多的数据和保护措施。


专注资料整理