Indicator · Pine Script v5 · On the chart

Smart Money Concepts

by TamTH-Dev· code credited to tamth_

This indicator maps market structure and session activity directly on the price chart.

View on GitHub

Repaints

GitHub stars2on the repository
Pine versionv5runs as is
Lines of code1,350
Settings40inputs
LicenceNone statedfile: MPL-2.0
Last update6 weeks ago18 Sep 2026

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,350 lines of Pine Script v5 from collections/SmartMoneyConcepts.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

SmartMoneyConcepts.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tamth_

// @version=5
indicator("Smart Money Concepts", "Smart Money Concepts", overlay = true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500, max_bars_back = 500)


//-----------------------------------------------------------------------------*
//  Constants
//-----------------------------------------------------------------------------{
color TRANSPARENT_COLOR = #ffffff00
color WHITE_COLOR = #ffffff
color RED_COLOR = #e91e62
color AQUA_COLOR = #00bbf9
color LIGHT_GRAY_COLOR = #364156
color DARK_GRAY_COLOR = #212d40
color GREEN_COLOR = #008170
color PURPLE_COLOR = #764AF1
color YELLOW_COLOR = #f2e40a
color ORANGE_COLOR = #f2aa0a
color TXT_COLOR = #787b86
color BLOCK_COLOR = #5d606b19

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


//-----------------------------------------------------------------------------*
// Common Functions
//-----------------------------------------------------------------------------{
debug(simple string txt) =>
    // Create label on the first bar
    var label = label.new(bar_index, na, txt, xloc.bar_index, yloc.price, color(na), label.style_none, WHITE_COLOR, size.large, text.align_left)
    // On next bars, update the label"s x and y position, and the text it displays
    label.set_xy(label, bar_index, ta.highest(10)[1])
    label.set_text(label, txt)

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


//-----------------------------------------------------------------------------*
// Common Variables
//-----------------------------------------------------------------------------{
series int barIdx = bar_index
series int BAR_IDX_RIGHT_ALIGNMENT = barIdx + 21
series int BAR_TIME_RIGHT_ALIGNMENT = timenow + (time[1] - time[101]) / 100 * 20

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


//-----------------------------------------------------------------------------*
// General Settings
//-----------------------------------------------------------------------------{
string generalGrp = "General"

// 7 * 24 * 4
int generalLen = input.int(672, "Length", minval = 1, maxval = 672, group = generalGrp, tooltip = "Number of most recent bars to use for the calculation of supporter")

bool shouldDisplay = last_bar_index - bar_index < generalLen

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


//-----------------------------------------------------------------------------*
// Volume Profile
//-----------------------------------------------------------------------------*
// Settings
//-----------------------------------------------------------------------------{
string vpGrp = "Volume Profile"
bool vpShowed = input.bool(false, "Show", group = vpGrp)
int vpLen = input.int(200, "Lookback", minval = 1, maxval = 500, tooltip = "Number of most recent bars", group = vpGrp)
int vpLevelsSize = input.int(360, "Size Of Levels", minval = 1, maxval = 400, group = vpGrp)
int vpWidth = input.int(16, "Profile's Width", minval = 1, maxval = 100, tooltip = "The length of the bars relative to the Lookback value", group = vpGrp)
int vpBarWidth = input.int(2, "Bar's Width", tooltip = "Width of each bar", group = vpGrp)
int vpOffset = input.int(20, "Offset From Last Bar", group = vpGrp)
color vpColor = input.color(DARK_GRAY_COLOR, "Color", group = vpGrp)

//-----------------------------------------------------------------------------}
// Render Volume Profile
//-----------------------------------------------------------------------------{
var vpLines = array.new_line()

if barstate.isfirst
    for i = 0 to vpLevelsSize - 1
        array.push(vpLines, line.new(na, na, na, na, width = 2))

series float vpHighest = ta.highest(vpLen)
series float vpLowest = ta.lowest(vpLen)
array<float> vpLevels = array.new_float(0)
array<int> vpVols = array.new_int(0)
array<float> vpSumv = array.new_float(0)
line vpLine = na

if barstate.islast and vpShowed
    for i = 0 to vpLevelsSize
        array.push(vpLevels, vpLowest + i / vpLevelsSize * (vpHighest - vpLowest))

    for i = 0 to vpLevelsSize - 1
        series float sum = 0.

        for j = 0 to vpLen - 1
            sum := high[j] > array.get(vpLevels, i) and low[j] < array.get(vpLevels, i + 1) ? sum + volume[j] : sum

        array.push(vpSumv, sum)


    for i = 0 to vpLevelsSize - 1
        series float mult = array.get(vpSumv, i) / array.max(vpSumv)
        series int vol = math.round(vpLen * vpWidth / 100 * mult)
        array.push(vpVols, vol)
    
    series int maxVol = array.max(vpVols)
    int shift = barIdx + maxVol + vpOffset

    for i = 0 to vpLevelsSize - 1
        series int vol = array.get(vpVols, i)
        series float level = array.get(vpLevels, i)
        vpLine := array.get(vpLines, i)
        line.set_xy1(vpLine, shift, level)
        line.set_xy2(vpLine, shift - vol, level)
        line.set_color(vpLine, vpColor)
        line.set_width(vpLine, vpBarWidth)

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




//-----------------------------------------------------------------------------*
// Killzones
//------------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------{
string kzGrp = "Killzones"
bool kzShowed = input.bool(false, "Show", group = kzGrp)
bool kzLabelsShowed = input.bool(false, "Show Labels", group = kzGrp)
// New York
bool kzNyShowed = input.bool(true, "New York", inline = "ny", group = kzGrp)
color kzNyColor = input.color(color.new(YELLOW_COLOR, 96), "", inline = "ny", group = kzGrp)
// London Open
bool kzLdnoShowed = input.bool(true, "London Open", inline = "ldno", group = kzGrp)
color kzLdnoColor = input.color(color.new(AQUA_COLOR, 96), "", inline = "ldno", group = kzGrp)
// London Close
bool kzLdncShowed = input.bool(true, "London Close", inline = "ldnc", group = kzGrp)
color kzLdncColor = input.color(color.new(RED_COLOR, 96), "", inline = "ldnc", group = kzGrp)
// Asian
bool kzAsianShowed = input.bool(true, "Asian", inline = "asian", group = kzGrp)
color kzAsiaColor = input.color(color.new(PURPLE_COLOR, 96), "", inline = "asian", group = kzGrp)

//-----------------------------------------------------------------------------}
// Functions
//-----------------------------------------------------------------------------{
showKzLabels(bool session, color css, string txt)=>
    var label lbl = na
    var float max = na 
    var int anchor = na
    var getCss = color.rgb(color.r(css), color.g(css), color.b(css))

    if session and not session[1]
        max := high
        anchor := time
        lbl := label.new(anchor, max
          , txt
          , xloc.bar_time
          , color = #ffffff00
          , style = label.style_label_down
          , textcolor = getCss
          , size = size.small)
    
    if session
        max := math.max(high, max)
        label.set_x(lbl, int(math.avg(time - (time[1] - time[5]) / 5, anchor)))
        label.set_y(lbl, max)

