//@version=5
indicator("Demand Zone Detector with Adjusted Proximal and Distal Lines", overlay=true)

// User Inputs
base_candles = input.int(2, "Number of Base Candles", minval=1, maxval=6)
small_range_multiplier = input.float(0.5, "Small Range Multiplier", minval=0.1, step=0.1)
atr_length = input.int(14, "ATR Length", minval=1)

// Calculations
avg_range = ta.sma(ta.tr, atr_length)

// Arrays to store zones and associated data
var box[] demand_zones = array.new<box>()
var line[] proximal_lines = array.new<line>()
var line[] distal_lines = array.new<line>()
var float[] proximal_prices = array.new_float()
var float[] distal_prices = array.new_float()

// Function to check for violation
func f_check_violation(index)
    distal_price = array.get(distal_prices, index)
    // For Demand Zones, violation occurs when price closes below the distal line (lower boundary)
    if close < distal_price
        // Remove box and lines
        box.delete(array.get(demand_zones, index))
        line.delete(array.get(proximal_lines, index))
        line.delete(array.get(distal_lines, index))
        // Remove from arrays
        array.remove(demand_zones, index)
        array.remove(proximal_lines, index)
        array.remove(distal_lines, index)
        array.remove(proximal_prices, index)
        array.remove(distal_prices, index)

// RBR Detection

// Check if initial rally candle before base candles is bullish
rally1 = close[base_candles + 1] > open[base_candles + 1]

// Initialize variables
base = true
total_base_size = 0.0
float proximal_price = na  // Proximal line (top of base candles' bodies)
float distal_price = na    // Distal line (lowest low of base candles and leg-out candle, excluding leg-in candle)

// Variables to store data
highest_body_top = 0.0
float lowest_low = na

// Check base candles and find proximal and distal prices
for i = 0 to base_candles - 1
    idx = base_candles - i
    base_candle_size = math.abs(close[idx] - open[idx])
    base := base and base_candle_size < avg_range[idx] * small_range_multiplier
    total_base_size += base_candle_size
    // Calculate candle body top (highest open or close)
    body_top = math.max(open[idx], close[idx])
    highest_body_top := math.max(highest_body_top, body_top)
    // Calculate candle low (including wick)
    candle_low = low[idx]
    lowest_low := na(lowest_low) ? candle_low : math.min(lowest_low, candle_low)

// Set proximal price
proximal_price := highest_body_top

// Include leg-out candle in distal_price calculation (exclude leg-in candle)
legout_low = low
lowest_low := math.min(lowest_low, legout_low)

// Legout candle size
legout_candle_size = math.abs(close - open)
avg_base_size = total_base_size / base_candles

// Check if leg-out candle is bullish and its size is more than double the avg base candle size
rally2 = (close > open) and (legout_candle_size > 2 * avg_base_size)

// Ensure overall upward movement
overall_up = close > close[base_candles + 1]

// Determine if pattern is RBR
isRBR = rally1 and base and rally2 and overall_up

// DBR Detection

// Check if initial drop candle before base candles is bearish
drop1 = close[base_candles + 1] < open[base_candles + 1]

// Initialize variables
base_d = true
total_base_size_d = 0.0
float proximal_price_d = na  // Proximal line (top of base candles' bodies)
float distal_price_d = na    // Distal line (lowest low of base candles and leg-out candle, excluding leg-in candle)

// Variables to store data
highest_body_top_d = 0.0
float lowest_low_d = na

// Check base candles and find proximal and distal prices
for i = 0 to base_candles - 1
    idx = base_candles - i
    base_candle_size_d = math.abs(close[idx] - open[idx])
    base_d := base_d and base_candle_size_d < avg_range[idx] * small_range_multiplier
    total_base_size_d += base_candle_size_d
    // Calculate candle body top (highest open or close)
    body_top_d = math.max(open[idx], close[idx])
    highest_body_top_d := math.max(highest_body_top_d, body_top_d)
    // Calculate candle low (including wick)
    candle_low_d = low[idx]
    lowest_low_d := na(lowest_low_d) ? candle_low_d : math.min(lowest_low_d, candle_low_d)

// Set proximal price
proximal_price_d := highest_body_top_d

// Include leg-out candle in distal_price calculation (exclude leg-in candle)
legout_low_d = low
lowest_low_d := math.min(lowest_low_d, legout_low_d)

// Legout candle size
legout_candle_size_d = math.abs(close - open)
avg_base_size_d = total_base_size_d / base_candles

// Check if leg-out candle is bullish and its size is more than double the avg base candle size
rally_d = (close > open) and (legout_candle_size_d > 2 * avg_base_size_d)
overall_up_d = close > close[base_candles + 1]

// Determine if pattern is DBR
isDBR = drop1 and base_d and rally_d and overall_up_d

// Plot Demand Zones
if isRBR or isDBR
    // Set proximal and distal prices
    proximal_price_final = isRBR ? proximal_price : proximal_price_d
    distal_price_final = isRBR ? lowest_low : lowest_low_d

    // Draw the demand zone rectangle
    zone_start = bar_index - base_candles    // Excludes leg-in candle
    zone_end = bar_index    // Up to current bar
    color_zone = color.new(color.green, 80)

    demand_zone = box.new(
        left=zone_start,
        right=zone_end,
        top=proximal_price_final,
        bottom=distal_price_final,
        xloc=xloc.bar_index,
        border_color=color_zone,
        bgcolor=color_zone
    )
    array.push(demand_zones, demand_zone)

    // Store prices
    array.push(proximal_prices, proximal_price_final)
    array.push(distal_prices, distal_price_final)

    // Draw proximal line
    proximal_line = line.new(
        x1=zone_start,
        y1=proximal_price_final,
        x2=zone_end,
        y2=proximal_price_final,
        xloc=xloc.bar_index,
        color=color.green,
        style=line.style_solid,
        width=2
    )
    array.push(proximal_lines, proximal_line)

    // Draw distal line
    distal_line = line.new(
        x1=zone_start,
        y1=distal_price_final,
        x2=zone_end,
        y2=distal_price_final,
        xloc=xloc.bar_index,
        color=color.red,
        style=line.style_dashed,
        width=2
    )
    array.push(distal_lines, distal_line)

// Check for violations and remove violated zones
i = array.size(demand_zones) - 1
while i >= 0
    f_check_violation(i)
    i := i - 1

// Include an invisible plot to ensure the indicator overlays on the main chart
plot(na)