3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 JoinQuant(聚宽)量化平台中,handle_data(context, data) 是策略运行的核心函数。关于 data 参数的作用以及文档中那段话的理解,可以从以下几个方面来详细剖析:
data 参数是干什么用的?简单来说,data 参数的作用是获取标的(如股票、基金等)在“前一个单位时间”的行情切片数据。
data 里面装的是昨天的行情数据(开盘价、收盘价、最高价、最低价、成交量等)。data 里面装的是上一分钟的行情数据。你可以通过 data['股票代码'] 获取到一个 SecurityUnitData 对象,然后调用它的属性,例如 data['000001.XSHE'].close 获取平安银行上一单位时间的收盘价。
这其实是聚宽底层引擎的一种性能优化机制(延迟加载 / Lazy Load)。
handle_data 时,系统都把这 4000 多只股票的数据全部查出来打包成一个巨大的字典传给你,那回测速度会慢得令人发指,而且极其消耗内存。handle_data 刚被触发时,传给你的 data 确实是一个空壳子。只有当你的代码执行到 data['000001.XSHE'] 这一句时,系统才会瞬间去底层数据库里把平安银行的数据拉取出来返回给你。你没查的股票,系统就不会去拉取数据,从而大大“加速”了回测过程。这是新手最容易踩的坑。量化回测是一个时间不断向前推进的过程。
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 作用域内使用它。如果需要历史时间序列,请使用 history 或 attribute_history 等专门的数据获取 API。