//-----------------------------------------------------------------------------}
// Retracements
//-----------------------------------------------------------------------------{
bool isKzNySession = time(timeframe.period, "0700-0900", "UTC-5") and kzShowed and kzNyShowed and shouldDisplay
bool isKzLdnoSession = time(timeframe.period, "0200-0500", "UTC-5") and kzShowed and kzLdnoShowed and shouldDisplay
bool isKzLdncSession = time(timeframe.period, "1000-1200", "UTC-5") and kzShowed and kzLdncShowed and shouldDisplay
bool isKzAsianSession = time(timeframe.period, "2000-0000", "UTC-5") and kzShowed and kzAsianShowed and shouldDisplay

//-----------------------------------------------------------------------------}
// Labels
//-----------------------------------------------------------------------------{
showKzLabels(kzLabelsShowed and isKzNySession, kzNyColor, "New York")
showKzLabels(kzLabelsShowed and isKzLdnoSession, kzLdnoColor, "London Open")
showKzLabels(kzLabelsShowed and isKzLdncSession, kzLdncColor, "London Close")
showKzLabels(kzLabelsShowed and isKzAsianSession, kzAsiaColor, "Asian")

//-----------------------------------------------------------------------------}
// Background
//-----------------------------------------------------------------------------{
bgcolor(isKzNySession ? kzNyColor : na, editable = false)
bgcolor(isKzLdnoSession ? kzLdnoColor : na, editable = false)
bgcolor(isKzLdncSession ? kzLdncColor : na, editable = false)
bgcolor(isKzAsianSession ? kzAsiaColor : na, editable = false)

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





//-----------------------------------------------------------------------------*
// Smart Money Concepts
//-----------------------------------------------------------------------------*
// 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
//----------------------------------------{
string mode = input.string("Historical"
  , options = ["Historical", "Present"]
  , group = "Smart Money Concepts"
  , tooltip = MODE_TOOLTIP)

bool trendShowed = input.bool(false, "Color Candles"
  , group = "Smart Money Concepts"
  , tooltip = COLOR_CANDLES_TOOLTIP)

//----------------------------------------}
// Internal Structure
//----------------------------------------{
bool iStructureShowed = input.bool(true, "Show Internal Structure"
  , group = "Real Time Internal Structure"
  , tooltip = SHOW_INTERNAL)

string iBullShowed = input.string("All", "Bullish Structure"
  , options = ["All", "BOS", "CHoCH"]
  , inline = "ibull"
  , group = "Real Time Internal Structure")

color iBullColor = input.color(AQUA_COLOR, ""
  , inline = "ibull"
  , group = "Real Time Internal Structure")

string iBearShowed = input.string("All", "Bearish Structure"
  , options = ["All", "BOS", "CHoCH"]
  , inline = "ibear"
  , group = "Real Time Internal Structure")

color iBearColor = input.color(RED_COLOR, ""
  , inline = "ibear"
  , group = "Real Time Internal Structure")

bool iConfluenceFiltered = input.bool(true, "Confluence Filter"
  , group = "Real Time Internal Structure"
  , tooltip = CONFLUENCE_FILTER)

int internalLen = input.int(5, ""
  , minval = 2
  , maxval = 10
  , inline = "swings"
  , group = "Real Time Swing Structure")

string iStructureSize = input.string("Tiny", "Internal Label Size"
  , options = ["Tiny", "Small", "Normal"]
  , group = "Real Time Internal Structure")

//----------------------------------------}
// Swing Structure
//----------------------------------------{
bool sStructureShowed = input.bool(true, "Show Swing Structure"
  , group = "Real Time Swing Structure"
  , tooltip = SHOW_SWING)

string sBullShowed = input.string("All", "Bullish Structure"
  , options = ["All", "BOS", "CHoCH"]
  , inline = "bull"
  , group = "Real Time Swing Structure")

color sBullColor = input.color(AQUA_COLOR, ""
  , inline = "bull"
  , group = "Real Time Swing Structure")

string sBearShowed = input.string("All", "Bearish Structure"
  , options = ["All", "BOS", "CHoCH"]
  , inline = "bear"
  , group = "Real Time Swing Structure")

color sBearColor = input.color(RED_COLOR, ""
  , inline = "bear"
  , group = "Real Time Swing Structure")

string sStructureSize = input.string("Small", "Swing Label Size"
  , options = ["Tiny", "Small", "Normal"]
  , group = "Real Time Swing Structure")

bool swingsPointsShowed = input.bool(false, "Show Swings Points"
  , inline = "swings"
  , group = "Real Time Swing Structure"
  , tooltip = SHOW_SWING_POINTS)

int swingLen = input.int(50, ""
  , minval = 10
  , inline = "swings"
  , group = "Real Time Swing Structure")

bool hlSwingsShowed = input.bool(true, "Show Strong/Weak High/Low"
  , group = "Real Time Swing Structure"
  , tooltip = SHOW_SWHL_POINTS)

//----------------------------------------}
// Order Blocks
//----------------------------------------{
string obGrp = "Order Blocks"
bool iobShowed = input.bool(true, 'Internal Order Blocks', inline = 'iob', group = obGrp, tooltip = INTERNAL_OB)
int numOfIobs = input.int(5, '', minval = 1, inline = 'iob', group = obGrp)
bool sobShowed = input.bool(false, 'Swing Order Blocks', inline = 'ob', group = obGrp, tooltip = SWING_OB)
int numOfSobs = input.int(5, '', minval = 1, inline = 'ob', group = obGrp)
string obFilter = input.string('Atr', 'Order Block Filter', options = ['Atr', 'Cumulative Mean Range'], group = obGrp, tooltip = FILTER_OB)
color obBullishColor = input.color(BLOCK_COLOR, "", inline = "ob1", group = obGrp)
color obBearishColor = input.color(BLOCK_COLOR, "", inline = "ob1", group = obGrp)
var iobTop = array.new_float(0)
var iobBtm = array.new_float(0)
var iobLeft = array.new_int(0)
var iobType = array.new_int(0)
var obTop = array.new_float(0)
var obBtm = array.new_float(0)
var obLeft = array.new_int(0)
var obType = array.new_int(0)

//----------------------------------------}
// Liquidity Levels
//----------------------------------------{
string llGrp = "Liquidity Levels"
bool llShowed = input.bool(true, title = "Showed", group = llGrp, inline = "1")
var llTf = input.timeframe("", title = "Timeframe", group = llGrp, inline = "2")
int llPivotLen = input.int(8, title = "Pivot Length", group = llGrp, inline = "3")
int llMaxLevels = input.int(6, title = "Max Levels", group = llGrp, inline = "4")
color llSellSideColor = input.color(color.new(AQUA_COLOR, 50), "", group = llGrp, inline = "1")
color llBuySideColor = input.color(color.new(RED_COLOR, 50), "", group = llGrp, inline = "1")
float llBoxWidth = 1
series float llAtrLiq = ta.atr(300)
float llTholdLiq = llAtrLiq * llBoxWidth / 10

