🚀 AI 一键生成 qmt 策略代码

基于 TRIX 指标的金叉死叉交易策略 Python 源码

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

问题描述

帮我写一个 TRIX 指标的量化策略

解决方案

这是一个基于 TRIX(三重指数平滑移动平均) 指标的量化交易策略。

策略逻辑说明

TRIX 是一种动量指标,用于过滤短期价格波动,通过三次平滑处理来显示价格的长期趋势。

  1. 指标计算
    • TR (EMA3):对收盘价进行三次 N 日指数移动平均(EMA)。
    • TRIX:(今日 TR - 昨日 TR) / 昨日 TR * 100。
    • MATRIX (TRIXMA):TRIX 的 M 日简单移动平均。
  2. 交易信号
    • 金叉(买入):TRIX 线由下向上穿过 MATRIX 线。
    • 死叉(卖出):TRIX 线由上向下穿过 MATRIX 线。
  3. 参数设置
    • N = 12 (TRIX 周期)
    • M = 9 (MATRIX 周期)

策略代码实现

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

def init(ContextInfo):
    """
    策略初始化函数
    """
    # 1. 设置策略参数
    ContextInfo.N = 12  # TRIX指标的周期
    ContextInfo.M = 9   # TRIX均线(MATRIX)的周期
    
    # 2. 设置股票池 (示例:平安银行)
    # 在实际运行时,可以通过界面设置股票池,这里代码演示写死一个
    ContextInfo.stock_list = ['000001.SZ'] 
    ContextInfo.set_universe(ContextInfo.stock_list)
    
    # 3. 设置资金账号 (请替换为您自己的资金账号)
    ContextInfo.account_id = '6000000000' 
    ContextInfo.account_type = 'STOCK' # 账号类型:STOCK股票,FUTURE期货
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 4. 设置回测参数 (仅回测有效)
    ContextInfo.set_slippage(1, 0.0) # 设置滑点
    ContextInfo.set_commission(0, [0.0003, 0.0013, 0.0003, 0.0003, 0.0003, 5]) # 设置费率

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 获取当前设置的股票池
    stock_list = ContextInfo.get_universe()
    
    # 获取当前周期 (例如 '1d')
    period = ContextInfo.period
    
    # 遍历每只股票进行计算
    for stock in stock_list:
        # 1. 获取历史行情数据
        # TRIX经过三次平滑,需要较长的历史数据才能计算准确,这里取 100 根K线
        data_len = 100
        
        # 使用 get_market_data_ex 获取数据 (推荐使用 ex 接口)
        # subscribe=True 确保实盘时数据自动更新
        market_data = ContextInfo.get_market_data_ex(
            ['close'], 
            [stock], 
            period=period, 
            count=data_len, 
            dividend_type='front', # 前复权
            subscribe=True
        )
        
        if stock not in market_data or market_data[stock].empty:
            continue
            
        df = market_data[stock]
        
        # 数据长度不足以计算指标时跳过
        if len(df) < ContextInfo.N * 3 + ContextInfo.M:
            continue
            
        # 2. 计算 TRIX 指标
        # 计算步骤 1: 收盘价的 N 日 EMA (EMA1)
        ema1 = df['close'].ewm(span=ContextInfo.N, adjust=False).mean()
        
        # 计算步骤 2: EMA1 的 N 日 EMA (EMA2)
        ema2 = ema1.ewm(span=ContextInfo.N, adjust=False).mean()
        
        # 计算步骤 3: EMA2 的 N 日 EMA (EMA3, 即 TR)
        tr = ema2.ewm(span=ContextInfo.N, adjust=False).mean()
        
        # 计算步骤 4: TRIX = (今日TR - 昨日TR) / 昨日TR * 100
        # pct_change() 计算变化率,默认就是 (curr - prev) / prev
        trix = tr.pct_change() * 100
        
        # 计算步骤 5: MATRIX (TRIX 的 M 日简单移动平均)
        matrix = trix.rolling(window=ContextInfo.M).mean()
        
        # 3. 获取最新的指标值用于判断信号
        # prev_trix: 上一根K线的TRIX, curr_trix: 当前K线的TRIX
        # iloc[-1] 是当前最新值,iloc[-2] 是前一根K线值
        curr_trix = trix.iloc[-1]
        prev_trix = trix.iloc[-2]
        
        curr_matrix = matrix.iloc[-1]
        prev_matrix = matrix.iloc[-2]
        
        # 检查是否包含无效值(NaN),如果有则跳过
        if np.isnan(curr_trix) or np.isnan(curr_matrix) or np.isnan(prev_trix) or np.isnan(prev_matrix):
            continue

        # 4. 获取当前持仓信息
        # 获取持仓对象列表
        positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
        current_vol = 0
        for pos in positions:
            if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock:
                current_vol = pos.m_nVolume
                break
        
        # 5. 生成交易信号并下单
        
        # 金叉:TRIX 上穿 MATRIX
        long_signal = (prev_trix < prev_matrix) and (curr_trix > curr_matrix)
        
        # 死叉:TRIX 下穿 MATRIX
        short_signal = (prev_trix > prev_matrix) and (curr_trix < curr_matrix)
        
        # 执行买入逻辑
        if long_signal:
            if current_vol == 0: # 如果当前无持仓
                # 示例:全仓买入 (实际策略请做好资金管理)
                # 这里简单演示:按最新价买入 1000 股
                # 23: 买入, 1101: 单股单账号普通股/手下单, 5: 最新价
                passorder(23, 1101, ContextInfo.account_id, stock, 5, -1, 1000, ContextInfo)
                print(f"TRIX金叉,买入 {stock}")
                
        # 执行卖出逻辑
        elif short_signal:
            if current_vol > 0: # 如果当前有持仓
                # 卖出所有持仓
                # 24: 卖出
                passorder(24, 1101, ContextInfo.account_id, stock, 5, -1, current_vol, ContextInfo)
                print(f"TRIX死叉,卖出 {stock}")

    # 6. 绘图 (可选,用于回测界面展示)
    # 注意:paint函数只能画在主图对应的品种上,如果遍历多个股票,这里只演示画当前主图品种
    # 获取当前主图代码
    main_stock = ContextInfo.stockcode + '.' + ContextInfo.market
    if main_stock in market_data:
        # 重新计算一遍主图的指标用于绘图
        m_df = market_data[main_stock]
        m_ema1 = m_df['close'].ewm(span=ContextInfo.N, adjust=False).mean()
        m_ema2 = m_ema1.ewm(span=ContextInfo.N, adjust=False).mean()
        m_tr = m_ema2.ewm(span=ContextInfo.N, adjust=False).mean()
        m_trix = m_tr.pct_change() * 100
        m_matrix = m_trix.rolling(window=ContextInfo.M).mean()
        
        # 在副图画线
        ContextInfo.paint('TRIX', m_trix.iloc[-1], -1, 0, 'white', 'noaxis')
        ContextInfo.paint('MATRIX', m_matrix.iloc[-1], -1, 0, 'yellow', 'noaxis')

