🚀 AI 一键生成 joinquant 策略代码

如何使用 get_ticks API 在 JoinQuant 中分析并绘制日内成交量分布图?

joinquant | 发布时间: 2025/12/18
以下内容由 EasyQuant 生成。

问题描述

如何用tick数据画出当天的成交量分布图(Volume Profile)?

解决方案

在聚宽(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')

关键逻辑解析

  1. 数据获取 (get_ticks):

    • 使用 get_ticks 函数获取高频数据。
    • fields 参数必须包含 volume (累计成交量) 和 current (当前价格)。
    • df=True 直接返回 DataFrame 格式,方便后续处理。
  2. 计算增量 (diff):

    • 交易所推送的 Tick 数据中,volume 字段代表“开盘截至当前的累计成交量”。
    • 为了知道在特定价格成交了多少,必须计算增量:当前Tick累计量 - 上一Tick累计量
    • 代码中使用 ticks['volume'].diff() 完成此操作。
  3. 聚合 (groupby):

    • Volume Profile 的本质是价格与成交量的直方图。
    • 使用 groupby('current')['tick_vol'].sum() 将同一价格下的所有单笔成交量累加,得到该价格档位的总成交量。
  4. 可视化 (barh):

    • 通常 Volume Profile 是横向展示的,因此使用 plt.barh
    • Y 轴表示价格水平,X 轴表示该价格处的成交量堆积。
    • POC (Point of Control):图中红线标注了当天成交量最大的价格位置,这是筹码交换最密集的区域,通常具有较强的支撑或阻力作用。

注意事项

  • 数据范围: 股票 Tick 数据支持 2010-01-01 至今。如果是期货,支持 2010-01-01 至今。
  • 回测环境: 在回测策略中(initialize, handle_data 等函数内),无法直接使用 plt.show() 弹窗绘图。上述代码主要用于研究环境进行分析。
  • 策略应用: 如果要在策略中使用 Volume Profile(例如寻找支撑位),只需要执行到“第3步聚合”即可,得到 volume_profile Series 对象后,即可通过代码逻辑判断当前价格与 POC 的关系,而无需绘图。

Q&A

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_dtend_dt 需要涵盖夜盘时间段(前一交易日 21:00 到当日 15:00)。