PulseHub
OPEN-SOURCE SCRIPT

V4

246
//version=5
indicator("ICT MTF Entry: OB + CHoCH + FVG + Liquidity", overlay=true, max_boxes_count=500, max_lines_count=500, max_labels_count=500)

// ================= INPUTS =================
grpHTF = "HTF Order Block / POI"
htfOB = input.timeframe("60", "Order Block TF", group=grpHTF)
dispMult = input.float(1.5, "Displacement (x ATR)", group=grpHTF)
obMaxAgeBars = input.int(150, "OB Max Age (chart bars)", group=grpHTF)

grpMTF = "MTF CHoCH"
mtfCHoCH = input.timeframe("15", "CHoCH TF", group=grpMTF)
pivotLen = input.int(5, "Swing Pivot Length (MTF bars)", group=grpMTF)
chochWindow = input.int(30, "CHoCH Validity Window (chart bars)", group=grpMTF)

grpLTF = "Entry FVG / IFVG (uses chart timeframe - run this on your 1M/5M chart)"
useIFVG = input.bool(true, "Include Inverse FVG entries", group=grpLTF)
requireFVG = input.bool(false, "Require FVG/IFVG for entry (off = CHoCH + OB retest only)", group=grpLTF)

grpLIQ = "Liquidity Targets"
liqPivotLen = input.int(10, "Liquidity Pivot Length", group=grpLIQ)
liqTolTicks = input.float(2, "Equal High/Low Tolerance (ticks)", group=grpLIQ)

grpVis = "Display"
showOB = input.bool(true, "Show Order Blocks", group=grpVis)
showCHoCH = input.bool(true, "Show CHoCH Labels", group=grpVis)
showFVG = input.bool(true, "Show FVG/IFVG", group=grpVis)
showLiq = input.bool(true, "Show Liquidity", group=grpVis)

grpKZ = "Kill Zones (New York time)"
useKillzones = input.bool(false, "Only allow entries within kill zones", group=grpKZ)
asiaSession = input.session("2000-0000", "Asia Killzone", group=grpKZ)
londonSession = input.session("0200-0500", "London Killzone", group=grpKZ)
nySession = input.session("0700-1000", "New York Killzone", group=grpKZ)
shadeKZ = input.bool(true, "Shade Kill Zones on Chart", group=grpKZ)

inAsia = not na(time(timeframe.period, asiaSession, "America/New_York"))
inLondon = not na(time(timeframe.period, londonSession, "America/New_York"))
inNY = not na(time(timeframe.period, nySession, "America/New_York"))
inKillzone = inAsia or inLondon or inNY

bgcolor(shadeKZ and inAsia ? color.new(color.purple, 92) : na, title="Asia KZ")
bgcolor(shadeKZ and inLondon ? color.new(color.blue, 92) : na, title="London KZ")
bgcolor(shadeKZ and inNY ? color.new(color.yellow, 92) : na, title="NY KZ")

tick = syminfo.mintick

// ================= HTF ORDER BLOCK =================
[hO, hH, hL, hC, hO1, hH1, hL1, hC1, hTime, hATR] = request.security(syminfo.tickerid, htfOB,
[open, high, low, close, open[1], high[1], low[1], close[1], time, ta.atr(14)], lookahead=barmerge.lookahead_off)

var box[] bullOBs = array.new_box()
var box[] bearOBs = array.new_box()
var int lastHTFTimeOB = na

newHTFBar = na(lastHTFTimeOB) or hTime != lastHTFTimeOB
if newHTFBar
lastHTFTimeOB := hTime
body = math.abs(hC - hO)
bullDisp = hC > hO and body >= hATR * dispMult
bearDisp = hC < hO and body >= hATR * dispMult
prevBear = hC1 < hO1
prevBull = hC1 > hO1

