Indicator · Pine Script v5 · On the chart

Price Action Concepts Open Source

by KanekiCraynet

Plots institutional price action and smart money concepts directly on the price chart.

View on GitHub

Repaints

GitHub stars8on the repository
Pine versionv5runs as is
Lines of code1,846
Settings40inputs
LicenceMIT
Last update1 year ago1 Mar 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

1,846 lines of Pine Script v5 from Price Action Concepts.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

Price Action Concepts.pine
//@version=5
indicator("Price Action Concepts Open source
 🎴", "Price Action Concepts Open source
 🎴"
  , overlay = true
  , max_labels_count = 500
  , max_lines_count = 500
  , max_boxes_count = 500
  , max_bars_back = 500)
//-----------------------------------------------------------------------------{
//Constants
//-----------------------------------------------------------------------------{
color TRANSP_CSS = #ffffff00

//Tooltips
string MODE_TOOLTIP          = 'Allows to display historical Structure or only the recent ones'
string STYLE_TOOLTIP         = 'Indicator color theme'
string COLOR_CANDLES_TOOLTIP = 'Display additional candles with a color reflecting the current trend detected by structure'
string SHOW_INTERNAL         = 'Display internal market structure'
string CONFLUENCE_FILTER     = 'Filter non significant internal structure breakouts'
string SHOW_SWING            = 'Display swing market Structure'
string SHOW_SWING_POINTS     = 'Display swing point as labels on the chart'
string SHOW_SWHL_POINTS      = 'Highlight most recent strong and weak high/low points on the chart'
string INTERNAL_OB           = 'Display internal order blocks on the chart\n\nNumber of internal order blocks to display on the chart'
string SWING_OB              = 'Display swing order blocks on the chart\n\nNumber of internal swing blocks to display on the chart'
string FILTER_OB             = 'Method used to filter out volatile order blocks \n\nIt is recommended to use the cumulative mean range method when a low amount of data is available'
string SHOW_EQHL             = 'Display equal highs and equal lows on the chart'
string EQHL_BARS             = 'Number of bars used to confirm equal highs and equal lows'
string EQHL_THRESHOLD        = 'Sensitivity threshold in a range (0, 1) used for the detection of equal highs & lows\n\nLower values will return fewer but more pertinent results'
string SHOW_FVG              = 'Display fair values gaps on the chart'
string AUTO_FVG              = 'Filter out non significant fair value gaps'
string FVG_TF                = 'Fair value gaps timeframe'
string EXTEND_FVG            = 'Determine how many bars to extend the Fair Value Gap boxes on chart'
string PED_ZONES             = 'Display premium, discount, and equilibrium zones on chart'

//-----------------------------------------------------------------------------{
//Settings
//-----------------------------------------------------------------------------{
//General
//----------------------------------------{
mode = input.string("Historical", "Market Concepts Mode", options = ["Historical", "Present"], group = "Internal Structure")
style = 'Colored'

//----------------------------------------}
//Internal Structure
//----------------------------------------{
show_internals = input(true, 'Show Internal Structure'
  , group = 'Internal Structure'
  , tooltip = SHOW_INTERNAL)

show_ibull = input.string('All', 'Structure Type'
  , options = ['All', 'BOS', 'CHoCH']
  , inline = 'ibull'
  , group = 'Internal Structure')

swing_ibull_css = input(#089981, ''
  , inline = 'ibull'
  , group = 'Internal Structure')

//Bear Structure 
show_ibear = show_ibull

swing_ibear_css = input(#f23645, ''
  , inline = 'ibull'
  , group = 'Internal Structure')
bool structureScannerOn = input.bool(true, "Structure Scanner", inline = "ttt", group = "Internal Structure")
scannerTimeframe = input.timeframe("D", "", group = "Internal Structure", inline = "ttt")
var int internalBOS = 1
var int swingBOS = 1


show_trend = input(false, 'Color Candles'
  , group = 'Internal Structure'
  , tooltip = COLOR_CANDLES_TOOLTIP, inline = "testest")

