🚀 AI 一键生成 qmt 策略代码

如何使用 Z-Score 指标在 QMT 平台构建均值回归交易策略?

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

问题描述

请帮我用 Z-Score 指标写一个均值回归策略,当价格偏离均值太远时就反向开仓。

解决方案

这是一个基于 Z-Score(标准分数) 的均值回归策略。

策略逻辑

Z-Score 用于衡量当前价格偏离移动平均线多少个标准差。

  1. 计算公式:$Z = \frac{\text{当前收盘价} - \text{N日移动平均价}}{\text{N日标准差}}$
  2. 做多信号:当 $Z < -Threshold$(例如 -2.0),说明价格统计上过低,买入/做多
  3. 做空信号:当 $Z > Threshold$(例如 2.0),说明价格统计上过高,卖出/做空
  4. 平仓信号:当 $|Z| < ExitThreshold$(例如 0.5),说明价格回归均值附近,平仓

策略代码

以下代码适用于 QMT 的 Python 策略编辑器。代码包含了详细注释,并使用了 get_market_data_ex 获取数据,以及 order_target_percent 进行仓位管理。

# -*- coding: gbk -*-
import pandas as pd
import numpy as np

def init(ContextInfo):
    """
    策略初始化函数
    """
    # 设置策略参数
    ContextInfo.period = 20          # 均线和标准差的计算周期
    ContextInfo.threshold = 2.0      # 开仓阈值(2倍标准差)
    ContextInfo.exit_threshold = 0.5 # 平仓阈值(回归到0.5倍标准差以内)
    
    # 设置交易账号(请替换为您自己的资金账号)
    # ContextInfo.set_account('您的资金账号') 
    
    # 设定股票池(这里以沪深300ETF为例,也可以在界面上选择运行品种)
    ContextInfo.target_stock = '510300.SH' 
    ContextInfo.set_universe([ContextInfo.target_stock])
    
    # 策略运行变量
    ContextInfo.holding_side = 0 # 0:空仓, 1:多头, -1:空头

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 获取当前主图或设置的标的代码
    stock_code = ContextInfo.target_stock
    
    # 获取当前K线索引
    index = ContextInfo.barpos
    
    # 获取历史行情数据
    # 我们需要 period 个数据来计算均值,为了稳健多取一点
    count = ContextInfo.period + 5
    
    # 使用 get_market_data_ex 获取数据 (推荐接口)
    # 注意:subscribe=True 确保实盘时数据自动更新
    data_map = ContextInfo.get_market_data_ex(
        ['close'], 
        [stock_code], 
        period=ContextInfo.period, 
        count=count,
        subscribe=True
    )
    
    if stock_code not in data_map:
        return

    # 获取收盘价序列 (pandas DataFrame)
    df = data_map[stock_code]
    
    # 确保数据量足够计算
    if len(df) < ContextInfo.period:
        return
    
    # 提取收盘价 Series
    close_series = df['close']
    
    # --- 计算 Z-Score ---
    # 1. 计算 N 日移动平均线 (MA)
    ma = close_series.rolling(window=ContextInfo.period).mean()
    
    # 2. 计算 N 日标准差 (Std)
    std = close_series.rolling(window=ContextInfo.period).std()
    
    # 3. 计算 Z-Score
    # Z = (当前价格 - 均值) / 标准差
    z_score_series = (close_series - ma) / std
    
    # 获取当前最新的 Z-Score 值
    current_z = z_score_series.iloc[-1]
    
    # 如果计算结果无效(如NaN),则跳过
    if np.isnan(current_z):
        return

    # --- 绘图 (可选) ---
    # 在副图画出 Z-Score,方便观察
    ContextInfo.paint('Z_Score', current_z, -1, 0)
    ContextInfo.paint('Upper', ContextInfo.threshold, -1, 0, 'red')
    ContextInfo.paint('Lower', -ContextInfo.threshold, -1, 0, 'green')

    # --- 交易逻辑 ---
    
    # 1. 开空/卖出信号:价格偏离均值过高 (Z > 2.0)
    if current_z > ContextInfo.threshold:
        # 如果当前不是空头,则做空
        # 注意:股票账户通常只能卖出持仓(Target=0),期货/融券账户可以做空(Target<0)
        # 这里演示设为 0% (清仓/卖出),如果是期货可设为 -0.5
        print(f"Z-Score: {current_z:.2f} > {ContextInfo.threshold}, 触发卖出/做空信号")
        order_target_percent(stock_code, 0.0, ContextInfo, ContextInfo.get_account(0))
        ContextInfo.holding_side = 0 # 标记状态

    # 2. 开多/买入信号:价格偏离均值过低 (Z < -2.0)
    elif current_z < -ContextInfo.threshold:
        # 如果当前不是多头,则做多
        # 目标仓位设为 50%
        print(f"Z-Score: {current_z:.2f} < -{ContextInfo.threshold}, 触发买入/做多信号")
        order_target_percent(stock_code, 0.5, ContextInfo, ContextInfo.get_account(0))
        ContextInfo.holding_side = 1

    # 3. 平仓/回归信号:价格回归均值附近 (|Z| < 0.5)
    elif abs(current_z) < ContextInfo.exit_threshold:
        # 如果当前持有头寸,则平仓
        if ContextInfo.holding_side != 0:
            print(f"Z-Score: {current_z:.2f} 回归均值, 平仓")
            order_target_percent(stock_code, 0.0, ContextInfo, ContextInfo.get_account(0))
            ContextInfo.holding_side = 0

