Dynamic Price Range Breakout-Retracement-Reversal Multi-Strategy Trading System

FVG 5M 1M R:R RANGE TRADING SCALPING
Created on: 2025-08-22 09:55:06 Modified on: 2025-08-22 09:55:06
Copy: 0 Number of hits: 332
avatar of ianzeng123 ianzeng123
2
Follow
319
Followers

 Dynamic Price Range Breakout-Retracement-Reversal Multi-Strategy Trading System  Dynamic Price Range Breakout-Retracement-Reversal Multi-Strategy Trading System

Overview

The Dynamic Price Range Breakout-Retracement-Reversal Multi-Strategy Trading System is a day trading strategy designed for short-term traders, based on the price range formed by the first 5-minute candle after market open. This strategy integrates three different entry modes: breakout entry, trap entry, and reversal entry, by identifying Fair Value Gaps (FVG) and price range breakout patterns. The strategy focuses on the high-volatility period during the first hour after the US market opens (9:30-10:30 EST), executing trades on a 1-minute chart, and employing a fixed 2:1 risk-reward ratio for profit and loss management. The strategy is straightforward, requiring no complex indicators or higher timeframe bias, providing day traders with a systematic framework.

Strategy Principles

The core principle of this strategy is built around price behavior patterns after forming an initial range during early market hours, with operations divided into three steps:

  1. Mark the Range (9:30 AM):

    • Wait for the first 5-minute candle (9:30-9:35) to close
    • Mark the high and low of this candle as the trading range
    • Switch to a 1-minute chart for actual trading
  2. Find Entry Points (Trading only for the first hour): The strategy offers three different entry methods:

    • Breakout Entry:

      • Requires a Fair Value Gap (FVG) condition
      • Any candle in the FVG pattern closes outside the range
      • FVG is defined as a three-candle gap pattern (wick-gap)
    • Trap Entry:

      • Price first breaks outside the range boundary
      • Then retests back inside the range
      • Finally closes outside the range again
    • Reversal Entry:

      • After a failed breakout in one direction
      • An opposite FVG appears back into the range
  3. Trade Management:

    • Stop Loss Setting:
      • Break/Trap strategy: Uses the low/high of the first candle that closed outside the range
      • Reversal strategy: Uses the low/high of the first candle in the FVG pattern
    • Take Profit Setting:
      • Always employs a 2:1 risk-reward ratio
      • Risk \(100 to make \)200

The strategy code implements a complete logical framework, including automatic detection of trading ranges, identification of various entry conditions, setting stop-loss and take-profit levels, and calculating appropriate position sizes. The system also includes time filters to ensure trading only within specific periods and allows selective enabling or disabling of different entry strategies.

Strategy Advantages

  1. Clear and Precise Rules: The strategy rules are clear and intuitive, requiring no subjective judgment, reducing the impact of emotions on trading decisions. The conditional logic and state tracking in the code ensure strict rule enforcement.

  2. Flexibility with Multiple Entry Methods: Provides three different entry strategies (breakout, trap, and reversal), allowing traders to adapt to different market environments. This flexibility is implemented through the enableBreak, enableTrap, and enableReversal parameters in the code.

  3. Focus on High-Probability Time Periods: The strategy trades only during the first hour after market open, leveraging the typically higher volatility and liquidity during this period. The code ensures this through the inWindow condition that only executes trades between 9:30 and 10:30.

  4. Strict Risk Management: Fixed 2:1 risk-reward ratio and stop-loss settings based on specific price action provide clear risk control for each trade. The riskPct parameter in the code allows users to adjust the risk percentage for each trade according to their risk preferences.

  5. No Complex Indicators Required: The strategy does not rely on complex technical indicators but is based purely on price action and structure, reducing the risk of overfitting.

  6. Seasonal Avoidance: The code has a built-in holiday blackout period (December 15 to January 15), avoiding times when markets may be unstable or have lower liquidity.

  7. Flexible Position Sizing: The system provides two position management methods based on risk percentage or fixed contract quantity, adapting to different fund management needs.

Strategy Risks

  1. False Breakout Risk: Markets may produce false breakouts, causing price to quickly reverse after a trade is triggered. To mitigate this risk, the strategy incorporates trap and reversal entry modes, but still requires careful monitoring.

  2. Range Width Issues: If the first 5-minute candle after open has a range that is too wide or too narrow, it may affect the strategy’s effectiveness. A too-narrow range may lead to frequent signal triggering, while a too-wide range may result in distant stop-loss points.

  3. Opportunity Cost of Time Limitation: Trading only for one hour may miss favorable opportunities during other times of the day. However, this limitation is also a discipline that prevents overtrading.

  4. Limitations of Fixed Risk-Reward Ratio: While a 2:1 risk-reward ratio provides consistency, it may not be the optimal choice in certain market environments. In strong trending markets, a higher risk-reward ratio might be more suitable.

  5. Market Anomalies During Holidays: Although the strategy avoids trading between December 15 and January 15, market behavior around other holidays may also be anomalous, affecting strategy performance.

  6. Dependence on FVG: The strategy relies on FVG patterns for breakout and reversal entries, but these patterns may not easily form or be identifiable under certain market conditions.

  7. Limitations of Single Timeframe: Relying completely on the 1-minute chart may cause the strategy to overlook important market structures on larger timeframes.

Strategy Optimization Directions

  1. Adaptive Range Width: Consider dynamically adjusting the range width based on market volatility, for example, using wider ranges on days with higher volatility and narrower ranges on days with lower volatility. This can be implemented by calculating the Average True Range (ATR) or similar indicators of recent volatility.

  2. Optimize Time Window: Research optimal trading time windows for different markets rather than fixing it at 9:30-10:30. Some markets may show more obvious range breakout patterns during different periods.

  3. Dynamic Risk-Reward Settings: Dynamically adjust the risk-reward ratio based on market conditions and volatility, for example, increasing targets when trends are strong and reducing targets in ranging markets.

  4. Integrate Market Sentiment Indicators: Consider adding market breadth indicators or volatility indicators as filters to avoid trading when market conditions are unfavorable.

  5. Multi-Timeframe Confirmation: While executing trades still on the 1-minute chart, add confirmation conditions from higher timeframes, such as trend direction consistency checks on 15-minute or 1-hour charts.

  6. Optimize FVG Definition: The current FVG definition is relatively simple; consider more complex or precise imbalance area definitions, such as considering candle bodies rather than just wicks.

  7. Add Volume Confirmation: Adding volume confirmation to entry conditions may improve signal quality, especially for breakout entries.

  8. Adaptive Stop Loss: Dynamically adjusting stop-loss levels based on market volatility may improve the strategy’s adaptability in different market environments.

Summary

The Dynamic Price Range Breakout-Retracement-Reversal Multi-Strategy Trading System is a day trading strategy with clear structure and rules, seeking trading opportunities by identifying the price range formed during early market hours and subsequent breakout, trap, or reversal patterns. The main advantages of this strategy lie in its simplicity and flexibility with multiple entry methods, while strict time constraints and risk management principles help maintain trading discipline.

However, the strategy also faces risks such as false breakouts, inappropriate range widths, and dependence on specific price patterns. By optimizing range setting methods, adjusting time windows, dynamically setting risk-reward ratios, and integrating multi-timeframe analysis, the strategy’s robustness and adaptability can be further enhanced.

Ultimately, this strategy provides a systematic framework for short-term traders, particularly suitable for investors seeking efficient trading during the daily opening period. As with all trading strategies, thorough backtesting and appropriate risk management should be conducted before practical application.

Strategy source code
/*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")