ICT SMC Expert Signals

Category: Indicators By: Iván González Created: July 9, 2026, 2:33 PM
July 9, 2026, 2:33 PM
Indicators
0 Comments

Introduction

Most Smart Money Concepts overlays stop at drawing structure — order blocks, fair value gaps, a Break of Structure label — and leave the actual trade to you. ICT SMC Expert Signals, by Forex_Market_Insights, takes the opposite stance: it fires a concrete Buy/Sell on a liquidity sweep and immediately projects the whole plan — entry, stop-loss and three take-profit targets by risk-reward — with the risk and reward zones shaded on the chart. A stack of optional filters (EMA bias, an ATR volatility floor, a volume check and a cooldown) lets you go from raw sweeps to a heavily-confirmed setup, and a status panel keeps the current plan in a corner of the chart.

This ProBuilder port keeps the whole package: the sweep logic, the filters, the last-signal trade plan with its zones, and the panel.

Theory Behind the Indicator

1. The liquidity sweep

The core idea is the classic stop-hunt. Over the last sweepLookback bars (default 10, excluding the current one) the indicator tracks the highest high and the lowest low — the pools of resting liquidity:

recentHigh = highest[sweepLookback](high[1])
recentLow  = lowest[sweepLookback](low[1])

A bar that pushes above the prior high but closes back below it has swept buy-side liquidity and failed — a bearish signal:

buySideSweep = high > recentHigh and close < recentHigh     // -> SELL
sellSideSweep = low < recentLow and close > recentLow        // -> BUY

With requireReversal on (default), the signal bar must also be a reversal candle (a down candle for a Sell, an up candle for a Buy). That is the entire entry trigger — deliberately simple; the quality control lives in the filters.

2. Four optional filters (all off by default)

Each filter can only block a signal, never create one:

  • EMA bias — longs only above a 50-EMA, shorts only below.
  • ATR volatility floor — skip signals when ATR as a percent of price is below a threshold (avoids dead, rangebound tape).
  • Volume — require the signal bar’s volume above a multiple of its moving average.
  • Cooldown — enforce a minimum number of bars between signals so a choppy zone doesn’t spray entries.

(The original also carried a higher-timeframe bias via request.security; it was dropped in the port — see the rendering note below.)

3. The trade plan

On every accepted signal the indicator builds the plan from the entry close and an ATR-based stop:

entry = close
SL    = buy ? low - ATR * stopMult : high + ATR * stopMult
risk  = |entry - SL|
TPn   = buy ? entry + risk * rrN : entry - risk * rrN     (n = 1,2,3)

with default risk-reward ratios 1 / 2 / 3. It then draws entry, SL and the three TP lines, price labels, a risk zone (entry→SL) and a reward zone (entry→TP3) — for the last signal, extended to the right so it tracks the live market.

How to Read the Indicator

  1. BUY / SELL marks are the sweeps. A Sell prints when price grabs the highs and closes back under them; a Buy when it grabs the lows and closes back over them.
  2. The trade plan belongs to the last signal. Entry (blue), SL (red), TP1/TP2/TP3 (teal), with a red risk zone below/above entry and a green-teal reward zone out to TP3. It stays until the next signal replaces it.
  3. The stop sits beyond the sweep. The wick that did the hunting is protected by an extra ATR fraction — the level the market “should not” revisit if the sweep was real.
  4. Filters tighten, they don’t loosen. Turn them on one at a time: an EMA bias to trade with the trend, ATR% to avoid dead tape, cooldown to calm a noisy instrument.
  5. The panel is the current state. Direction, bars since the last signal, the entry/SL/TP levels and current ATR%.

Practical Applications

  1. A ready-made setup. The sweep gives the entry, the ATR gives the stop, the risk-reward gives three exits — an actionable plan without extra tooling.
  2. Sweep entries with your own bias. Keep the signal engine, add the EMA filter to only take sweeps in the direction of your trend read (use a longer EMA for a higher-timeframe feel).
  3. Partial-exit management. TP1/TP2/TP3 map naturally to scaling out (e.g. a third at each), moving the stop to break-even after TP1.
  4. Backtest the sweep idea. The Buy/Sell conditions are clean booleans; drop them into a ProOrder strategy to measure the edge before trading it.

