第四节:成交量加权平均价格算法(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 算法,我一般分三步走:
- 获取历史成交量分布——知道什么时间段流动性好
- 计算目标成交量曲线——把总订单按历史比例分配到每个时间片
- 执行并动态调整——根据实时成交情况修正后续下单量
下面是我常用的一个简化版实现:
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 是按成交量分配。两者有什么区别?我画个图你就明白了。
从图上你能看到:TWAP 像一条直线,每个时间点下单量都一样。VWAP 则跟着市场成交量走——市场活跃时多下,市场冷清时少下。
我把两者的核心区别整理成了一张表:
| 对比维度 | VWAP | TWAP |
|---|---|---|
| 分配依据 | 历史成交量分布 | 时间均匀分配 |
| 市场冲击 | 较低(跟随流动性) | 较高(可能逆流动性) |
| 执行成本 | 接近市场均价 | 可能偏离均价 |
| 适用场景 | 大单、流动性好的股票 | 小单、流动性差的股票 |
| 实现复杂度 | 较高(需要历史数据) | 较低(只需时间计算) |
| 对市场变化的适应性 | 强(可动态调整) | 弱(固定节奏) |
避坑指南:我曾经在创业板一只小盘股上用了纯VWAP算法,结果当天该股突然放量,算法跟着追量下单,直接把价格打飞了。后来我加了两个保护:一是单笔最大下单量限制,二是实时偏离度监控——如果当前价格偏离VWAP超过0.5%,就暂停下单。
4.4 什么时候用 VWAP,什么时候用 TWAP?
我个人的经验是:
- 大盘蓝筹股、成交活跃的品种——用 VWAP。流动性好,历史数据有参考价值,VWAP 能帮你吃到流动性的红利。
- 小盘股、流动性差的品种——用 TWAP 或者干脆手动拆单。因为历史成交量分布可能不稳定,VWAP 反而会误导你。
- 市场剧烈波动时——两个都不好用。这时候我倾向于暂停算法,等市场稳定了再继续。
其实很多机构是把两者结合用的。比如用 VWAP 决定每个小时的下单量,再用 TWAP 把每个小时内的订单均匀打出去。这样既跟上了流动性,又避免了在某一分钟内集中下单。
一句话总结:VWAP 是「跟着钱走」,TWAP 是「跟着表走」。前者更聪明,但需要更多的数据和保护措施。