Isolated Scroll Container

1. Indicator Description — Ideal SMC – Clean Pro

All-in-one SMC toolkit: Order Block, FVG, Liquidity Pools, BOS, HTF, Trend Filter & Setups. Clean, tidy, and clear.

1.1 Indicator Concept

The indicator follows Smart Money Concepts (SMC): price moves because of supply–demand imbalances and the hunt for liquidity. From there, we focus on four key pillars:

  • Order Block (OB): institutional order zones; price often retests them before continuing.
  • Fair Value Gap (FVG): a 3-candle void showing imbalance; price tends to fill it over time.
  • Liquidity Pools (EQH/EQL): clusters of equal highs/lows that collect liquidity.
  • Break of Structure (BOS): bullish/bearish structure break that confirms short-term trend change.

The indicator turns these ideas into clear tradable zones and setup signals, with Trend Filter, HTF (Higher Timeframe), TTL, Merge, etc., so your chart stays clean and actionable, not cluttered.

1.2 Indicator Features

  • Automatic Order Block detection (bullish/bearish) with ATR filter, zone merging (Merge), mitigation flag (Mitigated), lifetime limit (TTL), and control to keep extending to the live bar (extendToLive) or stop after boxWidthBars.
  • Automatic FVG detection (bull/bear) with ATR-based minimum size, mitigation tracking & TTL similar to OB.
  • Liquidity Pools (EQH/EQL):
    • Tolerance by ATR (useATRTol, tolATRmult) or by ticks (tolTicks).
  • BOS plotted directly with arrows (Up/Down) to confirm structure shifts.
  • Multi-Timeframe (useHTF, htfTf): draw stronger zones using higher timeframe data (e.g., H4/Daily).
  • Trend Filter: only show zones in the current trend direction (useTrendFilter, obInTrendOnly, fvgInTrendOnly).
  • Trade Setups “OB Retest + Trend”:
    • Auto-draw Entry / SL / TP, allow signals only on bar close (onlyConfirmed), and set targets by R:R (rrTarget).
    • Optional icons, labels, and lines (showIcons/showLabels/showLines).
  • Display optimization: limit active zones (maxOBKeep, maxFVGKeep), and extend zones only while unmitigated (extendOnlyUnmitigated).

1.3 How to Use

  • Set your bias: check BOS + HTF zones to decide long vs. short priority.
  • Be patient: wait for price to return to an unmitigated OB/FVG in the Trend Filter direction.
  • Entry: take the indicator’s “OB Retest + Trend” signal. Entry is typically the zone midpoint; SL is the opposite boundary.
  • Risk management: set TP by rrTarget (default 2R). You can trail SL based on structure.
  • Tune to your style:
    Scalplower pivotLen, smaller boxWidthBars, shorter ttl, smaller minOBatr/minFVGatr. Intradaybalance sensitivity and cleanliness; combine with useHTF = H1/H4. Swingraise pivotLen, use HTF D/W, longer ttl, wider zones.
Quick tip: Turn extendToLive=true to stretch zones to the current bar; turn it off to keep charts airy when scrubbing history. Use extendOnlyUnmitigated=true so once a zone is touched it fades and stops extending.

1.4 How the Indicator Works

Inputs & roles

  • Core: pivotLen (swing length), bodyLookback (scan candle bodies for OB), atrLen (ATR), minOBatr, minFVGatr (minimum thresholds).
  • Display: ttlOBBars, ttlFVGBars, maxOBKeep, maxFVGKeep, extendOnlyUnmitigated, extendToLive, boxWidthBars.
  • Liquidity Pools: showLP, useATRTol, tolATRmult, tolTicks.
  • Filters: useTrendFilter, obInTrendOnly, fvgInTrendOnly.
  • HTF: useHTF, htfTf (e.g., “240” = H4).
  • Setups: showSetups, showIcons, showLabels, showLines, onlyConfirmed, rrTarget.

