I want to add Button to This Indicator that Shown on Top Right Corner that can turn off/on this Indicator without opening Indicator Setting: //@version=5
indicator("Supply and Demand Zone Detector with Follow-through", overlay=true, max_lines_count=500, max_boxes_count=500)

// ‣ Input Parameters
avgBodyPeriod = input.int(title="Average Body Period", defval=10, minval=1)
bigCandleFactor = input.float(title="Big Candle Factor (Multiplier of Avg Body Size)", defval=1.5, minval=0.1)
maxBaseCandles = input.int(title="Maximum Base Candles", defval=6, minval=1)
maxBaseBodyPercent = input.float(title="Max Base Candle Body Size (% of Big Candle)", defval=50.0, minval=0.1, maxval=100.0)
drawHistoricalZones = input.bool(title="Draw Historical Zones", defval=true)
maxHistoricalZonesToDisplay = input.int(title="Number of Historical Zones to Display", defval=5, minval=1)
maxActiveSupplyZonesToDisplay = input.int(title="Number of Active Supply Zones to Display", defval=5, minval=1)
maxActiveDemandZonesToDisplay = input.int(title="Number of Active Demand Zones to Display", defval=5, minval=1)
// New input parameters for follow-through
minFollowThroughCandles = input.int(title="Minimum Follow-through Candles", defval=2, minval=1)
minFollowThroughPerc = input.float(title="Minimum Follow-through Movement (%)", defval=0.5, minval=0.1)
// ** New input parameter to show/hide markers **
showMarkers = input.bool(title="Show Markers", defval=true)

// ‣ Calculations
avgBodySize = ta.sma(math.abs(close - open), avgBodyPeriod)
bigCandleBodyThreshold = bigCandleFactor * avgBodySize
maxBaseCandleBodySize = (maxBaseBodyPercent / 100.0) * bigCandleBodyThreshold

bodySize = math.abs(close - open)
isBigGreenCandle = (close > open) and (bodySize >= bigCandleBodyThreshold)
isBigRedCandle = (close < open) and (bodySize >= bigCandleBodyThreshold)
isSmallBodyCandle = bodySize <= maxBaseCandleBodySize
isGreenCandle = close > open
isRedCandle = close < open

// ‣ Pattern Detection States
// Demand Zone Patterns
var int rbr_state = 0
var int rbr_baseCount = 0
var float rbr_highestBody = na
var float rbr_lowestLow = na
var int rbr_startIndex = na
var int rbr_legOutIndex = na // Index of the potential Leg-out candle

var int dbr_state = 0
var int dbr_baseCount = 0
var float dbr_highestBody = na
var float dbr_lowestLow = na
var int dbr_startIndex = na
var int dbr_legOutIndex = na

// Supply Zone Patterns
var int dbd_state = 0
var int dbd_baseCount = 0
var float dbd_lowestBody = na
var float dbd_highestHigh = na
var int dbd_startIndex = na
var int dbd_legOutIndex = na

var int rbd_state = 0
var int rbd_baseCount = 0
var float rbd_lowestBody = na
var float rbd_highestHigh = na
var int rbd_startIndex = na
var int rbd_legOutIndex = na

// ‣ Arrays to Store Zones
// Each zone has:
// - box object
// - distal line (for violation checking)
// - zone type (1 for RBR, 2 for DBR, 3 for DBD, 4 for RBD)
// - isViolated flag (true if violated)
var box[] zones = array.new()
var float[] distalLines = array.new()
var int[] zoneTypes = array.new()
var bool[] isViolated = array.new()

// Function to check for good follow-through
f_hasGoodFollowThrough(startIdx, direction) =>
cumulativeMove = 0.0
followThrough = false
for i = 1 to minFollowThroughCandles
idx = startIdx + i
if idx <= bar_index
priceMovePerc = ((close[idx] - close[startIdx]) / close[startIdx]) * 100.0
if direction == "up" and priceMovePerc >= minFollowThroughPerc
followThrough := true
break
else if direction == "down" and priceMovePerc <= -minFollowThroughPerc
followThrough := true
break
followThrough

