🚀 AI 一键生成 qmt 策略代码

QMT 实战:开盘区间突破 (Opening Range Breakout) 策略 Python 实现

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

问题描述

帮我写一个 ORB 开盘突破策略

解决方案

这是一个基于 QMT 平台的 ORB (Opening Range Breakout,开盘区间突破) 策略实现。

策略逻辑说明

  1. 定义区间:设定开盘后的一段时间(例如 09:30 - 10:00)为“开盘区间”。
  2. 计算上下轨:记录该时间段内的最高价(High)和最低价(Low)。
  3. 突破交易
    • 当价格突破上轨时,买入(做多)。
    • 当价格跌破下轨时,卖出(平仓或做空,本代码以股票做多为例,跌破下轨则平仓止损)。
  4. 尾盘平仓:在收盘前(例如 14:55)强制平仓,不持仓过夜(日内策略)。

策略代码

请在 QMT 的策略编辑器中新建一个 Python 策略,并将以下代码复制进去。

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

def init(ContextInfo):
    """
    初始化函数
    """
    # 1. 设置策略参数
    ContextInfo.stock_code = '600000.SH'  # 交易标的,例如浦发银行
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID'  # 请替换为您的资金账号
    ContextInfo.account_type = 'STOCK'  # 账号类型:'STOCK'股票, 'FUTURE'期货
    
    # ORB 参数设置
    ContextInfo.orb_end_time = "100000"  # 开盘区间结束时间,格式 HHMMSS (10:00:00)
    ContextInfo.close_time = "145500"    # 尾盘平仓时间 (14:55:00)
    ContextInfo.trade_qty = 100          # 每次交易数量 (股/手)
    
    # 2. 设置股票池
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 3. 设置账号 (实盘/回测必须)
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 4. 初始化全局变量
    ContextInfo.current_date = None      # 当前交易日期记录
    ContextInfo.high_band = -1           # 上轨
    ContextInfo.low_band = 999999        # 下轨
    ContextInfo.has_traded = False       # 当日是否已开仓标记
    
    print("ORB 策略初始化完成")