Main logic blocks

  • Pivot & BOS detection: use ta.pivothigh / ta.pivotlow on srcH/srcL (HTF if enabled), then set bosUp/bosDown when breaking the nearest high/low.
  • Order Block:
    • Find the most recent bearish/bullish body (functions fLastBearBody*/fLastBullBody*) as OB bounds; filter by ATR & trend (allowBull/allowBear).
    • Create box, store arrays (obBox, obType, obMit, obBirth); limit count & TTL; Merge overlapping same-type zones (fMergeOBs).
    • Mark as “mitigated” when high/low intersects the zone; dim the color.
  • FVG:
    • 3-candle conditions: bullFVG if srcL > srcH[2]; bearFVG if srcH < srcL[2]; with ATR & trend filters.
    • Create box, track mitigation, TTL, and control the right edge like OB.
  • Liquidity Pools: detect EQH/EQL by comparing nearby pivots within tolerance tol (ATR-based or ticks), draw dotted lines extended to the right.
  • Setups:
    • Find the nearest unmitigated OB in the trend direction (fFindRecentOB), check price retest and candle confirmation.
    • Compute Entry (mid-zone), SL (opposite edge), TP = Entry ± rrTarget × Risk; draw lines/labels if enabled.

Outputs & how they are used

  • Zone boxes (OB/FVG) colored by direction; mitigated zones fade to reduce noise.
  • LP lines (EQH/EQL) as dotted guides to areas prone to liquidity grabs.
  • BOS arrows to confirm short-term bias.
  • Entry/SL/TP lines plus icons/labels (if enabled) for planning and tracking trades.
Quick example (bullish): a bosUp appears → Trend=up. A bullish OB is created and remains unmitigated. When price retests the zone and the bar closes green (or per onlyConfirmed), the “Long: OB Retest + Trend” setup triggers. Entry at the midpoint, SL at the lower bound, TP by 2R (default). To keep charts tidy, turn off extendToLive and reduce maxOBKeep/maxFVGKeep.
Zoom/Pan Layered Image
Background Overlay
− +

Use the analysis above + the code below to instruct AI to modify the indicator and turn it into a trading bot—no coding required!

How to do it here -> 👉ZERO2HERO👈

				
					//@version=5
indicator("Ideal SMC – Clean Pro (Trend, Merge, TTL, HTF, Compact + Setups) [Stable + Floating Boxes]", overlay=true, max_boxes_count=500, max_labels_count=500, max_lines_count=500)

//================ Inputs ================
pivotLen        = input.int(6,  "Swing Length (pivot detection)", minval=2, group="Core")
bodyLookback    = input.int(20, "Body Lookback (OB search)",      minval=3, group="Core")
atrLen          = input.int(14, "ATR Length", minval=5, group="Core")
minOBatr        = input.float(0.80, "Min OB Size (x ATR)", step=0.05, group="Core")
minFVGatr       = input.float(0.35, "Min FVG Size (x ATR)", step=0.05, group="Core")

ttlOBBars   = input.int(300, "OB TTL (Bars; 0 = unlimited)", minval=0, group="Display")
ttlFVGBars  = input.int(300, "FVG TTL (Bars; 0 = unlimited)", minval=0, group="Display")
maxOBKeep   = input.int(12,  "Max active OBs", minval=1, maxval=200, group="Display")
maxFVGKeep  = input.int(8,   "Max active FVGs", minval=1, maxval=200, group="Display")
extendOnlyUnmitigated = input.bool(true, "Extend boxes only while unmitigated", group="Display")

// >>> Neu: Steuerung, ob Zonen bis zur Live-Kerze verlängern <<<
extendToLive   = input.bool(true, title="Extend zones to live bar", group="Display")
boxWidthBars   = input.int(80,    title="Max box width (bars) when not extended", minval=5, group="Display")

showLP      = input.bool(true, "Show Liquidity Pools (EQH/EQL)", group="LP")
useATRTol   = input.bool(true, "LP tolerance uses ATR (else ticks)", group="LP")
tolATRmult  = input.float(0.05, "LP tolerance ATR multiplier", step=0.01, group="LP")
tolTicks    = input.int(3, "LP tolerance ticks", minval=1, group="LP")

