🚀 AI 一键生成 joinquant 策略代码
立即体验
👉 AI 生成代码 👈

JoinQuant 聚宽平台 handle_data(context, data) 中的 data 参数到底怎么用?

joinquant | 发布时间: 2026/6/1
以下内容由 EasyQuant 生成。

问题描述

handle_data函数中data问题

handle_data(context, data) API文档说明为:该函数每个单位时间会调用一次, 如果按天回测,则每天调用一次,如果按分钟,则每分钟调用一次。
参数 context: Context对象, 存放有当前的账户/标的持仓信息
data: 一个字典(dict), key是股票代码, value是当时的SecurityUnitData 对象. 存放前一个单位时间(按天回测, 是前一天, 按分钟回测, 则是前一分钟) 的数据. 注意:

请问这里传入data参数是干什么用的啊?怎么理解如下这句话呢?
为了加速, data 里面的数据是按需获取的, 每次 handle_data 被调用时, data 是空的 dict, 当你使用data[security],时该 security 的数据才会被获取.data 只在这一个时间点有效, 请不要存起来到下一个 handle_data 再用。

解决方案

在 JoinQuant(聚宽)量化平台中,handle_data(context, data) 是策略运行的核心函数。关于 data 参数的作用以及文档中那段话的理解,可以从以下几个方面来详细剖析:

1. data 参数是干什么用的?

简单来说,data 参数的作用是获取标的(如股票、基金等)在“前一个单位时间”的行情切片数据

  • 按天回测data 里面装的是昨天的行情数据(开盘价、收盘价、最高价、最低价、成交量等)。
  • 按分钟回测data 里面装的是上一分钟的行情数据。

你可以通过 data['股票代码'] 获取到一个 SecurityUnitData 对象,然后调用它的属性,例如 data['000001.XSHE'].close 获取平安银行上一单位时间的收盘价。

2. 怎么理解“按需获取”和“空 dict”?

这其实是聚宽底层引擎的一种性能优化机制(延迟加载 / Lazy Load)

  • 为什么是空的? 假设全市场有 4000 多只股票,如果每次调用 handle_data 时,系统都把这 4000 多只股票的数据全部查出来打包成一个巨大的字典传给你,那回测速度会慢得令人发指,而且极其消耗内存。
  • 什么是按需获取?handle_data 刚被触发时,传给你的 data 确实是一个空壳子。只有当你的代码执行到 data['000001.XSHE'] 这一句时,系统才会瞬间去底层数据库里把平安银行的数据拉取出来返回给你。你没查的股票,系统就不会去拉取数据,从而大大“加速”了回测过程。

3. 怎么理解“只在这一个时间点有效,请不要存起来”?

这是新手最容易踩的坑。量化回测是一个时间不断向前推进的过程。

  • 时间切片概念data 对象代表的是当前这一刻你能看到的历史切片。当时间推移到下一天(或下一分钟),系统会生成一个新的 data 对象传给 handle_data
  • 错误做法:如果你把今天的 data 存到了全局变量里(比如 g.my_data = data),到了明天,你再去访问 g.my_data['000001.XSHE'].close,你拿到的依然是昨天的数据,而不是最新的数据。更严重的是,聚宽底层可能会在时间推进后销毁旧的内存引用,导致你访问报错。

💡 代码示例对比

❌ 错误的做法(跨时间点缓存 data):

def initialize(context):
    g.cached_data = []
    g.stock = '000001.XSHE'

def handle_data(context, data):
    # 错误!把 data 对象存起来了
    g.cached_data.append(data)
    
    if len(g.cached_data) > 1:
        # 试图用昨天存的 data 和今天的 data 做对比,这是极其危险且错误的!
        # 且不说底层引用可能失效,如果发生除权除息,旧 data 的价格没有动态复权,会导致逻辑完全错误。
        if g.cached_data[-1][g.stock].close > g.cached_data[-2][g.stock].close:
            order(g.stock, 100)

✅ 正确的做法(使用 history 或 attribute_history 获取历史序列):

def initialize(context):
    g.stock = '000001.XSHE'

def handle_data(context, data):
    # 正确用法 1:只获取当前时间点的前一单位时间数据
    current_close = data[g.stock].close 
    
    # 正确用法 2:如果需要对比过去几天的数据,请使用 history 或 attribute_history API
    # 获取过去 2 天的收盘价(已处理好复权)
    hist = attribute_history(g.stock, 2, '1d', ['close'])
    
    if hist['close'][-1] > hist['close'][-2]:
        order(g.stock, 100)

总结:把 data 当作一个“阅后即焚”的查询工具,只在当前的 handle_data 作用域内使用它。如果需要历史时间序列,请使用 historyattribute_history 等专门的数据获取 API。