
动态价格区间突破-回撤-反转多策略交易系统是一种专为短线交易者设计的日内交易策略,基于早盘开市后第一个5分钟K线所形成的价格区间进行操作。该策略整合了三种不同的入场模式:突破入场、陷阱入场和反转入场,通过识别公允价值缺口(FVG)和价格区间突破模式进行交易。策略专注于美股市场开盘后第一个小时(9:30-10:30 EST)的高波动性时段,在1分钟图表上执行交易,并采用固定的2:1风险回报比进行止盈止损管理。该策略简洁明了,无需复杂指标或更高时间周期的偏见,为日内短线交易者提供了一个系统化的框架。
该策略的核心原理建立在价格在早盘形成初始区间后的行为模式上,具体操作分为三个步骤:
标记区间(9:30 AM):
寻找入场点(仅交易开盘后一小时): 该策略提供三种不同的入场方式:
突破入场(Break Entry):
陷阱入场(Trap Entry):
反转入场(Reversal Entry):
交易管理:
策略代码实现了完整的逻辑框架,包括自动检测交易区间、识别各种入场条件、设置止损止盈水平以及计算适当的仓位大小。系统还包含了时间过滤器,确保只在特定时间段内交易,并且可以选择性地启用或禁用不同的入场策略。
简洁明确的规则:策略规则清晰直观,无需主观判断,减少了情绪对交易决策的影响。代码中的条件逻辑和状态跟踪确保了规则的严格执行。
多种入场方式的灵活性:提供三种不同的入场策略(突破、陷阱和反转),使交易者能够适应不同的市场环境。代码通过enableBreak、enableTrap和enableReversal参数实现了这种灵活性。
专注于高概率时段:策略仅在开盘后第一个小时内交易,利用了这段时间内通常存在的较高波动性和流动性。代码通过inWindow条件确保只在9:30至10:30之间执行交易。
严格的风险管理:固定的2:1风险回报比和基于具体价格行为的止损设置,为每笔交易提供了清晰的风险控制。代码中的riskPct参数允许用户根据自己的风险偏好调整每笔交易的风险百分比。
无需复杂指标:策略不依赖于复杂的技术指标,而是基于纯粹的价格行为和结构,降低了过度拟合的风险。
季节性规避:代码内置了假期黑名单(12月15日至1月15日),避开市场可能不稳定或流动性较低的时期。
灵活的仓位管理:系统提供了基于风险百分比或固定合约数量的两种仓位管理方式,适应不同的资金管理需求。
假突破风险:市场可能产生假突破,导致交易触发后价格迅速反转。为了缓解这一风险,策略整合了陷阱和反转入场模式,但仍需谨慎监控。
区间宽度问题:如果开盘后第一个5分钟K线区间过宽或过窄,可能会影响策略的有效性。过窄的区间可能导致频繁触发信号,而过宽的区间可能导致止损点过远。
时间限制的机会成本:仅在一小时内交易可能会错过日内其他时段的有利机会。然而,这种限制也是一种纪律,防止过度交易。
固定风险回报比的局限性:虽然2:1的风险回报比提供了一致性,但在某些市场环境下可能不是最优选择。在强趋势市场中,更高的风险回报比可能更为适合。
假日期间的市场异常:尽管策略避开了12月15日至1月15日期间交易,但其他假日前后的市场行为也可能异常,影响策略表现。
对FVG的依赖:策略在突破和反转入场中依赖于FVG模式,但在某些市场条件下,FVG可能不容易形成或识别。
单一时间框架的局限性:完全依赖于1分钟图表可能使策略忽略更大时间框架的重要市场结构。
自适应区间宽度:可以考虑根据市场波动性动态调整区间宽度,例如在波动性较高的日子使用较宽的区间,在波动性较低的日子使用较窄的区间。这可以通过计算近期的平均真实波动范围(ATR)或类似指标来实现。
优化时间窗口:可以研究不同市场的最优交易时间窗口,而不是固定在9:30-10:30。某些市场可能在不同时段展现更明显的区间突破模式。
动态风险回报设置:可以根据市场状况和波动性动态调整风险回报比,例如在趋势强劲时增加目标,在盘整市场中减小目标。
整合市场情绪指标:可以考虑加入市场广度指标或波动性指标作为过滤器,在市场环境不利时避免交易。
多时间框架确认:虽然执行交易仍在1分钟图表上,但可以增加更高时间框架的确认条件,如15分钟或1小时图表上的趋势方向一致性检查。
优化FVG定义:当前的FVG定义相对简单,可以考虑更复杂或更精确的不平衡区域定义,如考虑蜡烛体而不仅是影线。
添加交易量确认:在入场条件中加入交易量确认可能会提高信号质量,尤其是对于突破入场。
自适应止损:根据市场波动性动态调整止损水平,可能会提高策略在不同市场环境下的适应性。
动态价格区间突破-回撤-反转多策略交易系统是一个结构清晰、规则明确的日内交易策略,通过识别早盘形成的价格区间和随后的突破、陷阱或反转模式来寻找交易机会。该策略的主要优势在于其简洁性和多种入场方式的灵活性,同时严格的时间限制和风险管理原则有助于维持交易纪律。
然而,该策略也面临着假突破、区间宽度不合适和对特定价格模式依赖等风险。通过优化区间设定方法、调整时间窗口、动态设置风险回报比和整合多时间框架分析等方式,可以进一步提高策略的稳健性和适应性。
最终,这一策略为短线交易者提供了一个系统化的框架,特别适合那些寻求在每日开盘时段进行高效交易的投资者。与所有交易策略一样,在实际应用前应进行充分的回测和适当的风险管理。
/*backtest
start: 2025-07-22 00:00:00
end: 2025-08-21 00:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Binance","currency":"ETH_USDT","balance":500000}]
*/
//@version=5
strategy("Three-Step 9:30 Range Scalping (Backtest)", overlay=true, calc_on_every_tick=false, process_orders_on_close=true,
initial_capital=100000, default_qty_type=strategy.percent_of_equity, default_qty_value=100, pyramiding=0)
// -------------------- Inputs
enableBreak = input.bool(true, "Enable Break Entry")
enableTrap = input.bool(false, "Enable Trap Entry")
enableReversal = input.bool(true, "Enable Reversal Entry")
rr = input.float(2.0, "Take-Profit R Multiple", step=0.25, minval=0.25)
oneTradePerDay = input.bool(false, "One Trade Per Day")
showRange = input.bool(true, "Show 9:30 5m Range")
// Risk management
riskPct = input.float(1.0, "Risk % of Equity per Trade", step=0.1, minval=0.1, maxval=100.0)
sizeMode = input.string("Risk %", "Position Sizing Mode", options=["Risk %", "Fixed contracts"])
fixedContracts = input.int(1, "Fixed Contracts", minval=1)
// Optional: warn if not on 1-minute chart (execution timeframe per PRD)
onOneMinute = timeframe.isminutes and timeframe.multiplier == 1
// -------------------- Time helpers (chart is assumed New York time)
newDay = ta.change(time("D")) != 0
// Trade the first hour only: 9:30:00 to 10:29:59
inWindow = (hour == 9 and minute >= 30) or (hour == 10 and minute <= 29)
// Holiday blackout window: Do not trade Dec 15 – Jan 15
inBlackout = (month == 12 and dayofmonth >= 15) or (month == 1 and dayofmonth <= 15)
// -------------------- First 5-min range (use the actual 9:30 5m candle via security())
var float rangeHi = na
var float rangeLo = na
var bool haveRange = false
// -------------------- State for entries
var bool breakUpFound = false
var bool breakDownFound = false
var float initBreakUpLow = na // for Break/Trap long SL
var float initBreakDownHigh = na // for Break/Trap short SL
var bool trapUpRetestedInside = false
var bool trapDownRetestedInside = false
var bool tradedToday = false
// Reset daily state at midnight (chart timezone)
if newDay
rangeHi := na
rangeLo := na
haveRange := false
breakUpFound := false
breakDownFound := false
initBreakUpLow := na
initBreakDownHigh := na
trapUpRetestedInside := false
trapDownRetestedInside := false
tradedToday := false
// Pull the 5-minute bar that STARTS at 9:30 (value available on its close at 9:35)
sess0930Hi = request.security(syminfo.tickerid, "5", (hour == 9 and minute == 30) ? high : na, barmerge.gaps_off, barmerge.lookahead_off)
sess0930Lo = request.security(syminfo.tickerid, "5", (hour == 9 and minute == 30) ? low : na, barmerge.gaps_off, barmerge.lookahead_off)
// Lock the range when the 9:30 5m candle closes (value appears non-na exactly then)
if not haveRange and not na(sess0930Hi) and not na(sess0930Lo)
rangeHi := sess0930Hi
rangeLo := sess0930Lo
haveRange := true
// reset session-specific flags at start of trading window
breakUpFound := false
breakDownFound := false
initBreakUpLow := na
initBreakDownHigh := na
trapUpRetestedInside := false
trapDownRetestedInside := false
tradedToday := false
// -------------------- Visuals
plot(showRange and haveRange ? rangeHi : na, "Range High", color=color.new(color.teal, 0), style=plot.style_linebr, linewidth=2)
plot(showRange and haveRange ? rangeLo : na, "Range Low", color=color.new(color.orange, 0), style=plot.style_linebr, linewidth=2)
plotchar(not onOneMinute, title="Use 1-minute chart", char="⚠", location=location.top, color=color.new(color.red, 0), size=size.tiny)
plotchar(inBlackout, title="Holiday blackout (Dec 15–Jan 15)", char="⛔", location=location.top, color=color.new(color.red, 0), size=size.tiny)
// -------------------- Convenience conditions
closeAbove = haveRange and close > rangeHi
closeBelow = haveRange and close < rangeLo
closeInside = haveRange and close <= rangeHi and close >= rangeLo
// Track first body-close outside the range in each direction (initial break-close)
if haveRange and inWindow and not tradedToday
if not breakUpFound and closeAbove
breakUpFound := true
initBreakUpLow := low
trapUpRetestedInside := false
if not breakDownFound and closeBelow
breakDownFound := true
initBreakDownHigh := high
trapDownRetestedInside := false
// Trap retest flags (retest back inside after first break)
if haveRange and inWindow and not tradedToday
if breakUpFound and not trapUpRetestedInside and closeInside
trapUpRetestedInside := true
if breakDownFound and not trapDownRetestedInside and closeInside
trapDownRetestedInside := true
// -------------------- FVG detectors (three-candle imbalance)
// Simple wick-gap definition, preserved through the third candle
// Bullish: gap exists if Candle C low > Candle A high AND Candle B low > Candle A high
// Bearish: gap exists if Candle C high < Candle A low AND Candle B high < Candle A low
bullFVG = not na(high[2]) and (low[1] > high[2]) and (low > high[2])
bearFVG = not na(low[2]) and (high[1] < low[2]) and (high < low[2])
// -------------------- Entry gating
allowEntry = haveRange and inWindow and not inBlackout and strategy.position_size == 0 and (not oneTradePerDay or not tradedToday)
// Calculate contracts based on selected sizing mode
calcOrderQty(entryPrice, stopPrice) =>
qty = 0
if sizeMode == "Fixed contracts"
qty := fixedContracts
else
riskCash = strategy.equity * riskPct / 100.0
riskPerContract = math.abs(entryPrice - stopPrice) * syminfo.pointvalue
qty := riskPerContract > 0 ? math.floor(riskCash / riskPerContract) : 0
qty
// -------------------- BREAK Entries (needs FVG and ANY of the 3 bars closes outside)
if enableBreak and allowEntry
// Long BREAK
breakLongOk = bullFVG and (close > rangeHi or close[1] > rangeHi or close[2] > rangeHi)
if breakLongOk
// Stop at the FIRST candle that closed outside among the 3 FVG candles
float stopL = na
stopL := close[2] > rangeHi ? low[2] : stopL
stopL := na(stopL) and close[1] > rangeHi ? low[1] : stopL
stopL := na(stopL) and close > rangeHi ? low : stopL
entryL = close
if entryL > stopL
tpL = entryL + rr * (entryL - stopL)
qtyL = calcOrderQty(entryL, stopL)
if qtyL > 0
strategy.entry("LONG_BREAK", strategy.long, qty=qtyL)
strategy.exit("LONG_BREAK_TP/SL", from_entry="LONG_BREAK", stop=stopL, limit=tpL)
tradedToday := oneTradePerDay ? true : tradedToday
// Short BREAK
breakShortOk = bearFVG and (close < rangeLo or close[1] < rangeLo or close[2] < rangeLo)
if breakShortOk
// Stop at the FIRST candle that closed outside among the 3 FVG candles
float stopS = na
stopS := close[2] < rangeLo ? high[2] : stopS
stopS := na(stopS) and close[1] < rangeLo ? high[1] : stopS
stopS := na(stopS) and close < rangeLo ? high : stopS
entryS = close
if entryS < stopS
tpS = entryS - rr * (stopS - entryS)
qtyS = calcOrderQty(entryS, stopS)
if qtyS > 0
strategy.entry("SHORT_BREAK", strategy.short, qty=qtyS)
strategy.exit("SHORT_BREAK_TP/SL", from_entry="SHORT_BREAK", stop=stopS, limit=tpS)
tradedToday := oneTradePerDay ? true : tradedToday
// -------------------- TRAP Entries (Break → Retest inside → Reclose outside; FVG not required)
if enableTrap and allowEntry
// Long TRAP
if breakUpFound and trapUpRetestedInside and closeAbove
stopL = na(initBreakUpLow) ? low : initBreakUpLow
entryL = close
if entryL > stopL
tpL = entryL + rr * (entryL - stopL)
qtyL = calcOrderQty(entryL, stopL)
if qtyL > 0
strategy.entry("LONG_TRAP", strategy.long, qty=qtyL)
strategy.exit("LONG_TRAP_TP/SL", from_entry="LONG_TRAP", stop=stopL, limit=tpL)
tradedToday := oneTradePerDay ? true : tradedToday
// Short TRAP
if breakDownFound and trapDownRetestedInside and closeBelow
stopS = na(initBreakDownHigh) ? high : initBreakDownHigh
entryS = close
if entryS < stopS
tpS = entryS - rr * (stopS - entryS)
qtyS = calcOrderQty(entryS, stopS)
if qtyS > 0
strategy.entry("SHORT_TRAP", strategy.short, qty=qtyS)
strategy.exit("SHORT_TRAP_TP/SL", from_entry="SHORT_TRAP", stop=stopS, limit=tpS)
tradedToday := oneTradePerDay ? true : tradedToday
// -------------------- REVERSAL Entries (Failed break + opposite FVG back into range)
if enableReversal and allowEntry
// After bearish break, bullish FVG back into range → LONG
if breakDownFound and bullFVG and closeInside
stopL = low[2] // first candle of the FVG
entryL = close
if entryL > stopL
tpL = entryL + rr * (entryL - stopL)
qtyL = calcOrderQty(entryL, stopL)
if qtyL > 0
strategy.entry("LONG_REV", strategy.long, qty=qtyL)
strategy.exit("LONG_REV_TP/SL", from_entry="LONG_REV", stop=stopL, limit=tpL)
tradedToday := oneTradePerDay ? true : tradedToday
// After bullish break, bearish FVG back into range → SHORT
if breakUpFound and bearFVG and closeInside
stopS = high[2] // first candle of the FVG
entryS = close
if entryS < stopS
tpS = entryS - rr * (stopS - entryS)
qtyS = calcOrderQty(entryS, stopS)
if qtyS > 0
strategy.entry("SHORT_REV", strategy.short, qty=qtyS)
strategy.exit("SHORT_REV_TP/SL", from_entry="SHORT_REV", stop=stopS, limit=tpS)
tradedToday := oneTradePerDay ? true : tradedToday
// -------------------- Markers
plotshape(enableBreak and (bullFVG and (close > rangeHi or close[1] > rangeHi or close[2] > rangeHi)) and allowEntry, title="Break Long", style=shape.triangleup, color=color.new(color.teal, 0), size=size.tiny, location=location.belowbar, text="Break")
plotshape(enableBreak and (bearFVG and (close < rangeLo or close[1] < rangeLo or close[2] < rangeLo)) and allowEntry, title="Break Short", style=shape.triangledown, color=color.new(color.orange, 0), size=size.tiny, location=location.abovebar, text="Break")
plotshape(enableTrap and breakUpFound and trapUpRetestedInside and closeAbove and allowEntry, title="Trap Long", style=shape.circle, color=color.new(color.teal, 0), size=size.tiny, location=location.belowbar, text="Trap")
plotshape(enableTrap and breakDownFound and trapDownRetestedInside and closeBelow and allowEntry, title="Trap Short", style=shape.circle, color=color.new(color.orange, 0), size=size.tiny, location=location.abovebar, text="Trap")
plotshape(enableReversal and breakDownFound and bullFVG and closeInside and allowEntry, title="Reversal Long", style=shape.diamond, color=color.new(color.teal, 0), size=size.tiny, location=location.belowbar, text="Rev")
plotshape(enableReversal and breakUpFound and bearFVG and closeInside and allowEntry, title="Reversal Short", style=shape.diamond, color=color.new(color.orange, 0), size=size.tiny, location=location.abovebar, text="Rev")