Supply and Demand Based Pattern [RH]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HasanRifat

//@version=5
indicator("Supply and Demand Based Pattern [RH]", overlay = true, max_lines_count = 500, max_boxes_count = 500)

// Tooltips
bodyHealthTip = "Our objective is to establish a stronger initial momentum by ensuring the presence of at least one candle with 60% health during the first rise/drop phase."
baseRetracementTip = "This input parameter ensures that the base retracement remains below 80%. By decreasing the value, we can effectively limit the extent of retracement, thus minimizing the potential for excessive pullbacks."

// User freedom
bodyHealthInput = input.int(defval = 60, title = "Candle Health %", minval = 1, maxval = 100, tooltip = bodyHealthTip)
baseRetracement = input.float(defval = 0.80, title = "Base Max Retracement", minval = 0.1, maxval = 1, tooltip = baseRetracementTip)

var float bullLow1   = na 
var float bullLow2   = na 
var float bullHigh   = na 
var int   bullPass   = 0
var int   bullLineX1 = na 

var float bearHigh1  = na 
var float bearHigh2  = na 
var float bearLow    = na 
var int   bearPass   = 0
var int   bearLineX1 = na  

bool bullPattern = na
bool bearPattern = na 

var int activePattern      = na
var int bullFinalLoopIndex = na 

var int   bullLeft   = na 
var int   bullRight  = na 
var float bullTop    = na 
var float bullBottom = na 
var int   bearLeft   = na 
var int   bearRight  = na 
var float bearTop    = na 
var float bearBottom = na

var bullHighArray   = array.new_float()
var bullLow1Array   = array.new_float()
var bullLow2Array   = array.new_float()
var bullLineX1Array = array.new_int()

var bearLowArray    = array.new_float()
var bearHigh1Array  = array.new_float()
var bearHigh2Array  = array.new_float()
var bearLineX1Array = array.new_int()

// Candle type for future calculations
bullCandle = close > open
bearCandle = close < open

// Body health calculation
a = high - low
b = close > open ? close - open : open - close 
bodyHealth = ((a-b)/a)*100

bullHealthPass = 0
bearHealthPass = 0

// First hook for RBR
if bullCandle and close > high[1]
    if bullPass[1] < 2
        bullPass := 1

// Base initialization and first rise value gathering
if bullPass == 1 and bearCandle
    for i = 1 to 10
        if bullCandle[i]
            array.push(bullLow1Array, open[i])
            array.push(bullHighArray, high[i])
            array.push(bullLineX1Array, bar_index[i])
            if bodyHealth[i] < (100 - bodyHealthInput)
                bullHealthPass := bullHealthPass + 1
        if bearCandle[i]
            break
    bullLow1 := array.min(bullLow1Array)
    bullLineX1 := array.min(bullLineX1Array)
    if bullHealthPass > 0
        if close > bullLow1
            array.push(bullHighArray, high)
            bullPass := 2
            bullFinalLoopIndex := bar_index
            bullLeft := bar_index - 1
            bullRight := bar_index + 1
            bullTop := high>high[1] ? high : high[1]
            bullBottom := low 
            
        if close < bullLow1
            bullPass := 0
    if bullHealthPass < 0
        bullPass := 0 
if bullPass == 2 and bearCandle
    if close < bullLow1
        bullPass := 0

// RBR Pattern confirmation 
if bullPass == 2 and bullCandle
    bullHigh := array.max(bullHighArray)
    if close < bullHigh
        bullPass := 3
        bullBottom := low[1]
    if close > bullHigh
        bullPass := 1
        for i = 0 to (bar_index-bullFinalLoopIndex)
            array.push(bullLow2Array, low[i])
        bullLow2 := array.min(bullLow2Array)
        if bullLow2 > bullLow1 + (bullHigh - bullLow1)*(1-baseRetracement)
            bullPattern := true
            activePattern := 1  
            box.new(bullLeft, bullTop, bar_index, bullBottom, bgcolor = color.new(color.green, 60), border_color = na)
            line.new(bullLineX1, bullLow1, bar_index, bullLow1, color = color.green)
            line.new(bar_index, bullLow1, bar_index, low, color = color.green, style = line.style_dashed)
if bullPass == 3 and bearCandle
    bullPass := 0
