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

为什么 get_industries 在 initialize 和 handle_data 中返回结果不同?

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

问题描述

get_industries BUG?

get_industries(name='sw_l1')

回测从2011-01-01开始。
在init里该调用返回:801740 801020 801110 801160 801060 801770 801010 801120 801750 801050 801890 801950 801980 801970 801170 801090 801710 801780 801960 801130 801180 801220 801760 801200 801230 801880 801140 801720 801080 801790 801030 801100 801190 801210 801070 801150 801730 801040

在handle_bar里返回:801020 801110 801160 801060 801010 801120 801050 801170 801090 801130 801180 801220 801200 801230 801140 801080 801030 801100 801190 801210 801070 801150 801040

get_industries不存在未来函数的概念吧?所以,请在不传入date的时候返回所有数据。

解决方案

这不是 BUG,而是为了严格规避未来函数以及反映真实的行业历史变迁而设计的机制。

1. 申万行业的历史调整

申万行业分类在历史上发生过多次重大调整(例如 2014 年 2 月 21 日和 2021 年底)。

  • 2011 年,申万一级行业确实只有 23 个
  • 在后续的调整中,剔除了一些旧行业,并新增了许多新行业,导致历史上出现过的申万一级行业总数达到了 30 多个。

2. 为什么 initializehandle_data 返回结果不同?

  • handle_data (或 handle_bar) 中:回测引擎已经进入了时间循环。为了防止策略在 2011 年“提前”看到 2014 年或 2021 年才设立的行业(这属于典型的未来函数),系统在底层会将未传入的 date 参数默认对齐到当前的回测逻辑时间 context.current_dt。因此,它准确地返回了 2011 年当时存在的 23 个行业。
  • initialize:此时回测的时间循环尚未正式开始,API 按照文档默认的 date=None 逻辑,返回了历史上存在过的所有行业代码(包含了未来新增的行业)。

3. 解决方案与建议

强烈建议在回测中使用当时的行业分类。如果在 2011 年的策略中使用了 2014 年才有的行业代码去选股,会导致获取不到数据或产生逻辑错误。

如果您确实需要在盘中获取包含未来所有调整的行业列表(不推荐,存在未来函数风险),您可以通过以下两种方式实现:

方式一:在 initialize 中缓存数据
在初始化时获取并存入全局变量 g 中,后续直接使用:

def initialize(context):
    # 获取所有历史行业并缓存
    g.all_sw_l1 = get_industries(name='sw_l1').index.tolist()

def handle_data(context, data):
    # 直接使用缓存的完整列表
    industries = g.all_sw_l1
    print(len(industries)) # 将输出 30+ 个

方式二:显式传入未来的日期(不推荐)
handle_data 中强制传入一个未来的日期(如今天),强制 API 返回最新的行业列表:

import datetime

def handle_data(context, data):
    # 强制获取最新日期的行业分类(引入未来函数)
    industries = get_industries(name='sw_l1', date=datetime.date.today()).index.tolist()

总结:量化回测的核心是“还原历史”。get_industries 在盘中返回 23 个行业,正是为了保证您的策略在 2011 年的环境下运行的是 2011 年的真实数据。