purplecolor = input.color(#56328f, "", inline = "testest", group = "Internal Structure")

ifilter_confluence = false 

internal_structure_size = 'Tiny'

//----------------------------------------}
//Swing Structure
//----------------------------------------{
show_Structure = input(true, 'Show Swing Structure'
  , group = 'Swing Structure'
  , tooltip = SHOW_SWING)

//Bull Structure
show_bull = input.string('All', 'Structure Type'
  , options = ['All', 'BOS', 'CHoCH']
  , inline = 'bull'
  , group = 'Swing Structure')

swing_bull_css = input(#089981, ''
  , inline = 'bull'
  , group = 'Swing Structure')



//Bear Structure
show_bear = show_bull

swing_bear_css = input(#f23645, ''
  , inline = 'bull'
  , group = 'Swing Structure')

swing_structure_size = 'Small'

//Swings
show_swings = input(false, 'Show Swings Points'
  , inline = 'swings'
  , group = 'Swing Structure'
  , tooltip = SHOW_SWING_POINTS)

length = input.int(50, ''
  , minval = 10
  , inline = 'swings'
  , group = 'Swing Structure')

show_hl_swings = input(false, 'Show Strong/Weak High/Low'
  , group = 'Swing Structure'
  , tooltip = SHOW_SWHL_POINTS)

showEntryZones = input(false, "Show Entry Zones", group = "Swing Structure", tooltip = "Show Fibonacci Entry Zones")


showInvalidated = false
OBsEnabled = true
orderBlockVolumetricInfo = true
obEndMethod = input.string("Wick", "Mitigation Method", options = ["Wick", "Close"],  group = "Volumetric Orderblocks", display = display.none)
combineOBs = true
maxATRMult = 10.
swingLength = input.int(10, 'Construction Length', minval = 3, tooltip="Swing length is used when finding order block formations. Smaller values will result in finding smaller order blocks.",group = "Volumetric Orderblocks", display = display.none, inline = "obColor")

bullOrderBlockColor = input(#22a08a, '', inline = 'obColor', group = 'Volumetric Orderblocks', display = display.none)
bearOrderBlockColor = input(#f23847, '', inline = 'obColor', group = 'Volumetric Orderblocks', display = display.none)
transp = 80

bullishOrderBlocks = input.int(5, "Zone Count", group = "Volumetric Orderblocks")
bearishOrderBlocks = bullishOrderBlocks

timeframe1Enabled = input.bool(true, "Timeframe 1", inline ="7", group = "Volumetric Orderblocks")
timeframe1 = input.timeframe("240", "", inline = "7", group = "Volumetric Orderblocks")
timeframe2Enabled = input.bool(true, "Timeframe 2", inline ="8", group = "Volumetric Orderblocks")
timeframe2 = input.timeframe("15", "", inline = "8", group = "Volumetric Orderblocks")
timeframe3Enabled = input.bool(true, "Timeframe 3", inline ="9", group = "Volumetric Orderblocks")
timeframe3 = input.timeframe("1", "", inline = "9", group = "Volumetric Orderblocks")


show_sd = showEntryZones
premium_css = swing_bear_css
eq_css = #b2b5be
discount_css = swing_bull_css
//----------------------------------------}
//Order Blocks
//----------------------------------------{
var int candlecolor = 0

//----------------------------------------}
//Price Action Concepts
//----------------------------------------{
activateliquidity = input.bool(false, inline = "Sweeps", group = "Liquidity Concepts", title = "Liquidity Sweeps")
len12345    = input.int(3, '', options = [1, 2, 3, 4, 5], group = 'Liquidity Concepts', inline = "Sweeps")
opt    = 'Only Wicks'
len12345 := len12345 * 2

colBl  = input.color(#0044ff, "", inline = "Sweeps", group = "Liquidity Concepts")
colBr  = input.color(#ff2b00, "", inline = "Sweeps", group = "Liquidity Concepts")
colBl2 = colBl
colBr2 = colBr

extend = false
maxB   = 300
colBl3 = colBl
colBr3 = colBr

oW = opt == 'Only Wicks'
oO = opt == 'Only Outbreaks & Retest'
WO = opt == 'Wicks + Outbreaks & Retest'

n = bar_index 

//-----------------------------------------------------------------------------}      
//UDT's
//-----------------------------------------------------------------------------{
type piv
    float prc // price
    int   bix // bar_index
    bool  brk // broken
    bool  mit // mitigated
    bool  tak // taken
    bool  wic // wick
    line  lin 

type boxBr 
    box  bx 
    line ln
    bool br
    int  dr

//-----------------------------------------------------------------------------}      
//Variables
//-----------------------------------------------------------------------------{
var array< piv >aPivH  = array.new< piv >(1, piv.new  ())
var array< piv >aPivL  = array.new< piv >(1, piv.new  ())
var array<boxBr>aBoxBr = array.new<boxBr>(1, boxBr.new())

//-----------------------------------------------------------------------------}      
//Methods - functions
//-----------------------------------------------------------------------------{
method n(float piv) => bool out = not na(piv)

method p(piv piv, float val) => float out = (100 / piv.prc * val) - 100

method l(piv get, color c, string s='sd') => 
    style = switch s 
        'dt' => line.style_dotted
        'ds' => line.style_dashed
        =>      line.style_solid 
    line.new(get.bix, get.prc, n, get.prc, color=c, style = style)

method br(piv get, color c3, color c, int d) =>
    y1 = d == 1 ? high : get.prc
    y2 = d == 1 ? get.prc : low
    boxBr.new( 
     box.new(n -1, y1, n +1, y2
     , border_color
     = color.new(
       na, na   )
     , bgcolor=c3)
     , line.new(n , y1, n, y2, color=c, width=3)
     , false
     , d)

lnDot(y, c) => line.new(n, y, n+3, y, color=c, style=line.style_dotted)

//-----------------------------------------------------------------------------}      
//Execution
//-----------------------------------------------------------------------------{
ph = ta.pivothigh(len12345, len12345)
pl = ta.pivotlow (len12345, len12345)

if ph.n() 
    aPivH.unshift(piv.new(ph, n -len12345, false, false, false, false))

if pl.n() 
    aPivL.unshift(piv.new(pl, n -len12345, false, false, false, false)) 
bool bullcandle = false
bool bearcandle = false

for i = aPivH.size() -1 to 0
    get = aPivH.get(i)
    if not get.mit
        if not get.brk
            if close > get.prc 
                if not oW
                    get.brk := true
                else 
                    get.mit := true
            if not oO and not get.wic 
                if high > get.prc and close < get.prc              
                    bearcandle := true
                    
                    
                    get.wic := true  
        else 
            if close < get.prc 
                get.mit := true
            if not oW and low < get.prc and close > get.prc                    
                
                
                get.tak := true 

    if n - get.bix > 2000 or get.mit or get.tak 
        aPivH.remove(i).lin.delete()
for i = aPivL.size() -1 to 0
    get = aPivL.get(i)
    if not get.mit
        if not get.brk
            if close < get.prc 
                if not oW
                    get.brk := true
                else 
                    get.mit := true
            if not oO and not get.wic 
                if low < get.prc and close > get.prc                              
                    bullcandle := true
                    
                    
                    get.wic := true 
        else 
            if close > get.prc 
                get.mit := true
            if not oW and high > get.prc and close < get.prc                     
                
                
                
                get.tak := true  

    if n - get.bix > 2000 or get.mit or get.tak 
        aPivL.remove(i).lin.delete()


plotcandle(math.max(close, open), high, math.max(close, open), high, "", color(na), color(na), bordercolor = bearcandle and activateliquidity ? colBr3 : color(na), editable = false)
plotcandle(math.min(close, open), math.min(close, open), low, low, "", color(na), color(na), false, bordercolor = bullcandle and activateliquidity? colBl3 : color(na))
if extend
    for bx in aBoxBr 
        if not bx.br and n - bx.bx.get_left() -1 <= maxB 
            bx.bx.set_right(bar_index)
            if bx.dr == -1 and close < bx.bx.get_bottom() 
                bx.br := true 
            if bx.dr ==  1 and close > bx.bx.get_top   () 
                bx.br := true 


//-----------------------------------------------------------------------------} 

activateLiq = input.bool(false, "Trendline Liquidity", inline = "a", group = "Liquidity Concepts")
len123   = input.int  (3        , ""        ,  inline = "a", group = "Liquidity Concepts", options = [1, 2, 3, 4, 5])
cup   = input.color(#0044ff, ""              , "" ,                              inline = "a", group = "Liquidity Concepts")
cdn   = input.color(#ff2b00, ""              , "" ,                              inline = "a", group = "Liquidity Concepts")
space = len123 + 1
shs   = false

len123 := len123 * 2
phhh = ta.pivothigh(high, len123, len123)
plll = ta.pivotlow (low , len123, len123)

type store
    float src
    int   n

type bar
    float o = open
    float h = high
    float l = low
    float c = close
    int   n = bar_index
    float v = volume

type draw
    line[] upln
    line[] dnln

var store[] upbin = array.new<store>()
var store[] dnbin = array.new<store>()

var draw d = draw.new(array.new<line>(), array.new<line>())

bar b = bar.new()
atr   = ta.atr(200)

method slope(line ln) =>
    x = ln.get_x2() - ln.get_x1()
    y = ln.get_y2() - ln.get_y1()
    y / x

vol() =>
    math.min(atr * 0.1, close * (0.1/100))

var bool broken  = false
color    active  = na
bool plup = false
bool pldn = false

if phhh and activateLiq
    bool   remove = false
    var bool valid = false

    upbin.unshift(store.new(b.h[len12345], b.n[len12345]))
    if upbin.size() > 1
        current = upbin.get(0)
        before  = upbin.get(1)
        if current.src < before.src
            if broken
                valid := true

            else
                valid := false
                if upbin.size() > 3
                    pastold = upbin.get(3)
                    pastcur = upbin.get(2)
                    now     = upbin.get(1)
                    late    = upbin.get(0)
                    if now.src < pastcur.src and now.src < pastold.src and late.src < pastcur.src and late.src < pastold.src
                        valid := true

                    else
                        valid := false

                else 
                    valid := false

            if valid

                d.upln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src                , y2 = current.src                , color = cdn))
                d.upln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src - vol() * space, y2 = current.src - vol() * space, color = cdn))

                ln = d.upln.get(1)
                for i = 0 to (b.n - before.n)
                    slope = ln.slope()
                    ln.set_x2(b.n[i])
                    ln.set_y2(ln.get_y2() - slope)
                    if low[i] > ln.get_y2()
                        remove := true
                        break

                if remove
                    d.upln.get(0).delete()
                    d.upln.get(1).delete()
                    d.upln       .clear()
                    upbin        .clear()
                    broken := true

                else
                    d.upln.get(0).delete()
                    d.upln.get(1).delete()   
                    d.upln       .clear ()

                    d.upln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src                , y2 = current.src                , color = cdn))
                    d.upln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src - vol() * space, y2 = current.src - vol() * space, color = cdn))

                    linefill.new(d.upln.get(0), d.upln.get(1), color = color.new(cdn, 75))
                    upbin.clear()
                    broken := false


if d.upln.size() > 1

    btm = d.upln.get(0)
    top = d.upln.get(1)
    
    if b.l > top.get_y2()
        d.upln.clear()
        broken := true
        upbin.clear()
        plup   := true

    if d.upln.size() > 1

        slup = top.slope()
        sldn = btm.slope()

        top.set_x2(b.n)
        top.set_y2(top.get_y2() + slup)

        btm.set_x2(b.n)
        btm.set_y2(btm.get_y2() + sldn)
 


if plll and activateLiq
    bool    remove = false
    var bool valid = false

    dnbin.unshift(store.new(b.l[len12345], b.n[len12345]))
    if dnbin.size() > 1
        current = dnbin.get(0)
        before  = dnbin.get(1)
        if current.src > before.src
            if broken
                valid := true

            else
                valid := false
                if dnbin.size() > 3
                    pastold = dnbin.get(3)
                    pastcur = dnbin.get(2)
                    now     = dnbin.get(1)
                    late    = dnbin.get(0)

                    if now.src > pastcur.src and now.src > pastold.src and late.src > pastcur.src and late.src > pastold.src
                        valid := true

                    else
                        valid := false

                else 
                    valid := false

            if valid
                d.dnln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src                , y2 = current.src                , color = cup))
                d.dnln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src + vol() * space, y2 = current.src + vol() * space, color = cup))

                ln = d.dnln.get(1)

                for i = 0 to (b.n - before.n)
                    slope = ln.slope()
                    ln.set_x2(b.n[i])
                    ln.set_y2(ln.get_y2() - slope)
                    if high[i] < ln.get_y2()
                        remove := true
                        break

                if remove
                    d.dnln.get(0).delete()
                    d.dnln.get(1).delete()
                    d.dnln       .clear ()
                    dnbin        .clear ()
                    broken := true

                else
                    d.dnln.get(0).delete()
                    d.dnln.get(1).delete()   
                    d.dnln       .clear ()

                    d.dnln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src, y2 = current.src, color = cup))
                    d.dnln.unshift(line.new(x1 = before.n, x2 = current.n, y1 = before.src + vol() * space, y2 = current.src + vol() * space, color = cup))

                    linefill.new(d.dnln.get(0), d.dnln.get(1), color = color.new(cup, 75))
                    dnbin.clear()
                    broken := false

if d.dnln.size() > 1

    btm = d.dnln.get(0)
    top = d.dnln.get(1)
    
    if b.h < btm.get_y2()
        d.dnln.clear()
        broken := true
        dnbin.clear()
        pldn   := true

    if d.dnln.size() > 1

        slup = top.slope()
        sldn = btm.slope()

        top.set_x2(b.n)
        top.set_y2(top.get_y2() + slup)

        btm.set_x2(b.n)
        btm.set_y2(btm.get_y2() + sldn)

plotshape(pldn and shs ? b.h[1] : na, "Breaking Down", shape.triangledown, location = location.abovebar, color = cdn  , offset = -1, size = size.tiny)
plotshape(plup and shs ? b.l[1] : na, "Breaking Up"  , shape.triangleup  , location = location.belowbar, color = cup, offset = -1, size = size.tiny)