if bullPass == 3 and bullCandle
    if close > bullHigh
        bullPass := 1
        for i = 0 to (bar_index-bullFinalLoopIndex)
            array.push(bullLow2Array, low[i])
        bullLow2 := array.min(bullLow2Array)
        if bullLow2 > bullLow1 + (bullHigh - bullLow1)*(1-baseRetracement)
            bullPattern := true
            activePattern := 1
            box.new(bullLeft, bullTop, bar_index, bullBottom, bgcolor = color.new(color.green, 60), border_color = na)
            line.new(bullLineX1, bullLow1, bar_index, bullLow1, color = color.green)
            line.new(bar_index, bullLow1, bar_index, low, color = color.green, style = line.style_dashed)

if bullPass == 1
    array.clear(bullHighArray)
    array.clear(bullLow1Array)
    array.clear(bullLow2Array)
    array.clear(bullLineX1Array)

// First hook for DBD
if bearCandle and close < low[1]
    if bearPass[1] < 2
        bearPass := 1

//Base initialization and first drop value gathering 
if bearPass == 1 and bullCandle
    for i = 1 to 10
        if bearCandle[i]
            array.push(bearHigh1Array, open[i])
            array.push(bearLowArray, low[i])
            array.push(bearLineX1Array, bar_index[i])
            if bodyHealth[i] < (100 - bodyHealthInput)
                bullHealthPass := bullHealthPass + 1
        if bullCandle[i]
            break
    bearHigh1 := array.max(bearHigh1Array)
    bearLineX1 := array.min(bearLineX1Array)
    if bullHealthPass > 0
        if close < bearHigh1
            array.push(bearLowArray, low)
            bearPass := 2
            bullFinalLoopIndex := bar_index
            bearLeft := bar_index - 1
            bearRight := bar_index + 1
            bearTop := high
            bearBottom := low < low[1] ? low : low[1]
        if close > bearHigh1
            bearPass := 0
    if bullHealthPass < 0
        bearPass := 0 
if bearPass == 2 and bullCandle
    if close > bearHigh1
        bearPass := 0

// DBD Pattern confirmation
if bearPass == 2 and bearCandle
    bearLow := array.min(bearLowArray)
    if close > bearLow
        bearPass := 3
        bearTop := high[1]
    if close < bearLow
        bearPass := 1
        for i = 0 to (bar_index-bullFinalLoopIndex)
            array.push(bearHigh2Array, high[i])
        bearHigh2 := array.max(bearHigh2Array)
        if bearHigh2 < bearHigh1 - (bearHigh1 - bearLow)*(1-baseRetracement)
            bearPattern := true
            activePattern := -1
            box.new(bearLeft, bearTop, bar_index, bearBottom, bgcolor = color.new(color.red, 60), border_color = na)
            line.new(bearLineX1, bearHigh1, bar_index, bearHigh1, color = color.red)
            line.new(bar_index, bearHigh1, bar_index, high, color = color.red, style = line.style_dashed)
if bearPass == 3 and bullCandle
    bearPass := 0
if bearPass == 3 and bearCandle
    if close < bearLow
        bearPass := 1
        for i = 0 to (bar_index-bullFinalLoopIndex)
            array.push(bearHigh2Array, high[i])
        bearHigh2 := array.max(bearHigh2Array)
        if bearHigh2 < bearHigh1 - (bearHigh1 - bearLow)*(1-baseRetracement)
            bearPattern := true
            activePattern := -1
            box.new(bearLeft, bearTop, bar_index, bearBottom, bgcolor = color.new(color.red, 60), border_color = na)
            line.new(bearLineX1, bearHigh1, bar_index, bearHigh1, color = color.red)
            line.new(bar_index, bearHigh1, bar_index, high, color = color.red, style = line.style_dashed)

if bearPass == 1
    array.clear(bearLowArray)
    array.clear(bearHigh1Array)
    array.clear(bearHigh2Array)
    array.clear(bearLineX1Array)

bullFirst = activePattern == 1 and activePattern[1] == -1
bearFirst = activePattern == -1 and activePattern[1] == 1

// Plotting
plotshape(bullFirst, title = "Bull Pattern", style = shape.labelup, color = color.green, location = location.belowbar, size = size.tiny, text = "RBR", textcolor = color.white)
plotshape(bearFirst, title = "Bear Pattern", style = shape.labeldown, color = color.red, location = location.abovebar, size = size.tiny, text = "DBD", textcolor = color.white)

// Alerts
alertcondition(bullPattern, title = "All Bullish(RBR) Pattern", message = "RBR")
alertcondition(bearPattern, title = "All Bearish(DBD) Pattern", message = "DBD")
alertcondition(bullFirst, title = "First Bullish(RBR) Pattern", message = "1st RBR")
alertcondition(bearFirst, title = "First Bearish(DBD) Pattern", message = "1st DBD")