// ‣ RBR Pattern Detection (Demand Zone)
if (rbr_state == 0)
if (isBigGreenCandle)
// RBR 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)
// RBR 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)
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)
// Potential Leg-out detected
rbr_legOutIndex := bar_index
// Check if Leg-out candle is big or small
if (not isBigGreenCandle)
// Leg-out candle is small, check for good follow-through
hasFollowThrough = f_hasGoodFollowThrough(rbr_legOutIndex, "up")
if hasFollowThrough
// Complete RBR pattern
if (rbr_baseCount >= 1)
// Include Leg-out candle's low in distal line
rbr_lowestLow := math.min(rbr_lowestLow, low)
// Create Demand Zone
newZone = 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(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, rbr_lowestLow)
array.push(zoneTypes, 1) // 1 for RBR
array.push(isViolated, violationFlag) // initially not violated
// Reset RBR pattern
rbr_state := 0
rbr_baseCount := 0
else
// Not enough follow-through, wait
na
else
// Leg-out candle is big, proceed as usual
if (rbr_baseCount >= 1)
// Include Leg-out candle's low in distal line
rbr_lowestLow := math.min(rbr_lowestLow, low)
// Create Demand Zone
newZone = 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(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, rbr_lowestLow)
array.push(zoneTypes, 1) // 1 for RBR
array.push(isViolated, violationFlag) // initially not violated
// Reset RBR pattern
rbr_state := 0
rbr_baseCount := 0
else
// Pattern invalidated, reset RBR state
rbr_state := 0
rbr_baseCount := 0

// ‣ DBR Pattern Detection (Demand Zone)
if (dbr_state == 0)
if (isBigRedCandle)
// DBR Leg-in detected
dbr_state := 1
dbr_baseCount := 0
dbr_startIndex := bar_index
dbr_highestBody := na
dbr_lowestLow := na
else if (dbr_state == 1)
if (isSmallBodyCandle)
// DBR 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)
candleHighBody = math.max(open, close)
dbr_highestBody := na(dbr_highestBody) ? candleHighBody : math.max(dbr_highestBody, candleHighBody)
// Update lowest low (distal line)
dbr_lowestLow := na(dbr_lowestLow) ? low : math.min(dbr_lowestLow, low)
else if (isGreenCandle)
// Potential Leg-out detected
dbr_legOutIndex := bar_index
// Check if Leg-out candle is big or small
if (not isBigGreenCandle)
// Leg-out candle is small, check for good follow-through
hasFollowThrough = f_hasGoodFollowThrough(dbr_legOutIndex, "up")
if hasFollowThrough
// Complete DBR pattern
if (dbr_baseCount >= 1)
// Include Leg-out candle's low in distal line
dbr_lowestLow := math.min(dbr_lowestLow, low)
// Create Demand Zone
newZone = 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(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, dbr_lowestLow)
array.push(zoneTypes, 2) // 2 for DBR
array.push(isViolated, violationFlag) // initially not violated
// Reset DBR pattern
dbr_state := 0
dbr_baseCount := 0
else
// Not enough follow-through, wait
na
else
// Leg-out candle is big, proceed as usual
if (dbr_baseCount >= 1)
// Include Leg-out candle's low in distal line
dbr_lowestLow := math.min(dbr_lowestLow, low)
// Create Demand Zone
newZone = 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(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, dbr_lowestLow)
array.push(zoneTypes, 2) // 2 for DBR
array.push(isViolated, violationFlag) // initially not violated
// Reset DBR pattern
dbr_state := 0
dbr_baseCount := 0
else
// Pattern invalidated, reset DBR state
dbr_state := 0
dbr_baseCount := 0

