Violate both Formation RBR and DBR  when Price Close below than Distal line: //@version=5
indicator("Demand Zone Detector (RBR and DBR) with Active & Historical Zones", overlay=true)

// ‣ Input Parameters

// Average body period for calculating average body size
avgBodyPeriod = input.int(title="Average Body Period", defval=10, minval=1)

// Big candle factor
bigCandleFactor = input.float(title="Big Candle Factor (Multiplier of Avg Body Size)", defval=1.5, minval=0.1)

// Max number of base candles
maxBaseCandles = input.int(title="Maximum Base Candles", defval=6, minval=1)

// Max base candle body size as percentage of big candle's body
maxBaseBodyPercent = input.float(title="Max Base Candle Body Size (% of Big Candle)", defval=50.0, minval=0.1, maxval=100.0)

// Option for drawing historical zones
drawHistoricalZones = input.bool(title="Draw Historical Demand Zones", defval=true)

// Limit the number of historical demand zones displayed
maxHistoricalZonesToDisplay = input.int(title="Number of Historical Demand Zones to Display", defval=5, minval=1)

// ‣ Calculations

// Calculate average body size
avgBodySize = ta.sma(math.abs(close - open), avgBodyPeriod)

// Define thresholds
bigCandleBodyThreshold = bigCandleFactor * avgBodySize
maxBaseCandleBodySize = (maxBaseBodyPercent / 100.0) * bigCandleBodyThreshold

// Identify candle types
bodySize = math.abs(close - open)
isBigGreenCandle = (close > open) and (bodySize >= bigCandleBodyThreshold)
isBigRedCandle = (close < open) and (bodySize >= bigCandleBodyThreshold)
isSmallBodyCandle = bodySize <= maxBaseCandleBodySize
isGreenCandle = close > open

// ‣ Variables for Patterns

// RBR Pattern Variables
var int rbr_state = 0  // 0: idle, 1: collecting base
var int rbr_baseCount = 0
var float rbr_highestBody = na
var float rbr_lowestLow = na
var int rbr_startIndex = na

// DBR Pattern Variables
var int dbr_state = 0  // 0: idle, 1: collecting base
var int dbr_baseCount = 0
var float dbr_highestBody = na
var float dbr_lowestLow = na
var int dbr_startIndex = na

// Arrays to store demand zones
// Each zone has:
//  - box object
//  - proximal line
//  - distal line
//  - isViolated flag
//  - zone type (1 for RBR, 2 for DBR)
var box[] demandZones = array.new_box()
var float[] proximalLines = array.new_float()
var float[] distalLines = array.new_float()
var bool[] zoneIsViolated = array.new_bool()
var int[] zoneTypes = array.new_int()

// ‣ RBR Pattern Detection

if rbr_state == 0
    if isBigGreenCandle
        // Leg-in detected
        rbr_state := 1
        rbr_baseCount := 0
        rbr_startIndex := bar_index
        rbr_highestBody := na
        rbr_lowestLow := na
else if rbr_state == 1
    if isSmallBodyCandle
        // Base candle detected
        rbr_baseCount += 1
        if rbr_baseCount > maxBaseCandles
            // Exceeded maximum base candles, reset pattern
            rbr_state := 0
            rbr_baseCount := 0
        else
            // Update highest body (proximal line)
            float candleHighBody = math.max(open, close)
            rbr_highestBody := na(rbr_highestBody) ? candleHighBody : math.max(rbr_highestBody, candleHighBody)
            // Update lowest low (distal line)
            rbr_lowestLow := na(rbr_lowestLow) ? low : math.min(rbr_lowestLow, low)
    else if isGreenCandle
        // Leg-out detected
        if rbr_baseCount >= 1
            // Include leg-out candle in distal line
            rbr_lowestLow := math.min(rbr_lowestLow, low)
            // Draw the demand zone
            box demandZone = box.new(left=rbr_startIndex, right=bar_index, top=rbr_highestBody, bottom=rbr_lowestLow,
                                 border_color=color.green, bgcolor=color.new(color.green, 85))
            box.set_extend(demandZone, extend.right)
            // Initialize isViolated flag as false
            bool isViolated = false
            // Store the zone
            array.push(demandZones, demandZone)
            array.push(proximalLines, rbr_highestBody)
            array.push(distalLines, rbr_lowestLow)
            array.push(zoneIsViolated, isViolated)
            array.push(zoneTypes, 1)  // 1 for RBR

            // Limit the number of historical zones displayed
            int historicalZonesCount = 0
            for idx = 0 to array.size(zoneIsViolated) - 1
                if array.get(zoneIsViolated, idx)
                    historicalZonesCount += 1

            // If number of historical zones exceeds the limit, remove the oldest historical zone
            while historicalZonesCount > maxHistoricalZonesToDisplay
                // Find the oldest historical zone (first in the array)
                for idx = 0 to array.size(zoneIsViolated) - 1
                    if array.get(zoneIsViolated, idx)
                        // Remove the oldest historical zone
                        box dzBoxToRemove = array.shift(demandZones)
                        box.delete(dzBoxToRemove)
                        array.shift(proximalLines)
                        array.shift(distalLines)
                        array.shift(zoneIsViolated)
                        array.shift(zoneTypes)
                        historicalZonesCount -= 1
                        break

        // Reset pattern
        rbr_state := 0
        rbr_baseCount := 0
    else
        // Pattern invalidated
        rbr_state := 0
        rbr_baseCount := 0