show_eq = input(false, 'Equal Highs/Lows'
  , group = 'Price Action Concepts'
  , tooltip = SHOW_EQHL, inline = "test")

eq_len = input.int(3, ''
  , options = [1, 2, 3, 4, 5]
  , group = 'Price Action Concepts'
  , tooltip = EQHL_BARS, inline = "test")

eq_threshold = 0.1*(6-eq_len)
if eq_len == 5
    eq_threshold == 0.05

eq_size = 'Tiny'

bool ontrendline = input.bool(true, "Auto-Trendlines", group = "Price Action Concepts", inline = "133")
atr10 = ta.atr(200)/2
int autotrendlinesens = input.int(3, "", [1, 2, 3, 4, 5], group = "Price Action Concepts", inline = "133")
length11111 = 5*autotrendlinesens+5
length111112 = 5*autotrendlinesens+5
astart = 2
aend = 0
bstart = 2
bend = 0
csrcsrcsrc = false
srcsrcsrc = close



//Conditions
upupup = ta.pivothigh(csrcsrcsrc ? srcsrcsrc : high, length11111, length11111)
dndndn = ta.pivotlow(csrcsrcsrc ? srcsrcsrc : low, length111112, length111112)


a1 = ta.valuewhen(not na(upupup), n, astart)
b1 = ta.valuewhen(not na(dndndn), n, bstart)
a2 = ta.valuewhen(not na(upupup), n, aend)
b2 = ta.valuewhen(not na(dndndn), n, bend)

//Colors
color111 = #a53e48
color222 = #5b8d5e


//Plots    
if ontrendline
    line upupupper1 = line.new(n[n - a1 + length11111], upupup[n - a1]+atr10, n[n - a2 + length11111], upupup[n - a2]+atr10, extend=extend.right, color=color.new(color111, 10),width=1)
    line lower1 = line.new(n[n - b1 + length111112], dndndn[n - b1], n[n - b2 + length111112], dndndn[n - b2], extend=extend.right, color=color.new(color222, 10), width=1)
    line upupupper2 = line.new(n[n - a1 + length11111], upupup[n - a1], n[n - a2 + length11111], upupup[n - a2], extend=extend.right, color=color.new(color111, 10), width=1)
    line lower2 = line.new(n[n - b1 + length111112], dndndn[n - b1]-atr10, n[n - b2 + length111112], dndndn[n - b2]-atr10, extend=extend.right, color=color.new(color222, 10), width=1)
    linefill t = linefill.new(upupupper1, upupupper2, color.new(color111, 75))
    linefill y = linefill.new(lower1, lower2, color.new(color222, 75))
    line.delete(upupupper1[1])
    line.delete(lower1[1])
    line.delete(upupupper2[1])
    line.delete(lower2[1])




//----------------------------------------}
//Fair Value Gaps
//----------------------------------------{
//----------------------------------------}
//Previous day/week high/low
//----------------------------------------{



//Daily
show_pdhl = input(false, 'Daily'
  , inline = 'daily'
  , group = 'Highs & Lows MTF')

pdhl_style = input.string('⎯⎯⎯', ''
  , options = ['⎯⎯⎯', '----', '····']
  , inline = 'daily'
  , group = 'Highs & Lows MTF')

pdhl_css = input(#2157f3, ''
  , inline = 'daily'
  , group = 'Highs & Lows MTF')

//Weekly
show_pwhl = input(false, 'Weekly'
  , inline = 'weekly'
  , group = 'Highs & Lows MTF')

pwhl_style = input.string('⎯⎯⎯', ''
  , options = ['⎯⎯⎯', '----', '····']
  , inline = 'weekly'
  , group = 'Highs & Lows MTF')

pwhl_css = input(#2157f3, ''
  , inline = 'weekly'
  , group = 'Highs & Lows MTF')

//Monthly
show_pmhl = input(false, 'Monthly'
  , inline = 'monthly'
  , group = 'Highs & Lows MTF')

pmhl_style = input.string('⎯⎯⎯', ''
  , options = ['⎯⎯⎯', '----', '····']
  , inline = 'monthly'
  , group = 'Highs & Lows MTF')

pmhl_css = input(#2157f3, ''
  , inline = 'monthly'
  , group = 'Highs & Lows MTF')

//----------------------------------------}
//Premium/Discount zones
//----------------------------------------{

//-----------------------------------------------------------------------------}
//Functions
//-----------------------------------------------------------------------------{
//n = bar_index


cmean_range = ta.cum(high - low) / n

//HL Output function
hl() => [high, low]

//Get ohlc values function
get_ohlc()=> [close[1], open[1], high, low, high[2], low[2]]

//Display Structure function
display_Structure(x, y, txt, css, dashed, down, lbl_size)=>
    structure_line = line.new(x, y, n, y
      , color = css
      , style = dashed ? line.style_dashed : line.style_solid)

    structure_lbl = label.new(int(math.avg(x, n)), y, txt
      , color = TRANSP_CSS
      , textcolor = css
      , style = down ? label.style_label_down : label.style_label_up
      , size = lbl_size)

    if mode == 'Present'
        line.delete(structure_line[1])
        label.delete(structure_lbl[1])

//Swings detection/measurements
swings(len)=>
    var os = 0
    
    upper = ta.highest(len)
    lower = ta.lowest(len)

    os := high[len] > upper ? 0 : low[len] < lower ? 1 : os[1]

    top = os == 0 and os[1] != 0 ? high[len] : 0
    btm = os == 1 and os[1] != 1 ? low[len] : 0

    [top, btm]

        
//Line Style function
get_line_style(style) =>
    out = switch style
        '⎯⎯⎯'  => line.style_solid
        '----' => line.style_dashed
        '····' => line.style_dotted

//Set line/labels function for previous high/lows
phl(h, l, tf, css)=>
    var line high_line = line.new(na,na,na,na
      , xloc = xloc.bar_time
      , color = css
      , style = get_line_style(pdhl_style))

    var label high_lbl = label.new(na,na
      , xloc = xloc.bar_time
      , text = str.format('P{0}H', tf)
      , color = TRANSP_CSS
      , textcolor = css
      , size = size.small
      , style = label.style_label_left)

    var line low_line = line.new(na,na,na,na
      , xloc = xloc.bar_time
      , color = css
      , style = get_line_style(pdhl_style))

    var label low_lbl = label.new(na,na
      , xloc = xloc.bar_time
      , text = str.format('P{0}L', tf)
      , color = TRANSP_CSS
      , textcolor = css
      , size = size.small
      , style = label.style_label_left)

    hy = ta.valuewhen(h != h[1], h, 1)
    hx = ta.valuewhen(h == high, time, 1)

    ly = ta.valuewhen(l != l[1], l, 1)
    lx = ta.valuewhen(l == low, time, 1)

    if barstate.islast
        ext = time + (time - time[1])*20

        //High
        line.set_xy1(high_line, hx, hy)
        line.set_xy2(high_line, ext, hy)

        label.set_xy(high_lbl, ext, hy)

        //Low
        line.set_xy1(low_line, lx, ly)
        line.set_xy2(low_line, ext, ly)

        label.set_xy(low_lbl, ext, ly)

//-----------------------------------------------------------------------------}
//Global variables
//-----------------------------------------------------------------------------{
var trend = 0, var itrend = 0

var top_y = 0., var top_x = 0
var btm_y = 0., var btm_x = 0

var itop_y = 0., var itop_x = 0
var ibtm_y = 0., var ibtm_x = 0

var trail_up = high, var trail_dn = low
var trail_up_x = 0,  var trail_dn_x = 0

var top_cross = true,  var btm_cross = true
var itop_cross = true, var ibtm_cross = true

var txt_top = '',  var txt_btm = ''

//Alerts
bull_choch_alert = false 
bull_bos_alert   = false 

bear_choch_alert = false 
bear_bos_alert   = false 

bull_ichoch_alert = false 
bull_ibos_alert   = false 

bear_ichoch_alert = false 
bear_ibos_alert   = false 

bull_iob_break = false 
bear_iob_break = false

bull_ob_break = false 
bear_ob_break = false

eqh_alert = false 
eql_alert = false 

//Structure colors
var bull_css = style == 'Monochrome' ? #b2b5be 
  : swing_bull_css

var bear_css = style == 'Monochrome' ? #b2b5be 
  : swing_bear_css

var ibull_css = style == 'Monochrome' ? #b2b5be 
  : swing_ibull_css

var ibear_css = style == 'Monochrome' ? #b2b5be 
  : swing_ibear_css

//Labels size
var internal_structure_lbl_size = internal_structure_size == 'Tiny' 
  ? size.tiny 
  : internal_structure_size == 'Small' 
  ? size.small 
  : size.normal 

var swing_structure_lbl_size = swing_structure_size == 'Tiny' 
  ? size.tiny 
  : swing_structure_size == 'Small' 
  ? size.small 
  : size.normal 

var eqhl_lbl_size = eq_size == 'Tiny' 
  ? size.tiny 
  : eq_size == 'Small' 
  ? size.small
  : size.normal 