//----------------------------------------}
// EQH/EQL
//----------------------------------------{
bool eqShowed = input.bool(false, "Equal High/Low"
  , group = "EQH/EQL"
  , tooltip = SHOW_EQHL)

int eqLen = input.int(3, "Bars Confirmation"
  , minval = 1
  , group = "EQH/EQL"
  , tooltip = EQHL_BARS)

float eqThreshold = input.float(0.1, "Threshold"
  , minval = 0
  , maxval = 0.5
  , step = 0.1
  , group = "EQH/EQL"
  , tooltip = EQHL_THRESHOLD)

string eqSize = input.string("Tiny", "Label Size"
  , options = ["Tiny", "Small", "Normal"]
  , group = "EQH/EQL")

//----------------------------------------}
// Fair Value Gaps
//----------------------------------------{
bool fvgShowed = input.bool(true, "Fair Value Gaps"
  , group = "Fair Value Gaps"
  , tooltip = SHOW_FVG)
  
bool fvgAuto = input.bool(true, "Auto Threshold"
  , group = "Fair Value Gaps"
  , tooltip = AUTO_FVG)

var fvgTf = input.timeframe("", "Timeframe"
  , group = "Fair Value Gaps"
  , tooltip = FVG_TF)

color fvgBullColor = input.color(color.new(AQUA_COLOR, 70), "Bullish FVG"
  , group = "Fair Value Gaps")

color fvgBearColor = input.color(color.new(RED_COLOR, 70), "Bearish FVG"
  , group = "Fair Value Gaps")

int fvgExtend = input.int(1, "Extend FVG"
  , minval = 0
  , group = "Fair Value Gaps"
  , tooltip = EXTEND_FVG)

//----------------------------------------}
// Previous day/week high/low
//----------------------------------------{
// Daily
bool pdhlShowed = input.bool(false, "Daily"
  , inline = "daily"
  , group = "Highs & Lows MTF")

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

color pdhlColor = input.color(TXT_COLOR, ""
  , inline = "daily"
  , group = "Highs & Lows MTF")

// Weekly
bool pwhlShowed = input.bool(false, "Weekly"
  , inline = "weekly"
  , group = "Highs & Lows MTF")

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

color pwhlColor = input.color(TXT_COLOR, ""
  , inline = "weekly"
  , group = "Highs & Lows MTF")

// Monthly
bool pmhlShowed = input.bool(false, "Monthly"
  , inline = "monthly"
  , group = "Highs & Lows MTF")

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

color pmhlColor = input.color(TXT_COLOR, ""
  , inline = "monthly"
  , group = "Highs & Lows MTF")

//----------------------------------------}
// Premium/Discount zones
//----------------------------------------{
bool pdzShowed = input.bool(false, "Premium/Discount Zones", group = "Premium & Discount Zones", tooltip = PED_ZONES)
color pdzBgColor = input.color(BLOCK_COLOR, "Background Color", group = "Premium & Discount Zones")
color pdzTextColor = input.color(TXT_COLOR, "Text Color", group = "Premium & Discount Zones")

//-----------------------------------------------------------------------------}
// Functions
//-----------------------------------------------------------------------------{
series float atr = ta.atr(200)
series float cmean_range = ta.cum(high - low) / barIdx

// Order block coordinates function
getObCoord(bool maxUsed, series int loc, array<float> targetTop, array<float> targetBtm, array<int> targetLeft, array<int> targetType) =>
    float min = 99999999.
    float max = 0.
    int idx = 1
    series float ob_threshold = obFilter == 'Atr' ? atr : cmean_range 

    // Search for highest/lowest high within the structure interval and get range
    if maxUsed
        for i = 1 to (barIdx - loc)-1
            if (high[i] - low[i]) < ob_threshold[i] * 2
                max := math.max(high[i], max)
                min := max == high[i] ? low[i] : min
                idx := max == high[i] ? i : idx
    else
        for i = 1 to (barIdx - loc)-1
            if (high[i] - low[i]) < ob_threshold[i] * 2
                min := math.min(low[i], min)
                max := min == low[i] ? high[i] : max
                idx := min == low[i] ? i : idx

    array.unshift(targetTop, max)
    array.unshift(targetBtm, min)
    array.unshift(targetLeft, time[idx])
    array.unshift(targetType, maxUsed ? -1 : 1)

displayOb(array<box> boxes, array<float> targetTop, array<float> targetBtm, array<int> targetLeft, array<int> targetType, int lastShowed, bool swing, series int size) =>
    for i = 0 to math.min(lastShowed - 1, size - 1)
        box box = array.get(boxes, i)
        box.set_lefttop(box, array.get(targetLeft, i), array.get(targetTop, i))
        box.set_rightbottom(box, BAR_TIME_RIGHT_ALIGNMENT, array.get(targetBtm, i))
        color contractionColor = na
        
        if swing 
            contractionColor := array.get(targetType, i) == 1 ? obBullishColor : obBearishColor
            box.set_border_color(box, contractionColor)
            box.set_bgcolor(box, contractionColor)
        else
            contractionColor := array.get(targetType, i) == 1 ? obBullishColor : obBearishColor
            box.set_border_color(box, contractionColor)
            box.set_bgcolor(box, contractionColor)

getHL() => [high, low]

getOHLC() => [close[1], open[1], high, low, high[2], low[2]]

displayStructure(series int x, series float y, string txt, color color, bool dashed, bool down, string lblSize) =>
    line structureLine = line.new(x, y, barIdx, y, color = color, style = dashed ? line.style_dashed : line.style_solid)
    label structureLbl = label.new(int(math.avg(x, barIdx)), y, txt, color = TRANSPARENT_COLOR, textcolor = color, style = down ? label.style_label_down : label.style_label_up, size = lblSize)

    if mode == "Present"
        line.delete(structureLine[1])
        label.delete(structureLbl[1])

getSwings(int len) =>
    var os = 0
    series float upper = ta.highest(len)
    series float lower = ta.lowest(len)
    os := high[len] > upper ? 0 : low[len] < lower ? 1 : os[1]
    float top = os == 0 and os[1] != 0 ? high[len] : 0
    float btm = os == 1 and os[1] != 1 ? low[len] : 0

    [top, btm]
        
getLineStyle(string style) =>
    string out = switch style
        "⎯⎯⎯"  => line.style_solid
        "----" => line.style_dashed
        "····" => line.style_dotted

