第三章 交易量分布分析:成交量分布(VPVR)、成交量剖面、成交量加权平均价(VWAP)
交易量分析,说白了就是看钱往哪儿流。
价格可以骗人,但成交量很难造假。我做了这么多年量化,最深的体会就是:价格是表象,成交量才是真相。今天咱们就聊聊三个最实用的成交量分析工具——VPVR、成交量剖面和VWAP。
3.1 成交量分布(VPVR) —— 市场的“记忆曲线”
VPVR,全称是Volume Profile Visible Range。名字挺唬人,其实原理很简单:把每个价格水平上的成交量画出来。
你想想看,如果某个价格区间成交量特别大,说明什么?说明多空双方在这个位置激烈博弈过。这个位置就成了“价值区域”,价格未来很容易被拉回来。
核心概念:
- 价值区域(VA):成交量占比70%的价格区间
- 控制点(POC):成交量最大的那个价格
- 高量节点:成交量显著高于相邻价格的节点
我个人习惯把VPVR当作“支撑阻力地图”来用。价格到了高量节点附近,我会格外小心——要么突破,要么反转,很少会平淡地穿过去。
实战技巧:
我曾经在BTC上吃过亏。价格突破POC后我直接追多,结果被假突破打脸。后来我学乖了:突破POC后,必须等成交量确认。如果突破时成交量萎缩,大概率是陷阱。
3.1.1 VPVR的计算逻辑
计算其实不复杂。把价格区间分成若干“格子”(比如0.5%一个格子),然后统计每个格子内的成交量总和。
def calculate_vpvr(df, num_bins=100):
"""
计算成交量分布(VPVR)
df: 包含'high','low','volume'的DataFrame
"""
price_min = df['low'].min()
price_max = df['high'].max()
bin_width = (price_max - price_min) / num_bins
# 初始化每个价格区间的成交量
volume_profile = {i: 0 for i in range(num_bins)}
for _, row in df.iterrows():
# 确定该K线覆盖的价格区间
start_bin = int((row['low'] - price_min) / bin_width)
end_bin = int((row['high'] - price_min) / bin_width)
# 将成交量均匀分配到每个价格区间
volume_per_bin = row['volume'] / (end_bin - start_bin + 1)
for bin_idx in range(start_bin, end_bin + 1):
volume_profile[bin_idx] += volume_per_bin
# 找到控制点(POC)
poc_bin = max(volume_profile, key=volume_profile.get)
poc_price = price_min + (poc_bin + 0.5) * bin_width
return volume_profile, poc_price, bin_width
注意:这里用的是均匀分配法,实际交易中成交量在K线内部可能不是均匀的。更精确的做法是用Tick数据,但计算量会大很多。我一般用1分钟K线做近似,效果已经够用了。
3.2 成交量剖面 —— 把时间维度加进去
VPVR只看价格和成交量,但忽略了时间。成交量剖面(Volume Profile)则把时间也考虑进来了。
什么意思呢?就是看每个时间段内,成交量在价格上的分布情况。比如开盘半小时、午盘、尾盘,各自的成交量分布可能完全不同。
我记得有一次做股指期货,发现尾盘30分钟的成交量剖面总是出现“双峰”结构。后来复盘发现,这是机构在调仓——一边平多一边开空。抓住这个规律后,我尾盘策略的胜率提高了不少。
3.2.1 时间分割的三种方式
| 分割方式 | 说明 | 适用场景 |
|---|---|---|
| 固定时间间隔 | 每30分钟一段 | 常规分析 |
| 成交量等量分割 | 每段成交量相等 | 高波动行情 |
| 关键事件分割 | 按数据发布、开盘收盘分割 | 事件驱动策略 |
我个人偏爱成交量等量分割。为什么呢?因为市场活跃的时候,信息密度高,需要更细的粒度;冷清的时候,粗一点也无所谓。
def volume_profile_by_time(df, time_bins=13):
"""
按时间分割计算成交量剖面
"""
# 假设df包含'datetime','price','volume'列
df['time_bin'] = pd.cut(df['datetime'].dt.hour,
bins=time_bins,
labels=False)
profile = {}
for bin_id in range(time_bins):
bin_data = df[df['time_bin'] == bin_id]
# 对该时间段内的数据计算VPVR
profile[bin_id] = calculate_vpvr(bin_data)
return profile
避坑指南:我曾经把时间分割做得太细(比如1分钟一段),结果每个剖面都稀稀拉拉的,根本看不出规律。后来我总结:时间段的成交量至少要有总成交量的5%,否则统计意义不大。
3.3 成交量加权平均价(VWAP) —— 机构的“成本线”
VWAP,机构交易员最熟悉的指标之一。说白了就是按成交量加权的平均成交价格。
为什么机构这么看重VWAP?因为大资金进出市场,最怕的就是滑点。VWAP就是他们的“锚”——低于VWAP买,高于VWAP卖,长期来看能跑赢市场平均成本。
3.3.1 VWAP的计算公式
公式很简单:
VWAP = Σ(价格 × 成交量) / Σ(成交量)
但实际应用中,我们通常计算的是日内累积VWAP,也就是从开盘到当前时刻的加权平均价。
def calculate_vwap(df):
"""
计算日内累积VWAP
df: 包含'price','volume'的DataFrame,按时间排序
"""
df['cum_vol'] = df['volume'].cumsum()
df['cum_pv'] = (df['price'] * df['volume']).cumsum()
df['vwap'] = df['cum_pv'] / df['cum_vol']
return df['vwap']
VWAP的三种用法:
- 趋势判断:价格在VWAP上方运行 = 多头强势;下方 = 空头强势
- 支撑阻力:VWAP本身就是一个动态的支撑/阻力位
- 偏离回归:价格远离VWAP时,有回归的倾向
嗯,这里要注意:VWAP是日内指标,跨日使用意义不大。我见过有人把5天的VWAP连起来用,结果发现跟移动平均线差不多,失去了VWAP本身的优势。
3.4 三个工具的组合使用
单独用哪个都差点意思,组合起来才是王炸。
我的习惯是:
- 先用VPVR画出全局的支撑阻力地图
- 再用成交量剖面看当前时间段的资金博弈情况
- 最后用VWAP作为日内交易的“锚”
举个例子:如果VPVR显示当前价格处于高量节点附近,成交量剖面显示该时间段资金在持续流入,而价格刚好在VWAP上方——这时候做多,胜率会高很多。
重要提醒:这三个工具都是基于历史数据的。市场结构可能突然变化(比如突发消息、政策变动),这时候历史规律会失效。我2015年股灾时就吃过这个亏——VPVR显示的支撑位在熔断面前一文不值。
3.5 本章知识体系
下面这张图展示了三个工具之间的关系和适用场景:
好了,这一章的内容就到这里。VPVR、成交量剖面和VWAP这三个工具,每一个单独拿出来都能写一本书。但作为量化交易者,我更看重的是它们如何协同工作。记住:工具是死的,组合是活的。