问题描述
解决方案
在 QMT 的 Python 策略中,存储和传递跨函数变量的最佳方式是利用 ContextInfo 对象。
ContextInfo 是 QMT 框架中的全局上下文对象,它会在策略的生命周期内一直存在,并且会作为参数传递给 init 和 handlebar 等核心函数。
核心机制
- 在
init(ContextInfo)中定义:
你可以将计算好的初始值赋值给ContextInfo的自定义属性(例如ContextInfo.my_variable)。 - 在
handlebar(ContextInfo)中调用:
由于ContextInfo是同一个对象,你可以在 K 线运行函数中直接通过ContextInfo.my_variable获取之前存储的值。
代码示例
以下是一个完整的策略示例,展示了如何在 init 中计算初始止损价格和自定义参数,并在 handlebar 中使用它们:
# -*- coding: gbk -*-
def init(ContextInfo):
"""
初始化函数:策略启动时只运行一次
"""
# 1. 设定股票池
ContextInfo.set_universe(['600000.SH'])
# 2. 计算或定义初始值
# 假设我们需要定义一个全局的止损比例和一个基于初始资金计算的单笔交易限额
stop_loss_ratio = 0.05
initial_capital = 1000000 # 假设初始资金
# 3. 将这些值存储到 ContextInfo 对象中
# 注意:不要使用与系统属性重名的变量名(如 barpos, benchmark 等)
ContextInfo.my_stop_loss = stop_loss_ratio
ContextInfo.my_trade_limit = initial_capital * 0.1
ContextInfo.my_custom_status = "Ready"
print("初始化完成。")
print("全局止损率设定为: {}".format(ContextInfo.my_stop_loss))
print("单笔交易限额设定为: {}".format(ContextInfo.my_trade_limit))
def handlebar(ContextInfo):
"""
K线运行函数:每根K线或每个Tick运行一次
"""
# 1. 获取当前 K 线位置
index = ContextInfo.barpos
# 2. 从 ContextInfo 中获取 init 中计算好的值
current_stop_loss = ContextInfo.my_stop_loss
current_limit = ContextInfo.my_trade_limit
# 3. 在逻辑中使用这些值
# 示例:打印当前状态
if ContextInfo.is_new_bar():
print("当前K线索引: {}, 使用全局止损率: {}, 交易限额: {}".format(
index, current_stop_loss, current_limit
))
# 4. 如果需要,也可以在 handlebar 中修改这些全局值,供下一次 handlebar 使用
# ContextInfo.my_custom_status = "Running"
注意事项
- 变量命名:在
ContextInfo上挂载自定义变量时,请避免使用 QMT 内置的属性名称(如ContextInfo.barpos,ContextInfo.capital,ContextInfo.benchmark等),以免覆盖系统数据导致策略出错。建议加上前缀,如my_或g_。 - 作用域:如果在
init中定义变量时不加ContextInfo.前缀(例如直接写a = 10),那么a只是init函数内的局部变量,函数执行结束后就会被销毁,无法在handlebar中访问。 - 全局变量 (
global):虽然 Python 支持使用global关键字定义全局变量,但在 QMT 框架中,使用ContextInfo挂载属性是官方推荐的做法,因为这样更能保证上下文的一致性,且在多策略运行或特定回测场景下更稳定。
Q&A
Q: 如果我在 handlebar 中修改了 ContextInfo.my_variable 的值,下一次 handlebar 运行时它会变吗?
A: 会变。ContextInfo 在整个策略运行期间是同一个对象实例。如果你在当前的 handlebar 中修改了 ContextInfo.my_variable = 20,那么在下一根 K 线调用 handlebar 时,取到的值就是 20。这常用于记录“上一次交易时间”或“当前持仓状态”等动态变化的全局状态。
Q: 除了数值,我可以存储其他类型的数据吗?
A: 可以。你可以存储任何 Python 对象,包括列表(List)、字典(Dict)、Pandas DataFrame,甚至是自定义的类的实例。
Q: 为什么不直接在文件最上方写 global 变量?
A: 虽然技术上可行,但 QMT 的策略运行环境较为复杂(可能涉及多线程或多次加载)。使用 ContextInfo 能够确保变量严格绑定在当前策略实例的上下文中,避免潜在的命名空间污染或状态丢失问题。