getPHL(series float h, series float l, string tf, color css) =>
    var line highLine = line.new(na,na,na,na
      , xloc = xloc.bar_time
      , color = css
      , style = getLineStyle(pdhlStyle))
    var label highLbl = label.new(na,na
      , xloc = xloc.bar_time
      , text = str.format("P{0}H", tf)
      , color = TRANSPARENT_COLOR
      , textcolor = css
      , size = size.small
      , style = label.style_label_left)
    var line lowLine = line.new(na,na,na,na
      , xloc = xloc.bar_time
      , color = css
      , style = getLineStyle(pdhlStyle))
    var label lowLbl = label.new(na,na
      , xloc = xloc.bar_time
      , text = str.format("P{0}L", tf)
      , color = TRANSPARENT_COLOR
      , textcolor = css
      , size = size.small
      , style = label.style_label_left)

    series float hy = ta.valuewhen(h != h[1], h, 1)
    series int hx = ta.valuewhen(h == high, time, 1)
    series float ly = ta.valuewhen(l != l[1], l, 1)
    series int lx = ta.valuewhen(l == low, time, 1)

    if barstate.islast
        // High
        line.set_xy1(highLine, hx, hy)
        line.set_xy2(highLine, BAR_TIME_RIGHT_ALIGNMENT, hy)
        label.set_xy(highLbl, BAR_TIME_RIGHT_ALIGNMENT, hy)
        // Low
        line.set_xy1(lowLine, lx, ly)
        line.set_xy2(lowLine, BAR_TIME_RIGHT_ALIGNMENT, ly)
        label.set_xy(lowLbl, BAR_TIME_RIGHT_ALIGNMENT, ly)

//-----------------------------------------------------------------------------}
// Global variables
//-----------------------------------------------------------------------------{
var trend = 0, var itrend = 0
var topY = 0., var topX = 0
var btmY = 0., var btmX = 0
var iTopY = 0., var iTopX = 0
var iBtmY = 0., var iBtmX = 0
var trailUp = high, var trailDn = low
var trailUpX = 0,  var trailDnX = 0
var topCross = true,  var btmCross = true
var iTopCross = true, var iBtmCross = true
var txtTop = "",  var txtBtm = ""

// Structure colors
var bull_css = sBullColor
var bear_css = sBearColor
var ibull_css = iBullColor
var ibear_css = iBearColor

// Labels size
var internal_structureLblSize = iStructureSize == "Tiny" 
  ? size.tiny 
  : iStructureSize == "Small" 
  ? size.small 
  : size.normal 
var swing_structureLblSize = sStructureSize == "Tiny" 
  ? size.tiny 
  : sStructureSize == "Small" 
  ? size.small 
  : size.normal 
var eqhl_lblSize = eqSize == "Tiny" 
  ? size.tiny 
  : eqSize == "Small" 
  ? size.small
  : size.normal 

// Swings
[top, btm] = getSwings(swingLen)
[itop, ibtm] = getSwings(internalLen)

//-----------------------------------------------------------------------------}
// Pivot High
//-----------------------------------------------------------------------------{
var line extendTop = na
var label extendTopLbl = label.new(na, na
  , color = TRANSPARENT_COLOR
  , textcolor = bear_css
  , style = label.style_label_down
  , size = size.tiny)

if top
    topCross := true
    txtTop := top > topY ? "HH" : "LH"

    if swingsPointsShowed
        top_lbl = label.new(barIdx - swingLen, top, txtTop
          , color = TRANSPARENT_COLOR
          , textcolor = bear_css
          , style = label.style_label_down
          , size = swing_structureLblSize)

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

    // Extend recent top to last bar
    line.delete(extendTop[1])
    series int xOffset = barIdx - swingLen
    extendTop := line.new(xOffset, top, barIdx, top, color = bear_css)
    topY := top
    topX := xOffset
    trailUp := top
    trailUpX := xOffset

if itop
    iTopCross := true
    iTopY := itop
    iTopX := barIdx - internalLen

// Trailing maximum
trailUp := math.max(high, trailUp)
trailUpX := trailUp == high ? barIdx : trailUpX

// Set top extension label/line
if barstate.islast and hlSwingsShowed
    line.set_xy1(extendTop, trailUpX, trailUp)
    line.set_xy2(extendTop, BAR_IDX_RIGHT_ALIGNMENT, trailUp)
    label.set_x(extendTopLbl, BAR_IDX_RIGHT_ALIGNMENT)
    label.set_y(extendTopLbl, trailUp)
    label.set_text(extendTopLbl, trend < 0 ? "Strong High" : "Weak High")

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

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

if btm
    btmCross := true
    txtBtm := btm < btmY ? "LL" : "HL"
    
    if swingsPointsShowed
        btm_lbl = label.new(barIdx - swingLen, btm, txtBtm
          , color = TRANSPARENT_COLOR
          , textcolor = bull_css
          , style = label.style_label_up
          , size = swing_structureLblSize)

        if mode == "Present"
            label.delete(btm_lbl[1])
    
    // Extend recent btm to last bar
    series int xOffset = barIdx - swingLen
    line.delete(extend_btm[1])
    extend_btm := line.new(xOffset, btm, barIdx, btm, color = bull_css)
    btmY := btm
    btmX := xOffset
    trailDn := btm
    trailDnX := xOffset

if ibtm
    iBtmCross := true
    iBtmY := ibtm
    iBtmX := barIdx - internalLen

// Trailing minimum
trailDn := math.min(low, trailDn)
trailDnX := trailDn == low ? barIdx : trailDnX

// Set btm extension label/line
if barstate.islast and hlSwingsShowed
    line.set_xy1(extend_btm, trailDnX, trailDn)
    line.set_xy2(extend_btm, BAR_IDX_RIGHT_ALIGNMENT, trailDn)
    label.set_x(extend_btm_lbl, BAR_IDX_RIGHT_ALIGNMENT)
    label.set_y(extend_btm_lbl, trailDn)
    label.set_text(extend_btm_lbl, trend > 0 ? "Strong Low" : "Weak Low")

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

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

// Detect internal bullish Structure
if ta.crossover(close, iTopY) and iTopCross and topY != iTopY and bullConcordant
    bool choch = na
    
    if itrend < 0
        choch := true
    
    txt = choch ? "CHoCH" : "BOS"

    if iStructureShowed
        if (iBullShowed == "All" or (iBullShowed == "BOS" and not choch) or (iBullShowed == "CHoCH" and choch)) and shouldDisplay
            displayStructure(iTopX, iTopY, txt, ibull_css, true, true, internal_structureLblSize)
    
    iTopCross := false
    itrend := 1

    // Internal Order Block
    if iobShowed
        getObCoord(false, iTopX, iobTop, iobBtm, iobLeft, iobType)

// Detect bullish Structure
if ta.crossover(close, topY) and topCross
    bool choch = na
    
    if trend < 0
        choch := true

    txt = choch ? "CHoCH" : "BOS"
    
    if sStructureShowed
        if (sBullShowed == "All" or (sBullShowed == "BOS" and not choch) or (sBullShowed == "CHoCH" and choch)) and shouldDisplay
            displayStructure(topX, topY, txt, bull_css, false, true, swing_structureLblSize)

    topCross := false
    trend := 1

    // Order Block
    if sobShowed
        getObCoord(false, topX, obTop, obBtm, obLeft, obType)

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

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

