🚀 AI 一键生成 qmt 策略代码

QMT 策略风控:如何实现基于移动平均线(MA)的动态跟踪止损?

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

问题描述

我希望设置一个动态的止损点,比如当股价跌破20日移动平均线时就自动卖出,该如何实现?

解决方案

这是一个非常实用的量化交易需求。在 QMT 中实现“跌破20日均线卖出”的动态止损策略,核心逻辑分为三步:

  1. 获取数据:获取标的股票过去20个交易日的收盘价。
  2. 计算指标:计算20日移动平均线(MA20)和当前最新价格。
  3. 交易判断:如果当前持仓且最新价小于MA20,则触发卖出信号。

为了让策略完整可运行,我在代码中加入了一个简单的买入逻辑(站上20日均线买入),以便您在回测时能看到开仓和平仓的完整过程。

QMT 动态止损策略代码

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

def init(ContextInfo):
    # 1. 设置策略基本参数
    ContextInfo.account_id = '6000000000'  # 请替换为您真实的资金账号
    ContextInfo.account_type = 'STOCK'     # 账号类型:STOCK-股票,FUTURE-期货
    ContextInfo.ma_period = 20             # 均线周期:20日
    
    # 2. 设置股票池(示例:平安银行)
    ContextInfo.stock_code = '000001.SZ'
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 3. 设置交易费率等(回测用)
    ContextInfo.set_commission(0.0003)     # 设置手续费万三
    
    print("策略初始化完成,监控标的: {}, 止损均线: MA{}".format(ContextInfo.stock_code, ContextInfo.ma_period))

def handlebar(ContextInfo):
    # 获取当前K线对应的股票代码
    # 如果是回测模式,handlebar会逐根K线运行;如果是实盘,会在最新K线运行
    stock_code = ContextInfo.stock_code
    
    # 1. 获取历史行情数据
    # 我们需要获取 ma_period 数量的数据来计算均线
    # 为了计算准确,通常多取一点数据,这里取 ma_period + 2 条
    data = ContextInfo.get_market_data_ex(
        ['close'], 
        [stock_code], 
        period='1d', 
        count=ContextInfo.ma_period + 2,
        dividend_type='front' # 使用前复权数据
    )
    
    # 检查数据是否获取成功
    if stock_code not in data or data[stock_code].empty:
        return
        
    df = data[stock_code]
    
    # 如果数据长度不足以计算MA20,则跳过
    if len(df) < ContextInfo.ma_period:
        return

    # 2. 计算指标
    # 计算收盘价的 rolling mean (移动平均)
    # 获取最近一根K线的MA20值
    ma_values = df['close'].rolling(window=ContextInfo.ma_period).mean()
    current_ma20 = ma_values.iloc[-1] # 最新MA20
    current_price = df['close'].iloc[-1] # 最新收盘价
    
    # 3. 获取当前持仓信息
    position = get_position(ContextInfo, stock_code)
    
    # 4. 交易逻辑判断
    
    # --- 止损/卖出逻辑:跌破20日均线 ---
    # 条件:持有仓位 且 当前价格 < 20日均线
    if position > 0 and current_price < current_ma20:
        print(f"时间:{ContextInfo.get_bar_timetag(ContextInfo.barpos)} | 触发止损: 现价{current_price:.2f} < MA20 {current_ma20:.2f}")
        
        # 执行卖出:opType=24(卖出), orderType=1101(单股单账号), priceType=5(最新价)
        passorder(24, 1101, ContextInfo.account_id, stock_code, 5, -1, position, ContextInfo)
    
    # --- 买入逻辑(为了演示完整性添加):站上20日均线 ---
    # 条件:无持仓 且 当前价格 > 20日均线
    elif position == 0 and current_price > current_ma20:
        print(f"时间:{ContextInfo.get_bar_timetag(ContextInfo.barpos)} | 触发买入: 现价{current_price:.2f} > MA20 {current_ma20:.2f}")
        
        # 执行买入:opType=23(买入), 买入1000股
        passorder(23, 1101, ContextInfo.account_id, stock_code, 5, -1, 1000, ContextInfo)

# 辅助函数:获取指定股票的持仓数量
def get_position(ContextInfo, stock_code):
    # 获取交易明细中的持仓信息
    # 注意:回测模式下,get_trade_detail_data 返回的是回测系统的虚拟持仓
    # 实盘模式下,返回的是真实账号的持仓
    positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
    
    for pos in positions:
        if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
            return pos.m_nVolume
    return 0

代码核心逻辑解析

  1. 数据获取 (get_market_data_ex):

    • 我们使用了 get_market_data_ex 接口,这是 QMT 推荐的新版数据接口。
    • count=ContextInfo.ma_period + 2:为了计算20日均线,我们至少需要20根K线。多取2根是为了防止数据边界问题。
    • dividend_type='front':使用前复权数据。在涉及价格比较(如均线策略)时,复权非常重要,否则分红除权会导致股价“假摔”跌破均线,触发错误止损。
  2. 指标计算:

    • 利用 pandasrolling(window=20).mean() 快速计算移动平均线。
    • iloc[-1] 获取序列中最新的一个数据点。
  3. 持仓查询 (get_position):

    • 我封装了一个 get_position 辅助函数,通过 get_trade_detail_data 遍历账户持仓,精确查找当前标的的持仓数量。这在实盘和回测中都通用。
  4. 下单函数 (passorder):

    • 卖出: opType=24
    • 买入: opType=23
    • 下单量: 卖出时使用了 position 变量,意味着全仓卖出(清仓止损)。

如何使用

  1. 打开 QMT 软件,进入【模型研究】或【策略编辑器】。
  2. 新建一个 Python 策略。
  3. 将上述代码完整复制进去。
  4. 修改账号: 将 ContextInfo.account_id 修改为您自己的资金账号(回测模式下可忽略或使用默认)。
  5. 运行回测: 在右侧设置回测区间(例如最近一年),点击【运行】即可看到买卖点和资金曲线。

Q&A

Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 较新的接口,返回的数据结构更清晰(字典包含 DataFrame),且在处理多股、多周期数据时性能更好,参数定义也更符合 Python 习惯。

Q: 如何将止损条件改为“收盘价跌破均线 3%”?
A: 您可以修改判断逻辑:
if current_price < current_ma20:
改为 if current_price < current_ma20 * 0.97:
这样只有当价格低于均线的 97%(即跌破 3%)时才会触发卖出,可以过滤掉一些假突破。

Q: 实盘运行时,这个策略会在盘中触发还是收盘触发?
A: 这取决于您运行策略的频率。

  • 如果您在界面上设置“K线生成后运行”,它会在每天收盘(日线走完)后检测,第二天开盘下单。
  • 如果您设置了“固定时间间隔”(如每3秒),它会在盘中实时检测。如果盘中价格瞬间跌破均线,就会立即触发卖出。对于均线策略,通常建议以收盘价为准,以避免盘中震荡洗盘。