//Swings
[top, btm] = swings(length)

[itop, ibtm] = swings(5)

//-----------------------------------------------------------------------------}
//Pivot High
//-----------------------------------------------------------------------------{
var line extend_top = na

var label extend_top_lbl = label.new(na, na
  , color = TRANSP_CSS
  , textcolor = bear_css
  , style = label.style_label_down
  , size = size.tiny)

if top
    top_cross := true
    txt_top := top > top_y ? 'HH' : 'LH'

    if show_swings
        top_lbl = label.new(n-length, top, txt_top
          , color = TRANSP_CSS
          , textcolor = bear_css
          , style = label.style_label_down
          , size = swing_structure_lbl_size)

        if mode == 'Present'
            label.delete(top_lbl[1])

    //Extend recent top to last bar
    line.delete(extend_top[1])
    
    if show_hl_swings
        extend_top := line.new(n-length, top, n, top     , color = bear_css)

    top_y := top
    top_x := n - length

    trail_up := top
    trail_up_x := n - length

if itop
    itop_cross := true

    itop_y := itop
    itop_x := n - 5

//Trailing maximum
trail_up := math.max(high, trail_up)
trail_up_x := trail_up == high ? n : trail_up_x

//Set top extension label/line
if barstate.islast and show_hl_swings
    line.set_xy1(extend_top, trail_up_x, trail_up)
    line.set_xy2(extend_top, n + 20, trail_up)

    label.set_x(extend_top_lbl, n + 20)
    label.set_y(extend_top_lbl, trail_up)
    label.set_text(extend_top_lbl, trend < 0 ? 'Strong High' : 'Weak High')

//-----------------------------------------------------------------------------}
//Pivot Low
//-----------------------------------------------------------------------------{
var line extend_btm = na 

var label extend_btm_lbl = label.new(na, na
  , color = TRANSP_CSS
  , textcolor = bull_css
  , style = label.style_label_up
  , size = size.tiny)

if btm
    btm_cross := true
    txt_btm := btm < btm_y ? 'LL' : 'HL'
    
    if show_swings
        btm_lbl = label.new(n - length, btm, txt_btm
          , color = TRANSP_CSS
          , textcolor = bull_css
          , style = label.style_label_up
          , size = swing_structure_lbl_size)

        if mode == 'Present'
            label.delete(btm_lbl[1])
    
    //Extend recent btm to last bar
    line.delete(extend_btm[1])
    if show_hl_swings
        extend_btm := line.new(n - length, btm, n, btm , color = bull_css)

    btm_y := btm
    btm_x := n-length

    trail_dn := btm
    trail_dn_x := n-length

if ibtm
    ibtm_cross := true

    ibtm_y := ibtm
    ibtm_x := n - 5

//Trailing minimum
trail_dn := math.min(low, trail_dn)
trail_dn_x := trail_dn == low ? n : trail_dn_x

//Set btm extension label/line
if barstate.islast and show_hl_swings
    line.set_xy1(extend_btm, trail_dn_x, trail_dn)
    line.set_xy2(extend_btm, n + 20, trail_dn)

    label.set_x(extend_btm_lbl, n + 20)
    label.set_y(extend_btm_lbl, trail_dn)
    label.set_text(extend_btm_lbl, trend > 0 ? 'Strong Low' : 'Weak Low')
    
    

//-----------------------------------------------------------------------------}
//Order Blocks Arrays
//-----------------------------------------------------------------------------{
var iob_top = array.new_float(0)
var iob_btm = array.new_float(0)
var iob_left = array.new_int(0)
var iob_type = array.new_int(0)

var ob_top = array.new_float(0)
var ob_btm = array.new_float(0)
var ob_left = array.new_int(0)
var ob_type = array.new_int(0)

//-----------------------------------------------------------------------------}
//Pivot High BOS/CHoCH
//-----------------------------------------------------------------------------{
//Filtering
var bull_concordant = true

if ifilter_confluence
    bull_concordant := high - math.max(close, open) > math.min(close, open - low)

//Detect internal bullish Structure
swingtrend = 1

if ta.crossover(close, itop_y) and itop_cross and top_y != itop_y and bull_concordant
    bool choch = na
    
    if itrend < 0
        choch := true
        candlecolor := 0
        bull_ichoch_alert := true
    else 
        bull_ibos_alert := true
        candlecolor := 1
    
    txt = choch ? 'CHoCH' : 'BOS'
    if choch
        internalBOS := 1
    else
        internalBOS += 1

    if show_internals
        if show_ibull == 'All' or (show_ibull == 'BOS' and not choch) or (show_ibull == 'CHoCH' and choch)
            display_Structure(itop_x, itop_y, txt, ibull_css, true, true, internal_structure_lbl_size)
    
    itop_cross := false
    itrend := 1
    

//Detect bullish Structure
if ta.crossover(close, top_y) and top_cross
    bool choch = na
    
    if trend < 0
        choch := true
        bull_choch_alert := true
    else 
        bull_bos_alert := true

    txt = choch ? 'CHoCH' : 'BOS'
    if choch
        swingBOS := 1
    else
        swingBOS += 1
    if show_Structure
        if show_bull == 'All' or (show_bull == 'BOS' and not choch) or (show_bull == 'CHoCH' and choch)
            display_Structure(top_x, top_y, txt, bull_css, false, true, swing_structure_lbl_size)

    //Order Block
  

    top_cross := false
    trend := 1
    swingtrend := 1

//-----------------------------------------------------------------------------}
//Pivot Low BOS/CHoCH
//-----------------------------------------------------------------------------{
var bear_concordant = true

if ifilter_confluence
    bear_concordant := high - math.max(close, open) < math.min(close, open - low)

//Detect internal bearish Structure
if ta.crossunder(close, ibtm_y) and ibtm_cross and btm_y != ibtm_y and bear_concordant
    bool choch = false
    
    if itrend > 0
        choch := true
        bear_ichoch_alert := true
        candlecolor := 0
    else 
        bear_ibos_alert := true
        candlecolor := -1
    
    txt = choch ? 'CHoCH' : 'BOS'
    if choch
        internalBOS := -1
    else
        internalBOS -= 1
    if show_internals
        if show_ibear == 'All' or (show_ibear == 'BOS' and not choch) or (show_ibear == 'CHoCH' and choch)
            display_Structure(ibtm_x, ibtm_y, txt, ibear_css, true, false, internal_structure_lbl_size)
    
    ibtm_cross := false
    itrend := -1
    
    //Internal Order Block
   

//Detect bearish Structure
if ta.crossunder(close, btm_y) and btm_cross
    bool choch = na
    
    if trend > 0
        choch := true
        bear_choch_alert := true
    else 
        bear_bos_alert := true
        

    txt = choch ? 'CHoCH' : 'BOS'
    if choch
        swingBOS := -1
    else
        swingBOS -=1
    if show_Structure
        if show_bear == 'All' or (show_bear == 'BOS' and not choch) or (show_bear == 'CHoCH' and choch)
            display_Structure(btm_x, btm_y, txt, bear_css, false, false, swing_structure_lbl_size)
    
    //Order Block
    
    btm_cross := false
    trend := -1
    swingtrend := -1

//-----------------------------------------------------------------------------}
//Order Blocks
//-----------------------------------------------------------------------------{
//Set order blocks

const bool DEBUG = false
const int maxBoxesCount = 500
const float overlapThresholdPercentage = 0
const int maxDistanceToLastBar = 1750 // Affects Running Time
const int maxOrderBlocks = 30



textColor = color.white
extendZonesBy = DEBUG ? input.int(15, "Extend Zones", group = "Style", minval = 1, maxval = 30, inline = "ExtendZones") : 15
extendZonesDynamic = DEBUG ? input.bool(true, "Dynamic", group = "Style", inline = "ExtendZones") : true
combinedText = DEBUG ? input.bool(false, "Combined Text", group = "Style", inline = "CombinedColor") : false
volumeBarsPlace = DEBUG ? input.string("Left", "Show Volume Bars At", options = ["Left", "Right"], group = "Style", inline = "volumebars") : "Left"
mirrorVolumeBars = DEBUG ? input.bool(true, "Mirror Volume Bars", group = "Style", inline = "volumebars") : true

volumeBarsLeftSide = (volumeBarsPlace == "Left")
extendZonesByTime = extendZonesBy * timeframe.in_seconds(timeframe.period) * 1000

atrt  = ta.atr(10)

type orderBlockInfo
    float top
    float bottom
    float obVolume
    string obType
    int startTime
    float bbVolume
    float obLowVolume
    float obHighVolume
    bool breaker
    int breakTime
    string timeframeStr
    bool disabled = false
    string combinedTimeframesStr = na
    bool combined = false

type orderBlock
    orderBlockInfo info
    bool isRendered = false

    box orderBox = na
    box breakerBox = na

    line orderBoxLineTop = na
    line orderBoxLineBottom = na
    line breakerBoxLineTop = na
    line breakerBoxLineBottom = na
    //
    box orderBoxText = na
    box orderBoxPositive = na
    box orderBoxNegative = na

    line orderSeperator = na
    line orderTextSeperator = na

