3. 成交量分布:VPIN 与 VWAP

做高频交易的人,每天打交道最多的东西是什么?

不是K线,不是均线,是成交量。

成交量是市场的血液。没有成交量,价格就是死水一潭。今天我要聊的两个工具——VPIN(成交量序贯概率反转指标)和VWAP(成交量加权平均价格),就是用来解剖这管血液的。

3.1 先说说 VWAP:机构最爱的基准

VWAP 其实不复杂。说白了就是:用成交量做权重的平均价格

公式长这样:

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

举个例子。假设某合约一天成交了3笔:

时间 价格 成交量
09:30 100 200
10:00 102 300
10:30 101 500

VWAP = (100×200 + 102×300 + 101×500) / (200+300+500) = 101.1

而简单均价是 (100+102+101)/3 = 101.0。差了一毛钱,但这一毛钱在百万级别的单子里,就是几万块的差距。

核心认知:VWAP 反映的是市场真实的成交成本。机构拆单时,目标就是让成交价尽量贴近 VWAP。低于 VWAP 买入,高于 VWAP 卖出,这就是 alpha 的来源。

我个人习惯把 VWAP 当作一条动态的「公允价格线」。价格在 VWAP 上方,说明买方力量强;在下方,卖方占优。但注意,这不是绝对的——我见过太多人把 VWAP 当支撑阻力用,结果被来回打脸。

3.2 VPIN:测一测市场的「毒性」

VPIN 这个名字听起来高大上,其实逻辑很直白:它衡量的是订单流的不平衡程度

怎么算?把一天切成若干等量成交量的「桶」(bucket),每个桶里统计主动买和主动卖的差值。差值越大,说明这个桶里的订单流越「毒」——也就是方向性越强。

公式:

VPIN = Σ|主动买量 - 主动卖量| / (n × 桶大小)

其中 n 是桶的数量,桶大小通常取日成交量的 1/50 或 1/100。

VPIN 的值在 0 到 1 之间。值越高,说明订单流越不平衡,市场越容易发生反转。

实战经验:我曾经在螺纹钢期货上跑 VPIN,发现当 VPIN 超过 0.6 时,接下来 5 分钟内价格反转的概率超过 70%。但要注意,这个阈值在不同品种上差异很大——股指期货的 VPIN 通常比商品期货低一个数量级。

3.3 VPIN 和 VWAP 怎么配合用?

单看一个指标,就像只用一只眼看路。我一般这样搭配:

  • VWAP 判断方向:价格在 VWAP 之上,偏多;之下,偏空。
  • VPIN 判断时机:VPIN 高 + 价格远离 VWAP = 反转概率大。
  • VPIN 低 + 价格贴近 VWAP = 震荡行情,别乱动。

举个例子。假设当前价格 101.5,VWAP 是 100.8,VPIN 飙到 0.7。这说明什么?

价格已经偏离了公允值,而且订单流极度不平衡。这时候追多?我劝你冷静。大概率是主力在拉高出货。我一般会等 VPIN 回落,或者价格回到 VWAP 附近再动手。

避坑指南:我曾经犯过一个错——在 VPIN 极高时直接反向开仓。结果市场继续朝原方向猛冲,把我打止损了。后来我加了一条规则:必须等 VPIN 从高位回落 20% 以上,才确认反转信号有效。这叫「等子弹飞一会儿」。

3.4 代码实现:手撸一个 VPIN

理论说完了,上代码。这是我在实盘中用过的简化版:

import pandas as pd
import numpy as np

def compute_vpin(trade_data, bucket_volume=1000, n_buckets=50):
    """
    trade_data: DataFrame,必须包含 'price', 'volume', 'side' 三列
    side: 1 表示主动买,-1 表示主动卖
    """
    # 按时间排序
    trade_data = trade_data.sort_values('time')
    
    # 计算累计成交量
    trade_data['cum_vol'] = trade_data['volume'].cumsum()
    
    # 按桶分组
    trade_data['bucket'] = trade_data['cum_vol'] // bucket_volume
    
    # 每个桶内计算净成交量
    bucket_stats = trade_data.groupby('bucket').agg(
        buy_vol=('volume', lambda x: x[trade_data.loc[x.index, 'side'] == 1].sum()),
        sell_vol=('volume', lambda x: x[trade_data.loc[x.index, 'side'] == -1].sum())
    )
    
    # 计算 VPIN
    bucket_stats['imbalance'] = abs(bucket_stats['buy_vol'] - bucket_stats['sell_vol'])
    vpin = bucket_stats['imbalance'].rolling(n_buckets).sum() / (n_buckets * bucket_volume)
    
    return vpin

这段代码的核心就两步:切桶、算不平衡。实际生产中,桶大小要根据品种的流动性动态调整。比如螺纹钢一天成交 200 万手,桶取 2 万手比较合适;而沪深 300 股指期货一天 20 万手,桶取 2000 手就够了。

3.5 一张图看懂本章逻辑

下面这张 SVG 图,把 VPIN 和 VWAP 的关系画清楚了:

成交量分布核心逻辑 VWAP 成交量加权平均价格 衡量市场公允成本 判断价格偏离程度 VPIN 成交量序贯概率反转 衡量订单流不平衡 预测短期反转概率 配合使用 交易决策信号 价格偏离 VWAP + VPIN 高 → 反转概率大 价格贴近 VWAP + VPIN 低 → 震荡行情

你看,VWAP 和 VPIN 就像市场的两只眼睛。一只看位置,一只看力度。两只眼睛一起睁开,才能看清市场的真实意图。

3.6 几个容易踩的坑

  1. 桶大小别乱设:太小了噪声大,太大了信号滞后。我一般用日成交量的 1/50 作为初始值,然后根据回测结果微调。
  2. VPIN 不是万能的:在趋势行情里,VPIN 会持续高位,这时候做反转就是接飞刀。一定要结合趋势过滤。
  3. VWAP 要区分周期:日内 VWAP 和日线 VWAP 含义不同。高频交易看日内 VWAP,中低频看日线 VWAP。
  4. 数据源要干净:主动买/主动卖的判断依赖逐笔数据。如果交易所给的 side 字段不准,VPIN 就是垃圾进垃圾出。

一个小技巧:我习惯把 VPIN 和 VWAP 画在同一个副图上。价格线、VWAP 线、VPIN 柱状图叠在一起,一眼就能看出什么时候该动手。用 Python 的 matplotlib 就能搞定,代码量不超过 20 行。

好了,这一章就到这里。VPIN 和 VWAP 是微观结构分析里最基础也最实用的两个工具。别嫌它们简单——我见过太多人把复杂模型跑得飞起,结果连 VWAP 都没算对。基础打牢了,后面讲订单簿重建、限价单簿动态博弈的时候,你才能跟得上。

专注资料整理