useTrendFilter = input.bool(true, "Filter: show zones only in trend direction", group="Filters")
obInTrendOnly  = input.bool(true, "Filter OBs by trend", group="Filters")
fvgInTrendOnly = input.bool(true, "Filter FVGs by trend", group="Filters")

useHTF     = input.bool(false, "Use HTF data for zones", group="HTF")
htfTf      = input.timeframe("240", "HTF timeframe", group="HTF")

// Setups
showSetups    = input.bool(true,  "Show Setups (OB Retest + Trend)", group="Setups")
showIcons     = input.bool(true,  "Setups: show icons",              group="Setups")
showLabels    = input.bool(false, "Setups: show labels (text)",      group="Setups")
showLines     = input.bool(true,  "Setups: draw Entry/SL/TP lines",  group="Setups")
onlyConfirmed = input.bool(true,  "Only on confirmed bar close",     group="Setups")
rrTarget      = input.float(2.0,  "TP Risk Reward (R multiple)", minval=1.0, step=0.5, group="Setups")

//================ Colors ================
color colBU  = color.new(color.lime,   80)
color colB   = color.new(color.red,    80)
color colFU  = color.new(color.teal,   80)
color colFD  = color.new(color.orange, 80)
color colEQH = color.new(color.fuchsia, 0)
color colEQL = color.new(color.aqua,    0)

//================ Core Series (typed) ================
float atrVal = ta.atr(atrLen)
float tick   = syminfo.mintick
float tol    = useATRTol and not na(atrVal) ? atrVal * tolATRmult : tick * tolTicks

float htfHigh  = request.security(syminfo.tickerid, htfTf, high,  barmerge.gaps_off, barmerge.lookahead_off)
float htfLow   = request.security(syminfo.tickerid, htfTf, low,   barmerge.gaps_off, barmerge.lookahead_off)
float htfClose = request.security(syminfo.tickerid, htfTf, close, barmerge.gaps_off, barmerge.lookahead_off)

float srcH = useHTF ? htfHigh  : high
float srcL = useHTF ? htfLow   : low
float srcC = useHTF ? htfClose : close

float ph = ta.pivothigh(srcH, pivotLen, pivotLen)
float pl = ta.pivotlow(srcL,  pivotLen, pivotLen)

var float lastHigh = na
var float lastLow  = na
if not na(ph)
    lastHigh := srcH[pivotLen]
if not na(pl)
    lastLow  := srcL[pivotLen]

bool bosUp   = not na(lastHigh) and srcC > lastHigh
bool bosDown = not na(lastLow)  and srcC < lastLow

var string msTrend = "none"
if bosUp
    msTrend := "up"
if bosDown
    msTrend := "down"

//================ Containers ================
var box[]    obBox    = array.new_box()
var string[] obType   = array.new_string()  // "BU"/"B"
var bool[]   obMit    = array.new_bool()
var int[]    obBirth  = array.new_int()

var box[]    fvgBox   = array.new_box()
var string[] fvgType  = array.new_string()  // "FU"/"FD"
var bool[]   fvgMit   = array.new_bool()
var int[]    fvgBirth = array.new_int()

//================ Helpers ================
fLastBearBodyLo() =>
    float lo = na
    for k = 1 to bodyLookback
        if close[k] < open[k]
            lo := math.min(open[k], close[k])
            break
    lo

fLastBearBodyHi() =>
    float hi = na
    for k = 1 to bodyLookback
        if close[k] < open[k]
            hi := math.max(open[k], close[k])
            break
    hi

fLastBullBodyLo() =>
    float lo = na
    for k = 1 to bodyLookback
        if close[k] > open[k]
            lo := math.min(open[k], close[k])
            break
    lo

fLastBullBodyHi() =>
    float hi = na
    for k = 1 to bodyLookback
        if close[k] > open[k]
            hi := math.max(open[k], close[k])
            break
    hi

