🚀 AI 一键生成 qmt 策略代码

QMT 策略基石:init 初始化函数的作用与最佳实践指南

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

问题描述

写策略的时候,init函数里到底应该放些什么东西?哪些操作是必须的?

解决方案

在 QMT 量化交易平台中,init(ContextInfo) 是策略的初始化函数。它是策略生命周期的起点,在整个策略运行过程中(无论是回测还是实盘),它只会在开始时被调用一次

它的核心作用是配置环境定义全局状态。你可以把它理解为策略的“设置面板”和“内存仓库”。

以下是关于 init 函数中应该包含内容的详细说明:

1. init 函数的核心职责

init 中,你通常需要完成以下五类操作:

(1) 设置账号信息 (实盘/回测必须)

这是连接策略与交易柜台的关键。

  • API: ContextInfo.set_account(account_id)
  • 作用: 绑定资金账号。在回测模式下,它会模拟一个账号;在实盘模式下,它决定了交易指令发送到哪个真实账户。如果不设置,后续的交易函数(如 passorder)将无法正常工作或无法接收回报。

(2) 设置股票池 (Universe) (强烈建议)

定义策略关注的标的集合。

  • API: ContextInfo.set_universe(stock_list)
  • 作用: 告诉系统你需要哪些股票的数据。很多数据获取函数(如 get_history_data)默认会使用这里设置的股票池。如果不设置,后续获取数据可能需要每次手动指定代码列表,且回测效率可能降低。

(3) 设置回测参数 (回测模式专用)

虽然可以在界面上设置,但在代码中设置可以确保策略逻辑的一致性,且代码设置的优先级高于界面。

  • 资金: ContextInfo.capital = 1000000
  • 费率: ContextInfo.set_commission(...)
  • 滑点: ContextInfo.set_slippage(...)
  • 时间: ContextInfo.start / ContextInfo.end

(4) 定义全局变量 (逻辑核心)

由于 handlebar 函数会随行情频繁运行,定义在 handlebar 内部的普通变量会在每次运行结束后销毁。如果你需要记录**“上一次买入价格”“当前持仓天数”“策略状态开关”**等跨周期的数据,必须在 init 中将它们挂载到 ContextInfo 对象上。

  • 示例: ContextInfo.my_last_buy_price = 0

(5) 设置定时器 (进阶用法)

如果你需要策略在特定的时间点运行(例如每天 14:50 进行尾盘选股),而不是每根 K 线都运行,可以在这里注册定时任务。

  • API: ContextInfo.run_time(...)

2. 哪些操作是必须的?

严格来说,Python 语法要求函数体不能为空(至少要写个 pass),但从策略运行的角度看:

  1. 必须项:

    • 函数定义: def init(ContextInfo): 必须存在。
    • 设置账号: ContextInfo.set_account(...)。如果没有这一步,交易回报回调(如 order_callback)不会生效,下单时若不指定账号也会报错。
  2. 非必须但极重要项:

    • 设置股票池: ContextInfo.set_universe(...)。除非你的策略完全不依赖历史数据接口的默认行为,否则这是基础。
    • 全局变量初始化: 除非你的策略是完全无状态的(即当下的决策只依赖当下的 K 线,完全不依赖之前的状态),否则必须在此初始化变量。

3. 代码示例

下面是一个标准的 init 函数模板,涵盖了上述要点:

# -*- coding: gbk -*-

def init(ContextInfo):
    # 1. 【必须】设置资金账号
    # 实盘中请替换为真实的资金账号,回测时可随意填写
    my_account = '600000248'
    ContextInfo.set_account(my_account)
    
    # 2. 【建议】设置股票池
    # 设定策略要操作的标的,例如:平安银行和贵州茅台
    stock_list = ['000001.SZ', '600519.SH']
    ContextInfo.set_universe(stock_list)
    
    # 3. 【回测专用】设置回测参数 (代码优先级 > 界面设置)
    # 设置初始资金为 100 万
    ContextInfo.capital = 1000000
    # 设置手续费:股票买入万三,卖出万三加千一印花税
    # 参数顺序: [买入印花税, 卖出印花税, 开仓佣金, 平仓佣金, 平今佣金, 最低佣金]
    ContextInfo.set_commission(0, [0, 0.001, 0.0003, 0.0003, 0, 5])
    # 设置滑点:按固定价格 0.01 元
    ContextInfo.set_slippage(1, 0.01)
    
    # 4. 【逻辑核心】定义全局变量
    # 注意:必须挂载在 ContextInfo 下,才能在 handlebar 中被调用和修改
    ContextInfo.hold_days = 0          # 记录持仓天数
    ContextInfo.last_entry_price = {}  # 记录每只股票的入场价格
    ContextInfo.stop_loss_pct = 0.05   # 定义全局止损比例 5%
    
    # 5. 【进阶】设置定时器 (可选)
    # 例如:每天 10:00:00 运行一次名为 on_time_check 的自定义函数
    # ContextInfo.run_time("on_time_check", "1nDay", "2023-01-01 10:00:00", "SH")

    print("策略初始化完成")

总结

init 函数是策略的配置中心

  • 不要init 里写交易逻辑(如判断均线金叉下单),因为这里没有行情驱动,只运行一次。
  • 一定要init 里把所有后续需要用到的“状态变量”都定义好,防止在 handlebar 中出现 NameError 或变量重置的问题。

Q&A

Q1: 如果我在 init 里写了死循环 while True 会怎么样?
A1: 策略会卡死在初始化阶段,永远无法进入 handlebar 处理行情,甚至导致客户端无响应。init 必须快速执行完毕。

Q2: ContextInfo.benchmark (基准) 可以在 init 里设置吗?
A2: QMT 的 Python API 中 ContextInfo.benchmark 是只读属性,不支持通过代码设置。回测基准需要在策略编辑器的“回测参数”面板中手动选择。

Q3: 我可以在 init 里获取历史数据吗?
A3: 可以。你可以调用 ContextInfo.get_history_data 来计算一些盘前指标(例如计算过去一年的波动率作为今天的参考),但要注意此时获取的是策略启动时刻之前的数据。

Q4: 为什么我的全局变量在 handlebar 里报错说不存在?
A4: 请检查是否在 init 中使用了 ContextInfo.变量名 = 值 的形式。如果只是写 my_var = 0,它只是 init 函数内的局部变量,函数运行完就销毁了,必须挂载到 ContextInfo 对象上才能跨函数传递。