Rubber Band Liquidity Signals [LuxAlgo]

Pine Script v6
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © LuxAlgo

//@version=6
indicator("Rubber Band Liquidity Signals [LuxAlgo]", overlay = true, max_boxes_count = 500, max_lines_count = 500, max_labels_count = 500)

// --- Inputs ---
var grp_trend = "Trend & Range Settings"
trendLookback = input.int(20, "Trend Lookback", group = grp_trend)
trendAtrMult  = input.float(2.0, "Trend Move ATR Multiplier", group = grp_trend, tooltip = "Minimum move size to be considered a strong trend")
rangeLength   = input.int(10, "Consolidation Length", group = grp_trend)
rangeMaxAtr   = input.float(3.5, "Range Max ATR Multiplier", group = grp_trend, tooltip = "Maximum width of the consolidation range")
maxRangeBars  = input.int(50, "Max Range Duration", group = grp_trend)

var grp_style = "Style"
bullColor     = input.color(#089981, "Bullish Color", group = grp_style)
bearColor     = input.color(#f23645, "Bearish Color", group = grp_style)

var grp_alerts = "Alerts"
alertSweep    = input.bool(true, "Alert on Sweep", group = grp_alerts)
alertBreakout = input.bool(true, "Alert on Breakout (B)", group = grp_alerts)

// --- Calculations ---
atr = ta.atr(14)

// 1. Identify Strong Trend
// Check if the price moved significantly over the trendLookback period
moveSize = close - close[trendLookback]
isBullTrend = moveSize > (trendAtrMult * atr[trendLookback])
isBearTrend = moveSize < -(trendAtrMult * atr[trendLookback])

// 2. Identify Range
// Calculate the highest and lowest points over the consolidation period
rangeHigh = ta.highest(high, rangeLength)
rangeLow  = ta.lowest(low, rangeLength)
rangeWidth = rangeHigh - rangeLow

isConsolidating = rangeWidth < (rangeMaxAtr * atr)

// State tracking
var int state = 0 // 0 = searching, 1 = range formed (bull), -1 = range formed (bear), 2 = sweep bull, -2 = sweep bear
var float activeRangeHigh = na
var float activeRangeLow = na
var int rangeStartBar = na
var int sweepBarIndex = na
var box currentBox = na

// Only start looking for a range if we are in state 0
// We look back to see if there was a trend leading up to this consolidation
bullRangeCondition = isBullTrend[rangeLength] and isConsolidating
bearRangeCondition = isBearTrend[rangeLength] and isConsolidating

if state == 0
    if bullRangeCondition
        state := 1
        activeRangeHigh := rangeHigh
        activeRangeLow := rangeLow
        rangeStartBar := bar_index - rangeLength
        currentBox := box.new(rangeStartBar, activeRangeHigh, bar_index, activeRangeLow, border_color = color.new(bullColor, 50), bgcolor = color.new(bullColor, 90))
    else if bearRangeCondition
        state := -1
        activeRangeHigh := rangeHigh
        activeRangeLow := rangeLow
        rangeStartBar := bar_index - rangeLength
        currentBox := box.new(rangeStartBar, activeRangeHigh, bar_index, activeRangeLow, border_color = color.new(bearColor, 50), bgcolor = color.new(bearColor, 90))

// Handle states once a range is formed
if state == 1 // Bullish Trend -> Range -> Waiting for Sweep (Liquidity Grab below range low)
    if not na(currentBox)
        box.set_right(currentBox, bar_index)
    
    // Check for liquidity grab (sweep below range low)
    if low < activeRangeLow and close > activeRangeLow
        state := 2
        sweepBarIndex := bar_index
        // Draw sweep indicator
        line.new(bar_index, low, bar_index, activeRangeLow, color = bullColor, style = line.style_solid, width = 2)
        label.new(bar_index, low, "Sweep", style = label.style_label_up, color = color.new(bullColor, 100), textcolor = bullColor, size = size.small)
        
        if alertSweep
            alert("Bullish Sweep Detected", alert.freq_once_per_bar_close)
    
    // Invalidation (Price breaks out without a sweep)
    else if close < activeRangeLow or close > activeRangeHigh
        state := 0
        if not na(currentBox)
            currentBox.delete()

else if state == -1 // Bearish Trend -> Range -> Waiting for Sweep (Liquidity Grab above range high)
    if not na(currentBox)
        box.set_right(currentBox, bar_index)
    
    // Check for liquidity grab (sweep above range high)
    if high > activeRangeHigh and close < activeRangeHigh
        state := -2
        sweepBarIndex := bar_index
        // Draw sweep indicator
        line.new(bar_index, activeRangeHigh, bar_index, high, color = bearColor, style = line.style_solid, width = 2)
        label.new(bar_index, high, "Sweep", style = label.style_label_down, color = color.new(bearColor, 100), textcolor = bearColor, size = size.small)
        
        if alertSweep
            alert("Bearish Sweep Detected", alert.freq_once_per_bar_close)
        
    // Invalidation (Price breaks out without a sweep)
    else if close > activeRangeHigh or close < activeRangeLow
        state := 0
        if not na(currentBox)
            currentBox.delete()

else if state == 2 // Swept low, waiting for continuation (breakout above range high)
    if not na(currentBox)
        box.set_right(currentBox, bar_index)
        
    if close > activeRangeHigh
        // Continuation signaled
        state := 0
        label.new(bar_index, high, "B", style = label.style_label_down, color = bullColor, textcolor = color.white, size = size.small)
        // Draw the V shape
        line.new(rangeStartBar, activeRangeHigh, sweepBarIndex, activeRangeLow, color = color.yellow, width = 2)
        line.new(sweepBarIndex, activeRangeLow, bar_index, activeRangeHigh, color = color.yellow, width = 2)
        
        if alertBreakout
            alert("Bullish Breakout (B)", alert.freq_once_per_bar_close)
            
    else if close < activeRangeLow
        // Invalidation (Fails after sweep)
        state := 0

else if state == -2 // Swept high, waiting for continuation (breakout below range low)
    if not na(currentBox)
        box.set_right(currentBox, bar_index)
        
    if close < activeRangeLow
        // Continuation signaled
        state := 0
        label.new(bar_index, low, "B", style = label.style_label_up, color = bearColor, textcolor = color.white, size = size.small)
        // Draw the inverted V shape
        line.new(rangeStartBar, activeRangeLow, sweepBarIndex, activeRangeHigh, color = color.yellow, width = 2)
        line.new(sweepBarIndex, activeRangeHigh, bar_index, activeRangeLow, color = color.yellow, width = 2)
        
        if alertBreakout
            alert("Bearish Breakout (B)", alert.freq_once_per_bar_close)
            
    else if close > activeRangeHigh
        // Invalidation (Fails after sweep)
        state := 0

// Force states to reset if range gets too long
if state != 0 and (bar_index - rangeStartBar > maxRangeBars)
    // If it never reached a sweep or continuation within the time limit, remove the box if it hasn't swept yet
    if state == 1 or state == -1
        if not na(currentBox)
            currentBox.delete()
    state := 0


Leave a Comment

Your email address will not be published. Required fields are marked *