fMergeOBs() =>
    int n = array.size(obBox)
    if n > 1
        for i = n - 1 to 1
            string ti = array.get(obType, i)
            box bi = array.get(obBox, i)
            float itop = box.get_top(bi)
            float ibot = box.get_bottom(bi)
            for k = i - 1 to 0
                string tk = array.get(obType, k)
                if tk == ti
                    box bk = array.get(obBox, k)
                    float ktop = box.get_top(bk)
                    float kbot = box.get_bottom(bk)
                    bool overlap = not (ibot > ktop or itop < kbot)
                    if overlap
                        float ntop = math.max(itop, ktop)
                        float nbot = math.min(ibot, kbot)
                        box.set_top(bk, ntop)
                        box.set_bottom(bk, nbot)
                        box.delete(bi)
                        array.remove(obBox, i)
                        array.remove(obType, i)
                        array.remove(obMit, i)
                        array.remove(obBirth, i)
                        break

//================ OB Logic ================
bool allowBull = not useTrendFilter or msTrend == "up"
bool allowBear = not useTrendFilter or msTrend == "down"

if bosUp and allowBull and not na(atrVal)
    float loB = fLastBearBodyLo()
    float hiB = fLastBearBodyHi()
    if not na(loB) and not na(hiB) and (hiB - loB) >= atrVal * minOBatr
        box b = box.new(bar_index, hiB, bar_index, loB, bgcolor=colBU)
        array.push(obBox, b)
        array.push(obType, "BU")
        array.push(obMit, false)
        array.push(obBirth, bar_index)
        while array.size(obBox) > maxOBKeep
            box old = array.shift(obBox)
            box.delete(old)
            array.shift(obType)
            array.shift(obMit)
            array.shift(obBirth)

if bosDown and allowBear and not na(atrVal)
    float loS = fLastBullBodyLo()
    float hiS = fLastBullBodyHi()
    if not na(loS) and not na(hiS) and (hiS - loS) >= atrVal * minOBatr
        box b2 = box.new(bar_index, hiS, bar_index, loS, bgcolor=colB)
        array.push(obBox, b2)
        array.push(obType, "B")
        array.push(obMit, false)
        array.push(obBirth, bar_index)
        while array.size(obBox) > maxOBKeep
            box old2 = array.shift(obBox)
            box.delete(old2)
            array.shift(obType)
            array.shift(obMit)
            array.shift(obBirth)

// OB maintain (Guards + floating right edge)
int nOb = array.size(obBox)
if nOb > 0
    for i = nOb - 1 to 0
        box bb  = array.get(obBox, i)
        float top = box.get_top(bb)
        float bot = box.get_bottom(bb)
        bool m = array.get(obMit, i)
        if not m and not na(top) and not na(bot) and high >= bot and low <= top
            array.set(obMit, i, true)
            color dimCol = array.get(obType, i) == "BU" ? color.new(color.lime, 90) : color.new(color.red, 90)
            box.set_bgcolor(bb, dimCol)
        // >>> HIER: rechte Kante abhängig von extendToLive / boxWidthBars <<<
        int bornX = array.get(obBirth, i)
        int rightX = extendToLive ? bar_index : bornX + boxWidthBars
        if extendOnlyUnmitigated ? not array.get(obMit, i) : true
            box.set_right(bb, rightX)

    if ttlOBBars > 0
        int n2 = array.size(obBox)
        if n2 > 0
            for i = n2 - 1 to 0
                int born = array.get(obBirth, i)
                if bar_index - born > ttlOBBars
                    box.delete(array.get(obBox, i))
                    array.remove(obBox, i)
                    array.remove(obType, i)
                    array.remove(obMit, i)
                    array.remove(obBirth, i)

fMergeOBs()