Indicator Configuration

  • sweepLookback = 10: liquidity window; larger = only the more significant swings get swept.
  • requireReversal = 1: demand a reversal candle on the signal bar. Set to 0 for more (rawer) signals.
  • useEmaBias / emaLength (50): trend filter, off by default. Raise emaLength for a slower, higher-timeframe-like bias.
  • useAtrVol / minAtrPercent (0.10): volatility floor, as % of price.
  • useVolume / volumeMaLength (20) / volumeMult (1.0): volume confirmation.
  • useCooldown = 1 / cooldownBars = 5: minimum spacing between signals.
  • tradeAtrLength = 14 / stopAtrMult = 0.5: the ATR stop distance beyond the sweep wick.
  • tp1RR / tp2RR / tp3RR = 1.0 / 2.0 / 3.0: the three targets, in R multiples.
  • levelExtend = 50: how far the plan lines project to the right.
  • showDashboard = 1, maxSignals = 100: panel toggle and how many historical BUY/SELL marks are redrawn.

A note on rendering. The indicator uses defparam drawonlastbaronly = true so the trade-plan lines and labels — which extend to the right edge and follow price — stay crisp on low timeframes; without it ProBuilder repaints and overlaps them on every tick. Because that flag would otherwise hide inline drawings on past bars, the historical BUY/SELL marks are stored in arrays and redrawn in a loop on the last bar.

The higher-timeframe filter was dropped. The original reads a 4-hour EMA through request.security with a timeframe input — something ProBuilder’s TIMEFRAME can’t reproduce (it needs a literal), and an embedded TIMEFRAME(4 hours) throws an error whenever the chart is on a higher timeframe (e.g. daily) or one that isn’t an integer multiple. Since the filter was off by default, it was removed entirely. For a slower, trend-aligned bias, use useEmaBias with a larger emaLength on the chart timeframe.

What was adapted or dropped. The signal engine, the five filters and the last-signal trade plan are intact. Three things have no clean ProBuilder equivalent:

  • Per-signal object management → last signal only. The Pine version manages entry/SL/TP as line/label/linefill objects it edits and deletes each bar; ProBuilder has no dynamic object handles, so the port keeps the last signal’s plan in persistent state and draws it on the last bar. The “TP1/TP2/TP3 HIT” markers and the historical plans are dropped; the live setup and its hit-tracking state are kept.
  • The 12-row dashboard table → an anchored text panel. Same information (direction, last signal, levels, ATR%) as fixed text in the top-right corner, without per-cell backgrounds or the progress bars.
  • Alerts. alertcondition has no indicator equivalent; alerts are configured from the ProRealTime UI. The longSignal / shortSignal conditions are left clean for ProScreener/ProOrder.

Code

//-----------------------------------------------------------//
//PRC_ICT SMC EXPERT SIGNALS (by Forex_Market_Insights)
//version = 1
//09.07.26
//Ivan Gonzalez @ www.prorealcode.com
//Sharing ProRealTime knowledge
//-----------------------------------------------------------//
defparam drawonlastbaronly = true
//-----Inputs (parametros configurables)---------------------//
showSignals = 1
sweepLookback = 10
requireReversal = 1
useEmaBias = 0
emaLength = 50
useAtrVol = 0
volAtrLength = 14
minAtrPercent = 0.10
useVolume = 0
volumeMaLength = 20
volumeMult = 1.0
useCooldown = 1
cooldownBars = 5
tradeAtrLength = 14
stopAtrMult = 0.5
tp1RR = 1.0
tp2RR = 2.0
tp3RR = 3.0
levelExtend = 50
showDashboard = 1
maxSignals = 100
//-----Colors (RGB)------------------------------------------//
buyR = 0
buyG = 190
buyB = 120
sellR = 230
sellG = 65
sellB = 85
entryR = 30
entryG = 140
entryB = 255
slR = 240
slG = 80
slB = 80
tpR = 30
tpG = 190
tpB = 160
//-----ATR and liquidity levels------------------------------//
atrVal = averagetruerange[tradeAtrLength]
volAtrVal = averagetruerange[volAtrLength]
recentHigh = highest[sweepLookback](high[1])
recentLow = lowest[sweepLookback](low[1])
//-----Liquidity sweep detection-----------------------------//
buySideSweep = high > recentHigh and close < recentHigh
sellSideSweep = low < recentLow and close > recentLow
bearRev = close < open
bullRev = close > open
if requireReversal = 1 then
   validShort = buySideSweep and bearRev
   validLong = sellSideSweep and bullRev
