🚀 AI 一键生成 qmt 策略代码

如何构建一个结合日线和5分钟线 KDJ 指标的多周期交易策略?

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

问题描述

请帮我编写一个交易策略,当 KDJ 指标在日线上处于超卖区时,在5分钟图上出现金叉时买入。

解决方案

这是一个基于 QMT 平台的量化交易策略代码。

策略逻辑分析

  1. 数据获取
    • 获取 日线 数据:用于判断大趋势是否处于“超卖”状态。
    • 获取 5分钟 数据:用于捕捉具体的入场点(金叉)。
  2. 指标计算
    • 使用 talib 库计算 KDJ 指标(参数默认 9, 3, 3)。
    • 计算公式:$J = 3K - 2D$。
  3. 交易信号
    • 买入条件
      1. 日线 K 值 < 20 且 日线 D 值 < 20(定义为超卖)。
      2. 5分钟 K 线出现金叉(即上一根 K 线 $K < D$,当前 K 线 $K > D$)。
    • 卖出条件(为了策略完整性补充):
      1. 5分钟 K 线出现死叉(即上一根 K 线 $K > D$,当前 K 线 $K < D$)。
  4. 执行
    • 使用 order_shares 进行下单。

QMT 策略代码

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

def init(ContextInfo):
    """
    初始化函数,策略启动时执行一次
    """
    # 设置资金账号,请修改为您自己的资金账号
    ContextInfo.account_id = '6000000000' 
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 策略参数设置
    ContextInfo.kdj_n = 9
    ContextInfo.kdj_m1 = 3
    ContextInfo.kdj_m2 = 3
    
    # 超卖阈值
    ContextInfo.oversold_threshold = 20
    
    # 每次交易数量
    ContextInfo.trade_vol = 100
    
    print("策略初始化完成")

def get_kdj(high, low, close, n, m1, m2):
    """
    计算KDJ指标的辅助函数
    """
    # 使用 talib 计算 K 和 D
    # 注意:talib.STOCH 默认参数与国内常用的 KDJ 算法略有差异,这里使用通用设置
    k, d = talib.STOCH(high, low, close, 
                       fastk_period=n, 
                       slowk_period=m1, 
                       slowk_matype=0, 
                       slowd_period=m2, 
                       slowd_matype=0)
    
    # 计算 J 值
    j = 3 * k - 2 * d
    return k, d, j

