//@version=6
// ============================================================================
// Volume Strata [WavesUnchained]
// Version: 1.10.1
// Author: WavesUnchained
// Build: 2026-10-12
// ============================================================================
// Fixed-range volume profile anchored to the right, with POC, VAH/VAL,
// HVN/LVN zones, Value Area fill, naked POC tracking, expanded context table,
// and alerts.
//
// Features:
// ✓ Right-anchored bars — profile grows left into the candle range
// ✓ Up / down volume split per row, value area highlighted (blue / yellow)
// ✓ Profile source: Volume or Money Flow (volume × mid-price)
// ✓ Point of Control (POC) – dashed line with right-side price label
// ✓ Value Area High / Low (VAH / VAL) – dotted lines with labels
// ✓ Value Area fill – light shaded zone between VAH and VAL across the lookback
// ✓ HVN zones – horizontal bands with gradient color intensity (faint at threshold,
// strong at peak volume)
// ✓ LVN zones – horizontal bands at low-volume nodes (local minima below mean − 1σ)
// ✓ Naked POC tracking – previous POCs not yet retested, drawn until price touches them
// ✓ Info table – levels, VA width, range, POC strength, up-vol %, total volume,
// shape (D/P/b/B), VA state, Virgin POC, developing POC, POC distance
// ✓ Alerts – POC cross, VA entry/exit, VAH/VAL touch
// ✓ Configurable label size (Tiny / Small / Normal)
// ✓ All visual layers individually toggleable
//
// Data Contract:
// Price: REQUIRED chart symbol
// Volume: REQUIRED real trade volume — feed must deliver volume and
// syminfo.volumetype must not be "tick"; otherwise not available
// OI: NO
// Reference: NO
// Verdict: Exchange-only
//
// ============================================================================
indicator('Volume Strata [WavesUnchained]', overlay=true, max_boxes_count=500, max_bars_back=501)
// Display flag for inputs — keeps the chart status line uncluttered
disp = display.all - display.status_line
// ── Inputs ────────────────────────────────────────────────────────────────────
var string G_RANGE = 'Range'
var string G_COLORS = 'Colors'
var string G_DISPLAY = 'Display'
bbars = input.int (150, 'Lookback Bars', minval=1, maxval=500, group=G_RANGE, display=disp)
cnum = input.int (24, 'Rows', minval=5, maxval=100, group=G_RANGE, display=disp)
va_pct = input.float(70., 'Value Area %', minval=0., maxval=100., group=G_RANGE, display=disp)
prof_offset = input.int (150, 'Profile Offset (bars right)', minval=0, maxval=500, group=G_RANGE, display=disp)
npoc_min_atr = input.float(0.1, 'Naked POC Min Distance (ATR)', minval=0., step=0.05, group=G_RANGE, display=disp,
tooltip='Minimum distance the POC must move (in ATR) before the previous level is tracked as a naked POC. Filters out noise-driven POC shifts.')
src_kind = input.string('Volume', 'Profile Source', options=['Volume', 'Money Flow'], group=G_RANGE, display=disp,
tooltip='Volume: raw traded contracts/shares per row. Money Flow: volume × mid-price of the row — gives a notional dollar value perspective.')
poc_color = input.color(#e53935, 'POC', inline='poc', group=G_COLORS, display=disp)
poc_width = input.int (2, 'Width', inline='poc', group=G_COLORS, minval=1, maxval=5, display=disp)
va_color = input.color(color.new(#9e9e9e, 0), 'VAH / VAL', group=G_COLORS, display=disp)
hvn_color = input.color(#ffd600, 'HVN', group=G_COLORS, display=disp)
lvn_color = input.color(#4dd0e1, 'LVN', group=G_COLORS, display=disp)
vup_color = input.color(color.new(#1e88e5, 30),'Value Area Up', group=G_COLORS, display=disp)
vdown_color = input.color(color.new(#fdd835, 30),'Value Area Down', group=G_COLORS, display=disp)
up_color = input.color(color.new(#1e88e5, 75),'Outside Up', group=G_COLORS, display=disp)
down_color = input.color(color.new(#fdd835, 75),'Outside Down', group=G_COLORS, display=disp)
label_size_str = input.string('Small', 'Label Size', options=['Tiny','Small','Normal'], group=G_COLORS, display=disp)
// String → size helper
f_gTS(string _t) =>
switch _t
'Tiny' => size.tiny
'Small' => size.small
'Normal' => size.normal
=> size.small
label_size = f_gTS(label_size_str)
show_profile = input.bool(true, 'Show profile bars', group=G_DISPLAY, display=disp)
show_poc = input.bool(true, 'POC label', group=G_DISPLAY, display=disp)
show_va = input.bool(true, 'VAH / VAL', group=G_DISPLAY, display=disp)
show_va_fill = input.bool(true, 'Value Area fill', group=G_DISPLAY, display=disp,
tooltip='Light shaded zone between VAH and VAL across the lookback range — visualizes the fair value area at a glance.')
show_hvn = input.bool(true, 'HVN zones', group=G_DISPLAY, display=disp,
tooltip='Horizontal shaded bands at high-volume node levels across the lookback range — strong reaction zones. Color intensity scales with relative volume.')
show_lvn = input.bool(true, 'LVN zones', group=G_DISPLAY, display=disp,
tooltip='Horizontal shaded bands at low-volume node levels — fast-move zones, price tends to skip through.')
show_naked_poc = input.bool(true, 'Naked POCs', group=G_DISPLAY, display=disp,
tooltip='Track previous POCs that have not been retested. Drawn as faint dashed lines; removed once price touches them. Tracks new naked POCs from script load.')
show_table = input.bool(true, 'Info table', group=G_DISPLAY, display=disp)
// ── Data validity ─────────────────────────────────────────────────────────────
// Echtes Handelsvolumen? Nur "tick" schließt es ausdrücklich aus (Anzahl
// Transaktionen, keine Menge). "n/a" heißt laut Referenz "kein Volumen ODER Art
// nicht angegeben" — die meisten Feeds außer Krypto melden das, auch Futures mit
// echten Kontrakten. Dort entscheidet, ob der Feed überhaupt Volumen liefert.
var bool feedHasVolume = false
if nz(volume) > 0
feedHasVolume := true
bool volumeIsReal = syminfo.volumetype != "tick" and feedHasVolume
// ── Persistent drawing objects ────────────────────────────────────────────────
var vol_bars = array.new_box(cnum * 2, na)
var hvn_bars = array.new_box(cnum, na)
var lvn_bars = array.new_box(cnum, na)
var box va_fill_box = na
var line poc_line = na
var label poc_lbl = na
var line vah_line = na
var line val_line = na
var label vah_lbl = na
var label val_lbl = na
// Naked POC tracking
var array<float> naked_pocs = array.new<float>()
var array<line> naked_poc_lns = array.new<line>()
NAKED_POC_MAX = 8
// Persisted levels (updated on barstate.islast, used by alertcondition)
var float poc_lvl_p = na
var float vah_lvl_p = na
var float val_lvl_p = na
var float prev_poc_lvl = na
color TC = color.new(color.gray, 20)
color HD = color.new(color.gray, 90)
color SEP = color.new(color.gray, 80)
var table info_tbl = table.new(position.top_right, 2, 15,
bgcolor=color.new(color.white, 5),
border_color=color.new(color.gray, 60), border_width=1,
frame_color=color.new(color.gray, 40), frame_width=1)
// Volume formatting helper
formatVol(float v) =>
v >= 1e9 ? str.tostring(v / 1e9, '0.##') + 'B' : v >= 1e6 ? str.tostring(v / 1e6, '0.##') + 'M' : v >= 1e3 ? str.tostring(v / 1e3, '0.##') + 'K' : str.tostring(math.round(v))
// ── ATR (runs every bar for alert and table use) ───────────────────────────────
atr_14 = ta.atr(14)
// ── Naked POC touch check (runs every bar) ────────────────────────────────────
if show_naked_poc and array.size(naked_pocs) > 0
int i = 0
while i < array.size(naked_pocs)
npoc = array.get(naked_pocs, i)
if high >= npoc and low <= npoc
array.remove(naked_pocs, i)
line.delete(array.remove(naked_poc_lns, i))
else
i += 1
// ── Level grid ────────────────────────────────────────────────────────────────
top = ta.highest(bbars)
bot = ta.lowest (bbars)
rng = math.max(top - bot, syminfo.mintick)
dist = rng / 500
step = rng / cnum
levels = array.new_float(cnum + 1)
for x = 0 to cnum
array.set(levels, x, bot + step * x)
// ── Volume intersection helper ─────────────────────────────────────────────────
get_vol(y11, y12, y21, y22, height, vol) =>
float result = 0.
if height > 0
overlap = math.max(math.min(math.max(y11, y12), math.max(y21, y22)) - math.max(math.min(y11, y12), math.min(y21, y22)), 0)
result := nz(overlap * vol / height)
result
// Kurzer Hinweis unten rechts; was fehlt, steht im Data Contract oben.
var table volWarn = table.new(position.bottom_right, 1, 1)
if barstate.islast and not volumeIsReal
table.cell(volWarn, 0, 0, "Volume Strata · not available",
bgcolor = color.new(color.orange, 20), text_color = color.white, text_size = size.small)
if barstate.islast and volumeIsReal
// ── Accumulate volume per row ──────────────────────────────────────────────
volumes = array.new_float(cnum * 2, 0.)
for bars = 0 to bbars - 1
body_top = math.max(close[bars], open[bars])
body_bot = math.min(close[bars], open[bars])
itsgreen = close[bars] >= open[bars]
tw = high[bars] - body_top
bw = body_bot - low[bars]
bd = body_top - body_bot
denom = math.max(2 * tw + 2 * bw + bd, syminfo.mintick)
bvol = bd * volume[bars] / denom
twvol = 2 * tw * volume[bars] / denom
bwvol = 2 * bw * volume[bars] / denom
for x = 0 to cnum - 1
lx = array.get(levels, x)
lx1 = array.get(levels, x + 1)
// Money Flow weighting: each row's contribution is scaled by row mid-price
row_mid = (lx + lx1) / 2
mf_weight = src_kind == 'Money Flow' ? row_mid : 1.0
upv = ((itsgreen ? get_vol(lx, lx1, body_bot, body_top, bd, bvol) : 0.) +
get_vol(lx, lx1, body_top, high[bars], tw, twvol) / 2 +
get_vol(lx, lx1, body_bot, low[bars], bw, bwvol) / 2) * mf_weight
dnv = ((itsgreen ? 0. : get_vol(lx, lx1, body_bot, body_top, bd, bvol)) +
get_vol(lx, lx1, body_top, high[bars], tw, twvol) / 2 +
get_vol(lx, lx1, body_bot, low[bars], bw, bwvol) / 2) * mf_weight
array.set(volumes, x, array.get(volumes, x) + upv)
array.set(volumes, x + cnum, array.get(volumes, x + cnum) + dnv)
// ── Total per row, POC, and stats ──────────────────────────────────────────
totalvols = array.new_float(cnum, 0.)
total_up = 0.
for x = 0 to cnum - 1
tv = array.get(volumes, x) + array.get(volumes, x + cnum)
array.set(totalvols, x, tv)
total_up += array.get(volumes, x)
total_all = array.sum(totalvols)
up_pct = total_all > 0 ? total_up / total_all * 100 : 50.
int poc = array.indexof(totalvols, array.max(totalvols))
// HVN / LVN thresholds
vol_mean = array.avg(totalvols)
vol_stdev = array.stdev(totalvols)
hvn_thresh = vol_mean + vol_stdev
lvn_thresh = math.max(0., vol_mean - vol_stdev)
// ── Value area ─────────────────────────────────────────────────────────────
totalmax = total_all * va_pct / 100.
va_total = array.get(totalvols, poc)
int va_up = poc
int va_down = poc
for x = 0 to cnum - 1
if va_total >= totalmax
break
uppervol = va_up < cnum - 1 ? array.get(totalvols, va_up + 1) : 0.
lowervol = va_down > 0 ? array.get(totalvols, va_down - 1) : 0.
if uppervol == 0. and lowervol == 0.
break
if uppervol >= lowervol
va_total += uppervol
va_up += 1
else
va_total += lowervol
va_down -= 1
// ── Scale volumes to bar widths ────────────────────────────────────────────
maxvol = math.max(array.max(totalvols), 1)
for x = 0 to cnum * 2 - 1
array.set(volumes, x, array.get(volumes, x) * bbars / (3 * maxvol))
// ── Key levels ─────────────────────────────────────────────────────────────
poc_lvl = (array.get(levels, poc) + array.get(levels, poc + 1)) / 2
vah_lvl = array.get(levels, va_up + 1)
val_lvl = array.get(levels, va_down)
// ── Context: Virgin POC (not touched in last 5 bars) ───────────────────────
poc_virgin = true
for t = 0 to 4
if high[t] >= poc_lvl and low[t] <= poc_lvl
poc_virgin := false
break
vpoc_str = poc_virgin ? 'Virgin' : 'Touched'
vpoc_col = poc_virgin ? #00c853 : TC
// ── Context: Developing POC direction ──────────────────────────────────────
poc_trend_str = na(prev_poc_lvl) ? '—' : poc_lvl > prev_poc_lvl ? '↑ Rising' : poc_lvl < prev_poc_lvl ? '↓ Falling' : '— Stable'
poc_trend_col = na(prev_poc_lvl) ? TC : poc_lvl > prev_poc_lvl ? #00c853 : poc_lvl < prev_poc_lvl ? #d50000 : TC
// ── Naked POC: when POC moves, the previous level is added as candidate ────
// Only added if not touched on the current bar (otherwise it's already retested)
npoc_already_touched = not na(prev_poc_lvl) and high >= prev_poc_lvl and low <= prev_poc_lvl
npoc_min_dist = math.max(syminfo.mintick, npoc_min_atr * atr_14)
if show_naked_poc and not na(prev_poc_lvl) and math.abs(poc_lvl - prev_poc_lvl) > npoc_min_dist and not npoc_already_touched
if array.size(naked_pocs) >= NAKED_POC_MAX
line.delete(array.shift(naked_poc_lns))
array.shift(naked_pocs)
array.push(naked_pocs, prev_poc_lvl)
npoc_line = line.new(bar_index - bbars + 1, prev_poc_lvl, bar_index, prev_poc_lvl,
extend=extend.right, color=color.new(poc_color, 50),
width=1, style=line.style_dashed)
array.push(naked_poc_lns, npoc_line)
prev_poc_lvl := poc_lvl
// ── Context: Value Area State ───────────────────────────────────────────────
va_state = close > vah_lvl ? 'Above VA' : close < val_lvl ? 'Below VA' : 'Inside VA'
va_state_col = close > vah_lvl ? #00c853 : close < val_lvl ? #d50000 : TC
// ── Context: POC Distance in ATR ───────────────────────────────────────────
poc_dist_raw = atr_14 > 0 ? (close - poc_lvl) / atr_14 : 0.
poc_dist_str = (poc_dist_raw >= 0 ? '+' : '') + str.tostring(math.round(poc_dist_raw, 2)) + ' ATR'
poc_dist_col = poc_dist_raw >= 0 ? #00c853 : #d50000
// ── Range, VA Width, POC Strength, Total Volume, Shape ─────────────────────
range_str = str.tostring(math.round_to_mintick(rng))
va_width = vah_lvl - val_lvl
va_width_pct = rng > 0 ? va_width / rng * 100 : 0.
va_width_str = str.tostring(math.round(va_width_pct, 1)) + '%'
va_width_col = va_width_pct < 30 ? #00c853 : va_width_pct > 60 ? #ff6f00 : TC
vol_at_poc = array.get(totalvols, poc)
poc_strength = total_all > 0 ? vol_at_poc / total_all * 100 : 0.
poc_str_str = str.tostring(math.round(poc_strength, 1)) + '%'
poc_str_col = poc_strength >= 15 ? #00c853 : poc_strength <= 8 ? #d50000 : TC
total_vol_str = formatVol(total_all)
// Profile Shape: position of POC within range + number of separate HVN nodes
// (a node = contiguous run of rows at/above the HVN threshold; a single smooth
// peak spans several adjacent rows but is still one node)
poc_pos = rng > 0 ? (poc_lvl - bot) / rng : 0.5
hvn_nodes = 0
bool in_hvn = false
for x = 0 to cnum - 1
bool row_hvn = array.get(totalvols, x) >= hvn_thresh
if row_hvn and not in_hvn
hvn_nodes += 1
in_hvn := row_hvn
shape_str = hvn_nodes >= 2 ? 'B (Double)' : poc_pos > 0.66 ? 'P (Accum)' : poc_pos < 0.34 ? 'b (Distri)' : 'D (Balanced)'
shape_col = hvn_nodes >= 2 ? #ffd600 : poc_pos > 0.66 ? #00c853 : poc_pos < 0.34 ? #d50000 : TC
// Persist levels for alert conditions
poc_lvl_p := poc_lvl
vah_lvl_p := vah_lvl
val_lvl_p := val_lvl
// ── Volume bars (right-anchored, growing left) ─────────────────────────────
for x = 0 to cnum - 1
box.delete(array.get(vol_bars, x))
box.delete(array.get(vol_bars, x + cnum))
if show_profile
in_va = x >= va_down and x <= va_up
up_w = math.round(array.get(volumes, x))
dn_w = math.round(array.get(volumes, x + cnum))
lev_top = array.get(levels, x + 1) - dist
lev_bot = array.get(levels, x) + dist
// up-volume on the right (innermost)
array.set(vol_bars, x, box.new(
bar_index + prof_offset - up_w, lev_top, bar_index + prof_offset, lev_bot,
border_width=0, bgcolor=in_va ? vup_color : up_color))
// down-volume extends further left
array.set(vol_bars, x + cnum, box.new(
bar_index + prof_offset - up_w - dn_w, lev_top, bar_index + prof_offset - up_w, lev_bot,
border_width=0, bgcolor=in_va ? vdown_color : down_color))
// ── HVN zones (extended horizontal bands across lookback range) ────────────
// Color intensity scales with relative volume: faded just above threshold,
// strong at the peak (POC).
float hvn_max = array.max(totalvols)
for x = 0 to cnum - 1
box.delete(array.get(hvn_bars, x))
lev_top = array.get(levels, x + 1) - dist
lev_bot = array.get(levels, x) + dist
vol_x = array.get(totalvols, x)
if show_hvn and vol_x >= hvn_thresh
// Gradient: from faint (88 transp) at threshold to strong (60 transp) at peak
color hvn_grad = color.from_gradient(vol_x, hvn_thresh, hvn_max,
color.new(hvn_color, 88), color.new(hvn_color, 60))
array.set(hvn_bars, x, box.new(
bar_index - bbars + 1, lev_top, bar_index, lev_bot,
border_width=0, bgcolor=hvn_grad))
else
array.set(hvn_bars, x, na)
// ── LVN zones (local minima below mean − 1σ — fast-move zones) ─────────────
for x = 0 to cnum - 1
box.delete(array.get(lvn_bars, x))
lev_top = array.get(levels, x + 1) - dist
lev_bot = array.get(levels, x) + dist
vol_x = array.get(totalvols, x)
is_local_min = x > 0 and x < cnum - 1 and vol_x < array.get(totalvols, x - 1) and vol_x < array.get(totalvols, x + 1)
if show_lvn and is_local_min and vol_x <= lvn_thresh
array.set(lvn_bars, x, box.new(
bar_index - bbars + 1, lev_top, bar_index, lev_bot,
border_width=0, bgcolor=color.new(lvn_color, 88)))
else
array.set(lvn_bars, x, na)
// ── Value Area fill (light zone between VAH and VAL) ───────────────────────
box.delete(va_fill_box)
if show_va_fill
va_fill_box := box.new(bar_index - bbars + 1, vah_lvl, bar_index, val_lvl,
border_width=0, bgcolor=color.new(va_color, 92))
// ── POC ────────────────────────────────────────────────────────────────────
line.delete(poc_line)
poc_line := line.new(bar_index - bbars + 1, poc_lvl, bar_index, poc_lvl,
extend=extend.right, color=poc_color, width=poc_width,
style=line.style_dashed)
label.delete(poc_lbl)
if show_poc
poc_lbl := label.new(bar_index + prof_offset + (show_hvn or show_lvn ? 5 : 2), poc_lvl,
text='POC ' + str.tostring(math.round_to_mintick(poc_lvl)),
style=label.style_label_left,
color=poc_color, textcolor=color.white, size=label_size)
// ── VAH / VAL ──────────────────────────────────────────────────────────────
line.delete(vah_line)
line.delete(val_line)
label.delete(vah_lbl)
label.delete(val_lbl)
if show_va
vah_line := line.new(bar_index - bbars + 1, vah_lvl, bar_index, vah_lvl,
extend=extend.right, color=va_color, width=1,
style=line.style_dotted)
val_line := line.new(bar_index - bbars + 1, val_lvl, bar_index, val_lvl,
extend=extend.right, color=va_color, width=1,
style=line.style_dotted)
int lbl_x = bar_index + prof_offset + (show_hvn or show_lvn ? 5 : 2)
vah_lbl := label.new(lbl_x, vah_lvl,
text='VAH ' + str.tostring(math.round_to_mintick(vah_lvl)),
style=label.style_label_left,
color=va_color, textcolor=color.white, size=label_size)
val_lbl := label.new(lbl_x, val_lvl,
text='VAL ' + str.tostring(math.round_to_mintick(val_lvl)),
style=label.style_label_left,
color=va_color, textcolor=color.white, size=label_size)
// ── Info table ─────────────────────────────────────────────────────────────
if show_table
up_col = up_pct >= 55 ? #00c853 : up_pct <= 45 ? #d50000 : TC
// Header
table.cell(info_tbl, 0, 0, 'VOLUME STRATA', text_color=TC, bgcolor=HD, text_size=size.small)
table.cell(info_tbl, 1, 0, '', text_color=TC, bgcolor=HD, text_size=size.small)
// Key levels
table.cell(info_tbl, 0, 1, 'POC', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 1, str.tostring(math.round_to_mintick(poc_lvl)),
text_color=poc_color, text_size=size.small)
table.cell(info_tbl, 0, 2, 'VAH', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 2, str.tostring(math.round_to_mintick(vah_lvl)),
text_color=TC, text_size=size.small)
table.cell(info_tbl, 0, 3, 'VAL', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 3, str.tostring(math.round_to_mintick(val_lvl)),
text_color=TC, text_size=size.small)
table.cell(info_tbl, 0, 4, 'VA Width', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 4, va_width_str,
text_color=va_width_col, text_size=size.small)
table.cell(info_tbl, 0, 5, 'Range', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 5, range_str,
text_color=TC, text_size=size.small)
// Separator
table.cell(info_tbl, 0, 6, '', bgcolor=SEP, text_size=size.small)
table.cell(info_tbl, 1, 6, '', bgcolor=SEP, text_size=size.small)
// Volume / structure
table.cell(info_tbl, 0, 7, 'POC Str', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 7, poc_str_str, text_color=poc_str_col, text_size=size.small)
table.cell(info_tbl, 0, 8, 'Up Vol', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 8, str.tostring(math.round(up_pct, 1)) + '%',
text_color=up_col, text_size=size.small)
table.cell(info_tbl, 0, 9, 'Total Vol', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 9, total_vol_str,
text_color=TC, text_size=size.small)
table.cell(info_tbl, 0, 10, 'Shape', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 10, shape_str, text_color=shape_col, text_size=size.small)
// Context
table.cell(info_tbl, 0, 11, 'VA State', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 11, va_state, text_color=va_state_col, text_size=size.small)
table.cell(info_tbl, 0, 12, 'POC', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 12, vpoc_str, text_color=vpoc_col, text_size=size.small)
table.cell(info_tbl, 0, 13, 'Dev POC', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 13, poc_trend_str, text_color=poc_trend_col, text_size=size.small)
table.cell(info_tbl, 0, 14, 'POC Dist', text_color=TC, text_size=size.small)
table.cell(info_tbl, 1, 14, poc_dist_str, text_color=poc_dist_col, text_size=size.small)
else
table.clear(info_tbl, 0, 0, 1, 14)
// ── Alert conditions ──────────────────────────────────────────────────────────
bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed
alertcondition(alertOk and (ta.cross(close, poc_lvl_p)),
'VS: POC Cross', 'VST · POC CROSS · {{ticker}} {{interval}}')
alertcondition(alertOk and (close > vah_lvl_p and close[1] <= vah_lvl_p[1]),
'VS: Exit VA (above)', 'VST · EXIT VA ABOVE · {{ticker}} {{interval}}')
alertcondition(alertOk and (close < val_lvl_p and close[1] >= val_lvl_p[1]),
'VS: Exit VA (below)', 'VST · EXIT VA BELOW · {{ticker}} {{interval}}')
alertcondition(alertOk and (close <= vah_lvl_p and close[1] > vah_lvl_p[1]),
'VS: Enter VA (from above)', 'VST · ENTER VA FROM ABOVE · {{ticker}} {{interval}}')
alertcondition(alertOk and (close >= val_lvl_p and close[1] < val_lvl_p[1]),
'VS: Enter VA (from below)', 'VST · ENTER VA FROM BELOW · {{ticker}} {{interval}}')
alertcondition(alertOk and (high >= vah_lvl_p and low < vah_lvl_p and not (high[1] >= vah_lvl_p[1] and low[1] < vah_lvl_p[1])),
'VS: VAH Touch', 'VST · VAH TOUCH · {{ticker}} {{interval}}')
alertcondition(alertOk and (high > val_lvl_p and low <= val_lvl_p and not (high[1] > val_lvl_p[1] and low[1] <= val_lvl_p[1])),
'VS: VAL Touch', 'VST · VAL TOUCH · {{ticker}} {{interval}}')
Overview
Volume Strata [WavesUnchained] calculates volume or money flow distribution over a configurable lookback period to highlight the Point of Control (POC), Value Area High and Low (VAH/VAL), High Volume Nodes (HVN), Low Volume Nodes (LVN), and naked POCs that remain untested. The indicator renders horizontal profile bars, level lines, value area shading, and an info table directly on the main chart.
Right-anchored volume profile with up and down volume split
Point of Control and Value Area High/Low tracking
High Volume Nodes (HVN) and Low Volume Nodes (LVN) highlighting
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Lookback Bars
int
150
bbars
Rows
int
24
cnum
Value Area %
float
70.
va_pct
Profile Offset (bars right)
int
150
prof_offset
Naked POC Min Distance (ATR)
float
0.1
npoc_min_atr
Profile Source
string
Volume
src_kind
POC
color
#e53935
poc_color
Width
int
2
poc_width
VAH / VAL
color
color.new(#9e9e9e, 0)
va_color
HVN
color
#ffd600
hvn_color
LVN
color
#4dd0e1
lvn_color
Value Area Up
color
color.new(#1e88e5, 30)
vup_color
Show all 25 settings
Setting
Type
Default
Variable
Value Area Down
color
color.new(#fdd835, 30)
vdown_color
Outside Up
color
color.new(#1e88e5, 75)
up_color
Outside Down
color
color.new(#fdd835, 75)
down_color
Label Size
string
Small
label_size_str
Show profile bars
bool
true
show_profile
POC label
bool
true
show_poc
VAH / VAL
bool
true
show_va
Value Area fill
bool
true
show_va_fill
HVN zones
bool
true
show_hvn
LVN zones
bool
true
show_lvn
Naked POCs
bool
true
show_naked_poc
Info table
bool
true
show_table
Alerts only on bar close (confirmed)
bool
true
alertsOnBarClose
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
This indicator plots a session VWAP of the typical price on the price chart. It marks an up triangle under a bar when close crosses above VWAP and a down triangle above a bar when close crosses below it. A star pinned at the top of the pane marks bars whose vo
The Holographic Market Microstructure indicator overlays market structure and smart money insights directly on the price chart. It identifies liquidity voids, order flow imbalances, accumulation zones, price and volume divergences, and fractal market structure
This indicator builds a frozen volume profile over a fixed, half-open timestamp range and draws it on the price chart. It aggregates one-minute source bars into output rows sized by a chosen tick count, marks a configurable value-area percentage, and can plot
This overlay indicator draws an on-chart depth-of-market table that splits volume at each price tick into estimated buy volume, sell volume, delta, and session volume. Growing text bars compare buyers and sellers, and recent bars approximate resting bid and as
This indicator overlays an order flow intelligence suite directly on the price chart. It renders a liquidity heatmap across configured price zones, alongside an on-chart information panel displaying volume delta, cumulative volume delta, a momentum compass bas
An overlay indicator that calculates multi-timeframe market conditions and displays them in an information panel on the price chart. It evaluates timeframe metrics such as 15-minute and 1-hour periods to monitor RSI values, support and resistance levels, and r