代码关键点解析

  1. 数据获取 (get_market_data_ex)

    • 使用了 get_market_data_ex 接口,这是 QMT 推荐的高效数据接口。
    • count=100:TRIX 指标涉及三次 EMA 平滑,对历史数据长度有要求,数据太短会导致计算初期的指标值不准确。
    • dividend_type='front':使用前复权数据,保证价格连续性,避免分红除权造成的指标跳变。
  2. 指标计算 (Pandas)

    • 利用 Pandas 的 ewm(span=N) 函数非常方便地计算指数移动平均。
    • pct_change() 用于计算变化率,即 (当前值 - 前值) / 前值,这正是 TRIX 的核心定义。
  3. 交易执行 (passorder)

    • 23 代表买入,24 代表卖出。
    • 1101 代表单股、单账号、按股数下单。
    • 5 代表使用最新价下单。
    • 代码中加入了简单的持仓判断:金叉时若无持仓则买入,死叉时若有持仓则卖出。
  4. 绘图 (paint)

    • 代码末尾包含了 paint 函数,可以在 QMT 的回测界面副图中画出 TRIX 和 MATRIX 线,方便验证策略信号。

如何使用

  1. 打开 QMT 客户端,进入“策略编辑器”。
  2. 新建一个 Python 策略。
  3. 将上述代码复制粘贴到编辑器中。
  4. 修改 init 函数中的 ContextInfo.account_id 为您真实的资金账号。
  5. 点击“编译”检查是否有语法错误。
  6. 在回测模式下选择标的和时间段进行回测,或在实盘模式下运行。

Q&A

Q: 为什么 TRIX 指标计算需要较长的历史数据?
A: TRIX 进行了三次指数平滑(EMA)。EMA 是一种递归计算,今天的数值依赖于昨天的数值。如果数据序列太短,初始值的偏差会严重影响后续的计算结果。通常建议数据长度至少是周期 N 的 3-5 倍以上。

Q: 如何调整策略的灵敏度?
A: 修改 init 函数中的 ContextInfo.NContextInfo.M

  • 减小 N(例如改为 5):指标对价格变化更敏感,信号更多,但假信号也更多。
  • 增大 N(例如改为 30):指标更平滑,过滤震荡效果更好,但信号会有明显的滞后。

Q: passorder 函数中的 quickTrade 参数有什么用?
A: 在本策略中,passorderquickTrade 参数(倒数第二个参数)默认为 0(代码中未显式写出,使用了默认行为)。在回测中,这意味着信号在 K 线走完后确认,并在下一根 K 线开盘时下单。如果设置为 1,则表示立即下单(非收盘确认),这在实盘中常用于盘中即时信号,但在回测中可能会引入“未来函数”风险,需谨慎使用。