// ‣ DBR Pattern Detection

if dbr_state == 0
    if isBigRedCandle
        // Leg-in detected
        dbr_state := 1
        dbr_baseCount := 0
        dbr_startIndex := bar_index
        dbr_highestBody := na
        dbr_lowestLow := low  // Include leg-in candle's low
else if dbr_state == 1
    if isSmallBodyCandle
        // Base candle detected
        dbr_baseCount += 1
        if dbr_baseCount > maxBaseCandles
            // Exceeded maximum base candles, reset pattern
            dbr_state := 0
            dbr_baseCount := 0
        else
            // Update highest body (proximal line)
            float candleHighBody = math.max(open, close)
            dbr_highestBody := na(dbr_highestBody) ? candleHighBody : math.max(dbr_highestBody, candleHighBody)
            // Update lowest low (distal line)
            dbr_lowestLow := math.min(dbr_lowestLow, low)
    else if isGreenCandle
        // Leg-out detected
        if dbr_baseCount >= 1
            // Include leg-out candle's low
            dbr_lowestLow := math.min(dbr_lowestLow, low)
            // Draw the demand zone
            box demandZone = box.new(left=dbr_startIndex, right=bar_index, top=dbr_highestBody, bottom=dbr_lowestLow,
                                 border_color=color.blue, bgcolor=color.new(color.blue, 85))
            box.set_extend(demandZone, extend.right)
            // Initialize isViolated flag as false
            bool isViolated = false
            // Store the zone
            array.push(demandZones, demandZone)
            array.push(proximalLines, dbr_highestBody)
            array.push(distalLines, dbr_lowestLow)
            array.push(zoneIsViolated, isViolated)
            array.push(zoneTypes, 2)  // 2 for DBR

            // Limit the number of historical zones displayed
            int historicalZonesCount = 0
            for idx = 0 to array.size(zoneIsViolated) - 1
                if array.get(zoneIsViolated, idx)
                    historicalZonesCount += 1

            // If number of historical zones exceeds the limit, remove the oldest historical zone
            while historicalZonesCount > maxHistoricalZonesToDisplay
                // Find the oldest historical zone (first in the array)
                for idx = 0 to array.size(zoneIsViolated) - 1
                    if array.get(zoneIsViolated, idx)
                        // Remove the oldest historical zone
                        box dzBoxToRemove = array.shift(demandZones)
                        box.delete(dzBoxToRemove)
                        array.shift(proximalLines)
                        array.shift(distalLines)
                        array.shift(zoneIsViolated)
                        array.shift(zoneTypes)
                        historicalZonesCount -= 1
                        break

        // Reset pattern
        dbr_state := 0
        dbr_baseCount := 0
    else
        // Pattern invalidated
        dbr_state := 0
        dbr_baseCount := 0

// ‣ Zone Violation Checking and Management

if array.size(demandZones) > 0
    // Initialize the loop index to last element
    int i = array.size(demandZones) - 1
    while i >= 0
        box dzBox = array.get(demandZones, i)
        float proximalLine = array.get(proximalLines, i)
        float distalLine = array.get(distalLines, i)
        bool viol = array.get(zoneIsViolated, i)
        int zoneType = array.get(zoneTypes, i)

        // Determine violation based on zone type
        bool isViolated = false
        if zoneType == 1  // RBR
            isViolated := close < distalLine
        else if zoneType == 2  // DBR
            isViolated := close > proximalLine

        if not viol and isViolated
            // Zone has been violated now
            if drawHistoricalZones
                // Update the zone's appearance to indicate it's historical
                box.set_border_color(dzBox, color.gray)
                box.set_bgcolor(dzBox, color.new(color.gray, 85))
                // Mark as violated
                array.set(zoneIsViolated, i, true)
            else
                // Delete the violated zone
                box.delete(dzBox)
                // Remove from all arrays
                array.remove(demandZones, i)
                array.remove(proximalLines, i)
                array.remove(distalLines, i)
                array.remove(zoneIsViolated, i)
                array.remove(zoneTypes, i)
        i := i - 1  // Decrement index

// ‣ Plot Markers (Optional)

// RBR Markers
plotshape(rbr_state == 1 and isBigGreenCandle and rbr_baseCount == 0, title="RBR Leg-in", style=shape.triangleup, location=location.belowbar, color=color.lime, size=size.small)
plotshape(rbr_state == 1 and isGreenCandle and rbr_baseCount >= 1 and not isBigGreenCandle, title="RBR Leg-out", style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small)

// DBR Markers
plotshape(dbr_state == 1 and isBigRedCandle and dbr_baseCount == 0, title="DBR Leg-in", style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small)
plotshape(dbr_state == 1 and isGreenCandle and dbr_baseCount >= 1, title="DBR Leg-out", style=shape.triangleup, location=location.belowbar, color=color.blue, size=size.small)