14、订单流与成交量结合:成交量加权平均价格(VWAP)的订单流版本

VWAP,成交量加权平均价格,是机构交易员最常用的基准之一。传统VWAP的计算方式很简单——把每一笔成交的价格乘以成交量,再除以总成交量。但这里有个问题:传统VWAP只看结果,不看过程。

什么意思呢?举个例子,某只股票在10块钱成交了1000手,又在10.01成交了500手。传统VWAP会告诉你均价是10.0033。但它不会告诉你,这1000手是被大单砸下来的,还是散户自然成交的。说白了,它丢失了订单流的信息。

我个人习惯把订单流数据融合进VWAP。这样算出来的VWAP,能反映真实的买卖压力。今天我们就来聊聊这个。

传统VWAP的局限性

先看传统VWAP的公式:

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

这个公式有个隐含假设:每一笔成交都是平等的。但做过实盘的人都知道,主动买和主动卖,意义完全不同。

我记得有一次做股指期货的日内策略,传统VWAP显示价格在均线上方,按理说应该偏多。但订单流数据显示,主动卖单的成交量远大于主动买单。结果呢?价格很快就掉下来了。从那以后,我再也不单独看传统VWAP了。

订单流VWAP的核心思想

订单流VWAP,说白了就是把成交拆成两部分:

  • 主动买成交量:以卖一价及以上成交的,视为买方主动
  • 主动卖成交量:以买一价及以下成交的,视为卖方主动

然后分别计算买方VWAP和卖方VWAP。两者的差值,就是订单流给我们的额外信息。

核心公式:

买方VWAP = Σ(主动买价格 × 主动买成交量) / Σ(主动买成交量)

卖方VWAP = Σ(主动卖价格 × 主动卖成交量) / Σ(主动卖成交量)

订单流VWAP差值 = 买方VWAP - 卖方VWAP

当买方VWAP高于卖方VWAP时,说明买方在更高的价位上持续买入,这是强势信号。反过来,就是弱势信号。

Python实现:从Tick数据到订单流VWAP

下面是我在实际项目中用过的代码框架。先声明一下,这里用的是简化版数据,真实场景下你需要处理Level2行情。

import pandas as pd
import numpy as np

def order_flow_vwap(tick_data):
    """
    计算订单流VWAP
    
    参数:
    tick_data: DataFrame,必须包含列:
        - price: 成交价格
        - volume: 成交量
        - side: 'buy' 或 'sell',表示主动买卖方向
    
    返回:
    DataFrame,包含买方VWAP、卖方VWAP和差值
    """
    
    # 分离主动买和主动卖
    buy_ticks = tick_data[tick_data['side'] == 'buy'].copy()
    sell_ticks = tick_data[tick_data['side'] == 'sell'].copy()
    
    # 计算累计值
    buy_ticks['price_vol'] = buy_ticks['price'] * buy_ticks['volume']
    sell_ticks['price_vol'] = sell_ticks['price'] * sell_ticks['volume']
    
    buy_ticks['cum_vol'] = buy_ticks['volume'].cumsum()
    buy_ticks['cum_price_vol'] = buy_ticks['price_vol'].cumsum()
    
    sell_ticks['cum_vol'] = sell_ticks['volume'].cumsum()
    sell_ticks['cum_price_vol'] = sell_ticks['price_vol'].cumsum()
    
    # 计算VWAP
    buy_ticks['vwap'] = buy_ticks['cum_price_vol'] / buy_ticks['cum_vol']
    sell_ticks['vwap'] = sell_ticks['cum_price_vol'] / sell_ticks['cum_vol']
    
    # 合并结果
    result = pd.DataFrame(index=tick_data.index)
    result['buy_vwap'] = buy_ticks['vwap']
    result['sell_vwap'] = sell_ticks['vwap']
    result['vwap_diff'] = result['buy_vwap'] - result['sell_vwap']
    
    return result

# 使用示例
# tick_data = pd.read_csv('tick_data.csv')
# vwap_result = order_flow_vwap(tick_data)
# print(vwap_result.tail())

实战小技巧:

我一般不会直接用原始tick数据。因为tick数据太嘈杂了。我会先按1分钟或5分钟聚合,再计算订单流VWAP。这样信号更稳定,回测效果也更好。