// ‣ DBD Pattern Detection (Supply Zone)
if (dbd_state == 0)
if (isBigRedCandle)
// DBD Leg-in detected
dbd_state := 1
dbd_baseCount := 0
dbd_startIndex := bar_index
dbd_lowestBody := na
dbd_highestHigh := na
else if (dbd_state == 1)
if (isSmallBodyCandle)
// DBD Base candle detected
dbd_baseCount += 1
if (dbd_baseCount > maxBaseCandles)
// Exceeded maximum base candles, reset pattern
dbd_state := 0
dbd_baseCount := 0
else
// Update lowest body (proximal line)
candleLowBody = math.min(open, close)
dbd_lowestBody := na(dbd_lowestBody) ? candleLowBody : math.min(dbd_lowestBody, candleLowBody)
// Update highest high (distal line)
dbd_highestHigh := na(dbd_highestHigh) ? high : math.max(dbd_highestHigh, high)
else if (isRedCandle)
// Potential Leg-out detected
dbd_legOutIndex := bar_index
// Check if Leg-out candle is big or small
if (not isBigRedCandle)
// Leg-out candle is small, check for good follow-through
hasFollowThrough = f_hasGoodFollowThrough(dbd_legOutIndex, "down")
if hasFollowThrough
// Complete DBD pattern
if (dbd_baseCount >= 1)
// Include Leg-out candle's high in distal line
dbd_highestHigh := math.max(dbd_highestHigh, high)
// Create Supply Zone
newZone = box.new(left=dbd_startIndex, right=bar_index, top=dbd_highestHigh, bottom=dbd_lowestBody,
border_color=color.red, bgcolor=color.new(color.red, 85))
box.set_extend(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, dbd_highestHigh)
array.push(zoneTypes, 3) // 3 for DBD
array.push(isViolated, violationFlag) // initially not violated
// Reset DBD pattern
dbd_state := 0
dbd_baseCount := 0
else
// Not enough follow-through, wait
na
else
// Leg-out candle is big, proceed as usual
if (dbd_baseCount >= 1)
// Include Leg-out candle's high in distal line
dbd_highestHigh := math.max(dbd_highestHigh, high)
// Create Supply Zone
newZone = box.new(left=dbd_startIndex, right=bar_index, top=dbd_highestHigh, bottom=dbd_lowestBody,
border_color=color.red, bgcolor=color.new(color.red, 85))
box.set_extend(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, dbd_highestHigh)
array.push(zoneTypes, 3) // 3 for DBD
array.push(isViolated, violationFlag) // initially not violated
// Reset DBD pattern
dbd_state := 0
dbd_baseCount := 0
else
// Pattern invalidated, reset DBD state
dbd_state := 0
dbd_baseCount := 0