if bullDisp and prevBear and showOB
b = box.new(bar_index, hH1, bar_index + obMaxAgeBars, hL1, border_color=color.new(color.teal, 0), bgcolor=color.new(color.teal, 85), extend=extend.none)
array.push(bullOBs, b)
if bearDisp and prevBull and showOB
b = box.new(bar_index, hH1, bar_index + obMaxAgeBars, hL1, border_color=color.new(color.red, 0), bgcolor=color.new(color.red, 85), extend=extend.none)
array.push(bearOBs, b)

f_manageOB(boxes, isBull) =>
if array.size(boxes) > 0
for i = array.size(boxes) - 1 to 0
b = array.get(boxes, i)
box.set_right(b, bar_index + 5)
top = box.get_top(b)
bot = box.get_bottom(b)
mitigated = isBull ? close < bot : close > top
expired = bar_index > box.get_left(b) + obMaxAgeBars
if mitigated or expired
box.delete(b)
array.remove(boxes, i)

f_manageOB(bullOBs, true)
f_manageOB(bearOBs, false)

priceInBullOB() =>
inside = false
if array.size(bullOBs) > 0
for i = 0 to array.size(bullOBs) - 1
b = array.get(bullOBs, i)
if close <= box.get_top(b) and close >= box.get_bottom(b)
inside := true
inside

priceInBearOB() =>
inside = false
if array.size(bearOBs) > 0
for i = 0 to array.size(bearOBs) - 1
b = array.get(bearOBs, i)
if close >= box.get_bottom(b) and close <= box.get_top(b)
inside := true
inside

// ================= MTF CHoCH =================
[pivHi, pivLo] = request.security(syminfo.tickerid, mtfCHoCH, [ta.pivothigh(pivotLen, pivotLen), ta.pivotlow(pivotLen, pivotLen)], lookahead=barmerge.lookahead_off)

var float lastSwingHigh = na
var float lastSwingLow = na
var string trend = "neutral"
var int bullCHoCHBar = na
var int bearCHoCHBar = na

if not na(pivHi)
lastSwingHigh := pivHi
if not na(pivLo)
lastSwingLow := pivLo

bullishCHoCH = trend != "up" and not na(lastSwingHigh) and close > lastSwingHigh
bearishCHoCH = trend != "down" and not na(lastSwingLow) and close < lastSwingLow

var bool bullSignaled = false
var bool bearSignaled = false

if bullishCHoCH
trend := "up"
bullCHoCHBar := bar_index
bullSignaled := false
if showCHoCH
label.new(bar_index, low, "CHoCH↑", style=label.style_label_up, color=color.new(color.lime, 0), textcolor=color.black, size=size.small)

if bearishCHoCH
trend := "down"
bearCHoCHBar := bar_index
bearSignaled := false
if showCHoCH
label.new(bar_index, high, "CHoCH↓", style=label.style_label_down, color=color.new(color.red, 0), textcolor=color.white, size=size.small)

recentBullCHoCH = not na(bullCHoCHBar) and (bar_index - bullCHoCHBar) <= chochWindow
recentBearCHoCH = not na(bearCHoCHBar) and (bar_index - bearCHoCHBar) <= chochWindow

// ================= LTF FVG / IFVG (runs on chart's own timeframe) =================
bullFVG = low > high[2]
bearFVG = high < low[2]

var box[] bullFVGs = array.new_box()
var box[] bearFVGs = array.new_box()

if showFVG and bullFVG
fb = box.new(bar_index[1], low, bar_index + 20, high[2], border_color=color.new(color.blue, 0), bgcolor=color.new(color.blue, 80))
array.push(bullFVGs, fb)
if showFVG and bearFVG
fb = box.new(bar_index[1], low[2], bar_index + 20, high, border_color=color.new(color.orange, 0), bgcolor=color.new(color.orange, 80))
array.push(bearFVGs, fb)

var bool bullIFVGSignal = false
var bool bearIFVGSignal = false
bullIFVGSignal := false
bearIFVGSignal := false