代码关键点说明

  1. 数据获取 (get_market_data_ex)

    • 这是 QMT 推荐的新版数据接口。
    • 它返回一个字典,Key 是股票代码,Value 是 Pandas DataFrame。
    • subscribe=True 非常重要,它保证了在实盘运行时,策略能自动订阅并接收最新的行情推送。
  2. 指标计算 (Pandas Rolling)

    • 使用了 Pandas 的 rolling(window=N).mean()rolling(window=N).std() 来高效计算移动平均和标准差。
    • 这种向量化计算比手动循环快得多。
  3. 交易执行 (order_target_percent)

    • order_target_percent(stock, target, ...) 是一个非常方便的函数。
    • 它会自动计算当前持仓和目标持仓的差额,并自动下单买入或卖出。
    • 注意
      • 如果是普通股票账户,目标仓位只能是 0.01.0(即只能做多或平仓)。代码中 Z > Threshold 时设置为 0.0 即代表卖出平仓。
      • 如果是信用账户(融券)期货账户,目标仓位可以设置为负数(例如 -0.5)来实现真正的反向开空。
  4. 绘图 (paint)

    • 代码中加入了 ContextInfo.paint,运行策略时,你可以在副图看到 Z-Score 的曲线变化,便于验证策略逻辑。

如何运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略交易】。
  2. 新建一个 Python 策略,将上述代码粘贴进去。
  3. init 函数中,将 ContextInfo.set_account 的注释取消,并填入你的真实资金账号(回测模式下不需要)。
  4. 点击【编译】,确保无误。
  5. 选择一个品种(如 510300.SH),点击【运行】或【回测】。

常见问题 Q&A

Q: 为什么 get_market_data_ex 返回的数据有时候是空的?
A: 请检查 ContextInfo.set_universe 是否设置了对应的股票,或者在界面上是否选择了正确的品种。另外,如果是回测,请确保本地已经下载了对应的历史数据(在数据管理中补充数据)。

Q: 如何实现真正的“做空”?
A: 在代码的 Z > Threshold 分支中,将 order_target_percent 的第二个参数设置为负数(例如 -0.5)。前提是你绑定的账号支持做空(如期货账号或两融账号)。

Q: 这里的 Z-Score 是基于收盘价计算的吗?
A: 是的,代码中使用 df['close'],即基于收盘价计算。如果需要基于盘中实时价格,逻辑是一样的,因为 handlebar 在实盘中会随每个 Tick 更新,iloc[-1] 取到的就是最新价。