Gold Session Framework v8 · ซอร์สโค้ด
Pine Script ต้นฉบับ
ไฟล์นี้ถูกคัดลอกจากต้นฉบับใน G:\Trader\PineSCript\ ตอนสร้างเว็บ
จึงเป็นโค้ดชุดเดียวกับที่ทำงานอยู่บนชาร์ตจริง ณ วันที่ปรับปรุง
Pine Script ยังไม่มีไวยากรณ์เฉพาะสำหรับตัวไฮไลต์ที่ใช้ จึงไฮไลต์ด้วยไวยากรณ์ TypeScript ซึ่งใกล้เคียงกันพอที่จะอ่านง่าย
ตัวเลขบรรทัดตรงกับไฟล์จริง ใช้อ้างอิงในบทเรียนได้ เช่น gold-session-v8.pine:120
สารบัญส่วนของโค้ด (25)
- 16 1. SESSION
- 25 2. RANGE BASIS + AVR MULTIPLIERS
- 46 3. DISPLAY TOGGLES (AVR)
- 63 3b. STYLE — AVR lines & boxes
- 92 3c. CHANDELIER (multi-TF) — 3 selectable-TF instances (USER-TUNED values)
- 143 3d. VOLUME PROFILE (single, chart-TF, KV-MACD ㉕ principle)
- 175 3e. VALUE-AREA REVERSION SIGNALS (VARS) — ported from LuxAlgo (Fabio strat)
- 194 3f. CVD DIVERGENCE — ported from TradingFinder CVD Divergence [Periodic/EMA]
- 210 3g. ANCHORED VWAP (AVWAP) — Grateful-Futures technique (developing polylines)
- 230 3h. P/B/D ZONE DETECTOR — Patrick Nill range/breakout model (visual)
- 255 4. HELPER FUNCTIONS
- 376 5. DAILY RANGE (non-repaint: previous COMPLETED daily bar)
- 383 6. ASIA SESSION STATE
- 418 7. AVR LEVELS
- 446 8. AVR PERSISTENT OBJECTS
- 514 9. AVR UPDATE HELPERS
- 544 10. AVR RENDER
- 579 11. AVR ZONE CLASSIFICATION + TABLE
- 621 12. CHANDELIER — stateless ingredients via security, recursion at global scope
- 653 13. VOLUME PROFILE — OBJECT POOL (create-once, update-coords: realtime-safe)
- 852 13b. VALUE-AREA REVERSION (VARS) — per-bar session profile + reclaim engine
- 1088 13c. CVD DIVERGENCE — CLV×vol proxy + fractal regular divergence (on price)
- 1151 13d. ANCHORED VWAP (AVWAP) — developing polyline curves (dB from §13)
- 1259 13e. P/B/D ZONE DETECTOR — consolidation range → 3-touch validate → breakout
- 1397 14. ALERTS
//@version=6
// ============================================================================
// GOLD SESSION FRAMEWORK v8 (XAUUSD)
// v1 AVR · v2/v3 Chandelier + VP · v4 chandelier-out-of-security · v5 buffer
// fix · v6 tuned defaults + VARS + CVD · v7 Anchored VWAP.
// v8 = NEW module: P/B/D ZONE DETECTOR — formalizes Patrick Nill's discretionary
// range/breakout model. Auto-detects consolidation ranges (Efficiency-Ratio
// box/path filter), VALIDATES by ≥3 touches, freezes the box on breakout,
// classifies SMALL→breakout / BIG→range (height ÷ ATR), and flags ALIGNED
// (confluent) zones. Developing zone shown live.
// VISUAL-only — location + confirmation map, NO trade engine. Walk-forward first.
// ============================================================================
indicator("Gold Session Framework v8", overlay=true, max_lines_count=500, max_boxes_count=500, max_labels_count=500, max_polylines_count=100, max_bars_back=2000)
// ===========================================================================
// 1. SESSION
// ===========================================================================
gS = "Session"
asia_session = input.session("1400-2300", "Asia Session (Exchange Time)", group=gS)
i_tzsel = input.string("Exchange", "Session Timezone", options=["Exchange","America/New_York","UTC","Asia/Tokyo","Asia/Bangkok","Asia/Shanghai"], group=gS, tooltip="Clip anchors ~18:00 ET. 'Exchange' = the chart symbol's own timezone.")
i_minbars = input.int(3, "Min Asia bars to validate", minval=1, group=gS)
tz = i_tzsel == "Exchange" ? syminfo.timezone : i_tzsel
// ===========================================================================
// 2. RANGE BASIS + AVR MULTIPLIERS
// ===========================================================================
gA = "Range Basis"
atr_length = input.int(20, "Daily ATR Length", minval=1, group=gA)
use_adr = input.bool(false, "Use ADR (High-Low) instead of ATR", group=gA)
gU = "Upward Multipliers (x range)"
up_lvl1 = input.float(0.326, "Level 1 Up", step=0.001, group=gU)
up_avrm = input.float(0.600, "AVR- Up", step=0.001, group=gU)
up_avr = input.float(0.850, "AVR Up", step=0.001, group=gU)
up_avrp = input.float(1.100, "AVR+ Up", step=0.001, group=gU)
up_max = input.float(1.350, "Max Up", step=0.001, group=gU)
gD = "Downward Multipliers (x range)"
dn_lvl1 = input.float(0.326, "Level 1 Down", step=0.001, group=gD)
dn_avrm = input.float(0.600, "AVR- Down", step=0.001, group=gD)
dn_avr = input.float(0.850, "AVR Down", step=0.001, group=gD)
dn_avrp = input.float(1.100, "AVR+ Down", step=0.001, group=gD)
dn_max = input.float(1.350, "Max Down", step=0.001, group=gD)
// ===========================================================================
// 3. DISPLAY TOGGLES (AVR)
// ===========================================================================
gV = "Display — AVR"
show_asia = input.bool(true, "Asia box (H/L/EQ)", group=gV)
show_quarts = input.bool(true, "Quarters Q1/Q3 (25/75%)", group=gV)
show_l1 = input.bool(true, "Level 1 zone + red OTE", group=gV)
show_consol = input.bool(true, "Consolidation band (AVR-)", group=gV)
show_up = input.bool(true, "Up projections", group=gV)
show_dn = input.bool(true, "Down projections", group=gV)
show_labels = input.bool(true, "Right-edge labels", group=gV)
show_table = input.bool(true, "Info table", group=gV)
show_warn = input.bool(true, "Discretion warning label", group=gV)
ote_lo = input.float(0.50, "OTE inner from", step=0.01, minval=0, maxval=1, group=gV)
ote_hi = input.float(0.79, "OTE inner to", step=0.01, minval=0, maxval=1, group=gV)
ext_bars = input.int(6, "Extend right (bars)", minval=0, group=gV)
// ===========================================================================
// 3b. STYLE — AVR lines & boxes
// ===========================================================================
gST = "Style — AVR Lines"
c_asia = input.color(color.white, "Asia H/L", group=gST, inline="s_asia")
w_asia = input.int(2, "w", minval=1, maxval=4, group=gST, inline="s_asia")
c_eq = input.color(color.white, "EQ", group=gST, inline="s_eq")
w_eq = input.int(2, "w", minval=1, maxval=4, group=gST, inline="s_eq")
c_q = input.color(color.new(color.gray,60), "Quarters",group=gST, inline="s_q")
w_q = input.int(1, "w", minval=1, maxval=4, group=gST, inline="s_q")
c_am = input.color(color.rgb(200,255,0), "AVR-", group=gST, inline="s_am")
w_am = input.int(1, "w", minval=1, maxval=4, group=gST, inline="s_am")
c_a = input.color(color.rgb(200,255,0), "AVR", group=gST, inline="s_a")
w_a = input.int(1, "w", minval=1, maxval=4, group=gST, inline="s_a")
c_ap = input.color(color.rgb(200,255,0), "AVR+", group=gST, inline="s_ap")
w_ap = input.int(1, "w", minval=1, maxval=4, group=gST, inline="s_ap")
c_mx = input.color(color.rgb(200,255,0), "Max", group=gST, inline="s_mx")
w_mx = input.int(1, "w", minval=1, maxval=4, group=gST, inline="s_mx")
sty_line = input.string("Solid", "Level line style", options=["Solid","Dashed","Dotted"], group=gST)
sty_band = input.string("Dashed", "EQ / Quarter style", options=["Solid","Dashed","Dotted"], group=gST)
gSB = "Style — AVR Zone fills"
c_l1 = input.color(color.new(color.gray,88), "Level-1 box fill", group=gSB, inline="s_l1")
cb_l1 = input.color(color.new(color.gray,70), "border", group=gSB, inline="s_l1")
c_ote = input.color(color.new(color.rgb(166,58,30),20), "OTE box fill", group=gSB, inline="s_ote")
cb_ote = input.color(color.new(color.rgb(166,58,30),10), "border", group=gSB, inline="s_ote")
c_con = input.color(color.new(color.rgb(41,128,185),92), "Consol fill", group=gSB, inline="s_con")
cb_con = input.color(color.new(color.rgb(41,128,185),90), "border", group=gSB, inline="s_con")
bw_box = input.int(1, "Box border width", minval=1, maxval=4, group=gSB)
// ===========================================================================
// 3c. CHANDELIER (multi-TF) — 3 selectable-TF instances (USER-TUNED values)
// ===========================================================================
gCH = "Chandelier — Shared"
ch_useclose = input.bool(true, "Use close for extremum", group=gCH)
ch_hlen = input.int(50, "Hurst lookback (Enhanced)", minval=10, group=gCH)
ch_trth = input.float(0.60, "Hurst TREND >", step=0.05, group=gCH)
ch_mrth = input.float(0.40, "Hurst MR <", step=0.05, group=gCH)
g1 = "Chandelier 1"
ch1_on = input.bool(true, "Enable", group=g1, inline="a")
ch1_tf = input.timeframe("3", "TF", group=g1, inline="a")
ch1_len = input.int(20, "len", minval=2, group=g1, inline="b")
ch1_eng = input.string("Enhanced", "Engine", options=["Enhanced","Simple ATR"], group=g1, inline="b")
ch1_mtr = input.float(5.618, "mTREND", step=0.1, group=g1, inline="c")
ch1_mmr = input.float(3.236, "mMR", step=0.1, group=g1, inline="c")
ch1_mb = input.float(6.1278, "mult base / Simple", step=0.1, group=g1)
ch1_sty = input.string("Line", "Type", options=["Line","Stepline","Circles"], group=g1, inline="d")
ch1_w = input.int(1, "w", minval=1, maxval=4, group=g1, inline="d")
ch1_cu = input.color(color.white, "Up", group=g1, inline="e")
ch1_cd = input.color(color.rgb(0,224,192), "Dn", group=g1, inline="e")
ch1_sh = input.bool(false, "Flip markers", group=g1)
g2 = "Chandelier 2"
ch2_on = input.bool(true, "Enable", group=g2, inline="a")
ch2_tf = input.timeframe("60", "TF", group=g2, inline="a")
ch2_len = input.int(14, "len", minval=2, group=g2, inline="b")
ch2_eng = input.string("Enhanced", "Engine", options=["Enhanced","Simple ATR"], group=g2, inline="b")
ch2_mtr = input.float(5.618, "mTREND", step=0.1, group=g2, inline="c")
ch2_mmr = input.float(3.236, "mMR", step=0.1, group=g2, inline="c")
ch2_mb = input.float(5.0, "mult base / Simple", step=0.1, group=g2)
ch2_sty = input.string("Line", "Type", options=["Line","Stepline","Circles"], group=g2, inline="d")
ch2_w = input.int(1, "w", minval=1, maxval=4, group=g2, inline="d")
ch2_cu = input.color(color.rgb(27,94,32), "Up", group=g2, inline="e")
ch2_cd = input.color(color.rgb(140,30,30), "Dn", group=g2, inline="e")
ch2_sh = input.bool(false, "Flip markers", group=g2)
g3 = "Chandelier 3"
ch3_on = input.bool(true, "Enable", group=g3, inline="a")
ch3_tf = input.timeframe("240", "TF", group=g3, inline="a")
ch3_len = input.int(14, "len", minval=2, group=g3, inline="b")
ch3_eng = input.string("Enhanced", "Engine", options=["Enhanced","Simple ATR"], group=g3, inline="b")
ch3_mtr = input.float(5.618, "mTREND", step=0.1, group=g3, inline="c")
ch3_mmr = input.float(3.236, "mMR", step=0.1, group=g3, inline="c")
ch3_mb = input.float(5.618, "mult base / Simple", step=0.1, group=g3)
ch3_sty = input.string("Line", "Type", options=["Line","Stepline","Circles"], group=g3, inline="d")
ch3_w = input.int(1, "w", minval=1, maxval=4, group=g3, inline="d")
ch3_cu = input.color(color.rgb(0,230,0), "Up", group=g3, inline="e")
ch3_cd = input.color(color.rgb(255,120,0), "Dn", group=g3, inline="e")
ch3_sh = input.bool(false, "Flip markers", group=g3)
// ===========================================================================
// 3d. VOLUME PROFILE (single, chart-TF, KV-MACD ㉕ principle)
// ===========================================================================
gVP = "Volume Profile"
vp_on = input.bool(true, "Enable Volume Profile", group=gVP)
vp_mode = input.string("Session", "Mode", options=["Session","Rolling","Composite"], group=gVP, tooltip="Session=current day (D bar). Rolling=last N bars. Composite=last N days.")
vp_roll = input.int(610, "Rolling window (bars)", minval=10, maxval=1990, group=gVP)
vp_days = input.int(3, "Composite sessions (days)", minval=1, group=gVP)
vp_maxbars = input.int(987, "Max bars profiled (cap)", minval=10, maxval=1990, group=gVP)
vp_bins = input.int(60, "Bins (rows)", minval=5, maxval=200, group=gVP)
vp_va = input.float(84, "Value area %", minval=1, maxval=100, group=gVP)
vp_maxwidth = input.int(60, "Profile width (bars)", minval=5, group=gVP)
vp_offset = input.int(5, "Profile offset (bars right)", minval=0, group=gVP)
vp_showhist = input.bool(true, "Show histogram", group=gVP)
vp_shownodes= input.bool(true, "Show HVN / LVN nodes", group=gVP)
vp_hvn = input.float(1.3, "HVN ≥ ×avg-bin", step=0.1, group=gVP)
vp_lvn = input.float(0.5, "LVN ≤ ×avg-bin", step=0.1, group=gVP)
vp_maxnodes = input.int(6, "Max nodes / side", minval=1, maxval=20, group=gVP)
gVP2 = "Volume Profile — Style"
vp_fill = input.color(color.new(color.gray,90), "Histogram fill", group=gVP2)
vp_vafill = input.color(color.new(color.rgb(140,90,20),45), "Value-area fill", group=gVP2)
vp_cpoc = input.color(color.rgb(255,0,255), "POC", group=gVP2, inline="p")
vp_wpoc = input.int(1, "w", minval=1, maxval=4, group=gVP2, inline="p")
vp_spoc = input.string("Dotted", "", options=["Solid","Dashed","Dotted"], group=gVP2, inline="p")
vp_cva = input.color(color.rgb(0,224,192), "VAH/VAL", group=gVP2, inline="v")
vp_wva = input.int(2, "w", minval=1, maxval=4, group=gVP2, inline="v")
vp_sva = input.string("Solid", "", options=["Solid","Dashed","Dotted"], group=gVP2, inline="v")
vp_chvn = input.color(color.new(color.green,0), "HVN", group=gVP2, inline="n")
vp_clvn = input.color(color.new(color.red,0), "LVN", group=gVP2, inline="n")
vp_wnode= input.int(1, "w", minval=1, maxval=4, group=gVP2, inline="n")
// ===========================================================================
// 3e. VALUE-AREA REVERSION SIGNALS (VARS) — ported from LuxAlgo (Fabio strat)
// Session-anchored VA (own per-bar profile). Signal = break VAL/VAH →
// volume slowing (absorption) → engulfing candle closes back inside VA
// within N bars, with growing dominant volume. VISUAL/location only.
// ===========================================================================
gVR = "Value-Area Reversion (VARS)"
vars_on = input.bool(true, "Enable VARS reclaim signals", group=gVR)
vars_prev = input.bool(true, " • signals on PREVIOUS session VA", group=gVR)
vars_curr = input.bool(false, " • signals on CURRENT session VA", group=gVR)
vars_showprevva = input.bool(true, "Show previous-session VA lines", group=gVR)
vars_colorcandles = input.bool(false, "Color candles on reclaim (barcolor)", group=gVR)
vars_anchorHour = input.int(18, "Session anchor hour (America/New_York)", minval=0, maxval=23, group=gVR, tooltip="Fabio anchors at 18:00 ET (futures open). VARS uses its OWN session profile independent of the display VP above.")
vars_maxbars = input.int(10, "Max bars to signal after breakout", minval=1, maxval=100, group=gVR)
vars_rows = input.int(60, "VARS profile rows", minval=10, maxval=150, group=gVR)
vars_va = input.float(70.0, "VARS value area %", minval=50.0, maxval=99.0, step=0.5, group=gVR, tooltip="Fabio/LuxAlgo default 70 (separate from the display-VP value area).")
vars_cbull = input.color(color.rgb(8,153,129), "Bullish reclaim", group=gVR, inline="vc")
vars_cbear = input.color(color.rgb(242,54,69), "Bearish reclaim", group=gVR, inline="vc")
// ===========================================================================
// 3f. CVD DIVERGENCE — ported from TradingFinder CVD Divergence [Periodic/EMA]
// CVD = CLV×vol proxy. Regular divergence via fractal pivots + EMA50 gate.
// Draws divergence lines + ±RD labels on price (bounded pool). VISUAL-only.
// ===========================================================================
gCV = "CVD Divergence"
cvd_on = input.bool(true, "Enable CVD divergence", group=gCV)
cvd_mode = input.string("Periodic", "Cumulative mode", options=["Periodic","EMA"], group=gCV)
cvd_period = input.int(21, "CVD period", minval=1, group=gCV)
cvd_n = input.int(2, "Fractal periods (pivot L/R)", minval=1, maxval=20, group=gCV)
cvd_maxlen = input.int(30, "Max bars between pivots", minval=2, maxval=200, group=gCV)
cvd_showlabels = input.bool(true, "Show ±RD labels", group=gCV)
cvd_maxdraw = input.int(50, "Max divergences drawn (pool)", minval=5, maxval=200, group=gCV)
cvd_cbull = input.color(color.rgb(21,151,25), "Bullish +RD", group=gCV, inline="cc")
cvd_cbear = input.color(color.rgb(212,44,44), "Bearish -RD", group=gCV, inline="cc")
// ===========================================================================
// 3g. ANCHORED VWAP (AVWAP) — Grateful-Futures technique (developing polylines)
// Session (day open) + optional prev-day + AUTO swing-pivot anchors.
// Reads trend control / dynamic S/R to pair with GEX levels. VISUAL-only.
// ===========================================================================
gAV = "Anchored VWAP (AVWAP)"
avw_on = input.bool(true, "Enable Anchored VWAP", group=gAV)
avw_src = input.source(hlc3, "Price source", group=gAV)
avw_sess = input.bool(true, "Session AVWAP (from day open)", group=gAV)
avw_prev = input.bool(false, "Previous-day AVWAP", group=gAV)
avw_swh = input.bool(true, "Swing-High AVWAP (sell-trend anchor)", group=gAV)
avw_swl = input.bool(true, "Swing-Low AVWAP (buy-trend anchor)", group=gAV)
avw_piv = input.int(20, "Swing pivot length (L/R)", minval=2, maxval=100, group=gAV, tooltip="Bigger = only more significant swings re-anchor the swing AVWAPs.")
avw_lbl = input.bool(true, "Curve labels", group=gAV)
avw_w = input.int(2, "Curve width", minval=1, maxval=4, group=gAV)
avw_c_sess = input.color(color.orange, "Session", group=gAV, inline="ac")
avw_c_prev = input.color(color.new(color.gray,30),"Prev-day", group=gAV, inline="ac")
avw_c_swh = input.color(color.red, "Swing-High",group=gAV, inline="ac2")
avw_c_swl = input.color(color.green, "Swing-Low", group=gAV, inline="ac2")
// ===========================================================================
// 3h. P/B/D ZONE DETECTOR — Patrick Nill range/breakout model (visual)
// Range = low-efficiency (choppy) window; VALID after ≥3 touches; freeze on
// breakout; SMALL(→breakout) vs BIG(→range) by height/ATR; flag ALIGNED zones.
// ===========================================================================
gPD = "P/B/D Zones (Patrick model)"
pd_on = input.bool(true, "Enable P/B/D zone detector", group=gPD)
pd_win = input.int(20, "Range window (bars)", minval=5, maxval=200, group=gPD)
pd_atrlen = input.int(14, "ATR length", minval=1, group=gPD)
pd_er = input.float(0.40, "Range if box/path ratio <", step=0.05, minval=0.1, maxval=0.9, group=gPD, tooltip="Efficiency ratio = window height ÷ Σ true-range. Low = choppy/range, high = trending. Lower value = stricter (only very choppy windows qualify).")
pd_tol = input.float(0.20, "Touch tolerance (×ATR)", step=0.05, minval=0.0, group=gPD)
pd_brk = input.float(0.25, "Breakout beyond edge (×ATR)", step=0.05, minval=0.0, group=gPD)
pd_mintou = input.int(3, "Min touches to validate", minval=2, maxval=12, group=gPD)
pd_maxage = input.int(200, "Max range age (bars)", minval=20, maxval=1000, group=gPD)
pd_smallK = input.float(2.0, "SMALL zone height ≤ ×ATR (→breakout)", step=0.1, group=gPD)
pd_bigK = input.float(4.0, "BIG zone height ≥ ×ATR (→range)", step=0.1, group=gPD)
pd_align = input.bool(true, "Flag aligned/confluent zones", group=gPD)
pd_atol = input.float(0.5, "Alignment tolerance (×ATR)", step=0.1, group=gPD)
pd_maxz = input.int(12, "Max zones drawn (pool)", minval=2, maxval=40, group=gPD)
pd_showdev= input.bool(true, "Show developing (forming) zone", group=gPD)
pd_c_sm = input.color(color.new(color.aqua,80), "Small(BO) fill", group=gPD, inline="pc")
pd_c_bg = input.color(color.new(color.orange,80), "Big(range) fill", group=gPD, inline="pc")
pd_c_al = input.color(color.new(color.fuchsia,70), "Aligned fill", group=gPD, inline="pc2")
pd_c_bd = input.color(color.new(color.gray,40), "Border", group=gPD, inline="pc2")
// ===========================================================================
// 4. HELPER FUNCTIONS
// ===========================================================================
f_linestyle(s) => s == "Dashed" ? line.style_dashed : s == "Dotted" ? line.style_dotted : line.style_solid
// Yang-Zhang per-bar return sigma (KV-MACD price-σ vol unit)
f_yzsigma(n) =>
float k = 0.34 / (1.34 + (n + 1.0) / (n - 1.0))
float lo_ = math.log(open / nz(close[1], open))
float lc_ = math.log(close / open)
float rs_ = math.log(high / close) * math.log(high / open) + math.log(low / close) * math.log(low / open)
float vo = ta.variance(lo_, n)
float vc = ta.variance(lc_, n)
float vr = ta.sma(rs_, n)
math.sqrt(math.max(vo + k * vc + vr, 0.0))
// Rescaled-range Hurst estimate over n bars
f_hurst(n) =>
float mean = 0.0
for i = 0 to n - 1
mean += nz(math.log(close[i] / close[i + 1]))
mean := mean / n
float cum = 0.0
float mx = -1e20
float mn = 1e20
float ss = 0.0
for i = 0 to n - 1
float dev = nz(math.log(close[i] / close[i + 1])) - mean
cum += dev
mx := math.max(mx, cum)
mn := math.min(mn, cum)
ss += dev * dev
float R = mx - mn
float S = math.sqrt(ss / n)
float rs = S > 0 ? R / S : na
(na(rs) or rs <= 0) ? 0.5 : math.log(rs) / math.log(n)
// STATELESS chandelier ingredients — safe to run inside request.security
// (no var, no self-history). Returns [highest, lowest, band(=vol×mult)].
f_chanIng(len, useClose, enhanced, hlen, mTr, mMr, mB, trTh, mrTh) =>
float atrv = ta.atr(len)
float volu = atrv
if enhanced
float sig = f_yzsigma(len)
float vpx = close * sig
volu := (na(vpx) or vpx <= 0) ? atrv : vpx
float mult = mB
if enhanced
float h = f_hurst(hlen)
mult := h > trTh ? mTr : (h < mrTh ? mMr : mB)
float band = volu * mult
float hh = useClose ? ta.highest(close, len) : ta.highest(high, len)
float ll = useClose ? ta.lowest(close, len) : ta.lowest(low, len)
[hh, ll, band]
// STATEFUL everget recursion — runs at GLOBAL scope (var on chart timeline).
// Each of the 3 call-sites gets its own persistent state. Returns [stop, dir].
f_trail(hh, ll, band) =>
var float ls = na
var float ss = na
var int dir = 1
float lb = hh - band
float sb = ll + band
ls := na(ls[1]) ? lb : (close[1] > ls[1] ? math.max(lb, ls[1]) : lb)
ss := na(ss[1]) ? sb : (close[1] < ss[1] ? math.min(sb, ss[1]) : sb)
dir := (dir == -1 and close > ss[1]) ? 1 : (dir == 1 and close < ls[1]) ? -1 : dir
float st = dir > 0 ? ls : ss
[st, dir]
// --- VARS profile helpers (array-based → no historical-buffer issue) ---
va_reset(arr, rows) =>
array.clear(arr)
for i = 0 to rows - 1
array.push(arr, 0.0)
va_add(bins, bLow, bHigh, bVol, rLow, bSize, rows) =>
int bc = array.size(bins)
if bVol > 0 and bSize > 0 and bc > 0
int fi = math.max(0, math.min(bc - 1, int(math.floor((bLow - rLow) / bSize))))
int li = math.max(0, math.min(bc - 1, int(math.floor((bHigh - rLow) / bSize))))
if fi <= li
float vpb = bVol / (li - fi + 1)
for k = fi to li
array.set(bins, k, array.get(bins, k) + vpb)
va_levels(bins, rLow, bSize, vaPct) =>
int bc = array.size(bins)
float tot = 0.0
float mx = 0.0
int poc = 0
if bc > 0
for k = 0 to bc - 1
float bv = array.get(bins, k)
tot += bv
if bv > mx
mx := bv
poc := k
float tgt = tot * vaPct / 100.0
float acc = mx
int loI = poc
int hiI = poc
if tot > 0
for e = 1 to bc
if acc >= tgt
break
int li = poc - e
int ui = poc + e
bool cl = li >= 0 and li < bc
bool cu = ui >= 0 and ui < bc
float lv = cl ? array.get(bins, li) : 0.0
float uv = cu ? array.get(bins, ui) : 0.0
if cl and (not cu or lv >= uv)
acc += lv
loI := li
else if cu
acc += uv
hiI := ui
float val = (tot > 0 and not na(bSize)) ? rLow + loI * bSize : na
float vah = (tot > 0 and not na(bSize)) ? rLow + (hiI + 1) * bSize : na
[vah, val]
// ===========================================================================
// 5. DAILY RANGE (non-repaint: previous COMPLETED daily bar)
// ===========================================================================
_atrD = request.security(syminfo.tickerid, "D", ta.atr(atr_length)[1], lookahead=barmerge.lookahead_on)
_adrD = request.security(syminfo.tickerid, "D", ta.sma(high - low, atr_length)[1], lookahead=barmerge.lookahead_on)
dRange = use_adr ? _adrD : _atrD
// ===========================================================================
// 6. ASIA SESSION STATE
// ===========================================================================
in_asia = not na(time(timeframe.period, asia_session, tz))
is_new = in_asia and not in_asia[1]
is_end = not in_asia and in_asia[1]
var float a_h = na
var float a_l = na
var int a_n = 0
var float L_h = na
var float L_l = na
var float L_rng = na
var int day_bi = na
if is_new
a_h := high
a_l := low
a_n := 1
L_h := na
L_l := na
L_rng := na
else if in_asia
a_h := math.max(a_h, high)
a_l := math.min(a_l, low)
a_n += 1
if is_end and a_n >= i_minbars
L_h := a_h
L_l := a_l
L_rng := dRange
day_bi := bar_index
show = not in_asia and not na(L_h) and not na(L_rng)
// ===========================================================================
// 7. AVR LEVELS
// ===========================================================================
eq = show ? (L_h + L_l) / 2.0 : na
arng = L_h - L_l
q1 = show ? L_l + arng * 0.25 : na
q3 = show ? L_l + arng * 0.75 : na
u_l1 = show ? L_h + L_rng * up_lvl1 : na
u_am = show ? L_h + L_rng * up_avrm : na
u_a = show ? L_h + L_rng * up_avr : na
u_ap = show ? L_h + L_rng * up_avrp : na
u_mx = show ? L_h + L_rng * up_max : na
d_l1 = show ? L_l - L_rng * dn_lvl1 : na
d_am = show ? L_l - L_rng * dn_avrm : na
d_a = show ? L_l - L_rng * dn_avr : na
d_ap = show ? L_l - L_rng * dn_avrp : na
d_mx = show ? L_l - L_rng * dn_max : na
u_band = L_rng * up_lvl1
d_band = L_rng * dn_lvl1
u_ote_a = show ? L_h + u_band * ote_lo : na
u_ote_b = show ? L_h + u_band * ote_hi : na
d_ote_a = show ? L_l - d_band * ote_lo : na
d_ote_b = show ? L_l - d_band * ote_hi : na
cAsia = c_asia
stLine = f_linestyle(sty_line)
stBand = f_linestyle(sty_band)
// ===========================================================================
// 8. AVR PERSISTENT OBJECTS
// ===========================================================================
var line ln_h = na
var line ln_l = na
var line ln_eq = na
var line ln_q1 = na
var line ln_q3 = na
var line ln_uam = na
var line ln_ua = na
var line ln_uap = na
var line ln_umx = na
var line ln_dam = na
var line ln_da = na
var line ln_dap = na
var line ln_dmx = na
var box bx_l1u = na
var box bx_oteu = na
var box bx_l1d = na
var box bx_oted = na
var box bx_con = na
var label lb_uam = na
var label lb_ua = na
var label lb_uap = na
var label lb_umx = na
var label lb_dam = na
var label lb_da = na
var label lb_dap = na
var label lb_dmx = na
var label lb_h = na
var label lb_l = na
var label lb_zone = na
var label lb_warn = na
var table tb = na
if barstate.isfirst
ln_h := line.new(bar_index,na,bar_index,na, color=c_asia, width=w_asia)
ln_l := line.new(bar_index,na,bar_index,na, color=c_asia, width=w_asia)
ln_eq := line.new(bar_index,na,bar_index,na, color=c_eq, width=w_eq, style=stBand)
ln_q1 := line.new(bar_index,na,bar_index,na, color=c_q, width=w_q, style=stBand)
ln_q3 := line.new(bar_index,na,bar_index,na, color=c_q, width=w_q, style=stBand)
ln_uam:= line.new(bar_index,na,bar_index,na, color=c_am, width=w_am, style=stLine)
ln_ua := line.new(bar_index,na,bar_index,na, color=c_a, width=w_a, style=stLine)
ln_uap:= line.new(bar_index,na,bar_index,na, color=c_ap, width=w_ap, style=stLine)
ln_umx:= line.new(bar_index,na,bar_index,na, color=c_mx, width=w_mx, style=stLine)
ln_dam:= line.new(bar_index,na,bar_index,na, color=c_am, width=w_am, style=stLine)
ln_da := line.new(bar_index,na,bar_index,na, color=c_a, width=w_a, style=stLine)
ln_dap:= line.new(bar_index,na,bar_index,na, color=c_ap, width=w_ap, style=stLine)
ln_dmx:= line.new(bar_index,na,bar_index,na, color=c_mx, width=w_mx, style=stLine)
bx_l1u := box.new(bar_index,na,bar_index,na, border_color=cb_l1, bgcolor=c_l1, border_width=bw_box)
bx_oteu:= box.new(bar_index,na,bar_index,na, border_color=cb_ote, bgcolor=c_ote, border_width=bw_box)
bx_l1d := box.new(bar_index,na,bar_index,na, border_color=cb_l1, bgcolor=c_l1, border_width=bw_box)
bx_oted:= box.new(bar_index,na,bar_index,na, border_color=cb_ote, bgcolor=c_ote, border_width=bw_box)
bx_con := box.new(bar_index,na,bar_index,na, border_color=cb_con, bgcolor=c_con, border_width=bw_box)
lb_uam:=label.new(bar_index,na,"", style=label.style_none, textcolor=c_am, size=size.small)
lb_ua :=label.new(bar_index,na,"", style=label.style_none, textcolor=c_a, size=size.small)
lb_uap:=label.new(bar_index,na,"", style=label.style_none, textcolor=c_ap, size=size.small)
lb_umx:=label.new(bar_index,na,"", style=label.style_none, textcolor=c_mx, size=size.small)
lb_dam:=label.new(bar_index,na,"", style=label.style_none, textcolor=c_am, size=size.small)
lb_da :=label.new(bar_index,na,"", style=label.style_none, textcolor=c_a, size=size.small)
lb_dap:=label.new(bar_index,na,"", style=label.style_none, textcolor=c_ap, size=size.small)
lb_dmx:=label.new(bar_index,na,"", style=label.style_none, textcolor=c_mx, size=size.small)
lb_h :=label.new(bar_index,na,"", style=label.style_none, textcolor=c_asia, size=size.small)
lb_l :=label.new(bar_index,na,"", style=label.style_none, textcolor=c_asia, size=size.small)
lb_zone:=label.new(bar_index,na,"", style=label.style_label_left, color=color.new(color.black,20), textcolor=color.white, size=size.small)
lb_warn:=label.new(bar_index,na,"", style=label.style_label_down, color=color.new(color.orange,25), textcolor=color.black, size=size.tiny)
tb := table.new(position.top_right, 2, 8, border_width=1, frame_color=color.new(color.gray,60), frame_width=1)
// ===========================================================================
// 9. AVR UPDATE HELPERS
// ===========================================================================
uH(l, x1, x2, y, vis) =>
yy = vis ? y : na
line.set_xy1(l, x1, yy)
line.set_xy2(l, x2, yy)
uB(b, x1, x2, top, bot, vis) =>
if vis and not na(top) and not na(bot)
box.set_left(b, x1)
box.set_right(b, x2)
box.set_top(b, top)
box.set_bottom(b, bot)
else
box.set_top(b, na)
box.set_bottom(b, na)
uL(lb, x, y, txt, vis) =>
if vis and not na(y)
label.set_xy(lb, x, y)
label.set_text(lb, txt)
else
label.set_xy(lb, x, na)
label.set_text(lb, "")
f_row(r, k, v, kc, vc) =>
table.cell(tb, 0, r, k, text_color=color.white, text_size=size.small, bgcolor=kc)
table.cell(tb, 1, r, v, text_color=color.white, text_size=size.small, bgcolor=vc)
// ===========================================================================
// 10. AVR RENDER
// ===========================================================================
if barstate.islast
x1 = na(day_bi) ? bar_index : day_bi
x2 = bar_index + ext_bars
uH(ln_h, x1, x2, L_h, show and show_asia)
uH(ln_l, x1, x2, L_l, show and show_asia)
uH(ln_eq, x1, x2, eq, show and show_asia)
uH(ln_q1, x1, x2, q1, show and show_quarts)
uH(ln_q3, x1, x2, q3, show and show_quarts)
uH(ln_uam, x1, x2, u_am, show and show_up)
uH(ln_ua, x1, x2, u_a, show and show_up)
uH(ln_uap, x1, x2, u_ap, show and show_up)
uH(ln_umx, x1, x2, u_mx, show and show_up)
uH(ln_dam, x1, x2, d_am, show and show_dn)
uH(ln_da, x1, x2, d_a, show and show_dn)
uH(ln_dap, x1, x2, d_ap, show and show_dn)
uH(ln_dmx, x1, x2, d_mx, show and show_dn)
uB(bx_l1u, x1, x2, u_l1, L_h, show and show_l1 and show_up)
uB(bx_oteu, x1, x2, u_ote_b, u_ote_a, show and show_l1 and show_up)
uB(bx_l1d, x1, x2, L_l, d_l1, show and show_l1 and show_dn)
uB(bx_oted, x1, x2, d_ote_a, d_ote_b, show and show_l1 and show_dn)
uB(bx_con, x1, x2, u_am, d_am, show and show_consol)
uL(lb_h, x2, L_h, "Asia H", show and show_labels and show_asia)
uL(lb_l, x2, L_l, "Asia L", show and show_labels and show_asia)
uL(lb_uam, x2, u_am, "AVR- ↑", show and show_labels and show_up)
uL(lb_ua, x2, u_a, "AVR ↑", show and show_labels and show_up)
uL(lb_uap, x2, u_ap, "AVR+ ↑", show and show_labels and show_up)
uL(lb_umx, x2, u_mx, "MAX ↑", show and show_labels and show_up)
uL(lb_dam, x2, d_am, "AVR- ↓", show and show_labels and show_dn)
uL(lb_da, x2, d_a, "AVR ↓", show and show_labels and show_dn)
uL(lb_dap, x2, d_ap, "AVR+ ↓", show and show_labels and show_dn)
uL(lb_dmx, x2, d_mx, "MAX ↓", show and show_labels and show_dn)
// ===========================================================================
// 11. AVR ZONE CLASSIFICATION + TABLE
// ===========================================================================
zoneTxt = "—"
if show
c = close
zoneTxt := c > u_mx ? "▲ > MAX (OUTLIER — rare)" :
c > u_ap ? "▲ AVR+ → MAX (exhaustion ↑)" :
c > u_a ? "▲ AVR → AVR+ (avg expansion ↑)" :
c > u_am ? "▲ AVR- → AVR ↑" :
c > L_h ? "▲ Level 1 ↑ (first expansion)" :
c > eq ? "◦ Upper Asia (balance)" :
c > L_l ? "◦ Lower Asia (balance)" :
c > d_am ? "▼ Level 1 ↓ (first expansion)" :
c > d_a ? "▼ AVR- → AVR ↓" :
c > d_ap ? "▼ AVR → AVR+ (avg expansion ↓)" :
c > d_mx ? "▼ AVR+ → MAX (exhaustion ↓)" :
"▼ < MAX (OUTLIER — rare)"
if barstate.islast
uL(lb_zone, bar_index + ext_bars, close, " " + zoneTxt, show and show_labels)
if show_warn and show
label.set_xy(lb_warn, bar_index, na(u_mx) ? high : u_mx)
label.set_text(lb_warn, "ZONES = LOCATION / EXHAUSTION — NOT ENTRY")
else
label.set_text(lb_warn, "")
label.set_xy(lb_warn, bar_index, na)
if barstate.islast and show_table
bg = color.new(color.black, 15)
hd = color.new(cAsia, 10)
table.cell(tb, 0, 0, "GOLD SESSION FRAMEWORK v8", text_color=color.white, text_size=size.small, bgcolor=hd)
table.cell(tb, 1, 0, use_adr ? "ADR" : "ATR", text_color=color.white, text_size=size.small, bgcolor=hd)
f_row(1, (use_adr?"ADR(":"ATR(")+str.tostring(atr_length)+")", show?str.tostring(L_rng, format.mintick):"—", bg, bg)
f_row(2, "Asia H / L", show?str.tostring(L_h, format.mintick)+" / "+str.tostring(L_l, format.mintick):"—", bg, bg)
f_row(3, "Asia range", show?str.tostring(arng, format.mintick):"—", bg, bg)
f_row(4, "Up AVR / MAX", show?str.tostring(u_a, format.mintick)+" / "+str.tostring(u_mx, format.mintick):"—", bg, color.new(color.red,80))
f_row(5, "Dn AVR / MAX", show?str.tostring(d_a, format.mintick)+" / "+str.tostring(d_mx, format.mintick):"—", bg, color.new(color.red,80))
f_row(6, "Current zone", show?zoneTxt:"waiting Asia close", bg, color.new(color.orange,70))
table.cell(tb, 0, 7, "LOCATION/EXHAUSTION — NOT ENTRY", text_color=color.yellow, text_size=size.tiny, bgcolor=bg)
table.cell(tb, 1, 7, "", bgcolor=bg)
// ===========================================================================
// 12. CHANDELIER — stateless ingredients via security, recursion at global scope
// ===========================================================================
[hh1, ll1, bd1] = request.security(syminfo.tickerid, ch1_tf, f_chanIng(ch1_len, ch_useclose, ch1_eng=="Enhanced", ch_hlen, ch1_mtr, ch1_mmr, ch1_mb, ch_trth, ch_mrth), lookahead=barmerge.lookahead_off)
[hh2, ll2, bd2] = request.security(syminfo.tickerid, ch2_tf, f_chanIng(ch2_len, ch_useclose, ch2_eng=="Enhanced", ch_hlen, ch2_mtr, ch2_mmr, ch2_mb, ch_trth, ch_mrth), lookahead=barmerge.lookahead_off)
[hh3, ll3, bd3] = request.security(syminfo.tickerid, ch3_tf, f_chanIng(ch3_len, ch_useclose, ch3_eng=="Enhanced", ch_hlen, ch3_mtr, ch3_mmr, ch3_mb, ch_trth, ch_mrth), lookahead=barmerge.lookahead_off)
[st1, dr1] = f_trail(hh1, ll1, bd1)
[st2, dr2] = f_trail(hh2, ll2, bd2)
[st3, dr3] = f_trail(hh3, ll3, bd3)
c1 = dr1 > 0 ? ch1_cu : ch1_cd
c2 = dr2 > 0 ? ch2_cu : ch2_cd
c3 = dr3 > 0 ? ch3_cu : ch3_cd
plot(ch1_on and ch1_sty=="Line" ? st1 : na, "Ch1 line", color=c1, linewidth=ch1_w, style=plot.style_linebr)
plot(ch1_on and ch1_sty=="Stepline" ? st1 : na, "Ch1 step", color=c1, linewidth=ch1_w, style=plot.style_stepline)
plot(ch1_on and ch1_sty=="Circles" ? st1 : na, "Ch1 dots", color=c1, linewidth=ch1_w, style=plot.style_circles)
plot(ch2_on and ch2_sty=="Line" ? st2 : na, "Ch2 line", color=c2, linewidth=ch2_w, style=plot.style_linebr)
plot(ch2_on and ch2_sty=="Stepline" ? st2 : na, "Ch2 step", color=c2, linewidth=ch2_w, style=plot.style_stepline)
plot(ch2_on and ch2_sty=="Circles" ? st2 : na, "Ch2 dots", color=c2, linewidth=ch2_w, style=plot.style_circles)
plot(ch3_on and ch3_sty=="Line" ? st3 : na, "Ch3 line", color=c3, linewidth=ch3_w, style=plot.style_linebr)
plot(ch3_on and ch3_sty=="Stepline" ? st3 : na, "Ch3 step", color=c3, linewidth=ch3_w, style=plot.style_stepline)
plot(ch3_on and ch3_sty=="Circles" ? st3 : na, "Ch3 dots", color=c3, linewidth=ch3_w, style=plot.style_circles)
plotshape(ch1_on and ch1_sh and dr1>0 and dr1[1]<0, "Ch1 ↑", shape.triangleup, location.belowbar, ch1_cu, size=size.tiny)
plotshape(ch1_on and ch1_sh and dr1<0 and dr1[1]>0, "Ch1 ↓", shape.triangledown, location.abovebar, ch1_cd, size=size.tiny)
plotshape(ch2_on and ch2_sh and dr2>0 and dr2[1]<0, "Ch2 ↑", shape.triangleup, location.belowbar, ch2_cu, size=size.tiny)
plotshape(ch2_on and ch2_sh and dr2<0 and dr2[1]>0, "Ch2 ↓", shape.triangledown, location.abovebar, ch2_cd, size=size.tiny)
plotshape(ch3_on and ch3_sh and dr3>0 and dr3[1]<0, "Ch3 ↑", shape.triangleup, location.belowbar, ch3_cu, size=size.tiny)
plotshape(ch3_on and ch3_sh and dr3<0 and dr3[1]>0, "Ch3 ↓", shape.triangledown, location.abovebar, ch3_cd, size=size.tiny)
// ===========================================================================
// 13. VOLUME PROFILE — OBJECT POOL (create-once, update-coords: realtime-safe)
// ===========================================================================
bool dB = ta.change(time("D")) != 0
var int sessN = 1
sessN := dB ? 1 : sessN + 1
stPoc = f_linestyle(vp_spoc)
stVa = f_linestyle(vp_sva)
var array<box> vpBox = array.new<box>()
var array<line> vpNode = array.new<line>()
var line vpLpoc = na
var line vpLvah = na
var line vpLval = na
var label vpLbP = na
var label vpLbH = na
var label vpLbV = na
if barstate.isfirst
for i = 0 to vp_bins - 1
array.push(vpBox, box.new(bar_index, na, bar_index, na, border_width=1))
for i = 0 to (vp_maxnodes * 2) - 1
array.push(vpNode, line.new(bar_index, na, bar_index, na))
vpLpoc := line.new(bar_index, na, bar_index, na)
vpLvah := line.new(bar_index, na, bar_index, na)
vpLval := line.new(bar_index, na, bar_index, na)
vpLbP := label.new(bar_index, na, "", style=label.style_label_left, size=size.tiny)
vpLbH := label.new(bar_index, na, "", style=label.style_label_left, size=size.tiny)
vpLbV := label.new(bar_index, na, "", style=label.style_label_left, size=size.tiny)
if barstate.islast
// --- hide the whole pool first ---
for b in vpBox
box.set_top(b, na)
box.set_bottom(b, na)
for l in vpNode
line.set_xy1(l, bar_index, na)
line.set_xy2(l, bar_index, na)
line.set_xy1(vpLpoc, bar_index, na)
line.set_xy2(vpLpoc, bar_index, na)
line.set_xy1(vpLvah, bar_index, na)
line.set_xy2(vpLvah, bar_index, na)
line.set_xy1(vpLval, bar_index, na)
line.set_xy2(vpLval, bar_index, na)
label.set_xy(vpLbP, bar_index, na)
label.set_xy(vpLbH, bar_index, na)
label.set_xy(vpLbV, bar_index, na)
if vp_on
// --- window length (clamped to available history) ---
int Nbars = vp_roll
if vp_mode == "Session"
Nbars := sessN
else if vp_mode == "Composite"
int seen = 0
int nb = 1
int scan = math.min(vp_maxbars, bar_index + 1) - 1
for i = 0 to scan
if dB[i]
seen += 1
if seen > vp_days
break
nb := i + 1
Nbars := nb
Nbars := math.min(Nbars, vp_maxbars)
Nbars := math.min(Nbars, bar_index + 1)
Nbars := math.max(Nbars, 2)
// --- price range ---
float hi = high
float lo = low
for i = 0 to Nbars - 1
if not na(high[i]) and not na(low[i])
hi := math.max(hi, high[i])
lo := math.min(lo, low[i])
float binSize = (hi - lo) / vp_bins
if binSize > 0
// --- accumulate volume into bins ---
array<float> binVol = array.new_float(vp_bins, 0.0)
for i = 0 to Nbars - 1
float v = volume[i]
if not na(v) and v > 0 and not na(high[i]) and not na(low[i])
int b0 = math.max(0, int(math.floor((low[i] - lo) / binSize)))
int b1 = math.min(vp_bins - 1, int(math.floor((high[i] - lo) / binSize)))
float vPer = v / math.max(1, b1 - b0 + 1)
for b = b0 to b1
array.set(binVol, b, array.get(binVol, b) + vPer)
// --- POC + totals ---
int pocIdx = 0
float pocVol = 0.0
float total = 0.0
for b = 0 to vp_bins - 1
float bv = array.get(binVol, b)
total += bv
if bv > pocVol
pocVol := bv
pocIdx := b
if total > 0 and pocVol > 0
float avgBin = total / vp_bins
float pocPrice = lo + (pocIdx + 0.5) * binSize
// --- value area expansion (larger-neighbor) ---
float target = total * vp_va / 100.0
float cum = pocVol
int lo_i = pocIdx
int hi_i = pocIdx
while cum < target and (lo_i > 0 or hi_i < vp_bins - 1)
float upV = hi_i < vp_bins - 1 ? array.get(binVol, hi_i + 1) : -1.0
float dnV = lo_i > 0 ? array.get(binVol, lo_i - 1) : -1.0
if upV >= dnV
hi_i += 1
cum += upV
else
lo_i -= 1
cum += dnV
float vah = lo + (hi_i + 1) * binSize
float val = lo + lo_i * binSize
int rX = bar_index + vp_offset
int lX = bar_index - math.min(Nbars, 300)
// --- histogram → update pool boxes ---
if vp_showhist
for b = 0 to vp_bins - 1
box bx = array.get(vpBox, b)
float bv = array.get(binVol, b)
int wBars = int(math.round(bv / pocVol * vp_maxwidth))
if bv > 0 and wBars >= 1
bool inVA = b >= lo_i and b <= hi_i
color fc = b == pocIdx ? color.new(vp_cpoc, 55) : inVA ? vp_vafill : vp_fill
box.set_left(bx, rX)
box.set_right(bx, rX + wBars)
box.set_top(bx, lo + (b + 1) * binSize)
box.set_bottom(bx, lo + b * binSize)
box.set_bgcolor(bx, fc)
box.set_border_color(bx, color.new(color.gray, 90))
// --- POC / VAH / VAL lines + labels ---
line.set_xy1(vpLpoc, lX, pocPrice)
line.set_xy2(vpLpoc, rX + vp_maxwidth, pocPrice)
line.set_color(vpLpoc, vp_cpoc)
line.set_width(vpLpoc, vp_wpoc)
line.set_style(vpLpoc, stPoc)
label.set_xy(vpLbP, rX + vp_maxwidth, pocPrice)
label.set_text(vpLbP, "POC " + str.tostring(pocPrice, format.mintick))
label.set_textcolor(vpLbP, vp_cpoc)
label.set_color(vpLbP, color.new(color.black, 30))
line.set_xy1(vpLvah, lX, vah)
line.set_xy2(vpLvah, rX + vp_maxwidth, vah)
line.set_color(vpLvah, vp_cva)
line.set_width(vpLvah, vp_wva)
line.set_style(vpLvah, stVa)
label.set_xy(vpLbH, rX + vp_maxwidth, vah)
label.set_text(vpLbH, "VAH")
label.set_textcolor(vpLbH, vp_cva)
label.set_color(vpLbH, color.new(color.black, 40))
line.set_xy1(vpLval, lX, val)
line.set_xy2(vpLval, rX + vp_maxwidth, val)
line.set_color(vpLval, vp_cva)
line.set_width(vpLval, vp_wva)
line.set_style(vpLval, stVa)
label.set_xy(vpLbV, rX + vp_maxwidth, val)
label.set_text(vpLbV, "VAL")
label.set_textcolor(vpLbV, vp_cva)
label.set_color(vpLbV, color.new(color.black, 40))
// --- HVN / LVN nodes → update pool lines ---
if vp_shownodes
int used = 0
int nH = 0
int nL = 0
int cap = array.size(vpNode)
for b = 1 to vp_bins - 2
if used < cap
float bv = array.get(binVol, b)
float bp = array.get(binVol, b - 1)
float bn = array.get(binVol, b + 1)
float ny = lo + (b + 0.5) * binSize
bool isH = nH < vp_maxnodes and bv >= vp_hvn * avgBin and bv >= bp and bv >= bn
bool isL = nL < vp_maxnodes and bv <= vp_lvn * avgBin and bv <= bp and bv <= bn and bv > 0
if isH or isL
line ln = array.get(vpNode, used)
line.set_xy1(ln, lX, ny)
line.set_xy2(ln, rX, ny)
line.set_color(ln, isH ? vp_chvn : vp_clvn)
line.set_width(ln, vp_wnode)
line.set_style(ln, isH ? line.style_solid : line.style_dotted)
used += 1
if isH
nH += 1
else
nL += 1
// ===========================================================================
// 13b. VALUE-AREA REVERSION (VARS) — per-bar session profile + reclaim engine
// ===========================================================================
// current-session profile state
var array<float> va_barHigh = array.new_float()
var array<float> va_barLow = array.new_float()
var array<float> va_barUp = array.new_float()
var array<float> va_barDn = array.new_float()
var array<float> va_pVol = array.new_float()
var array<float> va_prevVol = array.new_float()
var float va_low = na
var float va_high = na
var float va_bin = na
var float va_prevLow = na
var float va_prevBin = na
var bool va_hasPrev = false
var int va_activeAnchorTs = na
var int va_anchorBI = na
var int va_prevAnchorBI = na
// current state machine
var bool va_bearBO = false
var bool va_bullBO = false
var bool va_bearSlow = false
var bool va_bullSlow = false
var float va_lastBearVol = na
var float va_lastBullVol = na
var int va_bearBars = 0
var int va_bullBars = 0
// previous-session state machine
var bool va_pBearBO = false
var bool va_pBullBO = false
var bool va_pBearSlow = false
var bool va_pBullSlow = false
var float va_pLastBearVol = na
var float va_pLastBullVol = na
var int va_pBearBars = 0
var int va_pBullBars = 0
// anchor detection (Fabio 18:00 ET style)
int va_ly = year(time, "America/New_York")
int va_lm = month(time, "America/New_York")
int va_ld = dayofmonth(time, "America/New_York")
int va_todayTs = timestamp("America/New_York", va_ly, va_lm, va_ld, vars_anchorHour, 0)
int va_anchorTs = time >= va_todayTs ? va_todayTs : timestamp("America/New_York", va_ly, va_lm, va_ld - 1, vars_anchorHour, 0)
bool va_newAnchor = na(va_activeAnchorTs) or va_anchorTs != va_activeAnchorTs
if va_newAnchor
if not na(va_activeAnchorTs) and not na(va_low) and array.size(va_pVol) > 0
va_prevVol := array.copy(va_pVol)
va_prevAnchorBI := va_anchorBI
va_prevLow := va_low
va_prevBin := va_bin
va_hasPrev := true
va_activeAnchorTs := va_anchorTs
va_anchorBI := bar_index
array.clear(va_barHigh)
array.clear(va_barLow)
array.clear(va_barUp)
array.clear(va_barDn)
va_bearBO := false
va_bullBO := false
va_bearSlow := false
va_bullSlow := false
va_lastBearVol := na
va_lastBullVol := na
va_bearBars := 0
va_bullBars := 0
va_pBearBO := false
va_pBullBO := false
va_pBearSlow := false
va_pBullSlow := false
va_pLastBearVol := na
va_pLastBullVol := na
va_pBearBars := 0
va_pBullBars := 0
va_low := na
va_high := na
va_bin := na
va_reset(va_pVol, vars_rows)
float va_vol = nz(volume, 0.0)
float va_up = close >= open ? va_vol : 0.0
float va_dn = close < open ? va_vol : 0.0
array.push(va_barHigh, high)
array.push(va_barLow, low)
array.push(va_barUp, va_up)
array.push(va_barDn, va_dn)
bool va_rangeCh = na(va_low) or low < va_low or high > va_high
if va_rangeCh
va_low := na(va_low) ? low : math.min(va_low, low)
va_high := na(va_high) ? high : math.max(va_high, high)
va_bin := math.max((va_high - va_low) / vars_rows, syminfo.mintick)
va_reset(va_pVol, vars_rows)
int sb = array.size(va_barHigh)
for si = 0 to sb - 1
va_add(va_pVol, array.get(va_barLow, si), array.get(va_barHigh, si), array.get(va_barUp, si) + array.get(va_barDn, si), va_low, va_bin, vars_rows)
else
va_add(va_pVol, low, high, va_vol, va_low, va_bin, vars_rows)
[va_vah, va_val] = va_levels(va_pVol, va_low, va_bin, vars_va)
[va_pvah, va_pval] = va_levels(va_prevVol, va_prevLow, va_prevBin, vars_va)
bool va_bullEng = close > open and close[1] < open[1] and open <= close[1] and close >= open[1]
bool va_bearEng = close < open and close[1] > open[1] and open >= close[1] and close <= open[1]
// current-session reclaim state machine
bool va_bullReclaim = false
bool va_bearReclaim = false
bool va_hasVA = not na(va_val) and not na(va_vah)
if va_hasVA
if close < va_val
va_bullBO := false
va_bullBars := 0
va_bullSlow := false
va_lastBullVol := na
if not va_bearBO
va_bearSlow := false
va_lastBearVol := na
va_bearBars := 1
else
va_bearBars += 1
va_bearBO := true
if va_dn > 0
if not na(va_lastBearVol) and va_dn < va_lastBearVol
va_bearSlow := true
va_lastBearVol := va_dn
else if close > va_vah
va_bearBO := false
va_bearBars := 0
va_bearSlow := false
va_lastBearVol := na
if not va_bullBO
va_bullSlow := false
va_lastBullVol := na
va_bullBars := 1
else
va_bullBars += 1
va_bullBO := true
if va_up > 0
if not na(va_lastBullVol) and va_up < va_lastBullVol
va_bullSlow := true
va_lastBullVol := va_up
else
va_bullReclaim := va_bullEng and va_bearBO and va_bearBars <= vars_maxbars and va_bearSlow and va_up > va_dn and va_up > nz(va_lastBearVol, 0.0)
va_bearReclaim := va_bearEng and va_bullBO and va_bullBars <= vars_maxbars and va_bullSlow and va_dn > va_up and va_dn > nz(va_lastBullVol, 0.0)
va_bearBO := false
va_bullBO := false
va_bearBars := 0
va_bullBars := 0
va_bearSlow := false
va_bullSlow := false
va_lastBearVol := na
va_lastBullVol := na
// previous-session reclaim state machine
bool va_pBullReclaim = false
bool va_pBearReclaim = false
bool va_hasPVA = va_hasPrev and not na(va_pval) and not na(va_pvah)
if va_hasPVA
if close < va_pval
va_pBullBO := false
va_pBullBars := 0
va_pBullSlow := false
va_pLastBullVol := na
if not va_pBearBO
va_pBearSlow := false
va_pLastBearVol := na
va_pBearBars := 1
else
va_pBearBars += 1
va_pBearBO := true
if va_dn > 0
if not na(va_pLastBearVol) and va_dn < va_pLastBearVol
va_pBearSlow := true
va_pLastBearVol := va_dn
else if close > va_pvah
va_pBearBO := false
va_pBearBars := 0
va_pBearSlow := false
va_pLastBearVol := na
if not va_pBullBO
va_pBullSlow := false
va_pLastBullVol := na
va_pBullBars := 1
else
va_pBullBars += 1
va_pBullBO := true
if va_up > 0
if not na(va_pLastBullVol) and va_up < va_pLastBullVol
va_pBullSlow := true
va_pLastBullVol := va_up
else
va_pBullReclaim := va_bullEng and va_pBearBO and va_pBearBars <= vars_maxbars and va_pBearSlow and va_up > va_dn and va_up > nz(va_pLastBearVol, 0.0)
va_pBearReclaim := va_bearEng and va_pBullBO and va_pBullBars <= vars_maxbars and va_pBullSlow and va_dn > va_up and va_dn > nz(va_pLastBullVol, 0.0)
va_pBearBO := false
va_pBullBO := false
va_pBearBars := 0
va_pBullBars := 0
va_pBearSlow := false
va_pBullSlow := false
va_pLastBearVol := na
va_pLastBullVol := na
// signals
plotshape(vars_on and vars_prev and va_pBullReclaim, "PVA reclaim ↑", shape.triangleup, location.belowbar, vars_cbull, text="PVA", textcolor=color.white, size=size.small)
plotshape(vars_on and vars_prev and va_pBearReclaim, "PVA reclaim ↓", shape.triangledown, location.abovebar, vars_cbear, text="PVA", textcolor=color.white, size=size.small)
plotshape(vars_on and vars_curr and va_bullReclaim, "CVA reclaim ↑", shape.triangleup, location.belowbar, vars_cbull, text="CVA", textcolor=color.white, size=size.small)
plotshape(vars_on and vars_curr and va_bearReclaim, "CVA reclaim ↓", shape.triangledown, location.abovebar, vars_cbear, text="CVA", textcolor=color.white, size=size.small)
bool va_sigBull = (vars_prev and va_pBullReclaim) or (vars_curr and va_bullReclaim)
bool va_sigBear = (vars_prev and va_pBearReclaim) or (vars_curr and va_bearReclaim)
barcolor(vars_on and vars_colorcandles ? (va_sigBull ? vars_cbull : va_sigBear ? vars_cbear : na) : na, title="VARS reclaim candles")
// previous-session VA lines
var line va_lnPVAH = na
var line va_lnPVAL = na
if barstate.isfirst
va_lnPVAH := line.new(bar_index, na, bar_index, na, xloc=xloc.bar_index, extend=extend.right, style=line.style_dashed, width=1)
va_lnPVAL := line.new(bar_index, na, bar_index, na, xloc=xloc.bar_index, extend=extend.right, style=line.style_dashed, width=1)
if barstate.islast
bool va_showP = vars_on and vars_showprevva and va_hasPrev and not na(va_pvah) and not na(va_pval)
int va_px1 = na(va_prevAnchorBI) ? bar_index : va_prevAnchorBI
if va_showP
line.set_xy1(va_lnPVAH, va_px1, va_pvah)
line.set_xy2(va_lnPVAH, bar_index, va_pvah)
line.set_color(va_lnPVAH, color.new(vars_cbull, 25))
line.set_xy1(va_lnPVAL, va_px1, va_pval)
line.set_xy2(va_lnPVAL, bar_index, va_pval)
line.set_color(va_lnPVAL, color.new(vars_cbear, 25))
else
line.set_xy1(va_lnPVAH, bar_index, na)
line.set_xy2(va_lnPVAH, bar_index, na)
line.set_xy1(va_lnPVAL, bar_index, na)
line.set_xy2(va_lnPVAL, bar_index, na)
// ===========================================================================
// 13c. CVD DIVERGENCE — CLV×vol proxy + fractal regular divergence (on price)
// ===========================================================================
float cvd_rng = high - low
float cvd_clv = cvd_rng > 0 ? ((close - low) - (high - close)) / cvd_rng : 0.0
float cvd_dbar = nz(volume, 0.0) * cvd_clv
float cvdHist = cvd_mode == "Periodic" ? math.sum(cvd_dbar, cvd_period) : ta.ema(cvd_dbar, cvd_period)
float cvdEMA = ta.ema(close, 50)
bool cvd_up = close[cvd_n] > cvdEMA
bool cvd_dn = close[cvd_n] < cvdEMA
float cvd_upPiv = ta.pivothigh(cvd_n, cvd_n)
float cvd_dnPiv = ta.pivotlow(cvd_n, cvd_n)
bool cvd_upFrac = not na(cvd_upPiv) and cvd_up
bool cvd_dnFrac = not na(cvd_dnPiv) and cvd_dn
// bearish regular divergence (price HH + CVD LH), on up-fractal bars
float cvd_hiLastP = ta.valuewhen(cvd_upFrac, high[cvd_n], 0)
float cvd_hiPerP = ta.valuewhen(cvd_upFrac, high[cvd_n], 1)
float cvd_hiLastH = ta.valuewhen(cvd_upFrac, cvdHist[cvd_n], 0)
float cvd_hiPerH = ta.valuewhen(cvd_upFrac, cvdHist[cvd_n], 1)
int cvd_hiLastB = int(ta.valuewhen(cvd_upFrac, bar_index[cvd_n], 0))
int cvd_hiPerB = int(ta.valuewhen(cvd_upFrac, bar_index[cvd_n], 1))
bool cvd_bearDiv = cvd_on and cvd_upFrac and cvd_hiLastH > 0 and cvd_hiPerH > 0 and (cvd_hiLastB - cvd_hiPerB) < cvd_maxlen and cvd_hiLastP > cvd_hiPerP and cvd_hiLastH < cvd_hiPerH
// bullish regular divergence (price LL + CVD HL), on down-fractal bars
float cvd_loLastP = ta.valuewhen(cvd_dnFrac, low[cvd_n], 0)
float cvd_loPerP = ta.valuewhen(cvd_dnFrac, low[cvd_n], 1)
float cvd_loLastH = ta.valuewhen(cvd_dnFrac, cvdHist[cvd_n], 0)
float cvd_loPerH = ta.valuewhen(cvd_dnFrac, cvdHist[cvd_n], 1)
int cvd_loLastB = int(ta.valuewhen(cvd_dnFrac, bar_index[cvd_n], 0))
int cvd_loPerB = int(ta.valuewhen(cvd_dnFrac, bar_index[cvd_n], 1))
bool cvd_bullDiv = cvd_on and cvd_dnFrac and cvd_loLastH < 0 and cvd_loPerH < 0 and (cvd_loLastB - cvd_loPerB) < cvd_maxlen and cvd_loLastP < cvd_loPerP and cvd_loLastH > cvd_loPerH
// bounded round-robin pool (protects the persistent AVR/VP objects from culling)
var array<line> cvdLn = array.new<line>()
var array<label> cvdLb = array.new<label>()
var int cvdIdx = 0
if barstate.isfirst
for i = 0 to cvd_maxdraw - 1
array.push(cvdLn, line.new(bar_index, na, bar_index, na, xloc=xloc.bar_index, width=2))
array.push(cvdLb, label.new(bar_index, na, "", xloc=xloc.bar_index, size=size.tiny, textcolor=color.white))
if cvd_bearDiv or cvd_bullDiv
line cvd_ln = array.get(cvdLn, cvdIdx)
label cvd_lb = array.get(cvdLb, cvdIdx)
if cvd_bearDiv
line.set_xy1(cvd_ln, cvd_hiPerB, cvd_hiPerP)
line.set_xy2(cvd_ln, cvd_hiLastB, cvd_hiLastP)
line.set_color(cvd_ln, cvd_cbear)
label.set_xy(cvd_lb, cvd_hiLastB, cvd_hiLastP)
label.set_text(cvd_lb, cvd_showlabels ? "-RD" : "")
label.set_style(cvd_lb, label.style_label_down)
label.set_color(cvd_lb, cvd_cbear)
else
line.set_xy1(cvd_ln, cvd_loPerB, cvd_loPerP)
line.set_xy2(cvd_ln, cvd_loLastB, cvd_loLastP)
line.set_color(cvd_ln, cvd_cbull)
label.set_xy(cvd_lb, cvd_loLastB, cvd_loLastP)
label.set_text(cvd_lb, cvd_showlabels ? "+RD" : "")
label.set_style(cvd_lb, label.style_label_up)
label.set_color(cvd_lb, cvd_cbull)
cvdIdx := (cvdIdx + 1) % cvd_maxdraw
// ===========================================================================
// 13d. ANCHORED VWAP (AVWAP) — developing polyline curves (dB from §13)
// ===========================================================================
float avw_price = avw_src
float avw_v = nz(volume, 0.0)
// --- Session AVWAP (anchored at day open); prev-day = last completed session ---
var float sess_pv = 0.0
var float sess_v = 0.0
var array<chart.point> sess_pts = array.new<chart.point>()
var array<chart.point> prev_pts = array.new<chart.point>()
if dB
if array.size(sess_pts) > 0
prev_pts := array.copy(sess_pts)
sess_pv := 0.0
sess_v := 0.0
array.clear(sess_pts)
sess_pv += avw_price * avw_v
sess_v += avw_v
float sess_vwap = sess_v > 0 ? sess_pv / sess_v : na
if not na(sess_vwap)
array.push(sess_pts, chart.point.from_index(bar_index, sess_vwap))
// --- Swing-High AVWAP (re-anchor at each confirmed pivot high) ---
float avw_pivH = ta.pivothigh(avw_piv, avw_piv)
var float swh_pv = 0.0
var float swh_v = 0.0
var array<chart.point> swh_pts = array.new<chart.point>()
if not na(avw_pivH)
swh_pv := 0.0
swh_v := 0.0
array.clear(swh_pts)
for i = avw_piv to 0
float vv = nz(volume[i], 0.0)
swh_pv += avw_price[i] * vv
swh_v += vv
float vw = swh_v > 0 ? swh_pv / swh_v : na
if not na(vw)
array.push(swh_pts, chart.point.from_index(bar_index - i, vw))
else
swh_pv += avw_price * avw_v
swh_v += avw_v
float vw = swh_v > 0 ? swh_pv / swh_v : na
if not na(vw)
array.push(swh_pts, chart.point.from_index(bar_index, vw))
float swh_vwap = swh_v > 0 ? swh_pv / swh_v : na
// --- Swing-Low AVWAP (re-anchor at each confirmed pivot low) ---
float avw_pivL = ta.pivotlow(avw_piv, avw_piv)
var float swl_pv = 0.0
var float swl_v = 0.0
var array<chart.point> swl_pts = array.new<chart.point>()
if not na(avw_pivL)
swl_pv := 0.0
swl_v := 0.0
array.clear(swl_pts)
for i = avw_piv to 0
float vv = nz(volume[i], 0.0)
swl_pv += avw_price[i] * vv
swl_v += vv
float vw = swl_v > 0 ? swl_pv / swl_v : na
if not na(vw)
array.push(swl_pts, chart.point.from_index(bar_index - i, vw))
else
swl_pv += avw_price * avw_v
swl_v += avw_v
float vw = swl_v > 0 ? swl_pv / swl_v : na
if not na(vw)
array.push(swl_pts, chart.point.from_index(bar_index, vw))
float swl_vwap = swl_v > 0 ? swl_pv / swl_v : na
// --- draw polylines + labels ---
var polyline pl_sess = na
var polyline pl_prev = na
var polyline pl_swh = na
var polyline pl_swl = na
var label avw_lb_sess = na
var label avw_lb_swh = na
var label avw_lb_swl = na
if barstate.isfirst
avw_lb_sess := label.new(bar_index, na, "", style=label.style_none, textcolor=avw_c_sess, size=size.tiny)
avw_lb_swh := label.new(bar_index, na, "", style=label.style_none, textcolor=avw_c_swh, size=size.tiny)
avw_lb_swl := label.new(bar_index, na, "", style=label.style_none, textcolor=avw_c_swl, size=size.tiny)
if barstate.islast
if not na(pl_sess)
polyline.delete(pl_sess)
pl_sess := na
if avw_on and avw_sess and array.size(sess_pts) > 1
pl_sess := polyline.new(array.copy(sess_pts), xloc=xloc.bar_index, line_color=avw_c_sess, line_width=avw_w)
if not na(pl_prev)
polyline.delete(pl_prev)
pl_prev := na
if avw_on and avw_prev and array.size(prev_pts) > 1
pl_prev := polyline.new(array.copy(prev_pts), xloc=xloc.bar_index, line_color=avw_c_prev, line_width=avw_w)
if not na(pl_swh)
polyline.delete(pl_swh)
pl_swh := na
if avw_on and avw_swh and array.size(swh_pts) > 1
pl_swh := polyline.new(array.copy(swh_pts), xloc=xloc.bar_index, line_color=avw_c_swh, line_width=avw_w)
if not na(pl_swl)
polyline.delete(pl_swl)
pl_swl := na
if avw_on and avw_swl and array.size(swl_pts) > 1
pl_swl := polyline.new(array.copy(swl_pts), xloc=xloc.bar_index, line_color=avw_c_swl, line_width=avw_w)
uL(avw_lb_sess, bar_index + 3, sess_vwap, "D-VWAP", avw_on and avw_sess and avw_lbl)
uL(avw_lb_swh, bar_index + 3, swh_vwap, "SwH-VWAP", avw_on and avw_swh and avw_lbl)
uL(avw_lb_swl, bar_index + 3, swl_vwap, "SwL-VWAP", avw_on and avw_swl and avw_lbl)
// ===========================================================================
// 13e. P/B/D ZONE DETECTOR — consolidation range → 3-touch validate → breakout
// ===========================================================================
float pd_atr = ta.atr(pd_atrlen)
float pd_hh = ta.highest(high, pd_win)
float pd_ll = ta.lowest(low, pd_win)
float pd_path = math.sum(ta.tr(true), pd_win)
float pd_ratio = pd_path > 0 ? (pd_hh - pd_ll) / pd_path : 1.0
bool pd_consol = pd_atr > 0 and pd_ratio < pd_er
var int pd_state = 0
var float pd_top = na
var float pd_bot = na
var int pd_start = na
var int pd_tt = 0
var int pd_tb = 0
var bool pd_wasTop = false
var bool pd_wasBot = false
var array<float> pd_zTop = array.new_float()
var array<float> pd_zBot = array.new_float()
var array<box> pd_boxes = array.new<box>()
var array<label> pd_labels = array.new<label>()
var int pd_idx = 0
var box pd_dev = na
var label pd_devlb = na
if barstate.isfirst
for i = 0 to pd_maxz - 1
array.push(pd_boxes, box.new(bar_index, na, bar_index, na, border_width=1))
array.push(pd_labels, label.new(bar_index, na, "", style=label.style_none, size=size.tiny))
pd_dev := box.new(bar_index, na, bar_index, na, border_width=1)
pd_devlb := label.new(bar_index, na, "", style=label.style_none, size=size.tiny)
bool pd_newBOup = false
bool pd_newBOdn = false
if pd_on
if pd_state == 0
if pd_consol
pd_state := 1
pd_top := pd_hh
pd_bot := pd_ll
pd_start := bar_index - pd_win + 1
// seed touch counts by scanning the detection window (debounced)
float tol0 = pd_atr * pd_tol
int tt = 0
int tb = 0
bool wt = false
bool wb = false
for i = pd_win - 1 to 0
bool cT = high[i] >= pd_top - tol0
bool cB = low[i] <= pd_bot + tol0
if cT and not wt
tt += 1
if cB and not wb
tb += 1
wt := cT
wb := cB
pd_tt := tt
pd_tb := tb
pd_wasTop := wt
pd_wasBot := wb
else
float tol = pd_atr * pd_tol
bool cT = high >= pd_top - tol
bool cB = low <= pd_bot + tol
if cT and not pd_wasTop
pd_tt += 1
if cB and not pd_wasBot
pd_tb += 1
pd_wasTop := cT
pd_wasBot := cB
float brk = pd_atr * pd_brk
bool boUp = close > pd_top + brk
bool boDn = close < pd_bot - brk
bool tooOld = (bar_index - pd_start) > pd_maxage
if boUp or boDn or tooOld
bool valid = (pd_tt + pd_tb) >= pd_mintou and pd_tt >= 1 and pd_tb >= 1
if valid
float h = pd_top - pd_bot
float k = pd_atr > 0 ? h / pd_atr : na
bool aligned = false
if pd_align
float atol = pd_atr * pd_atol
int nz = array.size(pd_zTop)
if nz > 0
for zi = 0 to nz - 1
float zt = array.get(pd_zTop, zi)
float zb = array.get(pd_zBot, zi)
if (pd_bot - atol) <= zt and (pd_top + atol) >= zb
aligned := true
array.push(pd_zTop, pd_top)
array.push(pd_zBot, pd_bot)
if array.size(pd_zTop) > pd_maxz
array.shift(pd_zTop)
array.shift(pd_zBot)
box zbx = array.get(pd_boxes, pd_idx)
label zlb = array.get(pd_labels, pd_idx)
color fc = aligned ? pd_c_al : (k <= pd_smallK ? pd_c_sm : pd_c_bg)
string cls = k <= pd_smallK ? "SMALL→BO" : (k >= pd_bigK ? "BIG→RANGE" : "MID")
color tc = aligned ? color.fuchsia : (k <= pd_smallK ? color.aqua : color.orange)
box.set_left(zbx, pd_start)
box.set_right(zbx, bar_index)
box.set_top(zbx, pd_top)
box.set_bottom(zbx, pd_bot)
box.set_bgcolor(zbx, fc)
box.set_border_color(zbx, pd_c_bd)
label.set_xy(zlb, bar_index, pd_top)
label.set_text(zlb, (aligned ? "★ALIGN " : "") + cls + (boUp ? " ▲" : boDn ? " ▼" : ""))
label.set_style(zlb, label.style_label_down)
label.set_color(zlb, color.new(color.black, 30))
label.set_textcolor(zlb, tc)
pd_idx := (pd_idx + 1) % pd_maxz
pd_newBOup := boUp
pd_newBOdn := boDn
pd_state := 0
pd_top := na
pd_bot := na
// developing (forming) zone on last bar
if barstate.islast
bool showDev = pd_on and pd_showdev and pd_state == 1 and (pd_tt + pd_tb) >= pd_mintou and not na(pd_top)
if showDev
box.set_left(pd_dev, pd_start)
box.set_right(pd_dev, bar_index + 3)
box.set_top(pd_dev, pd_top)
box.set_bottom(pd_dev, pd_bot)
box.set_bgcolor(pd_dev, color.new(color.gray, 90))
box.set_border_color(pd_dev, pd_c_bd)
label.set_xy(pd_devlb, bar_index + 3, pd_bot)
label.set_text(pd_devlb, "forming " + str.tostring(pd_tt + pd_tb) + "t")
label.set_style(pd_devlb, label.style_label_up)
label.set_color(pd_devlb, color.new(color.black, 40))
label.set_textcolor(pd_devlb, color.gray)
else
box.set_top(pd_dev, na)
box.set_bottom(pd_dev, na)
label.set_xy(pd_devlb, bar_index, na)
// ===========================================================================
// 14. ALERTS
// ===========================================================================
alertcondition(show and ta.cross(close, u_a), "Reached AVR Up", "Gold: reached AVR Up (avg expansion / exhaustion watch)")
alertcondition(show and ta.cross(close, d_a), "Reached AVR Down", "Gold: reached AVR Down (avg expansion / exhaustion watch)")
alertcondition(show and ta.cross(close, u_mx), "Reached MAX Up", "Gold: reached MAX Up (statistical outlier)")
alertcondition(show and ta.cross(close, d_mx), "Reached MAX Down", "Gold: reached MAX Down (statistical outlier)")
alertcondition(dr1>0 and dr1[1]<0, "Ch1 flip UP", "Chandelier 1 flipped long")
alertcondition(dr1<0 and dr1[1]>0, "Ch1 flip DOWN", "Chandelier 1 flipped short")
alertcondition(vars_prev and va_pBullReclaim, "VARS prev-VA bullish reclaim", "Gold VARS: bullish reclaim of previous-session value area")
alertcondition(vars_prev and va_pBearReclaim, "VARS prev-VA bearish reclaim", "Gold VARS: bearish reclaim of previous-session value area")
alertcondition(vars_curr and va_bullReclaim, "VARS curr-VA bullish reclaim", "Gold VARS: bullish reclaim of current-session value area")
alertcondition(vars_curr and va_bearReclaim, "VARS curr-VA bearish reclaim", "Gold VARS: bearish reclaim of current-session value area")
alertcondition(cvd_bullDiv, "CVD bullish divergence (+RD)", "Gold CVD: bullish regular divergence (price LL / CVD HL)")
alertcondition(cvd_bearDiv, "CVD bearish divergence (-RD)", "Gold CVD: bearish regular divergence (price HH / CVD LH)")
alertcondition(pd_newBOup, "P/B/D breakout UP", "Gold P/B/D: validated (3-touch) zone broke UP")
alertcondition(pd_newBOdn, "P/B/D breakout DOWN", "Gold P/B/D: validated (3-touch) zone broke DOWN")