如何判断主动买卖方向

这里有个关键问题:怎么判断一笔成交是主动买还是主动卖?

在Level2行情中,每笔成交都会带一个标识。但如果你拿不到这个标识,可以用一个近似方法:

  • 如果成交价 >= 卖一价,视为主动买
  • 如果成交价 <= 买一价,视为主动卖
  • 如果成交价在买卖价之间,按比例分配

嗯,这里要注意。第二种情况在实盘中经常出现,尤其是流动性差的股票。我曾经因为这个分配比例的问题,回测和实盘对不上。后来我改成:如果价格在中间,就按最近10笔的买卖比例来分配。效果好了不少。

订单流VWAP的实战应用

我个人最常用的场景有两个:

  1. 趋势确认:当价格突破某个关键价位时,看订单流VWAP差值是否同步放大。如果价格涨了但差值在缩小,说明突破可能是假的。
  2. 背离信号:价格创新高,但买方VWAP没有创新高。这就是典型的顶背离。我在做期货日内时,这个信号的成功率大概在65%左右。

避坑指南:

我曾经犯过一个错误:在开盘前15分钟直接使用订单流VWAP。开盘时的成交量虽然大,但很多是噪音——集合竞价的单子、程序化试单等等。建议至少等开盘30分钟后再用。

可视化:订单流VWAP与价格的关系

下面这张图展示了订单流VWAP的核心逻辑。我习惯把买方VWAP和卖方VWAP画成两条线,再叠加价格K线。当两条线交叉时,往往意味着趋势的转折。

订单流VWAP核心逻辑图 买方VWAP 卖方VWAP 强势区 弱势区 交叉点 当买方VWAP在卖方VWAP上方 → 买方主导,偏多 当买方VWAP在卖方VWAP下方 → 卖方主导,偏空

进阶:动态权重VWAP

上面讲的是基础版本。在实际项目中,我还用过一种改进版——动态权重VWAP。核心思路是:越近的成交,权重越大。

为什么?因为市场是动态的。10分钟前的成交,对当前价格的参考意义远不如最近1分钟的成交。我一般用指数衰减权重:

def dynamic_order_flow_vwap(tick_data, decay_factor=0.95):
    """
    动态权重订单流VWAP
    
    参数:
    tick_data: DataFrame
    decay_factor: 衰减因子,越小衰减越快
    
    返回:
    DataFrame
    """
    weights = np.array([decay_factor ** i for i in range(len(tick_data))])
    weights = weights[::-1]  # 最近的权重最大
    weights = weights / weights.sum()  # 归一化
    
    buy_mask = tick_data['side'] == 'buy'
    sell_mask = tick_data['side'] == 'sell'
    
    buy_vwap = np.sum(
        tick_data.loc[buy_mask, 'price'] * 
        tick_data.loc[buy_mask, 'volume'] * 
        weights[buy_mask]
    ) / np.sum(tick_data.loc[buy_mask, 'volume'] * weights[buy_mask])
    
    sell_vwap = np.sum(
        tick_data.loc[sell_mask, 'price'] * 
        tick_data.loc[sell_mask, 'volume'] * 
        weights[sell_mask]
    ) / np.sum(tick_data.loc[sell_mask, 'volume'] * weights[sell_mask])
    
    return buy_vwap, sell_vwap, buy_vwap - sell_vwap

参数调优建议:

衰减因子我一般设在0.95到0.99之间。股票用0.98,期货用0.95。因为期货波动更快,需要更灵敏的响应。你可以根据自己的品种做回测,找到最优参数。

总结一下

订单流VWAP不是要替代传统VWAP,而是给它加了一个维度——买卖方向。你想想看,同样的成交量,主动买和主动卖的意义完全不同。忽略这个信息,就像只看K线不看成交量一样,会错过很多关键信号。

我个人建议,刚开始可以先在模拟盘上跑一跑。把订单流VWAP和传统VWAP画在一起,观察它们的差异。慢慢地,你会找到感觉。

嗯,今天就聊到这里。代码部分可以直接拿去用,但记得根据你的数据格式做调整。


交易系统化学习资料 微信Strategy888888