// Detect internal bearish Structure
if ta.crossunder(close, iBtmY) and iBtmCross and btmY != iBtmY and bear_concordant
    bool choch = false
    
    if itrend > 0
        choch := true
    
    string txt = choch ? "CHoCH" : "BOS"

    if iStructureShowed
        if (iBearShowed == "All" or (iBearShowed == "BOS" and not choch) or (iBearShowed == "CHoCH" and choch)) and shouldDisplay
            displayStructure(iBtmX, iBtmY, txt, ibear_css, true, false, internal_structureLblSize)
    
    iBtmCross := false
    itrend := -1

    // Internal Order Block
    if iobShowed
        getObCoord(true, iBtmX, iobTop, iobBtm, iobLeft, iobType)

// Detect bearish Structure
if ta.crossunder(close, btmY) and btmCross
    bool choch = na
    
    if trend > 0
        choch := true

    string txt = choch ? "CHoCH" : "BOS"
    
    if sStructureShowed
        if (sBearShowed == "All" or (sBearShowed == "BOS" and not choch) or (sBearShowed == "CHoCH" and choch))  and shouldDisplay
            displayStructure(btmX, btmY, txt, bear_css, false, false, swing_structureLblSize)

    btmCross := false
    trend := -1

    // Order Block
    if sobShowed
        getObCoord(true, btmX, obTop, obBtm, obLeft, obType)

//-----------------------------------------------------------------------------}
// Order Blocks
//-----------------------------------------------------------------------------{
// Set order blocks
var iobBoxes = array.new_box(0)
var obBoxes = array.new_box(0)

// Delete internal order blocks box coordinates if top/bottom is broken
for element in iobType
    series int idx = array.indexof(iobType, element)

    if close < array.get(iobBtm, idx) and element == 1
        array.remove(iobTop, idx) 
        array.remove(iobBtm, idx) 
        array.remove(iobLeft, idx) 
        array.remove(iobType, idx)
    else if close > array.get(iobTop, idx) and element == -1
        array.remove(iobTop, idx) 
        array.remove(iobBtm, idx)
        array.remove(iobLeft, idx) 
        array.remove(iobType, idx)

// Delete internal order blocks box coordinates if top/bottom is broken
for element in obType
    series int idx = array.indexof(obType, element)
    if close < array.get(obBtm, idx) and element == 1
        array.remove(obTop, idx) 
        array.remove(obBtm, idx) 
        array.remove(obLeft, idx) 
        array.remove(obType, idx)
    else if close > array.get(obTop, idx) and element == -1
        array.remove(obTop, idx) 
        array.remove(obBtm, idx)
        array.remove(obLeft, idx) 
        array.remove(obType, idx)

series int iobSize = array.size(iobType)
series int obSize = array.size(obType)

if barstate.isfirst
    if iobShowed
        for i = 0 to numOfIobs - 1
            array.push(iobBoxes, box.new(na,na,na,na, xloc = xloc.bar_time))
    if sobShowed
        for i = 0 to numOfSobs - 1
            array.push(obBoxes, box.new(na,na,na,na, xloc = xloc.bar_time))

if iobSize > 0
    if barstate.islast
        displayOb(iobBoxes, iobTop, iobBtm, iobLeft, iobType, numOfIobs, false, iobSize)

if obSize > 0
    if barstate.islast
        displayOb(obBoxes, obTop, obBtm, obLeft, obType, numOfSobs, true, obSize)

//-----------------------------------------------------------------------------}
// Liquidity Levels
//-----------------------------------------------------------------------------{
getTfMulti(string tf) =>
    simple int ts = timeframe.in_seconds("")
    series int htfs = timeframe.in_seconds(tf)
    htfs / ts

shiftLimitBox(array<box> arr) =>
    if array.size(arr) > llMaxLevels / 2
        box.delete(array.shift(arr))

extendBoxToCurrent(array<box> arr) =>
    if array.size(arr) > 0
        for i = array.size(arr) - 1 to 0 by 1
            box.set_right(array.get(arr, i), BAR_TIME_RIGHT_ALIGNMENT)

removeMitigatedBoxes(array<box> arr, string hl) =>
    bool removed = false

    if array.size(arr) > 0
        for i = array.size(arr) - 1 to 0 by 1
            series box l = array.get(arr, i)
            series float hh = close[1]
            series float ll = close[1]

            if hl == "High" and hh > box.get_top(l)
                array.remove(arr, i)
                box.delete(l)
                removed := true
            if hl == "Low" and ll < box.get_top(l)
                array.remove(arr, i)
                box.delete(l)
                removed := true

    shiftLimitBox(arr) 
    removed


[_llTime, _llOpen, _llHigh, _llLow, _llClose] = request.security(syminfo.tickerid, llTf, [time, open, high, low, close])

series float llPivotHigh = ta.pivothigh(_llHigh, llPivotLen * getTfMulti(llTf), llPivotLen + getTfMulti(llTf))
series float llPivotLow = ta.pivotlow(_llLow, llPivotLen * getTfMulti(llTf), llPivotLen + getTfMulti(llTf))
var llHtfHighBoxes = array.new_box()
var llHtfLowBoxes = array.new_box()

if llShowed
    series float displacement = llPivotLen + getTfMulti(llTf)

    if not na(llPivotHigh)
        series float yOffset = _llHigh[displacement] - llTholdLiq
        array.push(llHtfHighBoxes, box.new(_llTime[displacement], _llHigh[displacement], _llTime[+1], yOffset, bgcolor = llBuySideColor, border_color = color.new(llBuySideColor, 80), xloc = xloc.bar_time, border_style = line.style_solid, extend = extend.none, border_width = 2, text_halign = text.align_center, text_color = WHITE_COLOR, text_size = size.small))  
    if not na(llPivotLow)
        series float yOffset = _llLow[displacement] + llTholdLiq
        array.push(llHtfLowBoxes, box.new(_llTime[displacement], _llLow[displacement], _llTime[+1], yOffset, bgcolor = llSellSideColor, border_color = color.new(llSellSideColor, 80), xloc = xloc.bar_time, border_style = line.style_solid, extend = extend.none, border_width = 2, text_halign = text.align_center, text_color = WHITE_COLOR, text_size = size.small))


removeMitigatedBoxes(llHtfHighBoxes, "High")
removeMitigatedBoxes(llHtfLowBoxes, "Low")
extendBoxToCurrent(llHtfHighBoxes)
extendBoxToCurrent(llHtfLowBoxes)

//-----------------------------------------------------------------------------}
// EQH/EQL
//-----------------------------------------------------------------------------{
var eqPrevTop = 0.
var eqTopX = 0
var eqPrevBtm = 0.
var eqBtmX = 0

