PulseHub
OPEN-SOURCE SCRIPT

aaaaaaaaaaa

397
// ╔══════════════════════════════════════════════════════════════════════════════╗
// ║ SMC PRO — Smart Money Concepts (Supply & Demand + Volume Profile) ║
// ║ Pine Script v6 | Professional Indicator ║
// ║ ║
// ║ Phase 1: Automatic Supply & Demand Zones ║
// ║ Phase 2: Market Structure + Breakout Detection ║
// ║ Phase 3: Retest Confirmation + Long/Short Signals ║
// ║ Phase 4: Risk/Reward, SL, TP, and Alerts ║
// ║ Phase 5: Fixed Range Volume Profile (POC, VAH, VAL, HVN, LVN) ║
// ╚══════════════════════════════════════════════════════════════════════════════╝

//version=6
indicator("SMC Pro — Supply & Demand + Volume Profile",
overlay = true,
max_boxes_count = 500,
max_lines_count = 500,
max_labels_count = 500,
max_bars_back = 5000)

// ─────────────────────────────────────────────────────────────────────────────
// INPUTS
// ─────────────────────────────────────────────────────────────────────────────

// Zone Detection
string GRP_ZONE = "Zone Detection"
i_pivotLen = input.int(5, "Pivot Length", minval=2, maxval=50, group=GRP_ZONE)
i_zoneAtrMult = input.float(0.5,"Zone Width (ATR ×)", minval=0.1, maxval=3.0, step=0.1, group=GRP_ZONE)
i_minTouches = input.int(2, "Min Touches for Signal", minval=1, maxval=10, group=GRP_ZONE)
i_maxZones = input.int(20, "Max Active Zones", minval=5, maxval=50, group=GRP_ZONE)
i_maxZoneAge = input.int(500, "Max Zone Age (bars)", minval=50, maxval=2000, group=GRP_ZONE)

// Breakout & Volume
string GRP_BO = "Breakout Detection"
i_volMult = input.float(1.5,"Volume Multiplier", minval=1.0, maxval=5.0, step=0.1, group=GRP_BO)
i_atrPeriod = input.int(14, "ATR Period", minval=5, maxval=50, group=GRP_BO)
i_volSmooth = input.int(20, "Volume SMA Length", minval=5, maxval=100, group=GRP_BO)

// Signals
string GRP_SIG = "Signal Filters"
i_minRR = input.float(2.0,"Minimum Risk:Reward", minval=1.0, maxval=10.0, step=0.5, group=GRP_SIG)
i_slBuffer = input.float(0.5,"SL Buffer (ATR ×)", minval=0.1, maxval=2.0, step=0.1, group=GRP_SIG)
i_maxRetestDist = input.int(100,"Max Retest Distance (bars)", minval=10, maxval=500, group=GRP_SIG)
i_noOppZoneDist = input.float(2.0,"No Opposite Zone Within (ATR ×)", minval=0.5, maxval=5.0, step=0.5, group=GRP_SIG)

// Volume Profile
string GRP_VP = "Volume Profile"
i_showVP = input.bool(true,"Show Volume Profile", group=GRP_VP)
i_vpLookback = input.int(200, "VP Lookback Bars", minval=50, maxval=500, group=GRP_VP)
i_vpRows = input.int(50, "VP Row Count", minval=20, maxval=100, group=GRP_VP)
i_vpWidth = input.int(20, "VP Width (bars)", minval=5, maxval=80, group=GRP_VP)
i_vaPercent = input.float(70.0,"Value Area %", minval=50.0,maxval=90.0, group=GRP_VP)

// Display
string GRP_DISP = "Display"
i_showZones = input.bool(true, "Show Zones", group=GRP_DISP)
i_showLabels = input.bool(true, "Show Labels", group=GRP_DISP)
i_showSLTP = input.bool(true, "Show SL/TP Lines", group=GRP_DISP)
i_showStructure = input.bool(true,"Show Market Structure", group=GRP_DISP)