def handlebar(ContextInfo):
    """
    K线处理函数,每根K线执行一次
    """
    # 获取当前主图的股票代码
    stock_code = ContextInfo.stockcode + '.' + ContextInfo.market
    
    # ----------------------------------------------------------------
    # 1. 获取日线数据 (用于判断超卖)
    # ----------------------------------------------------------------
    # 获取过去 100 天的数据以确保指标计算准确
    daily_data = ContextInfo.get_market_data_ex(
        ['high', 'low', 'close'], 
        [stock_code], 
        period='1d', 
        count=100, 
        dividend_type='follow'
    )
    
    if stock_code not in daily_data or daily_data[stock_code].empty:
        return

    df_daily = daily_data[stock_code]
    
    # 计算日线 KDJ
    d_k, d_d, d_j = get_kdj(df_daily['high'].values, 
                            df_daily['low'].values, 
                            df_daily['close'].values, 
                            ContextInfo.kdj_n, 
                            ContextInfo.kdj_m1, 
                            ContextInfo.kdj_m2)
    
    # 获取最近一个完整的日线 K, D 值
    # 注意:如果是盘中运行,-1 代表当天(未走完),-2 代表昨天。
    # 策略逻辑:判断大趋势,通常看前一天的收盘状态,或者当天的实时状态。
    # 这里取 -1 (最新值)
    current_daily_k = d_k[-1]
    current_daily_d = d_d[-1]

    # 判断日线是否处于超卖区
    is_daily_oversold = (current_daily_k < ContextInfo.oversold_threshold) and \
                        (current_daily_d < ContextInfo.oversold_threshold)

    # ----------------------------------------------------------------
    # 2. 获取 5分钟数据 (用于金叉买入)
    # ----------------------------------------------------------------
    # 策略应当运行在 5分钟 图上,或者显式获取 5分钟 数据
    min5_data = ContextInfo.get_market_data_ex(
        ['high', 'low', 'close'], 
        [stock_code], 
        period='5m', 
        count=100, 
        dividend_type='follow'
    )
    
    if stock_code not in min5_data or min5_data[stock_code].empty:
        return

    df_5m = min5_data[stock_code]
    
    # 计算 5分钟 KDJ
    m_k, m_d, m_j = get_kdj(df_5m['high'].values, 
                            df_5m['low'].values, 
                            df_5m['close'].values, 
                            ContextInfo.kdj_n, 
                            ContextInfo.kdj_m1, 
                            ContextInfo.kdj_m2)
    
    # 获取 5分钟 KDJ 的当前值和前一值
    curr_m_k = m_k[-1]
    curr_m_d = m_d[-1]
    prev_m_k = m_k[-2]
    prev_m_d = m_d[-2]

    # 判断 5分钟 金叉 (上根K线 K<D,当前K线 K>D)
    is_5m_golden_cross = (prev_m_k < prev_m_d) and (curr_m_k > curr_m_d)
    
    # 判断 5分钟 死叉 (用于卖出逻辑,可选)
    is_5m_dead_cross = (prev_m_k > prev_m_d) and (curr_m_k < curr_m_d)

    # ----------------------------------------------------------------
    # 3. 交易逻辑执行
    # ----------------------------------------------------------------
    
    # 获取当前持仓 (简单示例,实际需根据 get_trade_detail_data 获取准确持仓)
    # 这里仅做信号触发演示
    
    # 信号 1: 买入
    # 条件: 日线超卖 AND 5分钟金叉 AND 不是最后一根K线(回测时防止未来函数)
    # 实盘时 ContextInfo.is_last_bar() 为 True,需要执行
    
    if is_daily_oversold and is_5m_golden_cross:
        print(f"时间: {ContextInfo.get_bar_timetag(ContextInfo.barpos)} - 触发买入信号: 日线超卖且5分钟金叉")
        # 下单买入
        order_shares(stock_code, ContextInfo.trade_vol, 'fix', df_5m['close'].iloc[-1], ContextInfo, ContextInfo.account_id)

    # 信号 2: 卖出 (简单止盈/止损逻辑:5分钟死叉即卖出)
    elif is_5m_dead_cross:
        print(f"时间: {ContextInfo.get_bar_timetag(ContextInfo.barpos)} - 触发卖出信号: 5分钟死叉")
        # 下单卖出
        order_shares(stock_code, -ContextInfo.trade_vol, 'fix', df_5m['close'].iloc[-1], ContextInfo, ContextInfo.account_id)

使用说明

  1. 运行周期:请将此策略加载到 5分钟 周期的 K 线图上运行。
  2. 账号设置:代码中的 ContextInfo.account_id = '6000000000' 需要修改为您实际的 QMT 资金账号。
  3. 编码格式:QMT 的 Python 编辑器强制要求文件头包含 # -*- coding: gbk -*-,请勿删除。
  4. 库依赖:代码使用了 talib 库,QMT 内置环境中通常已包含此库。如果报错,可以使用 pandas 手动实现 KDJ 计算逻辑。
  5. 实盘注意
    • handlebar 在实盘模式下,最后一根 K 线(当前正在波动的 K 线)每变动一次 tick 都会触发一次。
    • 为了避免信号闪烁(即盘中金叉,收盘没金叉),建议结合 ContextInfo.is_new_bar() 使用,或者使用 do_order 函数在下一根 K 线开盘时确认信号。上述代码使用的是即时信号逻辑。

常见问题 Q&A

Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 较新的接口,返回的数据结构更清晰(字典包含 DataFrame),且支持订阅模式,效率更高。

Q: 如何避免实盘中信号闪烁导致的重复下单?
A: 可以在 handlebar 开头添加判断,只在 K 线走完时下单,或者使用全局变量记录当前 K 线是否已经交易过。最简单的方法是使用 passorderquickTrade=0 参数(默认),它会在 K 线走完后才确认信号。

Q: 日线超卖的定义可以修改吗?
A: 可以。在 init 函数中修改 ContextInfo.oversold_threshold = 20 即可,例如改为 15 或 25。