else
   validShort = buySideSweep
   validLong = sellSideSweep
endif
//-----Bias and confirmation filters-------------------------//
emaBias = average[emaLength,1](close)
if useEmaBias = 1 then
   trendLongOk = close > emaBias
   trendShortOk = close < emaBias
else
   trendLongOk = 1
   trendShortOk = 1
endif
atrPercent = volAtrVal / close * 100
if useAtrVol = 1 then
   atrVolOk = atrPercent >= minAtrPercent
else
   atrVolOk = 1
endif
volumeMa = average[volumeMaLength](volume)
if useVolume = 1 then
   volumeOk = volume >= volumeMa * volumeMult
else
   volumeOk = 1
endif
//-----Cooldown and final signal-----------------------------//
once hasSignal = 0
once lastSignalBar = 0
if useCooldown = 0 then
   cooldownOk = 1
elsif hasSignal = 0 then
   cooldownOk = 1
elsif barindex - lastSignalBar >= cooldownBars then
   cooldownOk = 1
else
   cooldownOk = 0
endif
longSignal = (showSignals = 1) and validLong and trendLongOk and atrVolOk and volumeOk and cooldownOk
shortSignal = (showSignals = 1) and validShort and trendShortOk and atrVolOk and volumeOk and cooldownOk
//-----Live trade state + signal recording-------------------//
once nSig = 0
once tradeDir = 0
once entryBar = 0
once entryVal = 0
once slVal = 0
once tp1Val = 0
once tp2Val = 0
once tp3Val = 0
once tp1Done = 0
once tp2Done = 0
once tp3Done = 0
once slDone = 0
if longSignal then
   $sigX[nSig] = barindex
   $sigY[nSig] = low
   $sigType[nSig] = 1
   nSig = nSig + 1
   tradeDir = 1
   entryBar = barindex
   entryVal = close
   slVal = low - atrVal * stopAtrMult
   riskV = entryVal - slVal
   tp1Val = entryVal + riskV * tp1RR
   tp2Val = entryVal + riskV * tp2RR
   tp3Val = entryVal + riskV * tp3RR
   tp1Done = 0
   tp2Done = 0
   tp3Done = 0
   slDone = 0
   lastSignalBar = barindex
   hasSignal = 1
elsif shortSignal then
   $sigX[nSig] = barindex
   $sigY[nSig] = high
   $sigType[nSig] = 2
   nSig = nSig + 1
   tradeDir = 0 - 1
   entryBar = barindex
   entryVal = close
   slVal = high + atrVal * stopAtrMult
   riskV = slVal - entryVal
   tp1Val = entryVal - riskV * tp1RR
   tp2Val = entryVal - riskV * tp2RR
   tp3Val = entryVal - riskV * tp3RR
   tp1Done = 0
   tp2Done = 0
   tp3Done = 0
   slDone = 0
   lastSignalBar = barindex
   hasSignal = 1
endif
//-----Hit tracking of the live trade------------------------//
if tradeDir = 1 then
   if tp1Done = 0 and high >= tp1Val then
      tp1Done = 1
   endif
   if tp2Done = 0 and high >= tp2Val then
      tp2Done = 1
   endif
   if tp3Done = 0 and high >= tp3Val then
      tp3Done = 1
   endif
   if slDone = 0 and low <= slVal then
      slDone = 1
   endif
elsif tradeDir = 0 - 1 then
   if tp1Done = 0 and low <= tp1Val then
      tp1Done = 1
   endif
   if tp2Done = 0 and low <= tp2Val then
      tp2Done = 1
   endif
   if tp3Done = 0 and low <= tp3Val then
      tp3Done = 1
   endif
   if slDone = 0 and high >= slVal then
      slDone = 1
   endif
