问题描述
解决方案
在 QMT (Quantitative Trading Platform) 中,要在策略运行过程中动态修改股票池(Universe),可以通过调用 ContextInfo.set_universe(stock_list) 方法来实现。
虽然通常我们在 init() 方法中设置初始股票池,但在 handlebar() 或定时器回调函数中同样可以调用该方法来更新当前的股票池。
实现逻辑
- 初始化:在
init中设置一个初始的股票池。 - 动态更新:在
handlebar中,根据你的策略逻辑(例如:选股算法、时间触发、板块轮动等)生成新的股票列表。 - 应用更改:调用
ContextInfo.set_universe(new_stock_list)将股票池更新为新列表。 - 验证:可以通过
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)
注意事项
- 数据获取影响:
ContextInfo.set_universe()主要影响的是ContextInfo.get_universe()的返回值。如果你在使用ContextInfo.get_market_data()或ContextInfo.get_market_data_ex()时没有显式传入stock_code参数(即使用默认值),它们会去拉取当前set_universe设定列表的数据。 - 持仓管理:
修改股票池 不会 自动卖出旧股票池中的持仓。如果你的策略逻辑是“剔除出股票池即卖出”,你需要自己编写代码逻辑:比较旧的持仓列表和新的股票池,找出不在新池子里的持仓股票并执行卖出操作。 - 数据下载(回测模式):
在回测模式下,确保你动态添加的股票在其对应的时间段内有本地历史数据,否则get_market_data可能取不到值。 - 运行效率:
不要在每一根 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 设置的定时器回调函数中调用。