if eqShowed
    series float eqTop = ta.pivothigh(eqLen, eqLen)
    series float eqBtm = ta.pivotlow(eqLen, eqLen)

    if not na(eqTop) 
        max = math.max(eqTop, eqPrevTop)
        min = math.min(eqTop, eqPrevTop)
        
        if max < min + atr * eqThreshold and shouldDisplay
            eqhLine = line.new(eqTopX, eqPrevTop, barIdx - eqLen, eqTop
              , color = bear_css
              , style = line.style_dotted)
            eqhLbl = label.new(int(math.avg(barIdx - eqLen, eqTopX)), eqTop, "EQH"
              , color = #00000000
              , textcolor = bear_css
              , style = label.style_label_down
              , size = eqhl_lblSize)

            if mode == "Present"
                line.delete(eqhLine[1])
                label.delete(eqhLbl[1])
            

        eqPrevTop := eqTop
        eqTopX := barIdx - eqLen

    if not na(eqBtm) 
        max = math.max(eqBtm, eqPrevBtm)
        min = math.min(eqBtm, eqPrevBtm)
        
        if min > max - atr * eqThreshold and shouldDisplay
            eqlLine = line.new(eqBtmX, eqPrevBtm, barIdx - eqLen, eqBtm
              , color = bull_css
              , style = line.style_dotted)
            eqlLbl = label.new(int(math.avg(barIdx - eqLen, eqBtmX)), eqBtm, "EQL"
              , color = #00000000
              , textcolor = bull_css
              , style = label.style_label_up
              , size = eqhl_lblSize)

            if mode == "Present"
                line.delete(eqlLine[1])
                label.delete(eqlLbl[1])

        eqPrevBtm := eqBtm
        eqBtmX := barIdx - eqLen

//-----------------------------------------------------------------------------}
// Fair Value Gaps
//-----------------------------------------------------------------------------{
var bullishFvgMax = array.new_box(0)
var bullishFvgMin = array.new_box(0)
var bearishFvgMax = array.new_box(0)
var bearishFvgMin = array.new_box(0)
float bullishFvgAvg = na
float bearishFvgAvg = na
bool bullishFvgCond = false
bool bearishFvgCond = false

[srcC1, srcO1, srcH, srcL, srcH2, srcL2] = request.security(syminfo.tickerid, fvgTf, getOHLC())

if fvgShowed
    series float delta = (srcC1 - srcO1) / srcO1 * 100
    series bool tfChanged = timeframe.change(fvgTf)
    series float threshold = fvgAuto ? ta.cum(math.abs(tfChanged ? delta : 0)) / barIdx * 2 : 0

    // FVG conditions
    bullishFvgCond := srcL > srcH2
      and srcC1 > srcH2 
      and delta > threshold
      and tfChanged
    bearishFvgCond := srcH < srcL2 
      and srcC1 < srcL2 
      and -delta > threshold
      and tfChanged

    // FVG Areas
    if bullishFvgCond and shouldDisplay
        array.unshift(bullishFvgMax, box.new(barIdx - 1, srcL, barIdx + fvgExtend, math.avg(srcL, srcH2)
          , border_color = fvgBullColor
          , bgcolor = fvgBullColor))
        array.unshift(bullishFvgMin, box.new(barIdx - 1, math.avg(srcL, srcH2), barIdx + fvgExtend, srcH2
          , border_color = fvgBullColor
          , bgcolor = fvgBullColor))
    
    if bearishFvgCond and shouldDisplay
        array.unshift(bearishFvgMax, box.new(barIdx - 1, srcH, barIdx + fvgExtend, math.avg(srcH, srcL2)
          , border_color = fvgBearColor
          , bgcolor = fvgBearColor))
        array.unshift(bearishFvgMin, box.new(barIdx - 1, math.avg(srcH, srcL2), barIdx + fvgExtend, srcL2
          , border_color = fvgBearColor
          , bgcolor = fvgBearColor))

    for bx in bullishFvgMin
        if low < box.get_bottom(bx)
            box.delete(bx)
            box.delete(array.get(bullishFvgMax, array.indexof(bullishFvgMin, bx)))
    
    for bx in bearishFvgMax
        if high > box.get_top(bx)
            box.delete(bx)
            box.delete(array.get(bearishFvgMin, array.indexof(bearishFvgMax, bx)))

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

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

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

// Display Daily
if pdhlShowed
    getPHL(pdh, pdl, "D", pdhlColor)

// Display Weekly
if pwhlShowed
    getPHL(pwh, pwl, "W", pwhlColor)
    
// Display Monthly
if pmhlShowed
    getPHL(pmh, pml, "M", pmhlColor)

//-----------------------------------------------------------------------------}
// Premium/Discount/Equilibrium zones
//-----------------------------------------------------------------------------{
var premium = box.new(na, na, na, na, bgcolor = pdzBgColor, border_color = pdzBgColor, border_width = 2)

var premiumLbl = label.new(na, na
  , text = "Premium"
  , color = TRANSPARENT_COLOR
  , textcolor = pdzTextColor
  , style = label.style_label_down
  , size = size.small)

var eq = box.new(na, na, na, na, bgcolor = pdzBgColor, border_color = pdzBgColor, border_width = 2)

var eqLbl = label.new(na, na
  , text = "Equilibrium"
  , color = TRANSPARENT_COLOR
  , textcolor = pdzTextColor
  , style = label.style_label_left
  , size = size.small)

var discount = box.new(na, na, na, na, bgcolor = pdzBgColor, border_color = pdzBgColor, border_width = 2)

var discountLbl = label.new(na, na
  , text = "Discount"
  , color = TRANSPARENT_COLOR
  , textcolor = pdzTextColor
  , style = label.style_label_up
  , size = size.small)

// Show Premium/Discount Areas
if barstate.islast and pdzShowed
    // TODO
    series float avg = math.avg(trailUp, trailDn)
    box.set_lefttop(premium, math.max(topX, btmX), trailUp)
    box.set_rightbottom(premium, BAR_IDX_RIGHT_ALIGNMENT, .95 * trailUp + .05 * trailDn)
    label.set_xy(premiumLbl, int(math.avg(math.max(topX, btmX), BAR_IDX_RIGHT_ALIGNMENT)), trailUp)
    box.set_lefttop(eq, math.max(topX, btmX), .525 * trailUp + .475*trailDn)
    box.set_rightbottom(eq, BAR_IDX_RIGHT_ALIGNMENT, .525 * trailDn + .475 * trailUp)
    label.set_xy(eqLbl, BAR_IDX_RIGHT_ALIGNMENT, avg)
    box.set_lefttop(discount, math.max(topX, btmX), .95 * trailDn + .05 * trailUp)
    box.set_rightbottom(discount, BAR_IDX_RIGHT_ALIGNMENT, trailDn)
    label.set_xy(discountLbl, int(math.avg(math.max(topX, btmX), BAR_IDX_RIGHT_ALIGNMENT)), trailDn)

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