createOrderBlock (orderBlockInfo orderBlockInfoF) =>
    orderBlock newOrderBlock = orderBlock.new(orderBlockInfoF)
    newOrderBlock

safeDeleteOrderBlock (orderBlock orderBlockF) =>
    orderBlockF.isRendered := false

    box.delete(orderBlockF.orderBox)
    box.delete(orderBlockF.breakerBox)
    box.delete(orderBlockF.orderBoxText)
    box.delete(orderBlockF.orderBoxPositive)
    box.delete(orderBlockF.orderBoxNegative)

    line.delete(orderBlockF.orderBoxLineTop)
    line.delete(orderBlockF.orderBoxLineBottom)
    line.delete(orderBlockF.breakerBoxLineTop)
    line.delete(orderBlockF.breakerBoxLineBottom)
    line.delete(orderBlockF.orderSeperator)
    line.delete(orderBlockF.orderTextSeperator)

type timeframeInfo
    int index = na
    string timeframeStr = na
    bool isEnabled = false

    orderBlockInfo[] bullishOrderBlocksList = na
    orderBlockInfo[] bearishOrderBlocksList = na

newTimeframeInfo (index, timeframeStr, isEnabled) =>
    newTFInfo = timeframeInfo.new()
    newTFInfo.index := index
    newTFInfo.isEnabled := isEnabled
    newTFInfo.timeframeStr := timeframeStr

    newTFInfo

type obSwing
    int x = na    
    float y = na
    float swingVolume = na
    bool crossed = false

// ____ TYPES END ____

var timeframeInfo[] timeframeInfos = array.from(newTimeframeInfo(1, timeframe1, timeframe1Enabled), newTimeframeInfo(2, timeframe2, timeframe2Enabled), newTimeframeInfo(3, timeframe3, timeframe3Enabled))
var bullishOrderBlocksList = array.new<orderBlockInfo>(0)
var bearishOrderBlocksList = array.new<orderBlockInfo>(0)

var allOrderBlocksList = array.new<orderBlock>(0)

moveLine(_line, _x, _y, _x2) =>
    line.set_xy1(_line, _x,  _y)
    line.set_xy2(_line, _x2, _y)

moveBox (_box, _topLeftX, _topLeftY, _bottomRightX, _bottomRightY) =>
    box.set_lefttop(_box, _topLeftX, _topLeftY)
    box.set_rightbottom(_box, _bottomRightX, _bottomRightY)

isTimeframeLower (timeframe1F, timeframe2F) =>
    timeframe.in_seconds(timeframe1F) < timeframe.in_seconds(timeframe2F)


tfe (timeframe1F, timeframe2F) =>
    timeframe.in_seconds(timeframe1F) <= timeframe.in_seconds(timeframe2F)

getMinTimeframe (timeframe1F, timeframe2F) =>
    if isTimeframeLower(timeframe1F, timeframe2F)
        timeframe1F
    else
        timeframe2F

getMaxTimeframe (timeframe1F, timeframe2F) =>
    if isTimeframeLower(timeframe1F, timeframe2F)
        timeframe2F
    else
        timeframe1F

formatTimeframeString (formatTimeframe) =>
    timeframeF = formatTimeframe == "" ? timeframe.period : formatTimeframe
    
    if str.contains(timeframeF, "D") or str.contains(timeframeF, "W") or str.contains(timeframeF, "S") or str.contains(timeframeF, "M")
        timeframeF
    else
        seconds = timeframe.in_seconds(timeframeF)
        if seconds >= 3600
            hourCount = int(seconds / 3600)
            str.tostring(hourCount) + " Hour" + (hourCount > 1 ? "s" : "")
        else
            timeframeF + " Min"

betterCross(s1, s2) =>
    string ret = na
    if s1 >= s2 and s1[1] < s2
        ret := "Bull"
    if s1 < s2 and s1[1] >= s2
        ret := "Bear"
    ret

colorWithTransparency (colorF, transparencyX) =>
    color.new(colorF, color.t(colorF) * transparencyX)

