🚀 AI 一键生成 joinquant 策略代码

如何基于开盘后价格区间进行突破交易?(附 JoinQuant 源码)

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

问题描述

请帮我编写一个早盘区间突破(Opening Range Breakout)策略

解决方案

这是一个基于聚宽(JoinQuant)API编写的早盘区间突破策略(Opening Range Breakout, ORB)

策略逻辑说明

  1. 核心思想:利用开盘后的一段时间(例如前30分钟)的市场波动来确定当天的支撑位和阻力位。
  2. 区间定义
    • 上轨(High):开盘前30分钟(09:30 - 10:00)内的最高价。
    • 下轨(Low):开盘前30分钟(09:30 - 10:00)内的最低价。
  3. 交易信号
    • 买入:当价格突破上轨,且当前无持仓时,全仓买入。
    • 止损/卖出:当价格跌破下轨,且当前有持仓时,清仓止损。
  4. 尾盘平仓:无论盈亏,在收盘前(例如 14:55)强制平仓,不持仓过夜(日内策略)。
  5. 标的:示例中使用 000001.XSHE (平安银行),您可以根据需要修改。

策略代码

# -*- coding: utf-8 -*-
# 导入聚宽函数库
import jqdata
import datetime

def initialize(context):
    """
    初始化函数,设定基准、手续费、滑点、全局变量等
    """
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 设置日志级别
    log.set_level('order', 'info')
    
    # 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 定义全局变量
    g.security = '000001.XSHE'  # 操作的标的:平安银行
    g.range_minutes = 30        # 定义早盘区间的时间长度(分钟),这里是30分钟
    
    # 每日区间的高低点变量
    g.range_high = None
    g.range_low = None
    g.range_calculated = False  # 标记今日是否已经计算过区间
    
    # 运行频率:分钟级回测
    # 每天开盘前运行一次
    run_daily(before_market_open, time='before_open', reference_security=g.security)
    # 交易时间内每分钟运行一次
    run_daily(trade_logic, time='every_bar', reference_security=g.security)
    # 收盘后运行一次
    run_daily(after_market_close, time='after_close', reference_security=g.security)

def before_market_open(context):
    """
    开盘前运行函数
    """
    # 重置每日变量
    g.range_high = None
    g.range_low = None
    g.range_calculated = False
    
    log.info("准备开始新的交易日: %s" % context.current_dt.date())

def trade_logic(context):
    """
    主交易逻辑,每分钟执行一次
    """
    # 获取当前时间
    current_dt = context.current_dt
    current_time_str = current_dt.strftime('%H:%M')
    
    # 1. 定义时间窗口
    # 计算区间结束的时间点 (09:30 + g.range_minutes)
    # 简单的处理方式:直接判断分钟数。注意:聚宽的分钟bar时间是该分钟结束的时间。
    # 09:30-10:00 共有30根bar。第30根bar的时间是 10:00。
    
    # 设定区间结束时间字符串 (这里简化处理,假设是10:00)
    range_end_time = '10:00' 
    # 设定尾盘平仓时间
    exit_time = '14:55'

    # 2. 计算区间高低点 (仅在指定时间点计算一次)
    if not g.range_calculated and current_time_str == range_end_time:
        # 获取过去 g.range_minutes 分钟的分钟线数据
        # include_now=True 包含当前这一分钟(10:00)的数据
        bars = get_bars(g.security, count=g.range_minutes, unit='1m', 
                        fields=['high', 'low'], include_now=True)
        
        if bars is not None and len(bars) > 0:
            g.range_high = bars['high'].max()
            g.range_low = bars['low'].min()
            g.range_calculated = True
            log.info("早盘区间确定 (09:30-%s): High=%.2f, Low=%.2f" % (range_end_time, g.range_high, g.range_low))
        else:
            log.warn("获取行情数据失败,无法计算区间")

    # 3. 交易执行逻辑 (在区间确定后,且在收盘平仓前)
    if g.range_calculated and current_time_str > range_end_time and current_time_str < exit_time:
        
        # 获取当前最新价格
        current_data = get_current_data()
        current_price = current_data[g.security].last_price
        
        # 获取当前持仓
        position = context.portfolio.positions[g.security]
        
        # 策略逻辑:
        # 如果价格突破上轨,且无持仓 -> 买入
        if current_price > g.range_high and position.closeable_amount == 0:
            # 全仓买入
            order_value(g.security, context.portfolio.available_cash)
            log.info("价格(%.2f)突破上轨(%.2f),买入 %s" % (current_price, g.range_high, g.security))
            
        # 如果价格跌破下轨,且有持仓 -> 卖出 (止损)
        elif current_price < g.range_low and position.closeable_amount > 0:
            # 清仓
            order_target(g.security, 0)
            log.info("价格(%.2f)跌破下轨(%.2f),止损卖出 %s" % (current_price, g.range_low, g.security))

    # 4. 尾盘强制平仓
    if current_time_str == exit_time:
        position = context.portfolio.positions[g.security]
        if position.closeable_amount > 0:
            order_target(g.security, 0)
            log.info("尾盘(%s)强制平仓" % exit_time)

def after_market_close(context):
    """
    收盘后运行函数
    """
    log.info("交易日结束")
    # 打印当天的账户权益
    log.info("当前总权益: %.2f" % context.portfolio.total_value)

常见问题 Q&A

Q: 这个策略适合什么频率的回测?
A: 必须选择 “分钟” 级回测。因为策略依赖于日内的分钟K线来确定早盘区间(09:30-10:00)以及盘中的实时突破,天级回测无法获取这些细节。

Q: 如何修改早盘区间的时间长度?
A: 修改 initialize 函数中的 g.range_minutes 变量。例如改为 60,则区间变为 09:30 - 10:30。同时需要注意修改 trade_logicrange_end_time 的字符串判断逻辑以匹配新的时间。

Q: 为什么使用 get_bars 而不是 attribute_history
A: get_bars 是聚宽较新的API,支持获取包含当前分钟(include_now=True)的数据,这在计算截止到整点(如10:00)的区间时非常准确。

Q: 策略支持做空吗?
A: 上述代码是针对A股股票设计的,默认只支持做多(Long Only)。如果价格跌破下轨,策略会执行卖出平仓(止损),但不会开空单。如果您交易期货,可以使用 order_target 配合 side='short' 进行修改。