if trendShowed
    trendColor := itrend == 1 ? bull_css : bear_css

plotcandle(open, high, low, close, color = trendColor, wickcolor = trendColor, bordercolor = trendColor, editable = false)

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





//-----------------------------------------------------------------------------*
// OTE
//-----------------------------------------------------------------------------*
// Settings
//-----------------------------------------------------------------------------{
string oteGrp = "OTE"
bool oteShowed = input.bool(false, "Show", group = oteGrp)
string oteDepthTooltip = "The minimum number of bars that will be taken into account when calculating the indicator."
int oteDepth = input.int(10, "Depth", group = oteGrp, tooltip = oteDepthTooltip)
string oteDevTooltip = "Deviation is a multiplier that affects how much the price should deviate from the previous pivot in order for the bar to become a new pivot."
float oteThresholdMultiplier = input.float(3, "Deviation", group = oteGrp, tooltip = oteDevTooltip)
color oteColor = input.color(YELLOW_COLOR, "Color", group = oteGrp)

//-----------------------------------------------------------------------------}
// Types
//-----------------------------------------------------------------------------{
type Settings
    float devThreshold = 3
    int depth = 10

type Point
    int unixTime
    float price

type Pivot
    bool isHigh 
    float vol
    Point start
    Point end

type ZigZag
    Settings settings
    array<Pivot> pivots
    float volTotal = 0
    Pivot extend = na

//-----------------------------------------------------------------------------}
// Methods
//-----------------------------------------------------------------------------{
findPivotPoint(series float src, series float len, simple bool isHigh) =>
    float pivot = nz(src[len])

    if len == 0
        Point.new(time, pivot)
    else if len * 2 <= bar_index
        bool found = true

        for i = 0 to math.abs(len - 1)
            if (isHigh and src[i] > pivot) or (not isHigh and src[i] < pivot)
                found := false
                break

        for i = len + 1 to 2 * len
            if (isHigh and src[i] >= pivot) or (not isHigh and src[i] <= pivot)
                found := false
                break

        if found
            Point.new(time[len], pivot)

calcDev(series float basePrice, series float price) =>
    float result = 100 * (price - basePrice) / math.abs(basePrice)

method updatePivot(Pivot this, Point end, float vol, Settings settings) =>
    this.end := end
    this.vol := vol

getNewPivot(series Point start, series Point end, series float vol, series bool isHigh, series Settings settings) =>
    Pivot pivot = Pivot.new(isHigh, vol, start, end)
    pivot.updatePivot(end, vol, settings)
    pivot

method isPriceHigher(series Pivot this, series Point point) => 
    int mult = this.isHigh ? 1 : -1
    point.price * mult > this.end.price * mult

method getLastPivot(series ZigZag this) =>
    int size = this.pivots.size()
    size > 0 ? this.pivots.get(size - 1) : na

method updateLastPivot(series ZigZag this, series Point point) =>
    Pivot lastPivot = this.getLastPivot()

    if this.pivots.size() == 1
        lastPivot.start := point

    lastPivot.updatePivot(point, lastPivot.vol + this.volTotal, this.settings)
    this.volTotal := 0

method addNewPivot(series ZigZag this, series Pivot pivot) =>
    this.pivots.push(pivot)
    this.volTotal := 0

method hasNewPivotPointFound(series ZigZag this, simple bool isHigh, series Point point) =>
    bool found = false
    Pivot lastPivot = this.getLastPivot()

    if not na(lastPivot)
        if lastPivot.isHigh == isHigh 
            if lastPivot.isPriceHigher(point)
                this.updateLastPivot(point)
                found := true
        else
            float dev = calcDev(lastPivot.end.price, point.price)

            if (not lastPivot.isHigh and dev >= this.settings.devThreshold) or (lastPivot.isHigh and dev <= -1 * this.settings.devThreshold)
                this.addNewPivot(getNewPivot(lastPivot.end, point, this.volTotal, isHigh, this.settings))
                found := true
    else
        this.addNewPivot(getNewPivot(point, point, this.volTotal, isHigh, this.settings))
        found := true

    found

method findPivot(series ZigZag this, series float src, simple bool isHigh, series int depth, series bool pivotRegistered = true) =>
    Point point = findPivotPoint(src, depth, isHigh)
    not na(point) and pivotRegistered ? this.hasNewPivotPointFound(isHigh, point) : false

method updateZigzag(series ZigZag this, series float vols, series float highVal, series float lowVal, series int timeVal) =>
    int depth = math.max(2, math.floor(this.settings.depth / 2))
    this.volTotal += nz(vols[depth])
    bool updated = this.findPivot(highVal, true, depth)
    updated := this.findPivot(lowVal, false, depth, not updated) or updated
    Pivot lastPivot = this.getLastPivot()
    float remVol = math.sum(vols, math.max(depth, 1))

    if barstate.islast and not na(lastPivot)
        bool isHigh = not lastPivot.isHigh
        float curSeries = isHigh ? highVal : lowVal
        Point end = Point.new(timeVal, curSeries)

        if na(this.extend) or updated
            this.extend := getNewPivot(lastPivot.end, end, this.volTotal, isHigh, this.settings)

        this.extend.updatePivot(end, this.volTotal + remVol, this.settings)

    updated

initZigzag(series Settings settings = na) =>
    ZigZag result = ZigZag.new(na(settings) ? Settings.new() : settings, array.new<Pivot>())

getOTEVals(series float close_val, series float vol_val, series float highVal, series float lowVal, series int timeVal) =>
    var settings = Settings.new(oteThresholdMultiplier, oteDepth)
    var ZigZag zigzagInstance = initZigzag(settings)
    var Pivot lastPivot = na
    var float startPrice = na
    var float height = na
    settings.devThreshold := ta.atr(10) / close_val * 100 * oteThresholdMultiplier

    if zigzagInstance.updateZigzag(vol_val, highVal, lowVal, timeVal)
        lastPivot := zigzagInstance.getLastPivot()

        if not na(lastPivot)
            var line lastLine = na

            if na(lastLine) and oteShowed
                lastLine := line.new(lastPivot.start.unixTime, lastPivot.start.price, lastPivot.end.unixTime, lastPivot.end.price, xloc = xloc.bar_time, color = color.new(oteColor, 20), width = 1, style = line.style_dashed)
            else
                line.set_xy1(lastLine, lastPivot.start.unixTime, lastPivot.start.price)
                line.set_xy2(lastLine, lastPivot.end.unixTime, lastPivot.end.price)

            startPrice := lastPivot.end.price
            endPrice = lastPivot.start.price
            height := (startPrice > endPrice ? -1 : 1) * math.abs(startPrice - endPrice)

    [lastPivot, startPrice, height]

[oteLastPivot, oteStartPrice, oteHeight] = getOTEVals(close, volume, high, low, time)