// ‣ RBD Pattern Detection (Supply Zone)
if (rbd_state == 0)
if (isBigGreenCandle)
// RBD Leg-in detected
rbd_state := 1
rbd_baseCount := 0
rbd_startIndex := bar_index
rbd_lowestBody := na
rbd_highestHigh := high // Initialize with Leg-in candle's high
else if (rbd_state == 1)
if (isSmallBodyCandle)
// RBD Base candle detected
rbd_baseCount += 1
if (rbd_baseCount > maxBaseCandles)
// Exceeded maximum base candles, reset pattern
rbd_state := 0
rbd_baseCount := 0
else
// Update lowest body (proximal line)
candleLowBody = math.min(open, close)
rbd_lowestBody := na(rbd_lowestBody) ? candleLowBody : math.min(rbd_lowestBody, candleLowBody)
// Update highest high (distal line)
rbd_highestHigh := math.max(rbd_highestHigh, high)
else if (isRedCandle)
// Potential Leg-out detected
rbd_legOutIndex := bar_index
// Check if Leg-out candle is big or small
if (not isBigRedCandle)
// Leg-out candle is small, check for good follow-through
hasFollowThrough = f_hasGoodFollowThrough(rbd_legOutIndex, "down")
if hasFollowThrough
// Complete RBD pattern
if (rbd_baseCount >= 1)
// Include Leg-out candle's high in distal line
rbd_highestHigh := math.max(rbd_highestHigh, high)
// Create Supply Zone
newZone = box.new(left=rbd_startIndex, right=bar_index, top=rbd_highestHigh, bottom=rbd_lowestBody,
border_color=color.orange, bgcolor=color.new(color.orange, 85))
box.set_extend(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, rbd_highestHigh)
array.push(zoneTypes, 4) // 4 for RBD
array.push(isViolated, violationFlag) // initially not violated
// Reset RBD pattern
rbd_state := 0
rbd_baseCount := 0
else
// Not enough follow-through, wait
na
else
// Leg-out candle is big, proceed as usual
if (rbd_baseCount >= 1)
// Include Leg-out candle's high in distal line
rbd_highestHigh := math.max(rbd_highestHigh, high)
// Create Supply Zone
newZone = box.new(left=rbd_startIndex, right=bar_index, top=rbd_highestHigh, bottom=rbd_lowestBody,
border_color=color.orange, bgcolor=color.new(color.orange, 85))
box.set_extend(newZone, extend.right)
// Initialize isViolated flag as false
violationFlag = false
// Store zone details
array.push(zones, newZone)
array.push(distalLines, rbd_highestHigh)
array.push(zoneTypes, 4) // 4 for RBD
array.push(isViolated, violationFlag) // initially not violated
// Reset RBD pattern
rbd_state := 0
rbd_baseCount := 0
else
// Pattern invalidated, reset RBD state
rbd_state := 0
rbd_baseCount := 0

// ‣ Zone Violation Checking and Management
if array.size(zones) > 0
// Iterate through all zones
i = array.size(zones) -1
while i >= 0
currentZone = array.get(zones, i)
currentDistal = array.get(distalLines, i)
currentType = array.get(zoneTypes, i)
violated = array.get(isViolated, i)


    // Determine violation based on zone type and close price
    isCurrentlyViolated = false
    if currentType == 1 or currentType == 2
        // Demand Zones: Violation when close < distal line
        isCurrentlyViolated := (close < currentDistal) and (not violated)
    else if currentType == 3 or currentType == 4
        // Supply Zones: Violation when close > currentDistal
        isCurrentlyViolated := (close > currentDistal) and (not violated)
    
    if isCurrentlyViolated
        if drawHistoricalZones
            // Mark zone as violated: change appearance to gray
            box.set_border_color(currentZone, color.gray)
            box.set_bgcolor(currentZone, color.new(color.gray, 85))
            // Update violation status
            array.set(isViolated, i, true)
        else
            // Delete the violated zone
            box.delete(currentZone)
            // Remove from arrays
            array.remove(zones, i)
            array.remove(distalLines, i)
            array.remove(zoneTypes, i)
            array.remove(isViolated, i)
    i -=1

// After handling violations, manage the number of historical zones
if drawHistoricalZones
    // Count how many zones have been violated (isViolated=true)
    historicalZonesCount = 0
    if array.size(isViolated) > 0
        for idx = 0 to array.size(isViolated)-1
            if array.get(isViolated, idx)
                historicalZonesCount +=1

    // Remove oldest historical zones if count exceeds the limit
    while historicalZonesCount > maxHistoricalZonesToDisplay
        // Find the oldest historical zone (first in the array that is violated)
        found = false
        if array.size(isViolated) > 0
            for idx = 0 to array.size(isViolated)-1
                if array.get(isViolated, idx)
                    // Remove this zone
                    zoneToRemove = array.get(zones, idx)
                    box.delete(zoneToRemove)
                    array.remove(zones, idx)
                    array.remove(distalLines, idx)
                    array.remove(zoneTypes, idx)
                    array.remove(isViolated, idx)
                    historicalZonesCount -=1
                    found := true
                    break
        if not found
            break  // No more historical zones to remove