def handlebar(ContextInfo):
    """
    K线周期运行函数 (建议在 1分钟 或 5分钟 周期运行)
    """
    # 获取当前 K 线的位置
    index = ContextInfo.barpos
    
    # 获取当前 K 线的时间戳 (毫秒)
    timetag = ContextInfo.get_bar_timetag(index)
    
    # 将时间戳转换为 datetime 对象和字符串
    dt_obj = timetag_to_datetime(timetag, '%Y%m%d%H%M%S')
    current_date_str = dt_obj[0:8]   # YYYYMMDD
    current_time_str = dt_obj[8:]    # HHMMSS
    
    # --- 1. 新的一天,重置变量 ---
    if ContextInfo.current_date != current_date_str:
        ContextInfo.current_date = current_date_str
        ContextInfo.high_band = -1
        ContextInfo.low_band = 999999
        ContextInfo.has_traded = False
        print(f"=== 新交易日: {current_date_str} ===")

    # 获取当前 K 线的行情数据
    # 注意:get_market_data 返回的是 pandas Series/DataFrame
    # 这里取当前这根 bar 的高低收
    current_high = ContextInfo.get_market_data(['high'], stock_code=[ContextInfo.stock_code], count=1, period=ContextInfo.period, dividend_type='follow')
    current_low = ContextInfo.get_market_data(['low'], stock_code=[ContextInfo.stock_code], count=1, period=ContextInfo.period, dividend_type='follow')
    current_close = ContextInfo.get_market_data(['close'], stock_code=[ContextInfo.stock_code], count=1, period=ContextInfo.period, dividend_type='follow')
    
    # 数据校验,防止取不到数据报错
    if current_close.empty:
        return
        
    price = current_close.iloc[-1]
    high = current_high.iloc[-1]
    low = current_low.iloc[-1]

    # --- 2. 计算 ORB 区间 (09:30 - 10:00) ---
    if current_time_str <= ContextInfo.orb_end_time:
        # 更新区间最高价
        if high > ContextInfo.high_band:
            ContextInfo.high_band = high
        # 更新区间最低价
        if low < ContextInfo.low_band:
            ContextInfo.low_band = low
            
        # 在区间结束的那一刻打印一下确定的范围
        if current_time_str == ContextInfo.orb_end_time:
            print(f"ORB区间确定: 上轨={ContextInfo.high_band}, 下轨={ContextInfo.low_band}")

    # --- 3. 交易逻辑 (10:00 - 14:55) ---
    elif ContextInfo.orb_end_time < current_time_str < ContextInfo.close_time:
        
        # 获取当前持仓
        position = get_position(ContextInfo, ContextInfo.stock_code)
        
        # 策略逻辑:如果还没交易过,且没有持仓
        if not ContextInfo.has_traded and position == 0:
            # 突破上轨 -> 买入
            if price > ContextInfo.high_band:
                print(f"时间:{current_time_str} 价格:{price} 突破上轨:{ContextInfo.high_band} -> 买入")
                order_shares(ContextInfo.stock_code, ContextInfo.trade_qty, 'fix', price, ContextInfo, ContextInfo.account_id)
                ContextInfo.has_traded = True
            
            # 跌破下轨 -> (如果是期货可以做空,股票则无操作或视为弱势不操作)
            elif price < ContextInfo.low_band:
                # 股票策略通常不做空,这里仅打印日志
                # 如果是期货,可以使用 buy_open / sell_open
                print(f"时间:{current_time_str} 价格:{price} 跌破下轨:{ContextInfo.low_band} -> 弱势区域,不操作")
                ContextInfo.has_traded = True # 标记为已触发过信号,避免反复判断

        # 止损逻辑:如果持有仓位,且价格跌破下轨 -> 卖出止损
        elif position > 0:
            if price < ContextInfo.low_band:
                print(f"时间:{current_time_str} 价格:{price} 跌破下轨:{ContextInfo.low_band} -> 止损平仓")
                order_shares(ContextInfo.stock_code, -position, 'fix', price, ContextInfo, ContextInfo.account_id)

    # --- 4. 尾盘清仓 (14:55 以后) ---
    elif current_time_str >= ContextInfo.close_time:
        position = get_position(ContextInfo, ContextInfo.stock_code)
        if position > 0:
            print(f"尾盘清仓: {current_time_str}, 卖出 {position} 股")
            order_shares(ContextInfo.stock_code, -position, 'fix', price, ContextInfo, ContextInfo.account_id)

def get_position(ContextInfo, stock_code):
    """
    辅助函数:获取指定标的的持仓数量
    """
    # 获取持仓对象列表
    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. 运行周期

    • 请务必在 1分钟 (1m)5分钟 (5m) 周期下运行此策略。如果在日线周期运行,handlebar 每天只调用一次,无法捕捉日内的 10:00 时间点和盘中突破。
  2. 账号设置

    • 代码中的 ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 必须修改为您实际的资金账号。
    • 如果是股票,account_type 设为 'STOCK';如果是期货,设为 'FUTURE'
  3. 标的设置

    • 修改 ContextInfo.stock_code 为您想交易的股票或合约代码(如 '600000.SH''IF2312.IF')。
  4. 交易函数

    • 本代码使用了 order_shares(指定股数交易)。
    • 'fix' 代表限价单(以触发时的最新价下单)。如果需要更快的成交,可以改为 'market'(市价单,需注意滑点)。
  5. 回测与实盘

    • 回测:请确保已下载对应品种的分钟级历史数据。
    • 实盘:请确保 QMT 客户端已登录交易账号,并且策略界面中的“模型交易”已勾选该策略。

常见问题 (Q&A)

Q: 为什么策略没有在 10:00 准时下单?
A: 策略是基于 K 线驱动的。如果使用的是 5 分钟 K 线,信号只会在 5 分钟结束时触发。如果该股票流动性差,某分钟没有成交(没有 K 线生成),handlebar 也不会运行。建议使用流动性好的标的配合 1 分钟周期。

Q: 如何改为期货做空策略?
A: 在代码的“跌破下轨”逻辑中,将 print 语句替换为 passordersell_open 函数进行开空操作,并在尾盘平仓逻辑中加入平空单的逻辑(buy_close)。

Q: 为什么 get_market_data 有时候报错?
A: 请检查是否补充了完整的数据。另外,QMT 的数据接口返回的是 Pandas 格式,代码中使用了 .iloc[-1] 来获取最新值,确保了格式的正确解析。