drawFibLevel(Pivot pivot, float level, color color) =>
    var line fibLine = na, line.delete(fibLine)
    fibLine := line.new(pivot.start.unixTime, level, BAR_TIME_RIGHT_ALIGNMENT, level, color = color, width = 1, xloc = xloc.bar_time, extend = extend.none)
    fibLine

processFibLevel(Pivot pivot, series float startPrice, series float height, simple float val, color color) =>
    float level = startPrice + height * val

    if not na(pivot)
        line fibLine = drawFibLevel(pivot, level, color)
        [level, fibLine]
    else
        na

getPrdWidthByPerc(int prd, int perc) => (ta.highest(prd) - ta.lowest(prd)) * perc / 100

getTfTxt() =>
    string prd = timeframe.period
    string tf = na

    if str.contains(prd, "S")
        tf := str.length(prd) == 1 ? "1S" : prd
    else if str.contains(prd, "D")
        tf := str.length(prd) == 1 ? "1D" : prd
    else if str.contains(prd, "W")
        tf := str.length(prd) == 1 ? "1W" : prd
    else if str.contains(prd, "M")
        tf := str.length(prd) == 1 ? "1M" : prd
    else
        float mins = str.tonumber(prd)

        if mins >= 60
            tf := str.format("{0}H",  mins / 60)
        else
            tf := str.format("{0}M",  mins)

    tf

//-----------------------------------------------------------------------------}
// Plots
//-----------------------------------------------------------------------------{
if oteShowed
    [retracement1, retracement1Line] = processFibLevel(oteLastPivot, oteStartPrice, oteHeight, 0.618, color.new(oteColor, 20))
    [OTE, OTELine] = processFibLevel(oteLastPivot, oteStartPrice, oteHeight, 0.705, color.new(oteColor, 40))
    [retracement2, retracement2Line] = processFibLevel(oteLastPivot, oteStartPrice, oteHeight, 0.79, color.new(oteColor, 20))

    if barstate.islast
        linefill.new(retracement1Line, retracement2Line, color = color.new(oteColor, 95))
        float middle = (math.abs(retracement1 - retracement2) / 2) + (retracement1 > retracement2 ? retracement2 : retracement1)
        var label oteLabel = na, label.delete(oteLabel)
        oteLabel := label.new(BAR_TIME_RIGHT_ALIGNMENT, middle, xloc = xloc.bar_time, text = "OTE", color = TRANSPARENT_COLOR, textcolor = YELLOW_COLOR, size = size.small, style = label.style_label_left)

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

Overview

Smart Money Concepts detects and labels internal and swing structures with customizable confluence filters and candle coloring. Additionally, it highlights major trading killzones including New York, London Open, London Close, and Asian sessions, while offering an optional right-aligned volume profile. Settings allow users to adjust calculation length, structure visibility, session timeframes, and volume profile resolution.

Details

Published by

TamTH-Dev on GitHub

Code credit

tamth_, as written in the script's header

Draws

On the price chart

Timeframes

M1-M5, M15-H1

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
Lengthint672generalLen
ShowboolfalsevpShowed
Lookbackint200vpLen
Size Of Levelsint360vpLevelsSize
Profile's Widthint16vpWidth
Bar's Widthint2vpBarWidth
Offset From Last Barint20vpOffset
ColorcolorDARK_GRAY_COLORvpColor
ShowboolfalsekzShowed
Show LabelsboolfalsekzLabelsShowed
New YorkbooltruekzNyShowed
kzNyColorcolorcolor.new(YELLOW_COLOR, 96)kzNyColor
Show all 40 settings
SettingTypeDefaultVariable
London OpenbooltruekzLdnoShowed
kzLdnoColorcolorcolor.new(AQUA_COLOR, 96)kzLdnoColor
London ClosebooltruekzLdncShowed
kzLdncColorcolorcolor.new(RED_COLOR, 96)kzLdncColor
AsianbooltruekzAsianShowed
kzAsiaColorcolorcolor.new(PURPLE_COLOR, 96)kzAsiaColor
modestringHistoricalmode
Color CandlesboolfalsetrendShowed
Show Internal StructurebooltrueiStructureShowed
Bullish StructurestringAlliBullShowed
iBullColorcolorAQUA_COLORiBullColor
Bearish StructurestringAlliBearShowed
iBearColorcolorRED_COLORiBearColor
Confluence FilterbooltrueiConfluenceFiltered
internalLenint5internalLen
Internal Label SizestringTinyiStructureSize
Show Swing StructurebooltruesStructureShowed
Bullish StructurestringAllsBullShowed
sBullColorcolorAQUA_COLORsBullColor
Bearish StructurestringAllsBearShowed
sBearColorcolorRED_COLORsBearColor
Swing Label SizestringSmallsStructureSize
Show Swings PointsboolfalseswingsPointsShowed
swingLenint50swingLen
Show Strong/Weak High/LowbooltruehlSwingsShowed
Internal Order BlocksbooltrueiobShowed
numOfIobsint5numOfIobs
Swing Order BlocksboolfalsesobShowed
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

Moon Dev GEX Sweep & CVD

by moondevonyt
92 stars

The indicator detects liquidity sweeps across key price levels and calculates a cumulative volume delta proxy. It uses pivot high and low lookbacks to identify swing points, marking sweeps when price breaks beyond a level and quickly closes back inside. Featur

Indicator v5 Support & resistanceSMC / ICT / price action
Updated 2 months ago · Custom licence GitHub repository of Moon Dev GEX Sweep & CVD

CRT State Engine

by alboogycOdR
6 stars

CRT State Engine is an overlay indicator that maps New York-timed CRT models, unmitigated order blocks, market structure, liquidity pools, and volume profile on the price chart. It can draw session boxes for Asia, London, London lunch, and New York and optiona

Indicator v5 SMC / ICT / price actionSessions & time
Updated 7 months ago · None stated GitHub repository of CRT State Engine

This overlay indicator builds a weighted volume profile over a chosen number of bars and plots pivot points with a point of control line on the price chart. Volume can be weighted toward recent, normal, or past bars, and swing length, row count, and alignment

Indicator v5 VolumeSupport & resistance
Updated 5 months ago · None stated GitHub repository of Weighted Volume Profile Pivot Points | Flux Charts
1 stars

Advanced Market Theory calculates Market Profile, TPO, and Hybrid profiles directly on the price chart. It identifies key structural levels including the Point of Control, Value Area High and Low boundaries, Initial Balance range projections, single prints, po

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 11 months ago · None stated GitHub repository of Advanced Market Theory

VP

by dearvn
257 stars

This indicator calculates and plots a session-based volume profile directly on the price chart. It maps volume distribution across price levels, highlighting key structural reference points including the Point of Control, Value Area High, and Value Area Low. C

Indicator v5 VolumeSupport & resistance
Updated 7 weeks ago · None stated GitHub repository of VP

More SMC / ICT scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers