问题描述
解决方案
这是一个基于**开盘区间突破(Opening Range Breakout, ORB)**的日内交易策略。
策略逻辑说明
- 确定区间:记录开盘后前30分钟(09:30 - 10:00)内的最高价和最低价,作为当日的“波动区间”。
- 突破买入:当时间超过10:00后,如果价格突破区间最高价,则视为多头信号,全仓买入。
- 止损/反转:如果持仓期间价格跌破区间最低价,则视为趋势反转或假突破,立即平仓止损。
- 尾盘平仓:由于是日内策略,无论盈亏,在收盘前(例如 14:55)强制平仓,不持仓过夜。
注意事项(重要)
- T+1 限制:中国A股市场实行 T+1 交易制度(当日买入的股票当日不能卖出)。因此,此策略在普通股票实盘中无法实现当日买入当日卖出。
- 适用场景:
- 期货/期权:支持 T+0。
- ETF/LOF 基金:部分支持 T+0。
- 底仓做 T:如果您账户里已有该股票的底仓,可以通过买入和卖出底仓来实现变相的 T+0。
- 回测研究:用于验证策略有效性。
以下代码以股票/ETF为例编写,假设环境支持 T+0 或用于回测研究。
策略代码
def initialize(context):
"""
初始化函数,设置股票池和全局变量
"""
# 设置要操作的标的,这里以 510300.SS (沪深300ETF) 为例
# 如果是股票,请注意 T+1 限制
g.security = '510300.SS'
set_universe(g.security)
# 策略参数设置
g.setup_end_time = '10:00' # 区间建立结束时间
g.exit_time = '14:55' # 尾盘强制平仓时间
# 设置回测频率为分钟级,因为需要判断分钟K线突破
# 注意:在回测界面请选择“分钟”频率
def before_trading_start(context, data):
"""
盘前处理,每天开盘前重置当日变量
"""
# 初始化当日的最高价和最低价
g.day_high = 0
g.day_low = 99999
# 标记是否已经记录完区间
g.range_recorded = False
# 标记当日是否已经开仓,避免重复开仓
g.has_traded = False
def handle_data(context, data):
"""
盘中运行函数,每分钟执行一次
"""
# 获取当前时间字符串,格式如 '09:31', '14:00'
current_time = context.blotter.current_dt.strftime('%H:%M')
# 获取标的当前价格
security = g.security
# 检查标的是否停牌或数据不存在
if security not in data:
return
current_price = data[security]['close']
# -------------------------------------------------------
# 第一阶段:09:30 - 10:00 建立开盘区间
# -------------------------------------------------------
if current_time <= g.setup_end_time:
# 更新区间最高价
if current_price > g.day_high:
g.day_high = current_price
# 更新区间最低价
if current_price < g.day_low:
g.day_low = current_price
# 如果正好是 10:00,打印一下确定的区间
if current_time == g.setup_end_time:
g.range_recorded = True
log.info("区间建立完成: High=%.2f, Low=%.2f" % (g.day_high, g.day_low))
# -------------------------------------------------------
# 第二阶段:10:00 - 14:55 交易判断
# -------------------------------------------------------
elif current_time < g.exit_time:
# 确保区间已建立
if not g.range_recorded:
return
# 获取当前持仓数量
position = get_position(security).amount
# 1. 开仓逻辑:价格突破区间高点,且当前无持仓,且当日未交易过
# (g.has_traded 限制每天只做一次突破,防止震荡市反复止损)
if current_price > g.day_high and position == 0 and not g.has_traded:
# 获取可用资金
cash = context.portfolio.cash
if cash > 0:
# 全仓买入
order_value(security, cash)
g.has_traded = True
log.info("向上突破买入: Price=%.2f > RangeHigh=%.2f" % (current_price, g.day_high))
# 2. 止损逻辑:价格跌破区间低点,且当前有持仓
elif current_price < g.day_low and position > 0:
# 卖出所有持仓
order_target(security, 0)
log.info("跌破区间止损: Price=%.2f < RangeLow=%.2f" % (current_price, g.day_low))
# -------------------------------------------------------
# 第三阶段:14:55 尾盘强制平仓
# -------------------------------------------------------
elif current_time >= g.exit_time:
position = get_position(security).amount
if position > 0:
order_target(security, 0)
log.info("尾盘强制平仓: Time=%s" % current_time)
代码关键点解析
-
时间控制:
- 使用
context.blotter.current_dt.strftime('%H:%M')获取当前分钟的时间字符串,用于区分“区间建立期”、“交易期”和“尾盘平仓期”。
- 使用
-
区间记录:
- 在
09:30到10:00之间,不断比较current_price与g.day_high/g.day_low,从而动态锁定前30分钟的极值。
- 在
-
交易信号:
- 买入:
current_price > g.day_high。这里加了not g.has_traded限制,意味着每天只博弈一次突破,避免在震荡行情中反复开仓止损造成巨额亏损。 - 止损:
current_price < g.day_low。如果突破失败掉头向下,跌破了起涨区间的下沿,立即离场。
- 买入:
-
资金管理:
- 使用
order_value(security, cash)进行全仓买入。 - 使用
order_target(security, 0)进行清仓卖出。
- 使用
Q&A
Q1: 如果我想做期货(可以做空),代码怎么改?
A1: 期货需要使用 buy_open (多开), sell_close (多平), sell_open (空开), buy_close (空平)。
逻辑上:
- 价格 <
g.day_low:sell_open(做空)。 - 做空止损:价格 >
g.day_high时平空。
Q2: 为什么回测时没有成交?
A2:
- 请检查回测频率是否设置为**“分钟”**级别。日线级别无法获取 10:00 的价格。
- 请检查标的是否在 10:00 之后真的突破了前30分钟的高点。
- 如果是股票回测,请确认资金是否足够买入一手(100股)。
Q3: 如何修改区间时间?
A3: 修改 initialize 函数中的 g.setup_end_time 变量,例如改为 '09:45' 即为前15分钟突破策略。