//@version=6 indicator("Viper Order Blocks ", overlay=true, max_boxes_count=500, max_labels_count=500, max_lines_count=500) // --------------------------------------------------------------------------------------------------------------------} // πŸ“Œ π™π™Žπ™€π™ π™„π™‰π™‹π™π™π™Ž // --------------------------------------------------------------------------------------------------------------------{ grp1 = "Order Block Settings" maxBlocks = 10 dispThresh = input.float(0.5, title="Displacement Multiplier", step=0.1, group=grp1, tooltip="How much larger the engulfing candle must be to validate an OB.") strLookback = 100 grp2 = "Styling" bullColor = input.color(#1bd37a, title="Bullish OB Color", group=grp2) bearColor = input.color(#bc14e6, title="Bearish OB Color", group=grp2) borderColor = color.new(#ffffff, 100) textColor = input.color(color.new(#ffffff, 0), title="Text Color", group=grp2) showEqLine = input.bool(true, title="Show Equilibrium Line", group=grp2, tooltip="Draws a dashed midpoint line inside each Order Block.") powerFade = input.bool(true, title="Power Intensity", group=grp2, tooltip="Higher power OBs appear more vivid; weaker OBs are more transparent.") grp3 = "Signals" showRetests = input.bool(true, title="Show Retest Labels", group=grp3, tooltip="Plots an arrow label when price taps into an active Order Block.") // --------------------------------------------------------------------------------------------------------------------} // πŸ“Œ π™π™”π™‹π™€π™Ž // --------------------------------------------------------------------------------------------------------------------{ type OBBlock box bx box infoBox line eqLine int touches int birthBar float obTop float obBtm float powerPct // --------------------------------------------------------------------------------------------------------------------} // πŸ“Œ π™„π™‰π˜Ώπ™„π˜Ύπ˜Όπ™π™Šπ™ π˜Ύπ˜Όπ™‡π˜Ύπ™π™‡π˜Όπ™π™„π™Šπ™‰π™Ž // --------------------------------------------------------------------------------------------------------------------{ var OBBlock[] bullBlocks = array.new() var OBBlock[] bearBlocks = array.new() var int lastBullRetestBar = na var int lastBearRetestBar = na bool isBearishCandle = close[1] < open[1] bool isBullishDisp = close > open and close > high[1] and (close - open) > (high[1] - low[1]) * dispThresh bool bullOB = isBearishCandle and isBullishDisp bool isBullishCandle = close[1] > open[1] bool isBearishDisp = close < open and close < low[1] and (open - close) > (high[1] - low[1]) * dispThresh bool bearOB = isBullishCandle and isBearishDisp float maxCandleSize = ta.highest(high - low, strLookback) float atr = ta.atr(14) * 0.2 // Power % drives transparency: high power = low transp (vivid), low power = high transp (faded) // Power 100% -> transp 60, Power 0% -> transp 92 calcPowerTransp(float powerPct) => int transp = math.round(92 - (powerPct / 100.0) * 32) transp buildLabel(int touches, float pct) => "Power: " + str.tostring(math.round(pct)) + "% | Touches: " + str.tostring(touches) bool bullRetestRaw = false bool bearRetestRaw = false // --- Update bullish blocks --- int bullSize = array.size(bullBlocks) if bullSize > 0 for i = bullSize - 1 to 0 OBBlock ob = array.get(bullBlocks, i) float btm = ob.obBtm float top = ob.obTop if close < btm box.delete(ob.bx) box.delete(ob.infoBox) if not na(ob.eqLine) line.delete(ob.eqLine) array.remove(bullBlocks, i) else int transp = powerFade ? calcPowerTransp(ob.powerPct) : 80 box.set_bgcolor(ob.bx, color.new(bullColor, transp)) box.set_right(ob.bx, bar_index + 25) box.set_left(ob.infoBox, bar_index) box.set_right(ob.infoBox, bar_index + 25) box.set_top(ob.infoBox, top) box.set_bottom(ob.infoBox, btm) if showEqLine and not na(ob.eqLine) line.set_x2(ob.eqLine, bar_index) if low[1] <= top and low >= top and bar_index > box.get_left(ob.bx) bullRetestRaw := true ob.touches += 1 float pct = maxCandleSize > 0 ? ((top - btm) / maxCandleSize) * 100 : 0 box.set_text(ob.infoBox, "Bullish OB\n" + buildLabel(ob.touches, pct)) // --- Update bearish blocks --- int bearSize = array.size(bearBlocks) if bearSize > 0 for i = bearSize - 1 to 0 OBBlock ob = array.get(bearBlocks, i) float top = ob.obTop float btm = ob.obBtm if close > top box.delete(ob.bx) box.delete(ob.infoBox) if not na(ob.eqLine) line.delete(ob.eqLine) array.remove(bearBlocks, i) else int transp = powerFade ? calcPowerTransp(ob.powerPct) : 80 box.set_bgcolor(ob.bx, color.new(bearColor, transp)) box.set_right(ob.bx, bar_index + 25) box.set_left(ob.infoBox, bar_index) box.set_right(ob.infoBox, bar_index + 25) box.set_top(ob.infoBox, top) box.set_bottom(ob.infoBox, btm) if showEqLine and not na(ob.eqLine) line.set_x2(ob.eqLine, bar_index) if high[1] >= btm and high <= btm and bar_index > box.get_left(ob.bx) bearRetestRaw := true ob.touches += 1 float pct = maxCandleSize > 0 ? ((top - btm) / maxCandleSize) * 100 : 0 box.set_text(ob.infoBox, "Bearish OB\n" + buildLabel(ob.touches, pct)) // Apply 10-bar gap filter bool bullRetest = bullRetestRaw and (na(lastBullRetestBar) or bar_index - lastBullRetestBar >= 10) bool bearRetest = bearRetestRaw and (na(lastBearRetestBar) or bar_index - lastBearRetestBar >= 10) if bullRetest lastBullRetestBar := bar_index if bearRetest lastBearRetestBar := bar_index // --------------------------------------------------------------------------------------------------------------------} // πŸ“Œ π™‘π™„π™Žπ™π˜Όπ™‡π™„π™•π˜Όπ™π™„π™Šπ™‰ // --------------------------------------------------------------------------------------------------------------------{ if bullOB float obTop = high[1] float obBtm = low[1] float obMid = math.avg(obTop, obBtm) int obLeft = bar_index[1] float pct = maxCandleSize > 0 ? ((obTop - obBtm) / maxCandleSize) * 100 : 0 string lbl = "Bullish OB\n" + buildLabel(0, pct) int transp = powerFade ? calcPowerTransp(pct) : 80 if array.size(bullBlocks) > 0 for i = array.size(bullBlocks) - 1 to 0 OBBlock old = array.get(bullBlocks, i) if obTop >= old.obBtm and obBtm <= old.obTop box.delete(old.bx) box.delete(old.infoBox) if not na(old.eqLine) line.delete(old.eqLine) array.remove(bullBlocks, i) box newBx = box.new(obLeft, obTop, bar_index, obBtm, border_color = borderColor, border_style = line.style_dashed, bgcolor = color.new(bullColor, transp)) box infoBx = box.new(bar_index, obTop, bar_index + 25, obBtm, border_color = color.new(bullColor, 40), border_style = line.style_solid, bgcolor = color.new(bullColor, 92), text = lbl, text_color = textColor, text_size = size.auto, text_halign = text.align_center, text_valign = text.align_center) line eqL = na if showEqLine eqL := line.new(obLeft, obMid, bar_index, obMid, color = color.new(bullColor, 40), style = line.style_solid, width = 1) array.push(bullBlocks, OBBlock.new(newBx, infoBx, eqL, 0, bar_index, obTop, obBtm, pct)) int bullCount = array.size(bullBlocks) if bullCount > maxBlocks int trim = bullCount - maxBlocks for _i = 0 to trim - 1 if array.size(bullBlocks) > 0 OBBlock old = array.shift(bullBlocks) box.delete(old.bx) box.delete(old.infoBox) if not na(old.eqLine) line.delete(old.eqLine) if bearOB float obTop = high[1] float obBtm = low[1] float obMid = math.avg(obTop, obBtm) int obLeft = bar_index[1] float pct = maxCandleSize > 0 ? ((obTop - obBtm) / maxCandleSize) * 100 : 0 string lbl = "Bearish OB\n" + buildLabel(0, pct) int transp = powerFade ? calcPowerTransp(pct) : 80 if array.size(bearBlocks) > 0 for i = array.size(bearBlocks) - 1 to 0 OBBlock old = array.get(bearBlocks, i) if obTop >= old.obBtm and obBtm <= old.obTop box.delete(old.bx) box.delete(old.infoBox) if not na(old.eqLine) line.delete(old.eqLine) array.remove(bearBlocks, i) box newBx = box.new(obLeft, obTop, bar_index, obBtm, border_color = borderColor, border_style = line.style_dashed, bgcolor = color.new(bearColor, transp)) box infoBx = box.new(bar_index, obTop, bar_index + 25, obBtm, border_color = color.new(bearColor, 40), border_style = line.style_solid, bgcolor = color.new(bearColor, 92), text = lbl, text_color = textColor, text_size = size.auto, text_halign = text.align_center, text_valign = text.align_center) line eqL = na if showEqLine eqL := line.new(obLeft, obMid, bar_index, obMid, color = color.new(bearColor, 40), style = line.style_solid, width = 1) array.push(bearBlocks, OBBlock.new(newBx, infoBx, eqL, 0, bar_index, obTop, obBtm, pct)) int bearCount = array.size(bearBlocks) if bearCount > maxBlocks int trim = bearCount - maxBlocks for _i = 0 to trim - 1 if array.size(bearBlocks) > 0 OBBlock old = array.shift(bearBlocks) box.delete(old.bx) box.delete(old.infoBox) if not na(old.eqLine) line.delete(old.eqLine) // --- Retest labels --- if showRetests and bullRetest label.new( x = bar_index, y = low - atr, text = "⇑", style = label.style_label_up, color = color.new(bullColor, 100), textcolor = bullColor, size = size.large) if showRetests and bearRetest label.new( x = bar_index, y = high + atr, text = "⇣", style = label.style_label_down, color = color.new(bearColor, 100), textcolor = bearColor, size = size.large) // --- Alerts --- alertcondition(bullRetest, title="Bullish OB Retest", message="Price retested a Bullish Order Block.") alertcondition(bearRetest, title="Bearish OB Retest", message="Price retested a Bearish Order Block.")