if array.size(bullFVGs) > 0
for i = array.size(bullFVGs) - 1 to 0
b = array.get(bullFVGs, i)
bot = box.get_bottom(b)
box.set_right(b, bar_index + 5)
if close < bot
if useIFVG
bearIFVGSignal := true
box.delete(b)
array.remove(bullFVGs, i)
else if bar_index > box.get_left(b) + 100
box.delete(b)
array.remove(bullFVGs, i)

if array.size(bearFVGs) > 0
for i = array.size(bearFVGs) - 1 to 0
b = array.get(bearFVGs, i)
top = box.get_top(b)
box.set_right(b, bar_index + 5)
if close > top
if useIFVG
bullIFVGSignal := true
box.delete(b)
array.remove(bearFVGs, i)
else if bar_index > box.get_left(b) + 100
box.delete(b)
array.remove(bearFVGs, i)

// ================= LIQUIDITY (equal highs/lows) =================
liqPH = ta.pivothigh(liqPivotLen, liqPivotLen)
liqPL = ta.pivotlow(liqPivotLen, liqPivotLen)

var float lastPH = na
var float lastPH2 = na
var float lastPL = na
var float lastPL2 = na

if not na(liqPH)
lastPH2 := lastPH
lastPH := liqPH
if showLiq and not na(lastPH2) and math.abs(lastPH - lastPH2) <= liqTolTicks * tick
line.new(bar_index - liqPivotLen, lastPH, bar_index + 30, lastPH, color=color.new(color.fuchsia, 0), style=line.style_dashed)
label.new(bar_index, lastPH, "BSL", style=label.style_label_down, color=color.new(color.fuchsia, 0), textcolor=color.white, size=size.tiny)

if not na(liqPL)
lastPL2 := lastPL
lastPL := liqPL
if showLiq and not na(lastPL2) and math.abs(lastPL - lastPL2) <= liqTolTicks * tick
line.new(bar_index - liqPivotLen, lastPL, bar_index + 30, lastPL, color=color.new(color.aqua, 0), style=line.style_dashed)
label.new(bar_index, lastPL, "SSL", style=label.style_label_up, color=color.new(color.aqua, 0), textcolor=color.black, size=size.tiny)

// ================= ENTRY SIGNAL =================
kzOK = not useKillzones or inKillzone
fvgLongOK = not requireFVG or bullFVG or (useIFVG and bullIFVGSignal)
fvgShortOK = not requireFVG or bearFVG or (useIFVG and bearIFVGSignal)

longSetup = priceInBullOB() and trend == "up" and recentBullCHoCH and fvgLongOK and kzOK and not bullSignaled
shortSetup = priceInBearOB() and trend == "down" and recentBearCHoCH and fvgShortOK and kzOK and not bearSignaled

if longSetup
bullSignaled := true
if shortSetup
bearSignaled := true

plotshape(longSetup, title="Long Entry", style=shape.triangleup, location=location.belowbar, color=color.new(color.lime, 0), size=size.small)
plotshape(shortSetup, title="Short Entry", style=shape.triangledown, location=location.abovebar, color=color.new(color.red, 0), size=size.small)

if longSetup
label.new(bar_index, low, "BUY", style=label.style_label_up, color=color.new(color.lime, 0), textcolor=color.black, size=size.normal)
if shortSetup
label.new(bar_index, high, "SELL", style=label.style_label_down, color=color.new(color.red, 0), textcolor=color.white, size=size.normal)

alertcondition(longSetup, title="Long Setup", message="Bullish OB + CHoCH + FVG entry")
alertcondition(shortSetup, title="Short Setup", message="Bearish OB + CHoCH + FVG entry"

Disclaimer

The information and publications are not meant to be, and do not constitute, financial, investment, trading, or other types of advice or recommendations supplied or endorsed by PulseWire. Read more in the Terms of Use.