// Colors
string GRP_CLR = "Colors"
i_demandColor = input.color(color.new(#26a69a, 75), "Demand Zone", group=GRP_CLR)
i_supplyColor = input.color(color.new(#ef5350, 75), "Supply Zone", group=GRP_CLR)
i_brokenColor = input.color(color.new(#787b86, 90), "Broken Zone", group=GRP_CLR)
i_pocColor = input.color(color.new(#ff9800, 0), "POC Line", group=GRP_CLR)
i_vahColor = input.color(color.new(#2196f3, 0), "VAH Line", group=GRP_CLR)
i_valColor = input.color(color.new(#2196f3, 0), "VAL Line", group=GRP_CLR)
i_buyVolColor = input.color(color.new(#26a69a, 40), "Buy Volume", group=GRP_CLR)
i_sellVolColor = input.color(color.new(#ef5350, 40), "Sell Volume", group=GRP_CLR)
i_longColor = input.color(color.new(#00c853, 0), "Long Signal", group=GRP_CLR)
i_shortColor = input.color(color.new(#ff1744, 0), "Short Signal", group=GRP_CLR)

// ─────────────────────────────────────────────────────────────────────────────
// TYPE DEFINITIONS
// ─────────────────────────────────────────────────────────────────────────────

type Zone
float top = na
float bottom = na
bool isSupply = false
int birthBar = 0
int touches = 0
float totalVol = 0.0
bool broken = false
int brokenBar = 0
int breakDir = 0 // +1 = broken upward, -1 = broken downward
bool retested = false
float strength = 0.0
box bx = na

type Signal
bool isLong = false
float entry = na
float sl = na
float tp = na
float rr = na
int sigBar = 0

// ─────────────────────────────────────────────────────────────────────────────
// GLOBAL VARIABLES
// ─────────────────────────────────────────────────────────────────────────────

var array<Zone> zones = array.new<Zone>()

// Market Structure
var float msSwingHi1 = na // most recent swing high
var float msSwingHi2 = na // previous swing high
var float msSwingLo1 = na // most recent swing low
var float msSwingLo2 = na // previous swing low
var int msBias = 0 // +1 bullish, -1 bearish, 0 neutral

// Signal flags (for alerts — must be global scope)
var bool sigLong = false
var bool sigShort = false
var bool sigBullBO = false
var bool sigBearBO = false
var bool sigRetest = false
var bool sigNewDemand = false
var bool sigNewSupply = false

// Volume Profile drawing storage
var array<box> vpBoxes = array.new<box>()
var array<line> vpLines = array.new<line>()
var array<label> vpLabels = array.new<label>()

// Computed values
float atrVal = ta.atr(i_atrPeriod)
float avgVol = ta.sma(volume, i_volSmooth)
float bodySize = math.abs(close - open)

// Reset per-bar signal flags
sigLong := false
sigShort := false
sigBullBO := false
sigBearBO := false
sigRetest := false
sigNewDemand := false
sigNewSupply := false

// ─────────────────────────────────────────────────────────────────────────────
// HELPER FUNCTIONS
// ─────────────────────────────────────────────────────────────────────────────

// Check if two zones overlap
zonesOverlap(float top1, float bot1, float top2, float bot2) =>
top1 >= bot2 and top2 >= bot1

// Calculate zone strength score (normalized 0–100)
calcStrength(int touches, float vol, float avgV, int age) =>
float touchScore = math.min(touches / 5.0, 1.0) * 40.0
float volScore = math.min(vol / (avgV * 10.0), 1.0) * 35.0
float ageScore = math.min(age / 200.0, 1.0) * 25.0
touchScore + volScore + ageScore

// Check if a nearby opposite zone exists
hasOppositeZoneNearby(bool checkAbove, float refPrice, float maxDist) =>
bool found = false
if zones.size() > 0
for i = 0 to zones.size() - 1
Zone z = zones.get(i)
if z.broken
continue
if checkAbove and z.isSupply and z.bottom - refPrice < maxDist and z.bottom > refPrice
found := true
break
if not checkAbove and not z.isSupply and refPrice - z.top < maxDist and z.top < refPrice
found := true
break
found

// Format price to string
fmtPrice(float price) =>
str.tostring(price, format.mintick)

// ─────────────────────────────────────────────────────────────────────────────
// PHASE 1: AUTOMATIC SUPPLY & DEMAND ZONES
// ─────────────────────────────────────────────────────────────────────────────

// ── 1a. Pivot Detection ──
float pivotHi = ta.pivothigh(high, i_pivotLen, i_pivotLen)
float pivotLo = ta.pivotlow(low, i_pivotLen, i_pivotLen)

// ── 1b. Create Supply Zone from Pivot High ──
if not na(pivotHi) and i_showZones
int pBar = bar_index - i_pivotLen
float zTop = high[i_pivotLen]
float zBot = zTop - nz(atrVal) * i_zoneAtrMult
bool merged = false

// Merge with existing supply zone if overlapping
if zones.size() > 0
for i = 0 to zones.size() - 1
Zone z = zones.get(i)
if z.isSupply and not z.broken and zonesOverlap(zTop, zBot, z.top, z.bottom)
z.top := math.max(z.top, zTop)
z.bottom := math.min(z.bottom, zBot)
z.touches += 1
z.totalVol += nz(volume[i_pivotLen])
z.strength := calcStrength(z.touches, z.totalVol, nz(avgVol, 1), bar_index - z.birthBar)
if not na(z.bx)
z.bx.set_top(z.top)
z.bx.set_bottom(z.bottom)
merged := true
break

if not merged
Zone newZ = Zone.new()
newZ.top := zTop
newZ.bottom := zBot
newZ.isSupply := true
newZ.birthBar := pBar
newZ.touches := 1
newZ.totalVol := nz(volume[i_pivotLen])
newZ.strength := calcStrength(1, newZ.totalVol, nz(avgVol, 1), 1)
newZ.bx := box.new(left=pBar, top=zTop, right=bar_index + 20, bottom=zBot,
border_color=color.new(i_supplyColor, 50), border_width=1,
bgcolor=i_supplyColor, border_style=line.style_solid)
zones.push(newZ)
sigNewSupply := true

// ── 1c. Create Demand Zone from Pivot Low ──
if not na(pivotLo) and i_showZones
int pBar = bar_index - i_pivotLen
float zBot = low[i_pivotLen]
float zTop = zBot + nz(atrVal) * i_zoneAtrMult
bool merged = false

if zones.size() > 0
for i = 0 to zones.size() - 1
Zone z = zones.get(i)
if not z.isSupply and not z.broken and zonesOverlap(zTop, zBot, z.top, z.bottom)
z.top := math.max(z.top, zTop)
z.bottom := math.min(z.bottom, zBot)
z.touches += 1
z.totalVol += nz(volume[i_pivotLen])
z.strength := calcStrength(z.touches, z.totalVol, nz(avgVol, 1), bar_index - z.birthBar)
if not na(z.bx)
z.bx.set_top(z.top)
z.bx.set_bottom(z.bottom)
merged := true
break

if not merged
Zone newZ = Zone.new()
newZ.top := zTop
newZ.bottom := zBot
newZ.isSupply := false
newZ.birthBar := pBar
newZ.touches := 1
newZ.totalVol := nz(volume[i_pivotLen])
newZ.strength := calcStrength(1, newZ.totalVol, nz(avgVol, 1), 1)
newZ.bx := box.new(left=pBar, top=zTop, right=bar_index + 20, bottom=zBot,
border_color=color.new(i_demandColor, 50), border_width=1,
bgcolor=i_demandColor, border_style=line.style_solid)
zones.push(newZ)
sigNewDemand := true

// ── 1d. Zone Maintenance: Extend, Touch Count, Age-out, Capacity ──
if zones.size() > 0
for i = zones.size() - 1 to 0
Zone z = zones.get(i)

// Remove zones that are too old
if bar_index - z.birthBar > i_maxZoneAge
if not na(z.bx)
z.bx.delete()
zones.remove(i)
continue

// Extend active zone boxes to the right
if not z.broken and not na(z.bx)
z.bx.set_right(bar_index + 20)

// Count touches on active zones (price approaches & rejects)
if not z.broken
if z.isSupply and high >= z.bottom and high <= z.top and close < z.bottom
z.touches += 1
z.totalVol += volume
if not z.isSupply and low <= z.top and low >= z.bottom and close > z.top
z.touches += 1
z.totalVol += volume

// Update strength
z.strength := calcStrength(z.touches, z.totalVol, nz(avgVol, 1), bar_index - z.birthBar)

// Enforce max zone count — remove weakest active zones
if zones.size() > i_maxZones
// Find and remove weakest non-broken zone
float weakest = 999999.0
int weakIdx = -1
for i = 0 to zones.size() - 1
Zone z = zones.get(i)
if not z.broken and z.strength < weakest
weakest := z.strength
weakIdx := i
if weakIdx >= 0
Zone wz = zones.get(weakIdx)
if not na(wz.bx)
wz.bx.delete()
zones.remove(weakIdx)

// ─────────────────────────────────────────────────────────────────────────────
// PHASE 2: MARKET STRUCTURE + BREAKOUT DETECTION
// ─────────────────────────────────────────────────────────────────────────────

// ── 2a. Market Structure ──
if not na(pivotHi)
msSwingHi2 := msSwingHi1
msSwingHi1 := pivotHi

if not na(pivotLo)
msSwingLo2 := msSwingLo1
msSwingLo1 := pivotLo

// Determine bias
bool isHH = not na(msSwingHi1) and not na(msSwingHi2) and msSwingHi1 > msSwingHi2
bool isHL = not na(msSwingLo1) and not na(msSwingLo2) and msSwingLo1 > msSwingLo2
bool isLH = not na(msSwingHi1) and not na(msSwingHi2) and msSwingHi1 < msSwingHi2
bool isLL = not na(msSwingLo1) and not na(msSwingLo2) and msSwingLo1 < msSwingLo2

if isHH and isHL
msBias := 1
else if isLH and isLL
msBias := -1
else
msBias := 0

// Structure label on pivots
if i_showStructure and not na(pivotHi)
string sLabel = isHH ? "HH" : isLH ? "LH" : "SH"
color sColor = isHH ? i_longColor : isLH ? i_shortColor : color.gray
label.new(bar_index - i_pivotLen, high[i_pivotLen], sLabel,
style=label.style_label_down, color=color.new(sColor, 80),
textcolor=sColor, size=size.tiny)

if i_showStructure and not na(pivotLo)
string sLabel = isHL ? "HL" : isLL ? "LL" : "SL"
color sColor = isHL ? i_longColor : isLL ? i_shortColor : color.gray
label.new(bar_index - i_pivotLen, low[i_pivotLen], sLabel,
style=label.style_label_up, color=color.new(sColor, 80),
textcolor=sColor, size=size.tiny)

// ── 2b. Breakout Detection ──
bool volFilter = volume > avgVol * i_volMult
bool bodyFilter = bodySize > nz(atrVal)

if zones.size() > 0
for i = 0 to zones.size() - 1
Zone z = zones.get(i)
if z.broken
continue

// Bullish breakout: close above supply zone with confirmation
if z.isSupply and close > z.top and volFilter and bodyFilter
z.broken := true
z.brokenBar := bar_index
z.breakDir := 1
if not na(z.bx)
z.bx.set_bgcolor(i_brokenColor)
z.bx.set_border_color(color.new(i_brokenColor, 50))
z.bx.set_right(bar_index)
sigBullBO := true
if i_showLabels
label.new(bar_index, low, "BO▲",
style=label.style_label_up, color=color.new(i_longColor, 60),
textcolor=i_longColor, size=size.small)

// Bearish breakout: close below demand zone with confirmation
if not z.isSupply and close < z.bottom and volFilter and bodyFilter
z.broken := true
z.brokenBar := bar_index
z.breakDir := -1
if not na(z.bx)
z.bx.set_bgcolor(i_brokenColor)
z.bx.set_border_color(color.new(i_brokenColor, 50))
z.bx.set_right(bar_index)
sigBearBO := true
if i_showLabels
label.new(bar_index, high, "BO▼",
style=label.style_label_down, color=color.new(i_shortColor, 60),
textcolor=i_shortColor, size=size.small)

// ─────────────────────────────────────────────────────────────────────────────
// PHASE 3: RETEST CONFIRMATION + LONG/SHORT SIGNALS
// ─────────────────────────────────────────────────────────────────────────────

if zones.size() > 0
for i = 0 to zones.size() - 1
Zone z = zones.get(i)
if not z.broken or z.retested
continue

// Skip if breakout too old
if bar_index - z.brokenBar > i_maxRetestDist
continue

// Must be at least 2 bars after breakout for retest to form
if bar_index - z.brokenBar < 2
continue

// ── Bullish Retest (supply broken upward → now demand) ──
if z.breakDir == 1
// Previous bar: wick dipped into zone, closed above zone
bool prevRetest = low[1] <= z.top and low[1] >= z.bottom and close[1] > z.top
// Current bar: bullish confirmation
bool confirm = close > open and close > close[1]
bool volOK = volume > avgVol
bool structOK = msBias >= 0

if prevRetest and confirm and volOK and structOK
float entry = close
float sl = z.bottom - nz(atrVal) * i_slBuffer
float risk = entry - sl
float tp = entry + risk * i_minRR
float rr = risk > 0 ? (tp - entry) / risk : 0.0

// Check no supply zone nearby above
bool noOppZone = not hasOppositeZoneNearby(true, entry, nz(atrVal) * i_noOppZoneDist)

if risk > 0 and rr >= i_minRR and noOppZone and z.touches >= i_minTouches
z.retested := true
sigRetest := true
sigLong := true

// Draw entry arrow
label.new(bar_index, low, "▲ LONG\n" + fmtPrice(entry),
style=label.style_label_up, color=i_longColor,
textcolor=color.white, size=size.normal)

if i_showSLTP
// SL line
line.new(bar_index, sl, bar_index + 20, sl,
color=i_shortColor, width=1, style=line.style_dashed)
label.new(bar_index + 20, sl, "SL " + fmtPrice(sl),
style=label.style_label_left, color=color.new(i_shortColor, 70),
textcolor=i_shortColor, size=size.tiny)
// TP line
line.new(bar_index, tp, bar_index + 20, tp,
color=i_longColor, width=1, style=line.style_dashed)
label.new(bar_index + 20, tp, "TP " + fmtPrice(tp) + " (" + str.tostring(rr, "#.#") + "R)",
style=label.style_label_left, color=color.new(i_longColor, 70),
textcolor=i_longColor, size=size.tiny)
// Entry line
line.new(bar_index, entry, bar_index + 20, entry,
color=i_longColor, width=2, style=line.style_solid)

// ── Bearish Retest (demand broken downward → now supply) ──
if z.breakDir == -1
// Previous bar: wick pushed into zone, closed below zone
bool prevRetest = high[1] >= z.bottom and high[1] <= z.top and close[1] < z.bottom
// Current bar: bearish confirmation
bool confirm = close < open and close < close[1]
bool volOK = volume > avgVol
bool structOK = msBias <= 0

if prevRetest and confirm and volOK and structOK
float entry = close
float sl = z.top + nz(atrVal) * i_slBuffer
float risk = sl - entry
float tp = entry - risk * i_minRR
float rr = risk > 0 ? (sl - entry) / risk * i_minRR : 0.0

bool noOppZone = not hasOppositeZoneNearby(false, entry, nz(atrVal) * i_noOppZoneDist)

if risk > 0 and rr >= i_minRR and noOppZone and z.touches >= i_minTouches
z.retested := true
sigRetest := true
sigShort := true

label.new(bar_index, high, "▼ SHORT\n" + fmtPrice(entry),
style=label.style_label_down, color=i_shortColor,
textcolor=color.white, size=size.normal)

if i_showSLTP
line.new(bar_index, sl, bar_index + 20, sl,
color=i_shortColor, width=1, style=line.style_dashed)
label.new(bar_index + 20, sl, "SL " + fmtPrice(sl),
style=label.style_label_left, color=color.new(i_shortColor, 70),
textcolor=i_shortColor, size=size.tiny)
line.new(bar_index, tp, bar_index + 20, tp,
color=i_longColor, width=1, style=line.style_dashed)
label.new(bar_index + 20, tp, "TP " + fmtPrice(tp) + " (" + str.tostring(i_minRR, "#.#") + "R)",
style=label.style_label_left, color=color.new(i_longColor, 70),
textcolor=i_longColor, size=size.tiny)
line.new(bar_index, entry, bar_index + 20, entry,
color=i_shortColor, width=2, style=line.style_solid)

// ─────────────────────────────────────────────────────────────────────────────
// PHASE 4: SIGNAL INFO TABLE
// ─────────────────────────────────────────────────────────────────────────────

// Structure bias bar color (subtle background)
barcolor(i_showStructure ? (msBias == 1 ? color.new(i_longColor, 90) : msBias == -1 ? color.new(i_shortColor, 90) : na) : na)

// Info table on last bar
if barstate.islast
var table infoTbl = table.new(position.top_right, 2, 5,
bgcolor=color.new(#1e222d, 10), border_width=1, border_color=color.new(color.gray, 70))

string biasText = msBias == 1 ? "BULLISH" : msBias == -1 ? "BEARISH" : "NEUTRAL"
color biasClr = msBias == 1 ? i_longColor : msBias == -1 ? i_shortColor : color.gray

int activeCount = 0
int brokenCount = 0
if zones.size() > 0
for i = 0 to zones.size() - 1
if zones.get(i).broken
brokenCount += 1
else
activeCount += 1

infoTbl.cell(0, 0, "SMC PRO", text_color=color.white, text_size=size.small, bgcolor=color.new(#363a45, 0))
infoTbl.cell(1, 0, "", bgcolor=color.new(#363a45, 0))
infoTbl.cell(0, 1, "Structure", text_color=color.gray, text_size=size.tiny)
infoTbl.cell(1, 1, biasText, text_color=biasClr, text_size=size.tiny)
infoTbl.cell(0, 2, "Active Zones", text_color=color.gray, text_size=size.tiny)
infoTbl.cell(1, 2, str.tostring(activeCount), text_color=color.white, text_size=size.tiny)
infoTbl.cell(0, 3, "Broken Zones", text_color=color.gray, text_size=size.tiny)
infoTbl.cell(1, 3, str.tostring(brokenCount), text_color=color.white, text_size=size.tiny)
infoTbl.cell(0, 4, "ATR", text_color=color.gray, text_size=size.tiny)
infoTbl.cell(1, 4, fmtPrice(nz(atrVal)), text_color=color.white, text_size=size.tiny)

// ─────────────────────────────────────────────────────────────────────────────
// PHASE 5: FIXED RANGE VOLUME PROFILE
// ─────────────────────────────────────────────────────────────────────────────

if barstate.islast and i_showVP
// ── 5a. Cleanup previous VP drawings ──
if vpBoxes.size() > 0
for i = vpBoxes.size() - 1 to 0
box b = vpBoxes.get(i)
if not na(b)
b.delete()
vpBoxes.clear()

if vpLines.size() > 0
for i = vpLines.size() - 1 to 0
line l = vpLines.get(i)
if not na(l)
l.delete()
vpLines.clear()

if vpLabels.size() > 0
for i = vpLabels.size() - 1 to 0
label lb = vpLabels.get(i)
if not na(lb)
lb.delete()
vpLabels.clear()

// ── 5b. Calculate price range ──
int lookback = math.min(i_vpLookback, bar_index)
float rangeHi = ta.highest(high, lookback)
float rangeLo = ta.lowest(low, lookback)
float rowH = (rangeHi - rangeLo) / i_vpRows

if rowH > 0
// ── 5c. Accumulate volume per row ──
array<float> buyVol = array.new<float>(i_vpRows, 0.0)
array<float> sellVol = array.new<float>(i_vpRows, 0.0)

for b = 0 to lookback - 1
float bHi = high
float bLo = low
float bVol = nz(volume)
bool isBuy = close >= open

int startRow = math.max(0, math.floor((bLo - rangeLo) / rowH))
int endRow = math.min(i_vpRows - 1, math.floor((bHi - rangeLo) / rowH))

if endRow >= startRow
int numRows = endRow - startRow + 1
float volPerRow = bVol / numRows

for r = startRow to endRow
if isBuy
buyVol.set(r, buyVol.get(r) + volPerRow)
else
sellVol.set(r, sellVol.get(r) + volPerRow)

// ── 5d. Find POC, max volume, VAH, VAL ──
float maxVol = 0.0
int pocRow = 0
float totalVol = 0.0

array<float> totalPerRow = array.new<float>(i_vpRows, 0.0)

for r = 0 to i_vpRows - 1
float rv = buyVol.get(r) + sellVol.get(r)
totalPerRow.set(r, rv)
totalVol += rv
if rv > maxVol
maxVol := rv
pocRow := r

// Value Area calculation — expand outward from POC until vaPercent reached
float vaTarget = totalVol * i_vaPercent / 100.0
float vaSum = totalPerRow.get(pocRow)
int vaHiRow = pocRow
int vaLoRow = pocRow

for _step = 0 to i_vpRows - 1
if vaSum >= vaTarget
break
float aboveVol = vaHiRow < i_vpRows - 1 ? totalPerRow.get(vaHiRow + 1) : 0.0
float belowVol = vaLoRow > 0 ? totalPerRow.get(vaLoRow - 1) : 0.0

if aboveVol >= belowVol and vaHiRow < i_vpRows - 1
vaHiRow += 1
vaSum += aboveVol
else if vaLoRow > 0
vaLoRow -= 1
vaSum += belowVol
else if vaHiRow < i_vpRows - 1
vaHiRow += 1
vaSum += aboveVol
else
break

float pocPrice = rangeLo + (pocRow + 0.5) * rowH
float vahPrice = rangeLo + (vaHiRow + 1) * rowH
float valPrice = rangeLo + vaLoRow * rowH

// ── 5e. Draw histogram ──
int vpLeftBar = bar_index + 5
float volScale = maxVol > 0 ? i_vpWidth / maxVol : 0.0

for r = 0 to i_vpRows - 1
float rowBot = rangeLo + r * rowH
float rowTop = rowBot + rowH
float bv = buyVol.get(r)
float sv = sellVol.get(r)
float tv = bv + sv

if tv > 0
int totalWidth = math.max(1, math.round(tv * volScale))
int buyWidth = math.max(0, math.round(bv * volScale))
int sellWidth = totalWidth - buyWidth

// Buy volume box
if buyWidth > 0
box bxBuy = box.new(vpLeftBar, rowTop, vpLeftBar + buyWidth, rowBot,
border_color=color.new(i_buyVolColor, 70), border_width=0,
bgcolor=i_buyVolColor)
vpBoxes.push(bxBuy)

// Sell volume box (stacked after buy)
if sellWidth > 0
box bxSell = box.new(vpLeftBar + buyWidth, rowTop, vpLeftBar + buyWidth + sellWidth, rowBot,
border_color=color.new(i_sellVolColor, 70), border_width=0,
bgcolor=i_sellVolColor)
vpBoxes.push(bxSell)

// Highlight HVN / LVN
bool isHVN = tv > totalVol / i_vpRows * 1.5
bool isLVN = tv < totalVol / i_vpRows * 0.5

// HVN: subtle bright border
if isHVN and totalWidth > 2
box bxHVN = box.new(vpLeftBar, rowTop, vpLeftBar + totalWidth, rowBot,
border_color=color.new(color.white, 70), border_width=1,
bgcolor=color.new(color.white, 97))
vpBoxes.push(bxHVN)

// ── 5f. Draw POC, VAH, VAL lines ──
int lineRight = vpLeftBar + i_vpWidth + 5

line pocLine = line.new(bar_index - lookback, pocPrice, lineRight, pocPrice,
color=i_pocColor, width=2, style=line.style_solid)
vpLines.push(pocLine)

line vahLine = line.new(bar_index - lookback, vahPrice, lineRight, vahPrice,
color=i_vahColor, width=1, style=line.style_dashed)
vpLines.push(vahLine)

line valLine = line.new(bar_index - lookback, valPrice, lineRight, valPrice,
color=i_valColor, width=1, style=line.style_dashed)
vpLines.push(valLine)

// Labels
label pocLbl = label.new(lineRight, pocPrice, "POC " + fmtPrice(pocPrice),
style=label.style_label_left, color=color.new(i_pocColor, 70),
textcolor=i_pocColor, size=size.tiny)
vpLabels.push(pocLbl)

label vahLbl = label.new(lineRight, vahPrice, "VAH " + fmtPrice(vahPrice),
style=label.style_label_left, color=color.new(i_vahColor, 70),
textcolor=i_vahColor, size=size.tiny)
vpLabels.push(vahLbl)

label valLbl = label.new(lineRight, valPrice, "VAL " + fmtPrice(valPrice),
style=label.style_label_left, color=color.new(i_valColor, 70),
textcolor=i_valColor, size=size.tiny)
vpLabels.push(valLbl)

// ─────────────────────────────────────────────────────────────────────────────
// ALERTS
// ─────────────────────────────────────────────────────────────────────────────

alertcondition(sigNewDemand, title="New Demand Zone", message="SMC PRO: New Demand (Support) Zone detected")
alertcondition(sigNewSupply, title="New Supply Zone", message="SMC PRO: New Supply (Resistance) Zone detected")
alertcondition(sigBullBO, title="Bullish Breakout", message="SMC PRO: Bullish Breakout — price closed above supply zone with volume confirmation")
alertcondition(sigBearBO, title="Bearish Breakout", message="SMC PRO: Bearish Breakout — price closed below demand zone with volume confirmation")
alertcondition(sigRetest, title="Successful Retest", message="SMC PRO: Successful Retest confirmed at broken zone")
alertcondition(sigLong, title="Long Entry", message="SMC PRO: LONG Entry — breakout + retest + confirmation + volume + structure aligned")
alertcondition(sigShort, title="Short Entry", message="SMC PRO: SHORT Entry — breakout + retest + confirmation + volume + structure aligned")

// ─────────────────────────────────────────────────────────────────────────────
// END
// ─────────────────────────────────────────────────────────────────────────────

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.