🚀 AI 一键生成 qmt 策略代码

详解 set_universe 与 get_universe:如何在策略中动态调整标的?

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

问题描述

如何在策略运行过程中动态修改股票池 (universe)?

解决方案

在 QMT (Quantitative Trading Platform) 中,要在策略运行过程中动态修改股票池(Universe),可以通过调用 ContextInfo.set_universe(stock_list) 方法来实现。

虽然通常我们在 init() 方法中设置初始股票池,但在 handlebar() 或定时器回调函数中同样可以调用该方法来更新当前的股票池。

实现逻辑

  1. 初始化:在 init 中设置一个初始的股票池。
  2. 动态更新:在 handlebar 中,根据你的策略逻辑(例如:选股算法、时间触发、板块轮动等)生成新的股票列表。
  3. 应用更改:调用 ContextInfo.set_universe(new_stock_list) 将股票池更新为新列表。
  4. 验证:可以通过 ContextInfo.get_universe() 获取当前最新的股票池进行验证。

代码示例

以下代码展示了如何在策略运行过程中(例如在第 10 根 K 线时)动态改变股票池。

# -*- coding: gbk -*-

def init(ContextInfo):
    # 1. 设置初始股票池
    initial_universe = ['600000.SH', '600036.SH']
    ContextInfo.set_universe(initial_universe)
    print(f"初始股票池: {ContextInfo.get_universe()}")
    
    # 设置账号(实盘/回测必须)
    ContextInfo.set_account('account_id') 

def handlebar(ContextInfo):
    # 获取当前 K 线索引
    bar_pos = ContextInfo.barpos
    
    # 获取当前时间(用于演示)
    current_time = ContextInfo.get_bar_timetag(bar_pos)
    
    # 2. 模拟动态修改逻辑:假设在第 10 根 K 线时修改股票池
    # 在实际策略中,这里可以是基于因子选股的结果、日期判断等
    if bar_pos == 10:
        print(f"--- 触发动态修改股票池条件 (Bar: {bar_pos}) ---")
        
        # 定义新的股票列表
        new_universe = ['000001.SZ', '000002.SZ', '600000.SH']
        
        # 3. 更新股票池
        ContextInfo.set_universe(new_universe)
        
        print("股票池已更新。")

    # 4. 验证当前股票池
    # 注意:set_universe 更新的是 ContextInfo 中的全局变量
    # 后续调用 get_market_data 如果不传 code_list,将使用这个新列表
    current_universe = ContextInfo.get_universe()
    
    # 仅在修改时打印,避免日志过多
    if bar_pos == 10:
        print(f"当前生效的股票池: {current_universe}")
        
        # 演示:获取新股票池的行情数据
        #如果不传 stock_code 参数,默认使用 set_universe 设置的列表
        data = ContextInfo.get_market_data(['close'], period='1d', count=1)
        print("获取新股票池收盘价示例:")
        print(data)

注意事项

  1. 数据获取影响
    ContextInfo.set_universe() 主要影响的是 ContextInfo.get_universe() 的返回值。如果你在使用 ContextInfo.get_market_data()ContextInfo.get_market_data_ex() 时没有显式传入 stock_code 参数(即使用默认值),它们会去拉取当前 set_universe 设定列表的数据。
  2. 持仓管理
    修改股票池 不会 自动卖出旧股票池中的持仓。如果你的策略逻辑是“剔除出股票池即卖出”,你需要自己编写代码逻辑:比较旧的持仓列表和新的股票池,找出不在新池子里的持仓股票并执行卖出操作。
  3. 数据下载(回测模式)
    在回测模式下,确保你动态添加的股票在其对应的时间段内有本地历史数据,否则 get_market_data 可能取不到值。
  4. 运行效率
    不要在每一根 K 线(每一个 tick)都频繁调用 set_universe,除非确实有必要。通常建议在特定的时间点(如每日开盘前、每月调仓日)进行更新。

Q&A

Q: 修改股票池后,之前的持仓会自动平仓吗?
A: 不会。set_universe 仅仅是更新了策略关注的股票列表变量。你需要手动编写逻辑来遍历持仓(get_trade_detail_data),判断持仓股票是否仍在新的股票池中,如果不在,则调用 passorder 进行平仓。

Q: set_universe 支持多少只股票?
A: 理论上支持 Python 列表的最大长度,但在实际量化交易中,过大的股票池(如全市场 5000+ 只)会导致 handlebar 计算压力过大,建议根据策略类型合理控制数量。

Q: 可以在 init 之外的地方调用 set_universe 吗?
A: 可以。如上例所示,可以在 handlebar 中调用,也可以在 run_time 设置的定时器回调函数中调用。