动态价格区间突破-回撤-反转多策略交易系统

FVG 5M 1M R:R RANGE TRADING SCALPING
创建日期: 2025-08-22 09:55:06 最后修改: 2025-08-22 09:55:06
复制: 0 点击次数: 332
avatar of ianzeng123 ianzeng123
2
关注
319
关注者

动态价格区间突破-回撤-反转多策略交易系统 动态价格区间突破-回撤-反转多策略交易系统

概述

动态价格区间突破-回撤-反转多策略交易系统是一种专为短线交易者设计的日内交易策略,基于早盘开市后第一个5分钟K线所形成的价格区间进行操作。该策略整合了三种不同的入场模式:突破入场、陷阱入场和反转入场,通过识别公允价值缺口(FVG)和价格区间突破模式进行交易。策略专注于美股市场开盘后第一个小时(9:30-10:30 EST)的高波动性时段,在1分钟图表上执行交易,并采用固定的2:1风险回报比进行止盈止损管理。该策略简洁明了,无需复杂指标或更高时间周期的偏见,为日内短线交易者提供了一个系统化的框架。

策略原理

该策略的核心原理建立在价格在早盘形成初始区间后的行为模式上,具体操作分为三个步骤:

  1. 标记区间(9:30 AM):

    • 等待开盘后第一个5分钟K线(9:30-9:35)收盘
    • 标记该K线的最高点和最低点作为交易区间
    • 切换到1分钟图表进行实际交易
  2. 寻找入场点(仅交易开盘后一小时): 该策略提供三种不同的入场方式:

    • 突破入场(Break Entry):

      • 需要满足公允价值缺口(FVG)条件
      • FVG中的任意一根K线收盘价突破区间
      • FVG定义为三根K线形成的跳空模式(wick-gap)
    • 陷阱入场(Trap Entry):

      • 价格首先突破区间边界
      • 然后回测至区间内
      • 最后再次收盘于区间外
    • 反转入场(Reversal Entry):

      • 价格在一个方向的突破失败后
      • 出现朝向相反方向的FVG回到区间内
  3. 交易管理:

    • 止损设置:
      • 突破/陷阱策略:使用第一根收盘于区间外的K线的最低点/最高点
      • 反转策略:使用FVG模式中第一根K线的最低点/最高点
    • 止盈设置:
      • 始终采用2:1的风险回报比
      • 风险\(100获利\)200

策略代码实现了完整的逻辑框架,包括自动检测交易区间、识别各种入场条件、设置止损止盈水平以及计算适当的仓位大小。系统还包含了时间过滤器,确保只在特定时间段内交易,并且可以选择性地启用或禁用不同的入场策略。

策略优势

  1. 简洁明确的规则:策略规则清晰直观,无需主观判断,减少了情绪对交易决策的影响。代码中的条件逻辑和状态跟踪确保了规则的严格执行。

  2. 多种入场方式的灵活性:提供三种不同的入场策略(突破、陷阱和反转),使交易者能够适应不同的市场环境。代码通过enableBreakenableTrapenableReversal参数实现了这种灵活性。

  3. 专注于高概率时段:策略仅在开盘后第一个小时内交易,利用了这段时间内通常存在的较高波动性和流动性。代码通过inWindow条件确保只在9:30至10:30之间执行交易。

  4. 严格的风险管理:固定的2:1风险回报比和基于具体价格行为的止损设置,为每笔交易提供了清晰的风险控制。代码中的riskPct参数允许用户根据自己的风险偏好调整每笔交易的风险百分比。

  5. 无需复杂指标:策略不依赖于复杂的技术指标,而是基于纯粹的价格行为和结构,降低了过度拟合的风险。

  6. 季节性规避:代码内置了假期黑名单(12月15日至1月15日),避开市场可能不稳定或流动性较低的时期。

  7. 灵活的仓位管理:系统提供了基于风险百分比或固定合约数量的两种仓位管理方式,适应不同的资金管理需求。

策略风险

  1. 假突破风险:市场可能产生假突破,导致交易触发后价格迅速反转。为了缓解这一风险,策略整合了陷阱和反转入场模式,但仍需谨慎监控。

  2. 区间宽度问题:如果开盘后第一个5分钟K线区间过宽或过窄,可能会影响策略的有效性。过窄的区间可能导致频繁触发信号,而过宽的区间可能导致止损点过远。

  3. 时间限制的机会成本:仅在一小时内交易可能会错过日内其他时段的有利机会。然而,这种限制也是一种纪律,防止过度交易。

  4. 固定风险回报比的局限性:虽然2:1的风险回报比提供了一致性,但在某些市场环境下可能不是最优选择。在强趋势市场中,更高的风险回报比可能更为适合。

  5. 假日期间的市场异常:尽管策略避开了12月15日至1月15日期间交易,但其他假日前后的市场行为也可能异常,影响策略表现。

  6. 对FVG的依赖:策略在突破和反转入场中依赖于FVG模式,但在某些市场条件下,FVG可能不容易形成或识别。

  7. 单一时间框架的局限性:完全依赖于1分钟图表可能使策略忽略更大时间框架的重要市场结构。

策略优化方向

  1. 自适应区间宽度:可以考虑根据市场波动性动态调整区间宽度,例如在波动性较高的日子使用较宽的区间,在波动性较低的日子使用较窄的区间。这可以通过计算近期的平均真实波动范围(ATR)或类似指标来实现。

  2. 优化时间窗口:可以研究不同市场的最优交易时间窗口,而不是固定在9:30-10:30。某些市场可能在不同时段展现更明显的区间突破模式。

  3. 动态风险回报设置:可以根据市场状况和波动性动态调整风险回报比,例如在趋势强劲时增加目标,在盘整市场中减小目标。

  4. 整合市场情绪指标:可以考虑加入市场广度指标或波动性指标作为过滤器,在市场环境不利时避免交易。

  5. 多时间框架确认:虽然执行交易仍在1分钟图表上,但可以增加更高时间框架的确认条件,如15分钟或1小时图表上的趋势方向一致性检查。

  6. 优化FVG定义:当前的FVG定义相对简单,可以考虑更复杂或更精确的不平衡区域定义,如考虑蜡烛体而不仅是影线。

  7. 添加交易量确认:在入场条件中加入交易量确认可能会提高信号质量,尤其是对于突破入场。

  8. 自适应止损:根据市场波动性动态调整止损水平,可能会提高策略在不同市场环境下的适应性。

总结

动态价格区间突破-回撤-反转多策略交易系统是一个结构清晰、规则明确的日内交易策略,通过识别早盘形成的价格区间和随后的突破、陷阱或反转模式来寻找交易机会。该策略的主要优势在于其简洁性和多种入场方式的灵活性,同时严格的时间限制和风险管理原则有助于维持交易纪律。

然而,该策略也面临着假突破、区间宽度不合适和对特定价格模式依赖等风险。通过优化区间设定方法、调整时间窗口、动态设置风险回报比和整合多时间框架分析等方式,可以进一步提高策略的稳健性和适应性。

最终,这一策略为短线交易者提供了一个系统化的框架,特别适合那些寻求在每日开盘时段进行高效交易的投资者。与所有交易策略一样,在实际应用前应进行充分的回测和适当的风险管理。

策略源码
/*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")
相关推荐