else
    // Remove all violated zones when drawHistoricalZones is false
    i = array.size(zones) -1
    while i >= 0
        if array.get(isViolated, i)
            zoneToRemove = array.get(zones, i)
            box.delete(zoneToRemove)
            array.remove(zones, i)
            array.remove(distalLines, i)
            array.remove(zoneTypes, i)
            array.remove(isViolated, i)
        i -=1

// ‣ Manage the number of active zones separately for supply and demand
activeSupplyZonesCount = 0
activeDemandZonesCount = 0

// First, count the number of active (non-violated) supply and demand zones
if array.size(isViolated) > 0
    for idx = 0 to array.size(isViolated)-1
        if not array.get(isViolated, idx)
            currentType = array.get(zoneTypes, idx)
            if currentType == 1 or currentType == 2
                activeDemandZonesCount += 1
            else if currentType == 3 or currentType == 4
                activeSupplyZonesCount += 1

// Now, remove oldest active supply zones if count exceeds limit
while activeSupplyZonesCount > maxActiveSupplyZonesToDisplay
    // Find the oldest active supply zone
    found = false
    if array.size(isViolated) > 0
        for idx = 0 to array.size(isViolated)-1
            if not array.get(isViolated, idx)
                currentType = array.get(zoneTypes, idx)
                if currentType == 3 or currentType == 4  // Supply zone
                    // Remove this zone
                    zoneToRemove = array.get(zones, idx)
                    box.delete(zoneToRemove)
                    array.remove(zones, idx)
                    array.remove(distalLines, idx)
                    array.remove(zoneTypes, idx)
                    array.remove(isViolated, idx)
                    activeSupplyZonesCount -=1
                    found := true
                    break
    if not found
        break // No more active supply zones to remove

// Similarly, remove oldest active demand zones if count exceeds limit
while activeDemandZonesCount > maxActiveDemandZonesToDisplay
    // Find the oldest active demand zone
    found = false
    if array.size(isViolated) > 0
        for idx = 0 to array.size(isViolated)-1
            if not array.get(isViolated, idx)
                currentType = array.get(zoneTypes, idx)
                if currentType == 1 or currentType == 2  // Demand zone
                    // Remove this zone
                    zoneToRemove = array.get(zones, idx)
                    box.delete(zoneToRemove)
                    array.remove(zones, idx)
                    array.remove(distalLines, idx)
                    array.remove(zoneTypes, idx)
                    array.remove(isViolated, idx)
                    activeDemandZonesCount -=1
                    found := true
                    break
    if not found
        break // No more active demand zones to remove
// ‣ Plot Markers (Optional)

// Demand Zone Markers

// RBR Markers
plotshape(showMarkers and 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(showMarkers and 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(showMarkers and 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(showMarkers and dbr_state ==1 and isGreenCandle and dbr_baseCount >=1 and not isBigRedCandle,
title="DBR Leg-out",
style=shape.triangleup,
location=location.belowbar,
color=color.blue,
size=size.small)

// Supply Zone Markers

// DBD Markers
plotshape(showMarkers and dbd_state == 1 and isBigRedCandle and dbd_baseCount == 0,
title="DBD Leg-in",
style=shape.triangledown,
location=location.abovebar,
color=color.red,
size=size.small)
plotshape(showMarkers and dbd_state == 1 and isRedCandle and dbd_baseCount >=1 and not isBigRedCandle,
title="DBD Leg-out",
style=shape.triangledown,
location=location.abovebar,
color=color.maroon,
size=size.small)

// RBD Markers
plotshape(showMarkers and rbd_state == 1 and isBigGreenCandle and rbd_baseCount ==0,
title="RBD Leg-in",
style=shape.triangleup,
location=location.belowbar,
color=color.green,
size=size.small)
plotshape(showMarkers and rbd_state ==1 and isRedCandle and rbd_baseCount >=1 and not isBigGreenCandle,
title="RBD Leg-out",
style=shape.triangledown,
location=location.abovebar,
color=color.orange,
size=size.small)