Copy and paste the script below directly into the Pine Editor in TradingView and click Add to chart on an M1 timeframe.
Code: Select all
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Institutional Breakout Scalper [M15/M5/M1]
//@version=5
indicator("Institutional Breakout Scalper [M15/M5/M1]", shorttitle="IBS [M1/M5]", overlay=true, max_lines_count=500, max_boxes_count=200, max_labels_count=200)
// ============================================================================
// 1. USER INPUTS & STRATEGY PARAMETERS
// ============================================================================
grp_sess = "1. Session Windows & Liquidity Ranges (UTC)"
asianSess = input.session("0000-0630", "Asian Range (Liquidity Pool)", group=grp_sess)
preNySess = input.session("1030-1215", "Pre-NY Consolidation Range", group=grp_sess)
lonExecSess = input.session("0700-1000", "London Execution Window", group=grp_sess)
nyExecSess = input.session("1230-1530", "NY Open Execution Window", group=grp_sess)
useSessionGate = input.bool(true, "Restrict Signals to Execution Windows", group=grp_sess)
usePreNyRange = input.bool(true, "Trade Pre-NY Range Breakouts", group=grp_sess)
usePdhPdl = input.bool(true, "Trade Previous Day High/Low (PDH/PDL)", group=grp_sess)
grp_mtf = "2. M5 Compression & Trend Filters (Non-Repainting)"
m5EmaLen = input.int(20, "M5 EMA Length", minval=5, group=grp_mtf)
m5AtrLen = input.int(14, "M5 ATR Length", minval=5, group=grp_mtf)
minM5AtrPips = input.float(3.5, "Minimum M5 ATR Threshold (Pips)", minval=0.5, step=0.5, group=grp_mtf, tooltip="Use 3.5 for EURUSD, 4.5 for GBPUSD/USDJPY.")
requireM5Filter = input.bool(true, "Enforce M5 EMA & ATR Alignment", group=grp_mtf)
grp_disp = "3. M1 Displacement Candle Rules"
minBodyPct = input.float(65.0, "Min Body-to-Range Ratio (%)", minval=30.0, maxval=95.0, step=5.0, group=grp_disp)
minClearancePct = input.float(50.0, "Min Body Clearance Beyond Level (%)", minval=10.0, maxval=90.0, step=5.0, group=grp_disp)
volSmaLen = input.int(20, "Tick Volume SMA Length", minval=5, group=grp_disp)
minVolMult = input.float(1.5, "Min Volume Expansion Multiplier", minval=1.0, step=0.1, group=grp_disp)
m1AtrLen = input.int(14, "M1 ATR Length", minval=5, group=grp_disp)
minM1AtrMult = input.float(1.2, "Min Candle Length (x M1 ATR)", minval=0.5, step=0.1, group=grp_disp)
maxM1AtrMult = input.float(2.5, "Anti-Exhaustion Cap (x M1 ATR)", minval=1.5, step=0.1, group=grp_disp)
grp_risk = "4. Execution, Risk & Position Sizing"
acctBalance = input.float(50000.0, "Account Balance ($)", minval=100.0, step=1000.0, group=grp_risk)
riskPct = input.float(0.5, "Risk Per Trade (%)", minval=0.1, maxval=5.0, step=0.1, group=grp_risk)
pipValuePerLot = input.float(10.0, "Pip Value per Standard Lot ($)", minval=1.0, step=0.5, group=grp_risk, tooltip="$10 for USD-quoted majors (EURUSD, GBPUSD).")
entryBufferPips = input.float(0.4, "Model A Limit Entry Buffer (Pips)", minval=0.0, step=0.1, group=grp_risk)
slBufferPips = input.float(1.2, "Stop-Loss Buffer Beyond Candle (Pips)", minval=0.5, step=0.1, group=grp_risk)
maxDecayBars = input.int(4, "Model A Time Decay Limit (M1 Bars)", minval=1, maxval=15, group=grp_risk)
showHud = input.bool(true, "Show Institutional Telemetry HUD", group=grp_risk)
// ============================================================================
// 2. PIP CONVERSION & NON-REPAINTING MTF DATA
// ============================================================================
pipSize = syminfo.mintick * (syminfo.mintick == 0.00001 or syminfo.mintick == 0.001 ? 10 : 1)
// Local M1 Indicators
m1Ema = ta.ema(close, 20)
m1Atr = ta.atr(m1AtrLen)
volSma = ta.sma(volume, volSmaLen)
// Non-Repainting M5 Data (Confirmed closed M5 bar values)
m5EmaExpr = ta.ema(close, m5EmaLen)
m5AtrExpr = ta.atr(m5AtrLen)
[m5Close, m5Ema, m5EmaPrev, m5Atr] = request.security(syminfo.tickerid, "5", [close[1], m5EmaExpr[1], m5EmaExpr[2], m5AtrExpr[1]], barmerge.gaps_off, barmerge.lookahead_on)
// Non-Repainting Previous Day High & Low (PDH / PDL)
[pdh, pdl] = request.security(syminfo.tickerid, "D", [high[1], low[1]], barmerge.gaps_off, barmerge.lookahead_on)
// Evaluate M5 Institutional Filters
m5AtrPips = m5Atr / pipSize
isM5AtrOk = m5AtrPips >= minM5AtrPips
isM5Bull = m5Close > m5Ema and m5Ema > m5EmaPrev
isM5Bear = m5Close < m5Ema and m5Ema < m5EmaPrev
// ============================================================================
// 3. SESSION & LIQUIDITY POOL TRACKING (ASIAN, PRE-NY, PDH/PDL)
// ============================================================================
inAsian = not na(time(timeframe.period, asianSess + ":1234567", "UTC"))
inPreNy = not na(time(timeframe.period, preNySess + ":1234567", "UTC"))
inLonExe = not na(time(timeframe.period, lonExecSess + ":1234567", "UTC"))
inNyExe = not na(time(timeframe.period, nyExecSess + ":1234567", "UTC"))
inExecWindow = not useSessionGate or inLonExe or inNyExe
// Asian Range Variables
var float asianHigh = na
var float asianLow = na
var bool asianHighBroken = false
var bool asianLowBroken = false
if inAsian and not inAsian[1]
asianHigh := high
asianLow := low
asianHighBroken := false
asianLowBroken := false
else if inAsian
asianHigh := math.max(nz(asianHigh, high), high)
asianLow := math.min(nz(asianLow, low), low)
// Pre-NY Range Variables
var float preNyHigh = na
var float preNyLow = na
var bool preNyHighBroken = false
var bool preNyLowBroken = false
if inPreNy and not inPreNy[1]
preNyHigh := high
preNyLow := low
preNyHighBroken := false
preNyLowBroken := false
else if inPreNy
preNyHigh := math.max(nz(preNyHigh, high), high)
preNyLow := math.min(nz(preNyLow, low), low)
// Reset PDH/PDL break flags on new trading day
var bool pdhBroken = false
var bool pdlBroken = false
if ta.change(time("D"))
pdhBroken := false
pdlBroken := false
preNyHigh := na
preNyLow := na
// ============================================================================
// 4. M1 DISPLACEMENT CANDLE ENGINE
// ============================================================================
candleRange = high - low
candleBody = math.abs(close - open)
bodyRatio = candleRange > 0 ? (candleBody / candleRange) * 100.0 : 0.0
isBodyValid = bodyRatio >= minBodyPct
isVolValid = volume >= (volSma * minVolMult)
isAtrValid = candleRange >= (m1Atr * minM1AtrMult) and candleRange <= (m1Atr * maxM1AtrMult)
isBaseDisp = isBodyValid and isVolValid and isAtrValid
// Helper function to validate clearance across a specific horizontal liquidity level
f_checkBullBreak(float lvl, bool alreadyBroken) =>
if na(lvl) or alreadyBroken or close <= open
false
else
clearance = close - lvl
open < lvl and close > lvl and (clearance / candleBody) * 100.0 >= minClearancePct
f_checkBearBreak(float lvl, bool alreadyBroken) =>
if na(lvl) or alreadyBroken or close >= open
false
else
clearance = lvl - close
open > lvl and close < lvl and (clearance / candleBody) * 100.0 >= minClearancePct
// Check individual liquidity pools once their formation window is complete
asianReady = not inAsian and not na(asianHigh)
preNyReady = usePreNyRange and not inPreNy and not na(preNyHigh)
pdhReady = usePdhPdl and not na(pdh)
bullAsian = asianReady and f_checkBullBreak(asianHigh, asianHighBroken)
bearAsian = asianReady and f_checkBearBreak(asianLow, asianLowBroken)
bullPreNy = preNyReady and f_checkBullBreak(preNyHigh, preNyHighBroken)
bearPreNy = preNyReady and f_checkBearBreak(preNyLow, preNyLowBroken)
bullPdh = pdhReady and f_checkBullBreak(pdh, pdhBroken)
bearPdl = pdhReady and f_checkBearBreak(pdl, pdlBroken)
// Combine with M5 filter and Execution Window
m5BullGate = not requireM5Filter or (isM5AtrOk and isM5Bull)
m5BearGate = not requireM5Filter or (isM5AtrOk and isM5Bear)
validBullDisp = inExecWindow and isBaseDisp and m5BullGate and (bullAsian or bullPreNy or bullPdh)
validBearDisp = inExecWindow and isBaseDisp and m5BearGate and (bearAsian or bearPreNy or bearPdl)
// Identify the exact broken level and mark it as consumed for the session
float brokenLvl = na
string lvlName = ""
if validBullDisp
if bullAsian
brokenLvl := asianHigh
lvlName := "Asian High"
asianHighBroken := true
else if bullPreNy
brokenLvl := preNyHigh
lvlName := "Pre-NY High"
preNyHighBroken := true
else if bullPdh
brokenLvl := pdh
lvlName := "PDH"
pdhBroken := true
if validBearDisp
if bearAsian
brokenLvl := asianLow
lvlName := "Asian Low"
asianLowBroken := true
else if bearPreNy
brokenLvl := preNyLow
lvlName := "Pre-NY Low"
preNyLowBroken := true
else if bearPdl
brokenLvl := pdl
lvlName := "PDL"
pdlBroken := true
// Cancel out untouched levels if price breaks through without valid displacement criteria
if asianReady and close > asianHigh and not validBullDisp
asianHighBroken := true
if asianReady and close < asianLow and not validBearDisp
asianLowBroken := true
if preNyReady and close > preNyHigh and not validBullDisp
preNyHighBroken := true
if preNyReady and close < preNyLow and not validBearDisp
preNyLowBroken := true
if pdhReady and close > pdh and not validBullDisp
pdhBroken := true
if pdhReady and close < pdl and not validBearDisp
pdlBroken := true
// ============================================================================
// 5. STATE MACHINE: MODEL A LIMIT RETEST, 4-MIN DECAY & 2-BAR TRAP FILTER
// ============================================================================
// States: 0 = Flat, 1 = Pending Limit Order, 2 = Active Position
var int tradeState = 0
var int tradeDir = 0 // 1 = Long, -1 = Short
var int signalBarIndex = na
var float activeLevel = na
var float entryPrice = na
var float slPrice = na
var float tp1Price = na
var float tp2Price = na
var float activeLots = na
var int insideRangeCnt = 0
bool evtLimitFilled = false
bool evtOrderExpired = false
bool evtTrapScratch = false
bool evtTp2Hit = false
bool evtSlHit = false
// Manage Pending Order (State 1)
if tradeState == 1
barsElapsed = bar_index - signalBarIndex
if tradeDir == 1
// Check if limit filled
if low <= entryPrice
tradeState := 2
insideRangeCnt := 0
evtLimitFilled := true
// Check 4-minute time decay or runaway 1.5R move before fill
else if barsElapsed > maxDecayBars or high >= tp1Price
tradeState := 0
evtOrderExpired := true
else if tradeDir == -1
if high >= entryPrice
tradeState := 2
insideRangeCnt := 0
evtLimitFilled := true
else if barsElapsed > maxDecayBars or low <= tp1Price
tradeState := 0
evtOrderExpired := true
// Manage Active Trade (State 2)
else if tradeState == 2
if tradeDir == 1
// Check SL or TP2 first
if low <= slPrice
tradeState := 0
evtSlHit := true
else if high >= tp2Price
tradeState := 0
evtTp2Hit := true
else
// Two-Candle Acceptance Failure Rule (Bull Trap Filter)
insideRangeCnt := close < activeLevel ? insideRangeCnt + 1 : 0
if insideRangeCnt >= 2
tradeState := 0
evtTrapScratch := true
else if tradeDir == -1
if high >= slPrice
tradeState := 0
evtSlHit := true
else if low <= tp2Price
tradeState := 0
evtTp2Hit := true
else
// Two-Candle Acceptance Failure Rule (Bear Trap Filter)
insideRangeCnt := close > activeLevel ? insideRangeCnt + 1 : 0
if insideRangeCnt >= 2
tradeState := 0
evtTrapScratch := true
// Trigger New Setup on Valid M1 Displacement Candle
if (validBullDisp or validBearDisp) and tradeState == 0
tradeState := 1
tradeDir := validBullDisp ? 1 : -1
signalBarIndex := bar_index
activeLevel := brokenLvl
insideRangeCnt := 0
entryPrice := validBullDisp ? activeLevel + (entryBufferPips * pipSize) : activeLevel - (entryBufferPips * pipSize)
slPrice := validBullDisp ? low - (slBufferPips * pipSize) : high + (slBufferPips * pipSize)
// Enforce minimum 3.5-pip structural stop distance
minSlDist = 3.5 * pipSize
if math.abs(entryPrice - slPrice) < minSlDist
slPrice := validBullDisp ? entryPrice - minSlDist : entryPrice + minSlDist
riskDist = math.abs(entryPrice - slPrice)
slPips = riskDist / pipSize
tp1Price := validBullDisp ? entryPrice + (riskDist * 1.5) : entryPrice - (riskDist * 1.5)
tp2Price := validBullDisp ? entryPrice + (riskDist * 2.5) : entryPrice - (riskDist * 2.5)
// Dynamic Position Size Calculation (Standard Lots)
riskDollars = acctBalance * (riskPct / 100.0)
activeLots := slPips > 0 ? riskDollars / (slPips * pipValuePerLot) : 0.0
// Draw Setup Visuals (Boxes & Labels)
boxRight = bar_index + 12
box.new(bar_index, tp2Price, boxRight, entryPrice, border_color=color.new(color.teal, 40), bgcolor=color.new(color.teal, 85), text="TP2 (+2.5R)\nTP1 (+1.5R): " + str.tostring(tp1Price, format.mintick), text_color=color.white, text_size=size.small)
box.new(bar_index, entryPrice, boxRight, slPrice, border_color=color.new(color.maroon, 40), bgcolor=color.new(color.maroon, 85), text="SL (" + str.tostring(slPips, "#.#") + "p)", text_color=color.white, text_size=size.small)
lblText = (validBullDisp ? "LONG " : "SHORT ") + lvlName + "\nLimit: " + str.tostring(entryPrice, format.mintick) + "\nSize: " + str.tostring(activeLots, "#.##") + " Lots"
label.new(bar_index, validBullDisp ? low : high, lblText, style=validBullDisp ? label.style_label_up : label.style_label_down, color=validBullDisp ? color.teal : color.maroon, textcolor=color.white, size=size.small)
// ============================================================================
// 6. CHART PLOTS, BAR COLORING & HUD TABLE
// ============================================================================
plot(m1Ema, "M1 20 EMA", color=color.new(color.aqua, 20), linewidth=1)
plot(m5Ema, "M5 20 EMA (MTF)", color=color.new(color.orange, 10), linewidth=2)
plot(not inAsian ? asianHigh : na, "Asian High", color=color.new(color.yellow, 15), style=plot.style_linebr, linewidth=2)
plot(not inAsian ? asianLow : na, "Asian Low", color=color.new(color.yellow, 15), style=plot.style_linebr, linewidth=2)
plot(usePreNyRange and not inPreNy ? preNyHigh : na, "Pre-NY High", color=color.new(color.fuchsia, 25), style=plot.style_linebr, linewidth=1)
plot(usePreNyRange and not inPreNy ? preNyLow : na, "Pre-NY Low", color=color.new(color.fuchsia, 25), style=plot.style_linebr, linewidth=1)
plot(usePdhPdl ? pdh : na, "PDH", color=color.new(color.gray, 30), style=plot.style_circles, linewidth=1)
plot(usePdhPdl ? pdl : na, "PDL", color=color.new(color.gray, 30), style=plot.style_circles, linewidth=1)
// Highlight Active Execution Sessions & Displacement Candles
bgcolor(inLonExe or inNyExe ? color.new(color.blue, 94) : na, title="Execution Window Highlight")
barcolor(validBullDisp ? color.lime : validBearDisp ? color.red : na, title="Displacement Candle Highlight")
// Plot State Transition Markers
plotshape(evtLimitFilled, title="Limit Order Filled", style=shape.circle, location=location.abovebar, color=color.aqua, size=size.tiny, text="FILL")
plotshape(evtOrderExpired, title="4-Min Decay Cancelled", style=shape.xcross, location=location.abovebar, color=color.gray, size=size.tiny, text="4m EXP")
plotshape(evtTrapScratch, title="2-Bar Trap Scratch", style=shape.Warning, location=location.abovebar, color=color.orange, size=size.small, text="TRAP SCRATCH")
// Institutional Telemetry HUD
var table hud = table.new(position.top_right, 2, 7, bgcolor=color.new(color.black, 20), border_color=color.new(color.gray, 60), border_width=1)
if barstate.islast and showHud
sessStr = inLonExe ? "LONDON OPEN (ACTIVE)" : inNyExe ? "NY OPEN (ACTIVE)" : inAsian ? "ASIAN (BUILDING)" : "OFF-PEAK (STANDBY)"
sessCol = (inLonExe or inNyExe) ? color.lime : color.gray
biasStr = isM5Bull ? "BULLISH (Above 20 EMA)" : isM5Bear ? "BEARISH (Below 20 EMA)" : "NEUTRAL / CHOP"
biasCol = isM5Bull ? color.lime : isM5Bear ? color.red : color.orange
stateStr = tradeState == 1 ? "PENDING LIMIT (4m Timer)" : tradeState == 2 ? "IN POSITION (Monitoring)" : "SCANNING LEVELS"
stateCol = tradeState == 1 ? color.yellow : tradeState == 2 ? color.aqua : color.white
table.cell(hud, 0, 0, "INSTITUTIONAL SCALPER", text_color=color.white, bgcolor=color.new(color.blue, 40), text_size=size.small)
table.cell(hud, 1, 0, syminfo.ticker, text_color=color.white, bgcolor=color.new(color.blue, 40), text_size=size.small)
table.cell(hud, 0, 1, "Session Window", text_color=color.silver, text_size=size.small)
table.cell(hud, 1, 1, sessStr, text_color=sessCol, text_size=size.small)
table.cell(hud, 0, 2, "M5 Volatility (ATR)", text_color=color.silver, text_size=size.small)
table.cell(hud, 1, 2, str.tostring(m5AtrPips, "#.##") + " Pips " + (isM5AtrOk ? "[OK]" : "[LOW]"), text_color=isM5AtrOk ? color.lime : color.red, text_size=size.small)
table.cell(hud, 0, 3, "M5 20 EMA Pressure", text_color=color.silver, text_size=size.small)
table.cell(hud, 1, 3, biasStr, text_color=biasCol, text_size=size.small)
table.cell(hud, 0, 4, "M1 Volume Spike", text_color=color.silver, text_size=size.small)
table.cell(hud, 1, 4, str.tostring(volume / volSma, "#.##") + "x Avg", text_color=isVolValid ? color.lime : color.silver, text_size=size.small)
table.cell(hud, 0, 5, "Engine Status", text_color=color.silver, text_size=size.small)
table.cell(hud, 1, 5, stateStr, text_color=stateCol, text_size=size.small)
table.cell(hud, 0, 6, "Last Calc Lot Size", text_color=color.silver, text_size=size.small)
table.cell(hud, 1, 6, na(activeLots) ? "N/A" : str.tostring(activeLots, "#.##") + " Lots (" + str.tostring(riskPct) + "%)", text_color=color.aqua, text_size=size.small)
// ============================================================================
// 7. REAL-TIME EXECUTION ALERTS
// ============================================================================
alertcondition(validBullDisp, title="1. Bullish Displacement Setup", message="BULLISH M1 Displacement Breakout on {{ticker}}! Place Limit Retest Order.")
alertcondition(validBearDisp, title="2. Bearish Displacement Setup", message="BEARISH M1 Displacement Breakout on {{ticker}}! Place Limit Retest Order.")
alertcondition(evtLimitFilled, title="3. Model A Limit Order Filled", message="Model A Limit Order FILLED on {{ticker}}.")
alertcondition(evtOrderExpired, title="4. Limit Cancelled (4-Min Rule)", message="CANCEL PENDING LIMIT on {{ticker}}: 4-Minute Time Decay or 1.5R reached unfilled.")
alertcondition(evtTrapScratch, title="5. EMERGENCY: 2-Bar Trap Scratch", message="TRAP DETECTED on {{ticker}}: 2 consecutive M1 closes back inside range. SCRATCH TRADE AT MARKET!")