endif
//-----Drawing (all on the last bar)-------------------------//
if islastbarupdate then
   startI = 0
   if nSig > maxSignals then
      startI = nSig - maxSignals
   endif
   if nSig > 0 then
      for i = startI to nSig - 1 do
         if $sigType[i] = 1 then
            drawtext("BUY", $sigX[i], $sigY[i] - atrVal * 0.6) coloured(buyR, buyG, buyB)
         else
            drawtext("SELL", $sigX[i], $sigY[i] + atrVal * 0.6) coloured(sellR, sellG, sellB)
         endif
      next
   endif
   
   if tradeDir <> 0 then
      endBar = barindex + levelExtend
      
      if tradeDir = 1 then
         drawrectangle(entryBar, slVal, endBar, entryVal) coloured(slR, slG, slB, 0) fillcolor(slR, slG, slB, 10)
         drawrectangle(entryBar, entryVal, endBar, tp3Val) coloured(tpR, tpG, tpB, 0) fillcolor(tpR, tpG, tpB, 8)
      else
         drawrectangle(entryBar, entryVal, endBar, slVal) coloured(slR, slG, slB, 0) fillcolor(slR, slG, slB, 10)
         drawrectangle(entryBar, tp3Val, endBar, entryVal) coloured(tpR, tpG, tpB, 0) fillcolor(tpR, tpG, tpB, 8)
      endif
      
      drawsegment(entryBar, entryVal, endBar, entryVal) coloured(entryR, entryG, entryB) style(dottedline)
      drawsegment(entryBar, slVal, endBar, slVal) coloured(slR, slG, slB) style(line, 2)
      drawsegment(entryBar, tp1Val, endBar, tp1Val) coloured(tpR, tpG, tpB) style(dottedline)
      drawsegment(entryBar, tp2Val, endBar, tp2Val) coloured(tpR, tpG, tpB) style(dottedline)
      drawsegment(entryBar, tp3Val, endBar, tp3Val) coloured(tpR, tpG, tpB) style(dottedline)
      
      drawtext("Entry #entryVal#", endBar, entryVal+0.20*atrVal) coloured(entryR, entryG, entryB)
      drawtext("SL #slVal#", endBar, slVal+0.20*atrVal) coloured(slR, slG, slB)
      drawtext("TP1 #tp1Val#", endBar, tp1Val+0.20*atrVal) coloured(tpR, tpG, tpB)
      drawtext("TP2 #tp2Val#", endBar, tp2Val+0.20*atrVal) coloured(tpR, tpG, tpB)
      drawtext("TP3 #tp3Val#", endBar, tp3Val+0.20*atrVal) coloured(tpR, tpG, tpB)
   endif
   
   if showDashboard = 1 then
      panelX = 0 - 250
      topY = 0 - 30
      lineH = 20
      
      drawtext("ICT SMC EXPERT SIGNALS", panelX, topY, sansserif, bold, 11) coloured(entryR, entryG, entryB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
      
      if tradeDir = 1 then
         drawtext("Direction: LONG", panelX, topY - lineH, sansserif, bold, 10) coloured(buyR, buyG, buyB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
      elsif tradeDir = 0 - 1 then
         drawtext("Direction: SHORT", panelX, topY - lineH, sansserif, bold, 10) coloured(sellR, sellG, sellB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
      else
         drawtext("Direction: --", panelX, topY - lineH, sansserif, standard, 10) coloured(150, 150, 150) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
      endif
      
      barsAgo = barindex - lastSignalBar
      if hasSignal = 1 then
         drawtext("Last signal: #barsAgo# bars", panelX, topY - lineH * 2, sansserif, standard, 10) coloured(200, 200, 200) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
         drawtext("Entry #entryVal#", panelX, topY - lineH * 3, sansserif, standard, 10) coloured(entryR, entryG, entryB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
         drawtext("SL #slVal#", panelX, topY - lineH * 4, sansserif, standard, 10) coloured(slR, slG, slB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
         drawtext("TP1 #tp1Val#", panelX, topY - lineH * 5, sansserif, standard, 10) coloured(tpR, tpG, tpB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
         drawtext("TP2 #tp2Val#", panelX, topY - lineH * 6, sansserif, standard, 10) coloured(tpR, tpG, tpB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
         drawtext("TP3 #tp3Val#", panelX, topY - lineH * 7, sansserif, standard, 10) coloured(tpR, tpG, tpB) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
      else
         drawtext("no signal yet", panelX, topY - lineH * 2, sansserif, standard, 10) coloured(150, 150, 150) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
      endif
      
      drawtext("ATR%: #atrPercent#", panelX, topY - lineH * 8, sansserif, standard, 10) coloured(200, 200, 200) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
   endif
endif
//-----------------------------------------------------------//
return

Download
Filename: PRC_ICT-SMC-EXPERT-SIGNALS.itf
Downloads: 14
Iván González Legend
As an architect of digital worlds, my own description remains a mystery. Think of me as an undeclared variable, existing somewhere in the code.
Author’s Profile

Comments

Logo Logo
Loading...