//================ FVG Logic ================
if bar_index >= 2 and not na(atrVal)
    bool bullFVG = srcL > srcH[2]
    if bullFVG and (not fvgInTrendOnly or allowBull)
        float topFU = srcL
        float botFU = srcH[2]
        if (topFU - botFU) >= atrVal * minFVGatr
            box bxU = box.new(bar_index, topFU, bar_index, botFU, bgcolor=colFU)
            array.push(fvgBox, bxU)
            array.push(fvgType, "FU")
            array.push(fvgMit, false)
            array.push(fvgBirth, bar_index)
            while array.size(fvgBox) > maxFVGKeep
                box ofu = array.shift(fvgBox)
                box.delete(ofu)
                array.shift(fvgType)
                array.shift(fvgMit)
                array.shift(fvgBirth)

    bool bearFVG = srcH < srcL[2]
    if bearFVG and (not fvgInTrendOnly or allowBear)
        float topFD = srcH
        float botFD = srcL[2]
        if (botFD - topFD) >= atrVal * minFVGatr
            box bxD = box.new(bar_index, topFD, bar_index, botFD, bgcolor=colFD)
            array.push(fvgBox, bxD)
            array.push(fvgType, "FD")
            array.push(fvgMit, false)
            array.push(fvgBirth, bar_index)
            while array.size(fvgBox) > maxFVGKeep
                box ofd = array.shift(fvgBox)
                box.delete(ofd)
                array.shift(fvgType)
                array.shift(fvgMit)
                array.shift(fvgBirth)

// FVG maintain (floating right edge)
int nF = array.size(fvgBox)
if nF > 0
    for i = nF - 1 to 0
        box fb = array.get(fvgBox, i)
        float ftop = box.get_top(fb)
        float fbot = box.get_bottom(fb)
        bool fm = array.get(fvgMit, i)
        if not fm and not na(ftop) and not na(fbot) and high >= fbot and low <= ftop
            array.set(fvgMit, i, true)
            color dim = array.get(fvgType, i) == "FU" ? color.new(color.teal, 90) : color.new(color.orange, 90)
            box.set_bgcolor(fb, dim)
        // >>> HIER: rechte Kante abhängig von extendToLive / boxWidthBars <<<
        int bornXF  = array.get(fvgBirth, i)
        int rightXF = extendToLive ? bar_index : bornXF + boxWidthBars
        if extendOnlyUnmitigated ? not array.get(fvgMit, i) : true
            box.set_right(fb, rightXF)

    if ttlFVGBars > 0
        int nF2 = array.size(fvgBox)
        if nF2 > 0
            for i = nF2 - 1 to 0
                int bornF = array.get(fvgBirth, i)
                if bar_index - bornF > ttlFVGBars
                    box.delete(array.get(fvgBox, i))
                    array.remove(fvgBox, i)
                    array.remove(fvgType, i)
                    array.remove(fvgMit, i)
                    array.remove(fvgBirth, i)

//================ LPs ================
if showLP
    float phL = ta.pivothigh(high, pivotLen, pivotLen)
    float plL = ta.pivotlow(low,  pivotLen, pivotLen)
    if not na(phL) and not na(phL[1]) and math.abs(phL - phL[1]) <= tol
        line.new(bar_index, phL, bar_index, phL, extend=extend.right, color=colEQH, style=line.style_dotted)
    if not na(plL) and not na(plL[1]) and math.abs(plL - plL[1]) <= tol
        line.new(bar_index, plL, bar_index, plL, extend=extend.right, color=colEQL, style=line.style_dotted)

//================ BOS arrows ================
plotshape(bosUp,   "BOS Up",   location=location.abovebar, style=shape.triangleup,   color=color.lime, size=size.tiny)
plotshape(bosDown, "BOS Down", location=location.belowbar, style=shape.triangledown, color=color.red,  size=size.tiny)

//================ Helper: find recent OB ================
fFindRecentOB(isBull) =>
    bool found = false
    float top = na
    float bot = na
    int n = array.size(obBox)
    if n > 0
        string want = isBull ? "BU" : "B"
        for i = n - 1 to 0
            if array.get(obType, i) == want and not array.get(obMit, i)
                box b = array.get(obBox, i)
                top := box.get_top(b)
                bot := box.get_bottom(b)
                found := true
                break
    [found, top, bot]

