3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)平台上,利用 Tick 数据绘制当天的成交量分布图(Volume Profile)主要分为三个步骤:获取 Tick 数据、计算单笔成交量、按价格聚合成交量。
由于 Tick 数据中的 volume 字段通常是累计成交量,因此核心逻辑是用当前 Tick 的累计成交量减去上一笔 Tick 的累计成交量,得出该笔交易的实际量,然后将其归类到当前的成交价格上。
以下是在**研究环境(Research)**中实现的完整代码示例。
# -*- coding: utf-8 -*-
from jqdata import *
import pandas as pd
import matplotlib.pyplot as plt
# 设置中文字体,防止绘图乱码(根据聚宽环境调整,通常SimHei可用)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def draw_volume_profile(security, date):
"""
绘制指定标的在指定日期的成交量分布图 (Volume Profile)
:param security: 标的代码,如 '000001.XSHE'
:param date: 日期字符串,如 '2023-05-10'
"""
# 1. 获取 Tick 数据
# start_dt 和 end_dt 覆盖全天交易时段
start_dt = f"{date} 09:30:00"
end_dt = f"{date} 15:00:00"
# 获取 tick 数据,包含时间、当前价、累计成交量
ticks = get_ticks(security, start_dt=start_dt, end_dt=end_dt,
fields=['time', 'current', 'volume'], df=True)
if ticks.empty:
print(f"未获取到 {security} 在 {date} 的Tick数据")
return
# 2. 数据处理
# Tick数据中的 volume 是累计成交量,需要计算差分得到每笔成交量
# diff() 计算当前行与上一行的差值
ticks['tick_vol'] = ticks['volume'].diff()
# 第一笔数据的 diff 结果为 NaN,通常第一笔是集合竞价产生的累积量,直接填充为原始值
ticks['tick_vol'].fillna(ticks['volume'], inplace=True)
# 过滤掉成交量为0或负数的数据(异常处理)
ticks = ticks[ticks['tick_vol'] > 0]
# 3. 按价格聚合成交量
# 将价格作为索引,对成交量求和
volume_profile = ticks.groupby('current')['tick_vol'].sum()
# 4. 绘图
plt.figure(figsize=(12, 8))
# 使用水平条形图 (barh),Y轴为价格,X轴为成交量
# width 参数控制条形的粗细,根据价格档位数量调整
plt.barh(volume_profile.index, volume_profile.values, height=0.005, color='skyblue', edgecolor='blue', alpha=0.6)
# 找出最大成交量的价格(POC - Point of Control)
poc_price = volume_profile.idxmax()
max_vol = volume_profile.max()
# 标记 POC
plt.axhline(y=poc_price, color='red', linestyle='--', label=f'POC (控制点): {poc_price}')
plt.title(f'{security} 成交量分布图 (Volume Profile) - {date}')
plt.xlabel('成交量 (股)')
plt.ylabel('价格')
plt.legend()
plt.grid(True, axis='x', linestyle='--', alpha=0.5)
plt.show()
# --- 调用示例 ---
# 请确保日期是交易日,且在Tick数据提供的范围内(股票2010年至今)
draw_volume_profile('000001.XSHE', '2023-06-01')
数据获取 (get_ticks):
get_ticks 函数获取高频数据。fields 参数必须包含 volume (累计成交量) 和 current (当前价格)。df=True 直接返回 DataFrame 格式,方便后续处理。计算增量 (diff):
volume 字段代表“开盘截至当前的累计成交量”。当前Tick累计量 - 上一Tick累计量。ticks['volume'].diff() 完成此操作。聚合 (groupby):
groupby('current')['tick_vol'].sum() 将同一价格下的所有单笔成交量累加,得到该价格档位的总成交量。可视化 (barh):
plt.barh。initialize, handle_data 等函数内),无法直接使用 plt.show() 弹窗绘图。上述代码主要用于研究环境进行分析。volume_profile Series 对象后,即可通过代码逻辑判断当前价格与 POC 的关系,而无需绘图。Q: 为什么第一笔 Tick 的成交量处理要用 fillna?
A: diff() 函数会对第一行产生 NaN 值。对于当天的第一笔 Tick(通常是 9:25 或 9:30 的数据),其 volume 值即为开盘集合竞价的成交量,因此直接用原始 volume 填充 NaN 是正确的。
Q: 如何在策略回测中利用 Volume Profile 数据?
A: 在策略中不需要画图。你可以计算出 volume_profile 后,使用 volume_profile.idxmax() 获取 POC 价格,或者计算 70% 成交量分布的价格区间(Value Area),以此作为交易信号的过滤条件。
Q: 期货的 Volume Profile 画法有区别吗?
A: 代码逻辑完全一致。只需将 security 换成期货合约代码(如 RB2310.XSGE)。但需注意期货有夜盘,start_dt 和 end_dt 需要涵盖夜盘时间段(前一交易日 21:00 到当日 15:00)。