
// ================================================================================
// ORDER BLOCK DETECTOR V3 – STRICT 4-RULE VALIDATION (FIXED)
// ================================================================================
// Based on: “Order Blocks ā 4 Rules That Separate Real Ones From Fake Ones”
//
// RULE 1: Liquidity Sweep (MANDATORY)
// RULE 2: FVG/Imbalance (3rd wick check)
// RULE 3: Unmitigated
// RULE 4: Break of Structure + Inducement
// ================================================================================
//version=5
indicator(“Order Blocks V3 – 4 Rules”, overlay=true, max_boxes_count=500, max_lines_count=500, max_labels_count=500)
// =============================================================================
// INPUTS
// =============================================================================
grp_main = “Main Settings”
swing_length = input.int(5, “Swing Detection Length”, minval=2, maxval=20, group=grp_main)
min_move_pct = input.float(0.8, “Min Move After OB (%)”, minval=0.1, step=0.1, group=grp_main)
max_ob_display = input.int(50, “Max OB History Bars”, minval=10, maxval=200, group=grp_main)
grp_fvg = “FVG Settings”
min_fvg_pct = input.float(0.2, “Min FVG Size (%)”, minval=0.05, step=0.05, group=grp_fvg)
show_fvg = input.bool(true, “Show FVG Zones”, group=grp_fvg)
grp_filter = “Quality Filter”
min_rules = input.int(2, “Min Rules to Pass”, minval=1, maxval=4, group=grp_filter)
show_mitigated = input.bool(false, “Show Mitigated OBs”, group=grp_filter)
grp_visual = “Visual Settings”
bull_ob_color = input.color(color.new(color.green, 75), “Bullish OB Color”, group=grp_visual)
bear_ob_color = input.color(color.new(color.red, 75), “Bearish OB Color”, group=grp_visual)
bull_fvg_color = input.color(color.new(color.teal, 85), “Bullish FVG Color”, group=grp_visual)
bear_fvg_color = input.color(color.new(color.maroon, 85), “Bearish FVG Color”, group=grp_visual)
entry_line_color = input.color(color.new(color.yellow, 30), “50% Entry Line”, group=grp_visual)
mitig_color = input.color(color.new(color.gray, 85), “Mitigated Color”, group=grp_visual)
show_labels = input.bool(true, “Show Labels”, group=grp_visual)
show_50pct_line = input.bool(true, “Show 50% Entry Level”, group=grp_visual)
show_bos_marker = input.bool(true, “Show BOS Markers”, group=grp_visual)
show_inducement = input.bool(true, “Show Inducement Markers”, group=grp_visual)
// =============================================================================
// SWING HIGH/LOW DETECTION
// =============================================================================
swingHigh = ta.pivothigh(high, swing_length, swing_length)
swingLow = ta.pivotlow(low, swing_length, swing_length)
var float lastSwingHigh = na
var float lastSwingLow = na
var int lastSwingHighBar = na
var int lastSwingLowBar = na
if not na(swingHigh)
lastSwingHigh := swingHigh
lastSwingHighBar := bar_index – swing_length
if not na(swingLow)
lastSwingLow := swingLow
lastSwingLowBar := bar_index – swing_length
// =============================================================================
// RULE 1: LIQUIDITY SWEEP DETECTION
// =============================================================================
bullishLiqSweep = low < low[1]
bearishLiqSweep = high > high[1]
// =============================================================================
// RULE 2: FVG DETECTION WITH 3RD CANDLE WICK CHECK
// =============================================================================
bullFVG_gap = low – high[2]
bullFVG_exists = bullFVG_gap > 0
bullFVG_size_pct = bullFVG_exists ? (bullFVG_gap / close) * 100 : 0.0
bullFVG_valid = bullFVG_size_pct >= min_fvg_pct
bullFVG_3rdWickTapped = low <= high[2]
bearFVG_gap = low[2] – high
bearFVG_exists = bearFVG_gap > 0
bearFVG_size_pct = bearFVG_exists ? (bearFVG_gap / close) * 100 : 0.0
bearFVG_valid = bearFVG_size_pct >= min_fvg_pct
bearFVG_3rdWickTapped = high >= low[2]
// =============================================================================
// RULE 4: BREAK OF STRUCTURE DETECTION
// =============================================================================
bullishBOS = high > lastSwingHigh and not na(lastSwingHigh) and bar_index – lastSwingHighBar < max_ob_display
bearishBOS = low < lastSwingLow and not na(lastSwingLow) and bar_index – lastSwingLowBar < max_ob_display
// =============================================================================
// INDUCEMENT DETECTION
// =============================================================================
bullishInducement = high > high[1] and close < high[1]
bearishInducement = low < low[1] and close > low[1]
// =============================================================================
// ORDER BLOCK DATA TYPE
// =============================================================================
type OrderBlockData
float top
float bottom
float entry50
int barIdx
bool isBullish
bool mitigated
int rule1
int rule2
int rule3
int rule4
bool hasFVG
bool fvgTapped
bool hasInducement
box obBox
line entryLine
label obLabel
var OrderBlockData[] orderBlocks = array.new<OrderBlockData>()
// =============================================================================
// STRONG MOVE DETECTION
// =============================================================================
strongBullMove = (close – close[2]) / close[2] * 100 > min_move_pct
strongBearMove = (close[2] – close) / close[2] * 100 > min_move_pct
// =============================================================================
// BULLISH ORDER BLOCK DETECTION
// =============================================================================
potentialBullOB = bullishLiqSweep[2] and strongBullMove
bull_r1 = potentialBullOB ? 1 : 0
bull_r2 = bullFVG_valid and not bullFVG_3rdWickTapped ? 1 : 0
bull_r3 = 1
bull_r4 = bullishBOS ? 1 : 0
bull_hasInducement = bullishInducement or bullishInducement[1] or bullishInducement[2]
bull_total_rules = bull_r1 + bull_r2 + bull_r3 + bull_r4
bull_valid = bull_r1 == 1 and bull_total_rules >= min_rules
if potentialBullOB and bull_valid
ob = OrderBlockData.new()
ob.top := high[2]
ob.bottom := low[2]
ob.entry50 := (high[2] + low[2]) / 2
ob.barIdx := bar_index – 2
ob.isBullish := true
ob.mitigated := false
ob.rule1 := bull_r1
ob.rule2 := bull_r2
ob.rule3 := bull_r3
ob.rule4 := bull_r4
ob.hasFVG := bullFVG_valid
ob.fvgTapped := bullFVG_3rdWickTapped
ob.hasInducement := bull_hasInducement
ob.obBox := box.new(bar_index – 2, ob.top, bar_index + max_ob_display, ob.bottom, bgcolor=bull_ob_color, border_color=color.green, border_width=1, extend=extend.right)
if show_50pct_line
ob.entryLine := line.new(bar_index – 2, ob.entry50, bar_index + max_ob_display, ob.entry50, color=entry_line_color, style=line.style_dashed, width=1, extend=extend.right)
if show_labels
rulesText = “R1:” + (bull_r1==1?”ā”:”ā”) + ” R2:” + (bull_r2==1?”ā”:”ā”) + ” R3:ā R4:” + (bull_r4==1?”ā”:”ā”)
labelText = “š¢ BULL OBn” + rulesText
if ob.hasInducement
labelText := labelText + “nā”Induce”
if ob.fvgTapped
labelText := labelText + “nā ļøFVG Tap”
ob.obLabel := label.new(bar_index – 2, ob.top, labelText, style=label.style_label_down, color=color.new(color.green, 30), textcolor=color.white, size=size.tiny)
array.push(orderBlocks, ob)
// =============================================================================
// BEARISH ORDER BLOCK DETECTION
// =============================================================================
potentialBearOB = bearishLiqSweep[2] and strongBearMove
bear_r1 = potentialBearOB ? 1 : 0
bear_r2 = bearFVG_valid and not bearFVG_3rdWickTapped ? 1 : 0
bear_r3 = 1
bear_r4 = bearishBOS ? 1 : 0
bear_hasInducement = bearishInducement or bearishInducement[1] or bearishInducement[2]
bear_total_rules = bear_r1 + bear_r2 + bear_r3 + bear_r4
bear_valid = bear_r1 == 1 and bear_total_rules >= min_rules
if potentialBearOB and bear_valid
ob = OrderBlockData.new()
ob.top := high[2]
ob.bottom := low[2]
ob.entry50 := (high[2] + low[2]) / 2
ob.barIdx := bar_index – 2
ob.isBullish := false
ob.mitigated := false
ob.rule1 := bear_r1
ob.rule2 := bear_r2
ob.rule3 := bear_r3
ob.rule4 := bear_r4
ob.hasFVG := bearFVG_valid
ob.fvgTapped := bearFVG_3rdWickTapped
ob.hasInducement := bear_hasInducement
ob.obBox := box.new(bar_index – 2, ob.top, bar_index + max_ob_display, ob.bottom, bgcolor=bear_ob_color, border_color=color.red, border_width=1, extend=extend.right)
if show_50pct_line
ob.entryLine := line.new(bar_index – 2, ob.entry50, bar_index + max_ob_display, ob.entry50, color=entry_line_color, style=line.style_dashed, width=1, extend=extend.right)
if show_labels
rulesText = “R1:” + (bear_r1==1?”ā”:”ā”) + ” R2:” + (bear_r2==1?”ā”:”ā”) + ” R3:ā R4:” + (bear_r4==1?”ā”:”ā”)
labelText = “š“ BEAR OBn” + rulesText
if ob.hasInducement
labelText := labelText + “nā”Induce”
if ob.fvgTapped
labelText := labelText + “nā ļøFVG Tap”
ob.obLabel := label.new(bar_index – 2, ob.bottom, labelText, style=label.style_label_up, color=color.new(color.red, 30), textcolor=color.white, size=size.tiny)
array.push(orderBlocks, ob)
// =============================================================================
// MITIGATION CHECK (Rule 3)
// =============================================================================
if array.size(orderBlocks) > 0
for i = array.size(orderBlocks) – 1 to 0
ob = array.get(orderBlocks, i)
if ob.mitigated
continue
mitigated = false
if ob.isBullish
mitigated := low <= ob.top
else
mitigated := high >= ob.bottom
if mitigated
ob.mitigated := true
ob.rule3 := 0
if not na(ob.obBox)
if show_mitigated
box.set_bgcolor(ob.obBox, mitig_color)
box.set_border_color(ob.obBox, color.gray)
box.set_right(ob.obBox, bar_index)
box.set_extend(ob.obBox, extend.none)
else
box.delete(ob.obBox)
if not na(ob.entryLine)
if show_mitigated
line.set_x2(ob.entryLine, bar_index)
line.set_extend(ob.entryLine, extend.none)
line.set_color(ob.entryLine, color.gray)
else
line.delete(ob.entryLine)
if not na(ob.obLabel) and not show_mitigated
label.delete(ob.obLabel)
if bar_index – ob.barIdx > max_ob_display * 2
if not na(ob.obBox)
box.delete(ob.obBox)
if not na(ob.entryLine)
line.delete(ob.entryLine)
if not na(ob.obLabel)
label.delete(ob.obLabel)
array.remove(orderBlocks, i)
// =============================================================================
// DRAW FVG ZONES
// =============================================================================
if show_fvg and bullFVG_valid and not bullFVG_3rdWickTapped
box.new(bar_index – 1, low, bar_index + 15, high[2], bgcolor=bull_fvg_color, border_color=color.teal, border_width=1)
if show_fvg and bearFVG_valid and not bearFVG_3rdWickTapped
box.new(bar_index – 1, low[2], bar_index + 15, high, bgcolor=bear_fvg_color, border_color=color.maroon, border_width=1)
// =============================================================================
// BOS MARKERS
// =============================================================================
plotshape(show_bos_marker and bullishBOS, “Bullish BOS”, shape.triangleup, location.belowbar, color.green, size=size.tiny, text=”BOS”)
plotshape(show_bos_marker and bearishBOS, “Bearish BOS”, shape.triangledown, location.abovebar, color.red, size=size.tiny, text=”BOS”)
// =============================================================================
// INDUCEMENT MARKERS
// =============================================================================
plotshape(show_inducement and bullishInducement, “Bull Inducement”, shape.xcross, location.abovebar, color.orange, size=size.tiny, text=”IND”)
plotshape(show_inducement and bearishInducement, “Bear Inducement”, shape.xcross, location.belowbar, color.orange, size=size.tiny, text=”IND”)
// =============================================================================
// INFO TABLE
// =============================================================================
var table infoTbl = table.new(position.top_right, 2, 10, bgcolor=color.new(color.black, 80))
if barstate.islast
bull_count = 0
bear_count = 0
if array.size(orderBlocks) > 0
for i = 0 to array.size(orderBlocks) – 1
ob = array.get(orderBlocks, i)
if not ob.mitigated
if ob.isBullish
bull_count += 1
else
bear_count += 1
table.cell(infoTbl, 0, 0, “OB Scanner V3”, text_color=color.white, text_size=size.small)
table.cell(infoTbl, 1, 0, “4-Rule System”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 1, “š¢ Bullish:”, text_color=color.green, text_size=size.tiny)
table.cell(infoTbl, 1, 1, str.tostring(bull_count), text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 0, 2, “š“ Bearish:”, text_color=color.red, text_size=size.tiny)
table.cell(infoTbl, 1, 2, str.tostring(bear_count), text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 0, 3, “āāāāāāā”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 1, 3, “”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 4, “R1:”, text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 1, 4, “Liq Sweep”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 5, “R2:”, text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 1, 5, “FVG Check”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 6, “R3:”, text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 1, 6, “Unmitigated”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 7, “R4:”, text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 1, 7, “BOS+Induce”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 8, “āāāāāāā”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 1, 8, “”, text_color=color.gray, text_size=size.tiny)
table.cell(infoTbl, 0, 9, “Min Rules:”, text_color=color.white, text_size=size.tiny)
table.cell(infoTbl, 1, 9, str.tostring(min_rules) + “/4”, text_color=color.yellow, text_size=size.tiny)
// =============================================================================
// ALERTS
// =============================================================================
alertcondition(potentialBullOB and bull_valid, “Bullish OB Formed”, “š¢ Bullish Order Block on {{ticker}}”)
alertcondition(potentialBearOB and bear_valid, “Bearish OB Formed”, “š“ Bearish Order Block on {{ticker}}”)
alertcondition(bullishBOS, “Bullish BOS”, “š Break of Structure UP on {{ticker}}”)
alertcondition(bearishBOS, “Bearish BOS”, “š Break of Structure DOWN on {{ticker}}”)