//================ Setups (OB Retest + Trend) ================
var float longEntry = na
var float longSL = na
var float longTP = na
var float riskL = na
var float shortEntry = na
var float shortSL = na
var float shortTP = na
var float riskS = na
var bool  longSigSeries = false
var bool  shortSigSeries = false

bool okBar = true
bool fL = false
bool fS = false
float topL = na
float botL = na
float topS = na
float botS = na
bool longSigTmp = false
bool shortSigTmp = false

if showSetups
    okBar := onlyConfirmed ? barstate.isconfirmed : true

    [fL, topL, botL] = fFindRecentOB(true)
    longSigTmp := fL and not na(topL) and not na(botL) and high >= botL and low <= topL and close > open and msTrend == "up" and okBar
    if longSigTmp
        longEntry := (topL + botL) / 2.0
        longSL    := botL
        riskL     := longEntry - longSL
        longTP    := longEntry + rrTarget * riskL
        longSigSeries := true
        if showLines
            line.new(bar_index, longEntry, bar_index + 20, longEntry, extend=extend.none, color=color.lime)
            line.new(bar_index, longSL,    bar_index + 20, longSL,    extend=extend.none, color=color.red)
            line.new(bar_index, longTP,    bar_index + 20, longTP,    extend=extend.none, color=color.teal)
    else
        longSigSeries := false

    [fS, topS, botS] = fFindRecentOB(false)
    shortSigTmp := fS and not na(topS) and not na(botS) and high >= botS and low <= topS and close < open and msTrend == "down" and okBar
    if shortSigTmp
        shortEntry := (topS + botS) / 2.0
        shortSL    := topS
        riskS      := shortSL - shortEntry
        shortTP    := shortEntry - rrTarget * riskS
        shortSigSeries := true
        if showLines
            line.new(bar_index, shortEntry, bar_index + 20, shortEntry, extend=extend.none, color=color.red)
            line.new(bar_index, shortSL,    bar_index + 20, shortSL,    extend=extend.none, color=color.lime)
            line.new(bar_index, shortTP,    bar_index + 20, shortTP,    extend=extend.none, color=color.orange)
    else
        shortSigSeries := false

// Icons/Labels
plotshape(showSetups and showIcons and longSigSeries,  "Long Setup",  style=shape.diamond, size=size.tiny, color=color.lime, location=location.belowbar)
plotshape(showSetups and showIcons and shortSigSeries, "Short Setup", style=shape.diamond, size=size.tiny, color=color.red,  location=location.abovebar)
if showSetups and showLabels and longSigSeries
    label.new(bar_index, close, "Long: OB Retest + Trend\nE:"+str.tostring(longEntry)+"  SL:"+str.tostring(longSL)+"  TP:"+str.tostring(longTP),
              style=label.style_label_up, color=color.new(color.lime, 70), textcolor=color.black)
if showSetups and showLabels and shortSigSeries
    label.new(bar_index, close, "Short: OB Retest + Trend\nE:"+str.tostring(shortEntry)+"  SL:"+str.tostring(shortSL)+"  TP:"+str.tostring(shortTP),
              style=label.style_label_down, color=color.new(color.red, 70), textcolor=color.white)

				
			

Indicator Insider

RSI Momentum Divergence Zones Indicator – Strategy

Finds divergences between price and an unusual RSI (an RSI computed on price momentum rather than on price itself), then turns every divergence into a horizontal price zone that stays alive until it is broken.

Smart Money Volume Activity Indicator – Strategy

An indicator that decomposes each chart candle into lower-timeframe volume, flags statistically abnormal volume spikes, and classifies each spike as a “Smart Money” or “Retail” footprint

Trend Pivots Profile Indicator – Strategy

Draws a separate volume profile for each swing between two pivot reversal points, using intrabar volume pulled from a lower timeframe, plus a Donchian-channel-style trend line.