createOBBox (boxColor, transparencyX = 1.0, xlocType = xloc.bar_time) =>
    box.new(na, na, na, na, text_size = size.normal, xloc = xlocType, extend = extend.none, bgcolor = color.new(boxColor, transparencyX), text_color = textColor, text_halign = text.align_center, border_color = #00000000)

renderOrderBlock (orderBlock ob) =>
    orderBlockInfo info = ob.info
    ob.isRendered := true
    orderColor = ob.info.obType == "Bull" ? bullOrderBlockColor : bearOrderBlockColor

    if OBsEnabled and (not false or not (false and info.breaker)) and not (not showInvalidated and info.breaker) and ob.info.disabled == false
        ob.orderBox := createOBBox(orderColor, transp)
        box.set_extend(ob.orderBox, extend.right)
        if ob.info.combined
            ob.orderBox.set_bgcolor(color.new(orderColor, transp))
        ob.orderBoxText := createOBBox(orderColor)
        if orderBlockVolumetricInfo
            ob.orderBoxPositive := createOBBox(bullOrderBlockColor, 30)
            ob.orderBoxNegative := createOBBox(bearOrderBlockColor, 30)
            ob.orderSeperator := line.new(na,na,na,na,xloc.bar_time,extend.none,orderColor,line.style_dashed,1)
            //ob.orderTextSeperator := line.new(na,na, na,na,xloc.bar_index,extend.none,orderColor,line.style_solid,1)
            

        zoneSize = extendZonesDynamic ? na(info.breakTime) ? extendZonesByTime : (info.breakTime - info.startTime) : extendZonesByTime
        if na(info.breakTime)
            zoneSize := (time + 1) - info.startTime

        startX = volumeBarsLeftSide ? info.startTime : info.startTime + zoneSize - zoneSize / 3
        maxEndX = info.startTime + zoneSize

        moveBox(ob.orderBox, info.startTime, info.top, info.startTime + zoneSize, info.bottom)
        moveBox(ob.orderBoxText, volumeBarsLeftSide ? maxEndX : info.startTime, info.top, volumeBarsLeftSide ? info.startTime + zoneSize : startX, info.bottom)

        percentage = int((math.min(info.obHighVolume, info.obLowVolume) / math.max(info.obHighVolume, info.obLowVolume)) * 100.0)
        OBText = (na(ob.info.combinedTimeframesStr) ? formatTimeframeString(ob.info.timeframeStr) : ob.info.combinedTimeframesStr) + " OB"
        box.set_text(ob.orderBoxText, (orderBlockVolumetricInfo ? str.tostring(ob.info.obVolume, format.volume) + " (" + str.tostring(percentage) + "%)" : "") + " " + formatTimeframeString(info.timeframeStr)) 
        box.set_text_halign(ob.orderBoxText, text.align_left)
        box.set_text_size(ob.orderBoxText, size.small)
        box.set_text_color(ob.orderBoxText, ob.info.obType == "Bull" ? bullOrderBlockColor : bearOrderBlockColor)
        
        if orderBlockVolumetricInfo
            showHighLowBoxText = false

            curEndXHigh = int(math.ceil((info.obHighVolume / info.obVolume) * (maxEndX - startX) + startX))
            curEndXLow = int(math.ceil((info.obLowVolume / info.obVolume) * (maxEndX - startX) + startX))

            moveBox(ob.orderBoxPositive, mirrorVolumeBars ? startX : curEndXLow, info.top, mirrorVolumeBars ? curEndXHigh : maxEndX, (info.bottom + info.top) / 2)
            box.set_text(ob.orderBoxPositive, showHighLowBoxText ? str.tostring(info.obHighVolume, format.volume) : "")
    
            moveBox(ob.orderBoxNegative, mirrorVolumeBars ? startX : curEndXHigh, info.bottom, mirrorVolumeBars ? curEndXLow : maxEndX, (info.bottom + info.top) / 2)
            box.set_text(ob.orderBoxNegative, showHighLowBoxText ? str.tostring(info.obLowVolume, format.volume) : "")

            moveLine(ob.orderSeperator, volumeBarsLeftSide ? startX : maxEndX, (info.bottom + info.top) / 2, volumeBarsLeftSide ? maxEndX : startX)

            //line.set_xy1(ob.orderTextSeperator, barstate.isconfirmed ? bar_index+1 : bar_index+1, info.top)
            //line.set_xy2(ob.orderTextSeperator, barstate.isconfirmed ? bar_index+1 : bar_index+1, info.bottom)
            //line.set_color(ob.orderTextSeperator, orderColor)
            

findOBSwings(len) =>
    var swingType = 0
    var obSwing top = obSwing.new(na, na)
    var obSwing bottom = obSwing.new(na, na)
    
    upper = ta.highest(len)
    lower = ta.lowest(len)

    swingType := high[len] > upper ? 0 : low[len] < lower ? 1 : swingType

    if swingType == 0 and swingType[1] != 0
        top := obSwing.new(bar_index[len], high[len], volume[len])
    
    if swingType == 1 and swingType[1] != 1
        bottom := obSwing.new(bar_index[len], low[len], volume[len])

    [top, bottom]

findOrderBlocks () =>
    if bar_index > last_bar_index - maxDistanceToLastBar
        [top, btm] = findOBSwings(swingLength)
        useBody = false
        max = useBody ? math.max(close, open) : high
        min = useBody ? math.min(close, open) : low

        // Bullish Order Block
        bullishBreaked = 0

        if bullishOrderBlocksList.size() > 0
            for i = bullishOrderBlocksList.size() - 1 to 0
                currentOB = bullishOrderBlocksList.get(i)
            
                if not currentOB.breaker 
                    if (obEndMethod == "Wick" ? low : math.min(open, close)) < currentOB.bottom
                        currentOB.breaker := true
                        currentOB.breakTime := time
                        currentOB.bbVolume := volume
                else
                    if high > currentOB.top
                        bullishOrderBlocksList.remove(i)
                    else if i < bullishOrderBlocks and top.y < currentOB.top and top.y > currentOB.bottom 
                        bullishBreaked := 1

        if close > top.y and not top.crossed
            top.crossed := true

            boxBtm = max[1]
            boxTop = min[1]
            boxLoc = time[1]

            for i = 1 to (bar_index - top.x) - 1
                boxBtm := math.min(min[i], boxBtm)
                boxTop := boxBtm == min[i] ? max[i] : boxTop
                boxLoc := boxBtm == min[i] ? time[i] : boxLoc

            newOrderBlockInfo = orderBlockInfo.new(boxTop, boxBtm, volume + volume[1] + volume[2], "Bull", boxLoc)
            newOrderBlockInfo.obLowVolume := volume[2]
            newOrderBlockInfo.obHighVolume := volume + volume[1]
            
            obSize = math.abs(newOrderBlockInfo.top - newOrderBlockInfo.bottom)
            if obSize <= atrt * maxATRMult
                bullishOrderBlocksList.unshift(newOrderBlockInfo)
                if bullishOrderBlocksList.size() > maxOrderBlocks
                    bullishOrderBlocksList.pop()

        // Bearish Order Block
        
        bearishBreaked = 0

        if bearishOrderBlocksList.size() > 0
            for i = bearishOrderBlocksList.size() - 1 to 0
                currentOB = bearishOrderBlocksList.get(i)

                if not currentOB.breaker 
                    if (obEndMethod == "Wick" ? high : math.max(open, close)) > currentOB.top
                        currentOB.breaker := true
                        currentOB.breakTime := time
                        currentOB.bbVolume := volume
                else
                    if low < currentOB.bottom
                        bearishOrderBlocksList.remove(i)
                    else if i < bearishOrderBlocks and btm.y > currentOB.bottom and btm.y < currentOB.top 
                        bearishBreaked := 1

        if close < btm.y and not btm.crossed
            btm.crossed := true

            boxBtm = min[1]
            boxTop = max[1]
            boxLoc = time[1]

            for i = 1 to (bar_index - btm.x) - 1
                boxTop := math.max(max[i], boxTop)
                boxBtm := boxTop == max[i] ? min[i] : boxBtm
                boxLoc := boxTop == max[i] ? time[i] : boxLoc

            newOrderBlockInfo = orderBlockInfo.new(boxTop, boxBtm, volume + volume[1] + volume[2], "Bear", boxLoc)
            newOrderBlockInfo.obLowVolume := volume + volume[1]
            newOrderBlockInfo.obHighVolume := volume[2]

            obSize = math.abs(newOrderBlockInfo.top - newOrderBlockInfo.bottom)
            if obSize <= atrt * maxATRMult
                bearishOrderBlocksList.unshift(newOrderBlockInfo)
                if bearishOrderBlocksList.size() > maxOrderBlocks
                    bearishOrderBlocksList.pop()
    true

areaOfOB (orderBlockInfo OBInfoF) =>
    float XA1 = OBInfoF.startTime
    float XA2 = na(OBInfoF.breakTime) ? time + 1 : OBInfoF.breakTime
    float YA1 = OBInfoF.top
    float YA2 = OBInfoF.bottom
    float edge1 = math.sqrt((XA2 - XA1) * (XA2 - XA1) + (YA2 - YA2) * (YA2 - YA2))
    float edge2 = math.sqrt((XA2 - XA2) * (XA2 - XA2) + (YA2 - YA1) * (YA2 - YA1))
    float totalArea = edge1 * edge2
    totalArea


isOBValid (orderBlockInfo OBInfo) =>
    valid = true
    if OBInfo.disabled
        valid := false
    valid


doOBsTouch (orderBlockInfo OBInfo1, orderBlockInfo OBInfo2) =>
    float XA1 = OBInfo1.startTime
    float XA2 = na(OBInfo1.breakTime) ? time + 1 : OBInfo1.breakTime
    float YA1 = OBInfo1.top
    float YA2 = OBInfo1.bottom

    float XB1 = OBInfo2.startTime
    float XB2 = na(OBInfo2.breakTime) ? time + 1 : OBInfo2.breakTime
    float YB1 = OBInfo2.top
    float YB2 = OBInfo2.bottom
    float intersectionArea = math.max(0, math.min(XA2, XB2) - math.max(XA1, XB1)) * math.max(0, math.min(YA1, YB1) - math.max(YA2, YB2))
    float unionArea = areaOfOB(OBInfo1) + areaOfOB(OBInfo2) - intersectionArea
    
    float overlapPercentage = (intersectionArea / unionArea) * 100.0

    if overlapPercentage > overlapThresholdPercentage
        true
    else
        false

combineOBsFunc () =>
    if allOrderBlocksList.size() > 0
        lastCombinations = 999
        while lastCombinations > 0
            lastCombinations := 0
            for i = 0 to allOrderBlocksList.size() - 1
                curOB1 = allOrderBlocksList.get(i)
                for j = 0 to allOrderBlocksList.size() - 1
                    curOB2 = allOrderBlocksList.get(j)
                    if i == j
                        continue
                    if not isOBValid(curOB1.info) or not isOBValid(curOB2.info)
                        continue
                    if curOB1.info.obType != curOB2.info.obType
                        continue
                    if doOBsTouch(curOB1.info, curOB2.info)
                        if curOB1.info.bottom < curOB2.info.top and curOB1.info.top > curOB2.info.top
                            curOB2.info.top := curOB1.info.bottom
                            lastCombinations := lastCombinations + 1
                        if curOB1.info.top >= curOB2.info.top and curOB1.info.bottom <= curOB2.info.bottom
                            curOB2.info.disabled := true
                            lastCombinations := lastCombinations + 1


reqSeq (timeframeStr) =>
    [bullishOrderBlocksListF, bearishOrderBlocksListF] = request.security(syminfo.tickerid, timeframeStr, [bullishOrderBlocksList, bearishOrderBlocksList])
    [bullishOrderBlocksListF, bearishOrderBlocksListF]

getTFData (timeframeInfo timeframeInfoF, timeframeStr) =>
    if not isTimeframeLower(timeframeInfoF.timeframeStr, timeframe.period) and timeframeInfoF.isEnabled
        [bullishOrderBlocksListF, bearishOrderBlocksListF] = reqSeq(timeframeStr)
        [bullishOrderBlocksListF, bearishOrderBlocksListF]
    else
        [na, na]

handleTimeframeInfo (timeframeInfo timeframeInfoF, bullishOrderBlocksListF, bearishOrderBlocksListF) =>
    if not isTimeframeLower(timeframeInfoF.timeframeStr, timeframe.period) and timeframeInfoF.isEnabled
        timeframeInfoF.bullishOrderBlocksList := bullishOrderBlocksListF
        timeframeInfoF.bearishOrderBlocksList := bearishOrderBlocksListF


handleOrderBlocksFinal () =>
    if DEBUG
        log.info("Bullish OB Count " + str.tostring(bullishOrderBlocksList.size()))
        log.info("Bearish OB Count " + str.tostring(bearishOrderBlocksList.size()))
    
    if allOrderBlocksList.size () > 0
        for i = 0 to allOrderBlocksList.size() - 1
            safeDeleteOrderBlock(allOrderBlocksList.get(i))
    allOrderBlocksList.clear()    

    for i = 0 to timeframeInfos.size() - 1
        curTimeframe = timeframeInfos.get(i)
        if na(curTimeframe)
            continue
        if not curTimeframe.isEnabled
            continue
        if na(curTimeframe.bullishOrderBlocksList)
            continue
        if curTimeframe.bullishOrderBlocksList.size() > 0
            while curTimeframe.bullishOrderBlocksList.size() > bullishOrderBlocks
                curTimeframe.bullishOrderBlocksList.pop()
            for j = 0 to math.min(curTimeframe.bullishOrderBlocksList.size() - 1, bullishOrderBlocks - 1)
                bool overlapped = false
                orderBlockInfoF = curTimeframe.bullishOrderBlocksList.get(j)
                orderBlockInfoF.timeframeStr := curTimeframe.timeframeStr
                if allOrderBlocksList.size() > 0
                    for t = 0 to allOrderBlocksList.size()-1
                        if doOBsTouch(orderBlockInfoF, allOrderBlocksList.get(t).info) 
                            overlapped := true
                    
                if overlapped == false
                    allOrderBlocksList.unshift(createOrderBlock(orderBlockInfo.copy(orderBlockInfoF)))

        if not na(curTimeframe.bearishOrderBlocksList) and curTimeframe.bearishOrderBlocksList.size() > 0
            while curTimeframe.bearishOrderBlocksList.size() > bearishOrderBlocks
                curTimeframe.bearishOrderBlocksList.pop()
            for j = 0 to math.min(curTimeframe.bearishOrderBlocksList.size() - 1, bearishOrderBlocks - 1)
                orderBlockInfoF = curTimeframe.bearishOrderBlocksList.get(j)
                orderBlockInfoF.timeframeStr := curTimeframe.timeframeStr
                bool overlapped = false
                if allOrderBlocksList.size() > 0
                    for t = 0 to allOrderBlocksList.size()-1
                        bt1 = orderBlockInfoF.bottom
                        tp1 = orderBlockInfoF.top
                        bt2 = allOrderBlocksList.get(t).info.bottom
                        tp2 = allOrderBlocksList.get(t).info.top
                        if tp2 >= tp1 and bt2 <= bt1
                            overlapped := true
                if overlapped == false
                    allOrderBlocksList.unshift(createOrderBlock(orderBlockInfo.copy(orderBlockInfoF)))

    if combineOBs
        combineOBsFunc()    

    if allOrderBlocksList.size() > 0
        for i = 0 to allOrderBlocksList.size() - 1
            curOB = allOrderBlocksList.get(i)
            if isOBValid(curOB.info)
                renderOrderBlock(curOB)

findOrderBlocks()

currentTF = timeframe.period

[bullishOrderBlocksListTimeframe1, bearishOrderBlocksListTimeframe1] = getTFData(timeframeInfos.get(0), timeframe1)

[bullishOrderBlocksListTimeframe2, bearishOrderBlocksListTimeframe2] = getTFData(timeframeInfos.get(1), "15")
[bullishOrderBlocksListTimeframe3, bearishOrderBlocksListTimeframe3] = getTFData(timeframeInfos.get(2), "1") 

if barstate.isconfirmed
    if tfe(currentTF, timeframe1)
        handleTimeframeInfo(timeframeInfos.get(0), bullishOrderBlocksListTimeframe1, bearishOrderBlocksListTimeframe1)
    if tfe(currentTF, "15")
        handleTimeframeInfo(timeframeInfos.get(1), bullishOrderBlocksListTimeframe2, bearishOrderBlocksListTimeframe2)
    if tfe(currentTF, "1")

        handleTimeframeInfo(timeframeInfos.get(2), bullishOrderBlocksListTimeframe3, bearishOrderBlocksListTimeframe3)
    handleOrderBlocksFinal()    

//-----------------------------------------------------------------------------}
//Price Action Concepts
//-----------------------------------------------------------------------------{
var eq_prev_top = 0.
var eq_top_x = 0

var eq_prev_btm = 0.
var eq_btm_x = 0

if show_eq
    eq_top = ta.pivothigh(eq_len, eq_len)
    eq_btm = ta.pivotlow(eq_len, eq_len)

    if eq_top 
        max = math.max(eq_top, eq_prev_top)
        min = math.min(eq_top, eq_prev_top)
        
        if max < min + atr * eq_threshold
            eqh_line = line.new(eq_top_x, eq_prev_top, n-eq_len, eq_top
              , color = bear_css
              , style = line.style_dotted)

            eqh_lbl = label.new(int(math.avg(n-eq_len, eq_top_x)), eq_top, 'EQH'
              , color = #00000000
              , textcolor = bear_css
              , style = label.style_label_down
              , size = eqhl_lbl_size)

            if mode == 'Present'
                line.delete(eqh_line[1])
                label.delete(eqh_lbl[1])
            
            eqh_alert := true

        eq_prev_top := eq_top
        eq_top_x := n-eq_len

    if eq_btm 
        max = math.max(eq_btm, eq_prev_btm)
        min = math.min(eq_btm, eq_prev_btm)
        
        if min > max - atr * eq_threshold
            eql_line = line.new(eq_btm_x, eq_prev_btm, n-eq_len, eq_btm
              , color = bull_css
              , style = line.style_dotted)

            eql_lbl = label.new(int(math.avg(n-eq_len, eq_btm_x)), eq_btm, 'EQL'
              , color = #00000000
              , textcolor = bull_css
              , style = label.style_label_up
              , size = eqhl_lbl_size)

            eql_alert := true

            if mode == 'Present'
                line.delete(eql_line[1])
                label.delete(eql_lbl[1])

        eq_prev_btm := eq_btm
        eq_btm_x := n-eq_len

//-----------------------------------------------------------------------------}
//Fair Value Gaps
//-----------------------------------------------------------------------------{
var bullish_fvg_max = array.new_box(0)
var bullish_fvg_min = array.new_box(0)

var bearish_fvg_max = array.new_box(0)
var bearish_fvg_min = array.new_box(0)

float bullish_fvg_avg = na
float bearish_fvg_avg = na

bullish_fvg_cnd = false
bearish_fvg_cnd = false


//-----------------------------------------------------------------------------}
//Previous day/week high/lows
//-----------------------------------------------------------------------------{
//Daily high/low
[pdh, pdl] = request.security(syminfo.tickerid, 'D', hl()
  , lookahead = barmerge.lookahead_on)

//Weekly high/low
[pwh, pwl] = request.security(syminfo.tickerid, 'W', hl()
  , lookahead = barmerge.lookahead_on)

//Monthly high/low
[pmh, pml] = request.security(syminfo.tickerid, 'M', hl()
  , lookahead = barmerge.lookahead_on)

//Display Daily
if show_pdhl
    phl(pdh, pdl, 'D', pdhl_css)

//Display Weekly
if show_pwhl
    phl(pwh, pwl, 'W', pwhl_css)
    
//Display Monthly
if show_pmhl
    phl(pmh, pml, 'M', pmhl_css)

//-----------------------------------------------------------------------------}
//Premium/Discount/Equilibrium zones
//-----------------------------------------------------------------------------{
var premium = box.new(na, na, na, na
  , bgcolor = color.new(premium_css, 80)
  , border_color = na)

var premium_lbl = label.new(na, na
  , text = 'Premium'
  , color = TRANSP_CSS
  , textcolor = premium_css
  , style = label.style_label_down
  , size = size.small)

var eq = box.new(na, na, na, na
  , bgcolor = color.rgb(120, 123, 134, 80)
  , border_color = na)

var eq_lbl = label.new(na, na
  , text = 'Equilibrium'
  , color = TRANSP_CSS
  , textcolor = eq_css
  , style = label.style_label_left
  , size = size.small)

var discount = box.new(na, na, na, na
  , bgcolor = color.new(discount_css, 80)
  , border_color = na)

var discount_lbl = label.new(na, na
  , text = 'Discount'
  , color = TRANSP_CSS
  , textcolor = discount_css
  , style = label.style_label_up
  , size = size.small)


var e = box.new(na, na, na, na
  , bgcolor = color.rgb(120, 123, 134, 80)
  , border_color = color.rgb(120, 123, 134))

var e_lbl = label.new(na, na
  , text = 'Entry Zone'
  , color = TRANSP_CSS
  , textcolor = eq_css
  , style = label.style_label_left
  , size = size.small)


//Show Premium/Discount Areas


var l = line.new(na, na, na, na, color=color.rgb(120, 123, 134), style=line.style_dashed, width=1)
fib1 = 0.618
fib2 = 0.786
fib3 = (fib1+fib2)/2

fib1 := 1-fib1
fib2 := 1-fib2
fib3 := 1-fib3
//timeLeft = math.max(top_x, btm_x)
price1 = swingBOS < 0 ? trail_up : trail_dn
price2 = swingBOS < 0 ? trail_dn : trail_up
diff = price2-price1
fib1level = price1+diff*fib1
fib2level = price1+diff*fib2
fib3level = price1+diff*fib3


//Show Premium/Discount Areas
if barstate.islast and show_sd
    line.set_xy1(l, math.max(top_x, btm_x), fib3level)
    line.set_xy2(l, bar_index, fib3level)
    box.set_lefttop(e, math.max(top_x, btm_x), fib1level)
    box.set_rightbottom(e, bar_index, fib2level)
    label.set_xy(e_lbl, bar_index, fib3level)


    avg = math.avg(trail_up, trail_dn)

    box.set_lefttop(premium, math.max(top_x, btm_x), trail_up)
    box.set_rightbottom(premium, n, .95 * trail_up + .05 * trail_dn)

    label.set_xy(premium_lbl, int(math.avg(math.max(top_x, btm_x), n)), trail_up)

    box.set_lefttop(eq, math.max(top_x, btm_x), .525 * trail_up + .475*trail_dn)
    box.set_rightbottom(eq, n, .525 * trail_dn + .475 * trail_up)

    label.set_xy(eq_lbl, n, avg)
    
    box.set_lefttop(discount, math.max(top_x, btm_x), .95 * trail_dn + .05 * trail_up)
    box.set_rightbottom(discount, n, trail_dn)
    label.set_xy(discount_lbl, int(math.avg(math.max(top_x, btm_x), n)), trail_dn)

//-----------------------------------------------------------------------------}
//Trend
//-----------------------------------------------------------------------------{
var color trend_css = na

if show_trend
    if style == 'Colored'
        trend_css := candlecolor == 1 ? bull_css : candlecolor == 0 ? purplecolor : bear_css
    else if style == 'Monochrome'
        trend_css := itrend == 1 ? #b2b5be : #5d606b

barcolor(trend_css)
//-----------------------------------------------------------------------------}
//Alerts
//-----------------------------------------------------------------------------{
//Internal Structure
alertcondition(bull_ibos_alert, 'Internal Bullish BOS', 'Internal Bullish BOS formed')
alertcondition(bull_ichoch_alert, 'Internal Bullish CHoCH', 'Internal Bullish CHoCH formed')

alertcondition(bear_ibos_alert, 'Internal Bearish BOS', 'Internal Bearish BOS formed')
alertcondition(bear_ichoch_alert, 'Internal Bearish CHoCH', 'Internal Bearish CHoCH formed')

//Swing Structure
alertcondition(bull_bos_alert, 'Bullish BOS', 'Internal Bullish BOS formed')
alertcondition(bull_choch_alert, 'Bullish CHoCH', 'Internal Bullish CHoCH formed')

alertcondition(bear_bos_alert, 'Bearish BOS', 'Bearish BOS formed')
alertcondition(bear_choch_alert, 'Bearish CHoCH', 'Bearish CHoCH formed')

//order Blocks
alertcondition(bull_iob_break, 'Bullish Internal OB Breakout', 'Price broke bullish internal OB')
alertcondition(bear_iob_break, 'Bearish Internal OB Breakout', 'Price broke bearish internal OB')

alertcondition(bull_ob_break, 'Bullish Swing OB Breakout', 'Price broke bullish swing OB')
alertcondition(bear_ob_break, 'Bearish Swing OB Breakout', 'Price broke bearish swing OB')

//Price Action Concepts
alertcondition(eqh_alert, 'Equal Highs', 'Equal highs detected')
alertcondition(eql_alert, 'Equal Lows', 'Equal lows detected')

//FVG
alertcondition(bullish_fvg_cnd, 'Bullish FVG', 'Bullish FVG formed')
alertcondition(bearish_fvg_cnd, 'Bearish FVG', 'Bearish FVG formed')

//-----------------------------------------------------------------------------}
tb = table.new(position.top_right, 5, 5, bgcolor = #1e222d  , border_color = #373a46  , border_width = 1  , frame_color = #373a46  , frame_width = 1)



dashboardGreenText =#42bda8
squeezegreent = #0a907a
dashboardRedText = #ee787d
squeezeredt = #ed3544

dashboardGreenBackground = #284444
squeezegreenbg = #1a3a3e
dashboardRedBackground = #49343e
squeezeredbg = #482632

swingBOS1 = request.security(syminfo.ticker, scannerTimeframe, swingBOS)
internalBOS1 = request.security(syminfo.ticker, scannerTimeframe, internalBOS)

string swingText = swingBOS1 == 1 or swingBOS1 == -1 ? "CHoCH" : "BOS (" + str.tostring(math.abs(swingBOS1)-1) + ")"
string internalText = internalBOS1 == 1 or internalBOS1 == -1 ? "CHoCH" : "BOS (" + str.tostring(math.abs(internalBOS1)-1) + ")"

swingTextColor = swingBOS1 > 0 ? dashboardGreenText : dashboardRedText
swingBackground = swingBOS1 > 0 ? squeezegreenbg : squeezeredbg

iTextColor = internalBOS1 > 0 ? dashboardGreenText : dashboardRedText
iBackground = internalBOS1 > 0 ? squeezegreenbg : squeezeredbg

if structureScannerOn
    if barstate.islast
        tb.cell(0, 0, "Structure Scanner (" + formatTimeframeString(scannerTimeframe) + ')', text_size = size.small, text_color = #9399a2)
        tb.cell(0, 1, internalText, text_color = iTextColor, bgcolor = iBackground, text_size = size.small)
        tb.cell(0, 2,swingText, text_color = swingTextColor, bgcolor = swingBackground, text_size = size.small)
        


if barstate.isfirst
    var table errorBox = table.new(position.bottom_right, 1, 1, bgcolor = color.new(#363a45, 100))
    table.cell(errorBox,  0,  0,  "SDK © Algoman.life",   text_color = color.gray, text_halign = text.align_center, text_size = size.normal)

Overview

Price Action Concepts Open Source detects swing and internal market structures, marking Break of Structure (BOS) and Change of Character (CHoCH) levels, swing points, and strong or weak highs and lows. The script also maps multi-timeframe order blocks with configurable mitigation methods, fair value gaps, liquidity sweeps, and premium or discount zones. Multi-timeframe requests may use lookahead data.

Details

Published by

KanekiCraynet on GitHub

Type

Indicator, Pine Script v5

Draws

On the price chart

Category

SMC / ICT / price action, Support & resistance, Trend

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Reads higher-timeframe data with lookahead on, so the history can show signals that were not there in real time

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Market Concepts ModestringHistoricalmode
Show Internal Structurebooltrueshow_internals
Structure TypestringAllshow_ibull
swing_ibull_csscolor#089981swing_ibull_css
swing_ibear_csscolor#f23645swing_ibear_css
Structure ScannerbooltruestructureScannerOn
scannerTimeframetimeframeDscannerTimeframe
Color Candlesboolfalseshow_trend
purplecolorcolor#56328fpurplecolor
Show Swing Structurebooltrueshow_Structure
Structure TypestringAllshow_bull
swing_bull_csscolor#089981swing_bull_css
Show all 40 settings
SettingTypeDefaultVariable
swing_bear_csscolor#f23645swing_bear_css
Show Swings Pointsboolfalseshow_swings
lengthint50length
Show Strong/Weak High/Lowboolfalseshow_hl_swings
Show Entry ZonesboolfalseshowEntryZones
Mitigation MethodstringWickobEndMethod
Construction Lengthint10swingLength
bullOrderBlockColorcolor#22a08abullOrderBlockColor
bearOrderBlockColorcolor#f23847bearOrderBlockColor
Zone Countint5bullishOrderBlocks
Timeframe 1booltruetimeframe1Enabled
timeframe1timeframe240timeframe1
Timeframe 2booltruetimeframe2Enabled
timeframe2timeframe15timeframe2
Timeframe 3booltruetimeframe3Enabled
timeframe3timeframe1timeframe3
Liquidity Sweepsboolfalseactivateliquidity
len12345int3len12345
colBlcolor#0044ffcolBl
colBrcolor#ff2b00colBr
Trendline LiquidityboolfalseactivateLiq
len123int3len123
cupcolor#0044ffcup
cdncolor#ff2b00cdn
Equal Highs/Lowsboolfalseshow_eq
eq_lenint3eq_len
Auto-Trendlinesbooltrueontrendline
autotrendlinesensint3autotrendlinesens
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

575 stars

This indicator detects equal highs and lows by comparing prices within user-defined left and right bars using a tolerance percentage. It plots horizontal lines on the price chart for equal highs in one color and lows in another. A screener allows input of mult

Indicator v5 Support & resistanceTrend
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Equal Highs and Lows with Screener

This Pine Script indicator plots a ZigZag on the price chart. It identifies reversal points when price moves more than a set percentage from the last pivot. The script calculates forecasts using zigzag data and labels detected geometric patterns including harm

Indicator v4 TrendSupport & resistance
218 stars

This indicator plots market structure on the price chart by identifying swing highs and lows. It connects these points with lines to visualize trend direction and detect Break of Structure (BOS) when price breaks a prior swing in the current trend. Change of C

Indicator v6 TrendSupport & resistance
Updated 3 months ago · None stated GitHub repository of Market Structure - BOS & CHoCH

Open Source Fractal

by cantolab
183 stars

This indicator implements the Fractal Model concept to plot market structure shifts, liquidity sweeps, and Change in State of Delivery (CISD) levels. It overlays custom higher-timeframe candle previews, open prices, and time markers directly on the price chart

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · MIT GitHub repository of Open Source Fractal
56 stars

BigBeluga Smart Money Concepts identifies and overlays market structure and volumetric order blocks directly on the price chart. It analyzes market swings, trend sweeps, and mapping structure using customizable pivot lengths and calculation modes. The indicato

Indicator v5 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · None stated GitHub repository of BigBeluga - Smart Money Concepts
53 stars

Apex Trend & Liquidity Master calculates a trend cloud using customizable moving averages expanded by a volatility multiplier directly on the main price chart. It identifies dynamic support and resistance liquidity zones from swing high and low pivots, extendi

Indicator v6 TrendSupport & resistance
Updated 2 months ago · MPL-2.0 GitHub repository of Apex Trend & Liquidity Master

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers