XAULearn

QC MomentumConfluence v12 · ซอร์สโค้ด

Pine Script ต้นฉบับ

ไฟล์นี้ถูกคัดลอกจากต้นฉบับใน G:\Trader\PineSCript\ ตอนสร้างเว็บ จึงเป็นโค้ดชุดเดียวกับที่ทำงานอยู่บนชาร์ตจริง ณ วันที่ปรับปรุง

QC_MomentumConfluence_v12.0.pine 1,336 บรรทัด 87 KB ต้นฉบับแก้ไขล่าสุด 11 สิงหาคม 2569 ดาวน์โหลด .pine

Pine Script ยังไม่มีไวยากรณ์เฉพาะสำหรับตัวไฮไลต์ที่ใช้ จึงไฮไลต์ด้วยไวยากรณ์ TypeScript ซึ่งใกล้เคียงกันพอที่จะอ่านง่าย ตัวเลขบรรทัดตรงกับไฟล์จริง ใช้อ้างอิงในบทเรียนได้ เช่น qc-v12.pine:120

สารบัญส่วนของโค้ด (24)
  1. 33 §1 INPUTS
  2. 188 §2 HELPER FUNCTIONS
  3. 276 §3 SESSION + FIX WINDOW
  4. 290 §3b HILBERT TRANSFORM — DOMINANT CYCLE (Ehlers Homodyne Discriminator)
  5. 332 §3c BASE TF DATA — v11 ⑮
  6. 351 §4 MLMI CORE
  7. 416 §5 SMI
  8. 441 §6 MACD
  9. 458 §7 QA VOLUME BUBBLES
  10. 529 §8 ICEBERG + VWAP REJECTION
  11. 545 §9 AMT BUBBLE PATTERN ENGINE
  12. 616 §10 MTF MLMI
  13. 678 §11 DIVERGENCE
  14. 710 §12 DELTA TABLE — DATA
  15. 768 §13 DELTA SIGNAL LOGIC
  16. 778 §13b SMI CONFLUENCE SIGNAL
  17. 793 §13c EHLERS FISHER TRANSFORM
  18. 822 §13d HURST EXPONENT
  19. 836 §13e CVD OSCILLATOR — NORMALIZED (z-score of per-bar wick-weighted delta)
  20. 858 §14 PLOTS
  21. 950 §15 LABELS
  22. 1059 §16 DELTA / VOLUME TABLE (barstate.islast — rebuilds every tick)
  23. 1218 §17 ALERTS
  24. 1242 ⑱ MOMENTUM STATE — Expansion / Fade + Fast-RSI [v12] DISPLAY-ONLY
//@version=6
// ╔══════════════════════════════════════════════════════════════════════════════╗
// ║  QC_MomentumConfluence v11.0                                                ║
// ║  Pine Script v6 | CE:0 RE:0 CW:0 | Standalone Pane                         ║
// ║  © Zeiierman (MLMI Core) | © QuantAlgo (QA Engine) | QC Integration        ║
// ║  v5→v10 Upgrades:                                                            ║
// ║   ⑪ Ehlers Fisher Transform — Gaussian OB/OS at ±1.618φ                    ║
// ║   ⑫ Hurst Exponent Regime Meta — Trend/MR/Random regime background          ║
// ║   ⑬ Adaptive SMI (Hilbert DC) — K period adapts to dominant cycle           ║
// ║   ⑭ CVD Oscillator Normalized — Delta z-score + MLMI divergence            ║
// ║  ── v11 FIXES & ENHANCEMENTS ──────────────────────────────────────────── ║
// ║   ⑮ Base TF selector — MLMI kNN now uses user-selectable TF (M5/15/H1/H4)  ║
// ║   ⑯ Fisher OB/OS Level Lines — now actually plotted (were invisible in v10) ║
// ║   ⑰ Dynamic chart-TF labels — "M5" hardcode replaced with actual TF name   ║
// ║  ── v12 (3-primitives momentum layer) ─────────────────────────────────── ║
// ║   ⑱ MOMENTUM STATE — Expansion/Fade + Fast-RSI (DISPLAY-ONLY, 0 budget):    ║
// ║      EXPANDING = SMI accelerating (breakout/trend); FADING = SMI rolling    ║
// ║      over ("exit when momentum disappears"); Fast-RSI(2) OB/OS = MR timing. ║
// ║      Corner table + pane markers (fade▼▲ / R-OB / R-OS). Label+table only   ║
// ║      → no plots/fills/alerts added (QC hit RE10140 before). No signal/gate. ║
// ╚══════════════════════════════════════════════════════════════════════════════╝
indicator(
     title     = "QC_MomentumConfluence v12.0 | MLMI+SMI+MACD+QA+AMT+MTF+DIV+Delta+Fisher+Hurst+AdaptSMI+CVD+MomState",
     shorttitle= "QC_MoConf v12.0",
     overlay   = false,
     precision = 2,
     max_labels_count = 500,
     max_bars_back    = 2000)

import TradingView/ta/12 as taLib

// ═══════════════════════════════════════════════════════════════════════════════
// §1  INPUTS
// ═══════════════════════════════════════════════════════════════════════════════
var string G_MLMI = "① MLMI"
numNeighbors   = input.int(200,  "Prediction Data (k)",  group=G_MLMI, minval=10, maxval=2000)
momentumWindow = input.int(20,   "Trend Length",         group=G_MLMI, step=2, minval=10, maxval=200)
i_mlmi_cap     = input.int(2500, "Max Stored Trades (perf cap)", group=G_MLMI, minval=300, maxval=10000, tooltip="RE-FIX: bounds the kNN trade memory so each instance (M5+H1+H4) cannot grow unbounded.\nKeeps the most-recent N crossover trades. Must be ≥ k. 2500 ≫ default k=200, so prediction is effectively unchanged while memory/exec-time stay bounded.")
// v11 ⑮ — Base TF selector: MLMI kNN crossover detection + price reference uses this TF
i_base_tf      = input.timeframe("", "Base TF (blank=chart TF)", group=G_MLMI, tooltip="Leave blank to use the current chart timeframe (default).\nExamples: '5'=M5  '15'=M15  '60'=H1  '240'=H4\nOnly affects MLMI kNN crossovers and price reference.\nSMI, MACD, Fisher, Hurst, CVD continue to use chart close.")
col_mlmi_pred  = input.color(color.new(#426eff,0),       "Prediction Line", group=G_MLMI)
col_mlmi_wma   = input.color(color.new(#31ffc8,0),       "WMA Line",        group=G_MLMI)
col_mlmi_mid   = input.color(color.new(color.gray,50),   "Mid Level",       group=G_MLMI)

var string G_SMI = "② SMI"
lengthK    = input.int(10, "%K Length",  group=G_SMI, minval=1)
lengthD    = input.int(3,  "%D Length",  group=G_SMI, minval=1)
lengthEMA  = input.int(3,  "EMA Length", group=G_SMI, minval=1)
col_smi_line = input.color(color.new(color.blue,0),     "SMI Line",    group=G_SMI)
col_smi_ema  = input.color(color.new(color.orange,0),   "SMI EMA",     group=G_SMI)
col_smi_band = input.color(color.new(color.gray,50),    "OB/OS Lines", group=G_SMI)

var string G_MACD = "③ MACD"
i_show_macd  = input.bool(true, "Show MACD",  group=G_MACD)
fastLength   = input.int(12,    "Fast",        group=G_MACD)
slowLength   = input.int(26,    "Slow",        group=G_MACD)
signalLength = input.int(9,     "Signal",      group=G_MACD)
col_macd       = input.color(color.new(#2962FF,0),  "MACD Line",   group=G_MACD)
col_sig_macd   = input.color(color.new(#FF6D00,0),  "Signal Line", group=G_MACD)
col_grow_above = input.color(color.new(#26A69A,50), "Hist GA",     group=G_MACD)
col_fall_above = input.color(color.new(#B2DFDB,50), "Hist FA",     group=G_MACD)
col_grow_below = input.color(color.new(#FFCDD2,50), "Hist GB",     group=G_MACD)
col_fall_below = input.color(color.new(#EF5350,50), "Hist FB",     group=G_MACD)

var string G_QA = "④ QA Volume Bubbles"
qa_detect    = input.string("Volume OR Delta","Detection",      group=G_QA, options=["Volume Only","Delta Only","Volume + Delta","Volume OR Delta"])
qa_classify  = input.string("Both",           "Classification", group=G_QA, options=["Candle Direction","Delta Direction","Both"])
qa_small_pct = input.float(75.0, "Small %ile",  group=G_QA, step=1.0)
qa_med_pct   = input.float(90.0, "Medium %ile", group=G_QA, step=1.0)
qa_big_pct   = input.float(97.0, "Big %ile",    group=G_QA, step=1.0)
qa_consensus = input.string("Majority (2 of 3)","Consensus",   group=G_QA, options=["Any Window","Majority (2 of 3)","All Windows (strictest)"])
qa_short_len = input.int(20,  "Short Window",   group=G_QA)
qa_mid_len   = input.int(50,  "Medium Window",  group=G_QA)
qa_long_len  = input.int(100, "Long Window",    group=G_QA)
qa_show_sm   = input.bool(true,  "Show Small",  group=G_QA)
qa_show_md   = input.bool(true,  "Show Medium", group=G_QA)
qa_show_bg   = input.bool(true,  "Show Big",    group=G_QA)
qa_show_lbl  = input.bool(true,  "Show Numbers",group=G_QA)
qa_c_buy     = input.color(color.new(#00ffaa,40),"Buy Color",  group=G_QA)
qa_c_sell    = input.color(color.new(#ff0000,20),"Sell Color", group=G_QA)
qa_c_mix     = input.color(color.new(#ffd600,30),"Mixed",      group=G_QA)

var string G_AMT = "⑤ AMT Bubbles"
i_show_bub = input.bool(true,  "Show AMT Bubbles",       group=G_AMT)
i_bub_lo   = input.float(2.5,  "Thr ASIA (xSMA50)",      group=G_AMT, step=0.1)
i_bub_hi   = input.float(3.5,  "Thr LON/NY (xSMA50)",    group=G_AMT, step=0.1)
i_bub_abs  = input.bool(true,  "Show Absorption",        group=G_AMT)
i_bub_acc  = input.bool(true,  "Show Acceptance",        group=G_AMT)
i_bub_cnt  = input.bool(true,  "Show Contrarian",        group=G_AMT)
i_bub_rej  = input.bool(true,  "Show Rejection",         group=G_AMT)
i_hl_rej   = input.bool(true,  "Highlight Rejection BG", group=G_AMT)
i_wick_rej = input.float(0.40, "Rejection Wick %",       group=G_AMT, step=0.05)
i_wick_abs = input.float(0.15, "Absorption Wick %",      group=G_AMT, step=0.05)
i_vol_dom  = input.float(1.50, "Volume Dom Ratio",       group=G_AMT, step=0.10)
i_show_ice = input.bool(true,  "Show Iceberg/VWAP Rej",  group=G_AMT)

var string G_MTF = "⑥ MTF OB/OS"
i_mtf_en     = input.bool(true,  "Enable MTF",                group=G_MTF)
i_mtf_tf1    = input.timeframe("60",  "Higher TF 1 (H1)",    group=G_MTF)
i_mtf_tf2    = input.timeframe("240", "Higher TF 2 (H4)",    group=G_MTF)
i_mtf_k      = input.int(50, "MTF kNN k",                    group=G_MTF, minval=5, maxval=200)
i_mtf_mw     = input.int(20, "MTF Momentum Window",           group=G_MTF, minval=5)
i_mtf_lbl    = input.bool(true, "Show Labels",                group=G_MTF)
i_mtf_offset = input.float(0.15,"Label Offset (%)",           group=G_MTF, step=0.05, minval=0.0)
col_mtf_c0   = input.color(color.new(#FF9800,95),"Single OB", group=G_MTF)
col_mtf_c1   = input.color(color.new(#FF6F00,90),"Double OB", group=G_MTF)
col_mtf_c2   = input.color(color.new(#FF1744,82),"Triple OB", group=G_MTF)
col_mtf_c3   = input.color(color.new(#00E676,82),"Triple OS", group=G_MTF)
col_mtf_c4   = input.color(color.new(#00BCD4,90),"Double OS", group=G_MTF)
col_mtf_c5   = input.color(color.new(#4CAF50,95),"Single OS", group=G_MTF)

var string G_DIV = "⑦ Divergence"
i_div_en       = input.bool(true, "Enable Divergence",      group=G_DIV)
i_div_lb       = input.int(5,     "Pivot Lookback",          group=G_DIV, minval=2, maxval=20)
i_div_show_lbl = input.bool(true, "Show Labels",             group=G_DIV)
col_div_bear   = input.color(color.new(#FF1744,0),"Bear Div",group=G_DIV)
col_div_bull   = input.color(color.new(#00E676,0),"Bull Div",group=G_DIV)

var string G_SESS = "⑧ Session + Fix"
i_show_sess  = input.bool(true, "Show Session BG",        group=G_SESS)
i_show_fix   = input.bool(true, "Show Fix Window",        group=G_SESS)
i_utc        = input.int(0,     "UTC Offset",             group=G_SESS, minval=-2, maxval=2)
col_sess_asia= input.color(color.new(#607D8B,96),"ASIA",  group=G_SESS)
col_sess_lon = input.color(color.new(#00BCD4,97),"LON",   group=G_SESS)
col_sess_ny  = input.color(color.new(#FF9800,97),"NY",    group=G_SESS)
col_fix_win  = input.color(color.new(#FFD700,88),"Fix",   group=G_SESS)

var string G_DTBL = "⑨ Delta / Volume Table"
i_dtbl_en         = input.bool(true,   "Show Table",                           group=G_DTBL)
i_dtbl_n          = input.int(20,      "Bars to display",                      group=G_DTBL, minval=5, maxval=40)
i_dtbl_pos        = input.string("middle_right","Position",                    group=G_DTBL, options=["middle_right","middle_left","middle_center","bottom_right","bottom_left","bottom_center"])
i_dtbl_sz         = input.string("Tiny","Cell Text Size",                      group=G_DTBL, options=["Tiny","Small"])
i_dtbl_cum_lb     = input.int(100,     "Cum Delta Lookback (bars)",             group=G_DTBL, minval=10, maxval=500)
i_dtbl_thresh     = input.float(1.5,   "Highlight Threshold (x stdev)",        group=G_DTBL, step=0.1)
i_dtbl_sess_reset = input.bool(true,   "Reset CumDelta at session open",       group=G_DTBL, tooltip="Resets at London+NY open. OFF = accumulates from indicator load.")
i_dtbl_show_mx    = input.bool(true,   "Show MaxDelta / MinDelta rows",        group=G_DTBL)
i_dtbl_show_v     = input.bool(true,   "Show Volume row",                      group=G_DTBL)
i_dtbl_show_r     = input.bool(true,   "Show Dlt/Vol% row",                    group=G_DTBL)
col_dtbl_pos      = input.color(color.new(#00E676,0), "Positive Color",        group=G_DTBL)
col_dtbl_neg      = input.color(color.new(#FF1744,0), "Negative Color",        group=G_DTBL)
col_dtbl_neu      = input.color(color.new(#607D8B,0), "Neutral Color",         group=G_DTBL)

var string G_SIG = "⑩ Delta Signal Labels"
i_sig_en     = input.bool(true,  "Enable Signal Labels",                       group=G_SIG)
i_sig_thresh = input.float(5.0,  "Delta Threshold",                            group=G_SIG, step=0.5, minval=0.0, tooltip="BUY: OS_H4 + CumDelta>thr + MaxDlt>thr + MinDlt>-thr\nSELL: OB_H4 + CumDelta<-thr + MinDlt<-thr + MaxDlt<thr")
i_sig_show_b = input.bool(true,  "Show BUY Labels",                            group=G_SIG)
i_sig_show_s = input.bool(true,  "Show SELL Labels",                           group=G_SIG)
col_sig_buy  = input.color(color.new(#00E676,0), "BUY Color",                  group=G_SIG)
col_sig_sell = input.color(color.new(#FF1744,0), "SELL Color",                 group=G_SIG)

var string G_FISH = "⑪ Ehlers Fisher Transform"
i_fish_en      = input.bool(true,  "Enable Fisher Transform",    group=G_FISH, tooltip="Normalizes price to Gaussian distribution. OB/OS at ±1.618φ aligns with GEX Matrix Fibonacci sigma system.")
i_fish_len     = input.int(10,     "Fisher Length",               group=G_FISH, minval=2, maxval=50)
i_fish_ob      = input.float(1.618,"OB Level (φ)",               group=G_FISH, step=0.001)
i_fish_os      = input.float(-1.618,"OS Level (−φ)",             group=G_FISH, step=0.001)
i_fish_show_lvl= input.bool(true,  "Show OB/OS Level Lines",     group=G_FISH)
col_fish_line  = input.color(color.new(#E040FB,0), "Fisher Line", group=G_FISH)
col_fish_trig  = input.color(color.new(#FF9800,0), "Trigger Line",group=G_FISH)
col_fish_ob    = input.color(color.new(#FF1744,40),"OB/OS Color", group=G_FISH)

var string G_HURST = "⑫ Hurst Exponent Regime"
i_hurst_en        = input.bool(true,  "Enable Hurst Regime",          group=G_HURST, tooltip="H>0.6=Trending (trust Breakout). H<0.4=Mean-Reverting (trust MR signals). H≈0.5=Random Walk (reduce size).")
i_hurst_len       = input.int(50,     "Lookback (bars)",               group=G_HURST, minval=20, maxval=200)
i_hurst_show_bg   = input.bool(true,  "Show Regime Background",        group=G_HURST)
i_hurst_show_lbl  = input.bool(true,  "Show Hurst Label",              group=G_HURST)
i_hurst_trend_thr = input.float(0.60, "Trending Threshold (H >)",      group=G_HURST, step=0.05, minval=0.50, maxval=0.85)
i_hurst_mr_thr    = input.float(0.40, "Mean-Rev Threshold (H <)",      group=G_HURST, step=0.05, minval=0.20, maxval=0.50)
col_hurst_trend   = input.color(color.new(#00E676,94), "Trending BG",  group=G_HURST)
col_hurst_mr      = input.color(color.new(#00BCD4,94), "Mean-Rev BG",  group=G_HURST)
col_hurst_rand    = input.color(color.new(#607D8B,97), "Random BG",    group=G_HURST)

var string G_ASMI = "⑬ Adaptive SMI (Hilbert DC)"
i_asmi_en     = input.bool(true,  "Enable Adaptive SMI K Period",  group=G_ASMI, tooltip="Uses Ehlers Homodyne Discriminator to adapt SMI K length to the dominant market cycle.\nFast market (DC~15) → K~7. Slow market (DC~40) → K~20.\nD and EMA lengths stay fixed.")
i_asmi_k_mult = input.float(0.50, "K Period Multiplier (×DC)",     group=G_ASMI, step=0.05, minval=0.20, maxval=1.50, tooltip="Adaptive K = round(DC × this). Default 0.5 = half the dominant cycle.")
i_asmi_d_mult = input.float(0.10, "D Period Multiplier (×DC)",     group=G_ASMI, step=0.01, minval=0.05, maxval=0.30, tooltip="Adaptive D = round(DC × this). Default 0.1 = one-tenth the cycle.")
i_asmi_show_dc= input.bool(true,  "Show DC Period Label",           group=G_ASMI)

var string G_CVD = "⑭ CVD Oscillator (Normalized)"
i_cvd_en       = input.bool(true,  "Enable CVD Oscillator",          group=G_CVD, tooltip="Normalizes per-bar delta as z-score. Shows buying/selling pressure relative to recent history.")
i_cvd_len      = input.int(50,     "Z-Score Lookback (bars)",         group=G_CVD, minval=10, maxval=200)
i_cvd_div_thr  = input.float(0.5,  "Divergence Threshold (|Z|>)",     group=G_CVD, step=0.1, minval=0.2, maxval=2.0, tooltip="CVD/MLMI divergence shown when |CVD z-score| exceeds this AND MLMI direction conflicts.")
i_cvd_show_div = input.bool(true,  "Highlight MLMI/CVD Divergence",   group=G_CVD)
col_cvd_pos    = input.color(color.new(#00E676,50), "Positive CVD",   group=G_CVD)
col_cvd_neg    = input.color(color.new(#FF1744,50), "Negative CVD",   group=G_CVD)
col_cvd_div    = input.color(color.new(#FFD700,0),  "Divergence Color",group=G_CVD)

// ═══════════════════════════════════════════════════════════════════════════════
// §2  HELPER FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════════
emaEma(float src, int len) => ta.ema(ta.ema(src, len), len)

f_qa_fmtn(float v) =>
    float av = math.abs(v)
    string sg = v < 0.0 ? "-" : ""
    string r  = sg + str.tostring(av,"#")
    if av >= 1000000000.0
        r := sg + str.tostring(av/1000000000.0,"#.##") + "B"
    else if av >= 1000000.0
        r := sg + str.tostring(av/1000000.0,"#.##") + "M"
    else if av >= 1000.0
        r := sg + str.tostring(av/1000.0,"#.##") + "K"
    r

f_qa_cons(bool pS, bool pM, bool pL, string mode) =>
    int hits = (pS?1:0)+(pM?1:0)+(pL?1:0)
    switch mode
        "Any Window"              => hits >= 1
        "All Windows (strictest)" => hits >= 3
        =>                           hits >= 2

f_qa_detect(bool vHit, bool dHit, string method) =>
    switch method
        "Volume Only"    => vHit
        "Delta Only"     => dHit
        "Volume + Delta" => vHit and dHit
        =>                  vHit or dHit

f_dtbl_fmt(float v) =>
    float av = math.abs(v)
    string sg = v < 0.0 ? "-" : "+"
    string r  = sg + str.tostring(av,"#")
    if av >= 1000000.0
        r := sg + str.tostring(av/1000000.0,"#.#") + "M"
    else if av >= 1000.0
        r := sg + str.tostring(av/1000.0,"#.#") + "K"
    r

f_dtbl_fmtv(float v) =>
    float av = math.abs(v)
    string r  = str.tostring(av,"#")
    if av >= 1000000.0
        r := str.tostring(av/1000000.0,"#.#") + "M"
    else if av >= 1000.0
        r := str.tostring(av/1000.0,"#.#") + "K"
    r

// Hurst Exponent via R/S (Rescaled Range) Analysis
f_hurst(float src, int len) =>
    float _mean = 0.0
    for _k = 0 to len - 1
        _mean += nz(src[_k])
    _mean /= float(len)
    float _max_dev = 0.0
    float _min_dev = 0.0
    float _cum     = 0.0
    float _sq_sum  = 0.0
    for _i = len - 1 to 0
        float _v = nz(src[_i])
        _cum     += _v - _mean
        _max_dev := math.max(_max_dev, _cum)
        _min_dev := math.min(_min_dev, _cum)
        _sq_sum  += math.pow(_v - _mean, 2.0)
    float _std = math.sqrt(math.max(_sq_sum / float(len), 0.0))
    float _rs  = _std > 0.0 ? (_max_dev - _min_dev) / _std : 1.0
    len > 2 and _rs > 0.0 ? math.log(_rs) / math.log(float(len) / 2.0) : 0.5

// v11 ⑰ — convert timeframe string to readable label
f_tf_name(string tf) =>
    switch tf
        "1"   => "M1"
        "3"   => "M3"
        "5"   => "M5"
        "15"  => "M15"
        "30"  => "M30"
        "45"  => "M45"
        "60"  => "H1"
        "120" => "H2"
        "180" => "H3"
        "240" => "H4"
        "D"   => "D1"
        "W"   => "W1"
        "M"   => "MN"
        =>       tf

// ═══════════════════════════════════════════════════════════════════════════════
// §3  SESSION + FIX WINDOW
// ═══════════════════════════════════════════════════════════════════════════════
int  _hm      = (hour(time,"UTC+0") + i_utc)*100 + minute(time,"UTC+0")
bool _s_asia  = _hm >= 2000 or  _hm < 300
bool _s_lon   = _hm >= 300  and _hm < 800
bool _s_ny    = _hm >= 800  and _hm < 1700
bool _in_am   = _hm >= 1000 and _hm < 1030
bool _in_pm   = _hm >= 1500 and _hm < 1515
bool _in_cme  = _hm >= 1320 and _hm < 1330
bool _in_fix  = _in_am or _in_pm or _in_cme
string _fix_nm = _in_am ? "LBMA AM" : _in_pm ? "LBMA PM" : _in_cme ? "CME NY" : ""
float _bub_thr = _s_asia ? i_bub_lo : i_bub_hi

// ═══════════════════════════════════════════════════════════════════════════════
// §3b  HILBERT TRANSFORM — DOMINANT CYCLE (Ehlers Homodyne Discriminator)
//      Used by §5 Adaptive SMI and §13f CVD Z-score context
// ═══════════════════════════════════════════════════════════════════════════════
var float _ht10_smooth = 0.0
var float _ht10_dtr    = 0.0
var float _ht10_I1     = 0.0
var float _ht10_Q1     = 0.0
var float _ht10_jI     = 0.0
var float _ht10_jQ     = 0.0
var float _ht10_I2     = 0.0
var float _ht10_Q2     = 0.0
var float _ht10_Re     = 0.0
var float _ht10_Im     = 0.0
var float _ht10_per    = 20.0
var float _ht10_dc     = 20.0

float _htc = 0.075 * nz(_ht10_dc, 20.0) + 0.54
_ht10_smooth := (4.0*close + 3.0*nz(close[1]) + 2.0*nz(close[2]) + nz(close[3])) / 10.0
_ht10_dtr    := (0.0962*_ht10_smooth + 0.5769*nz(_ht10_smooth[2]) - 0.5769*nz(_ht10_smooth[4]) - 0.0962*nz(_ht10_smooth[6])) * _htc
_ht10_I1     := nz(_ht10_dtr[3])
_ht10_Q1     := (0.0962*_ht10_dtr + 0.5769*nz(_ht10_dtr[2]) - 0.5769*nz(_ht10_dtr[4]) - 0.0962*nz(_ht10_dtr[6])) * _htc
_ht10_jI     := (0.0962*_ht10_I1 + 0.5769*nz(_ht10_I1[2]) - 0.5769*nz(_ht10_I1[4]) - 0.0962*nz(_ht10_I1[6])) * _htc
_ht10_jQ     := (0.0962*_ht10_Q1 + 0.5769*nz(_ht10_Q1[2]) - 0.5769*nz(_ht10_Q1[4]) - 0.0962*nz(_ht10_Q1[6])) * _htc
_ht10_I2     := 0.2*(_ht10_I1 - _ht10_jQ) + 0.8*nz(_ht10_I2[1])
_ht10_Q2     := 0.2*(_ht10_Q1 + _ht10_jI) + 0.8*nz(_ht10_Q2[1])
_ht10_Re     := 0.2*(_ht10_I2*nz(_ht10_I2[1]) + _ht10_Q2*nz(_ht10_Q2[1])) + 0.8*nz(_ht10_Re[1])
_ht10_Im     := 0.2*(_ht10_I2*nz(_ht10_Q2[1]) - _ht10_Q2*nz(_ht10_I2[1])) + 0.8*nz(_ht10_Im[1])
if _ht10_Im != 0.0 and _ht10_Re != 0.0
    _ht10_per := 2.0 * math.acos(-1.0) / math.atan(_ht10_Im / _ht10_Re)
if _ht10_per > 1.5 * nz(_ht10_per[1], 20.0)
    _ht10_per := 1.5 * nz(_ht10_per[1], 20.0)
if _ht10_per < 0.67 * nz(_ht10_per[1], 20.0)
    _ht10_per := 0.67 * nz(_ht10_per[1], 20.0)
_ht10_per := math.max(6.0, math.min(50.0, _ht10_per))
_ht10_dc  := 0.2 * _ht10_per + 0.8 * nz(_ht10_dc[1])
int _dc10 = math.max(6, math.min(50, math.round(_ht10_dc)))

// Adaptive SMI parameters derived from DC
int smi_k_eff = i_asmi_en ? math.max(3, math.min(50, math.round(float(_dc10) * i_asmi_k_mult))) : lengthK
int smi_d_eff = i_asmi_en ? math.max(2, math.min(10, math.round(float(_dc10) * i_asmi_d_mult))) : lengthD

// ═══════════════════════════════════════════════════════════════════════════════
// §3c  BASE TF DATA — v11 ⑮
//      Fetches MLMI kNN inputs from user-selected base TF via request.security.
//      When i_base_tf="" the effective TF equals timeframe.period (no change).
// ═══════════════════════════════════════════════════════════════════════════════
string _btf = i_base_tf == "" ? timeframe.period : i_base_tf

[_b_close, _b_maq, _b_mas, _b_rq, _b_rs] = request.security(
     syminfo.tickerid, _btf,
     [close,
      ta.wma(close, 5),
      ta.wma(close, 20),
      ta.wma(ta.rsi(close, 5),  momentumWindow),
      ta.wma(ta.rsi(close, 20), momentumWindow)],
     barmerge.gaps_off, barmerge.lookahead_off)

// Dynamic display name for base TF (used in labels, replaces hardcoded "M5")
string _tf0_display = f_tf_name(_btf)

// ═══════════════════════════════════════════════════════════════════════════════
// §4  MLMI CORE
// ═══════════════════════════════════════════════════════════════════════════════
// Uses base TF data (_b_*) — defaults to chart close when i_base_tf=""
float MA_quick  = _b_maq
float MA_slow   = _b_mas
float rsi_quick = _b_rq
float rsi_slow  = _b_rs
bool  pos       = ta.crossover(_b_maq, _b_mas)
bool  neg       = ta.crossunder(_b_maq, _b_mas)

type MLMIData
    array<float> parameter1
    array<float> parameter2
    array<float> priceArray
    array<float> resultArray

var mlmi_d = MLMIData.new(
     array.new_float(1,0.0), array.new_float(1,0.0),
     array.new_float(1,0.0), array.new_float(1,0.0))

method storeTrade(MLMIData d, float p1, float p2, float p_price) =>
    d.parameter1.push(d.parameter1.get(d.parameter1.size()-1))
    d.parameter2.push(d.parameter2.get(d.parameter2.size()-1))
    d.priceArray.push(d.priceArray.get(d.priceArray.size()-1))
    d.resultArray.push(p_price >= d.priceArray.get(d.priceArray.size()-1) ? 1.0 : -1.0)
    d.parameter1.set(d.parameter1.size()-1, p1)
    d.parameter2.set(d.parameter2.size()-1, p2)
    d.priceArray.set(d.priceArray.size()-1, p_price)
    // RE-FIX ①: bound memory — drop oldest trades beyond cap (keeps recent regime)
    int _cap = math.max(i_mlmi_cap, 300)
    if d.parameter1.size() > _cap
        d.parameter1.shift()
        d.parameter2.shift()
        d.priceArray.shift()
        d.resultArray.shift()

method knnPredict(MLMIData d, float p1, float p2, int k) =>
    distances = array.new_float(0)
    int n = d.parameter1.size()-1
    for i = 0 to n
        distances.push(math.sqrt(math.pow(p1-d.parameter1.get(i),2.0)+math.pow(p2-d.parameter2.get(i),2.0)))
    sortedD = distances.copy()
    sortedD.sort()
    float mx = sortedD.slice(0, math.min(k, sortedD.size())).max()
    nb = array.new_float(0)
    for i = 0 to distances.size()-1
        if distances.get(i) <= mx
            nb.push(d.resultArray.get(i))
    nb.sum()

if pos or neg
    mlmi_d.storeTrade(rsi_slow, rsi_quick, _b_close)

float prediction    = mlmi_d.knnPredict(rsi_slow, rsi_quick, numNeighbors)
float prediction_ma = ta.wma(prediction, 20)
float upper         = ta.highest(prediction, 2000)
float lower         = ta.lowest(prediction,  2000)
float upper_        = upper - ta.ema(ta.stdev(prediction,20),20)
float lower_        = lower + ta.ema(ta.stdev(prediction,20),20)
float pane_range    = upper - lower

bool cur_ob = prediction >= upper_
bool cur_os = prediction <= lower_

// ═══════════════════════════════════════════════════════════════════════════════
// §5  SMI
// ═══════════════════════════════════════════════════════════════════════════════
// Adaptive K: loop-based highest/lowest (supports variable length)
// RE-FIX ③: nz fallback → current bar value (neutral for max/min) so missing
//           history on early bars cannot collapse _lL_ad to 0 (price-domain garbage)
float _hH_ad = high
float _lL_ad = low
if i_asmi_en and smi_k_eff > 1
    for _ki = 1 to smi_k_eff - 1
        _hH_ad := math.max(_hH_ad, nz(high[_ki], high))
        _lL_ad := math.min(_lL_ad, nz(low[_ki],  low))
float hH       = i_asmi_en ? _hH_ad : ta.highest(high, lengthK)
float lL       = i_asmi_en ? _lL_ad : ta.lowest(low,   lengthK)
float hLR      = hH - lL
float relRange = close - (hH+lL)/2.0
float _eer     = emaEma(relRange, lengthD)
float _eeh     = emaEma(hLR, lengthD)
float smi_raw  = (hLR > 0.0 and _eeh != 0.0) ? 200.0*(_eer/_eeh) : 0.0
float smi_e_raw= ta.ema(smi_raw, lengthEMA)
float smi_sp   = upper_/40.0
float smi_sn   = lower_/-40.0
float smi_sc   = smi_raw  > 0.0 ? smi_raw  *smi_sp : smi_raw  *smi_sn
float smi_esc  = smi_e_raw> 0.0 ? smi_e_raw*smi_sp : smi_e_raw*smi_sn

// ═══════════════════════════════════════════════════════════════════════════════
// §6  MACD
// ═══════════════════════════════════════════════════════════════════════════════
float mf        = ta.ema(close, fastLength)
float ms        = ta.ema(close, slowLength)
float macd_raw  = mf - ms
float sig_raw   = ta.ema(macd_raw, signalLength)
float hist_raw  = macd_raw - sig_raw
float mx_m      = ta.highest(math.abs(macd_raw),200)
float _mx       = na(mx_m) or mx_m==0.0 ? 1.0 : mx_m
float msp       = upper*0.8
float msn       = math.abs(lower)*0.8
float macd_sc   = macd_raw > 0.0 ? (macd_raw/_mx)*msp : (macd_raw/_mx)*msn
float sig_sc    = sig_raw  > 0.0 ? (sig_raw /_mx)*msp : (sig_raw /_mx)*msn
float hist_sc   = hist_raw > 0.0 ? (hist_raw/_mx)*msp : (hist_raw/_mx)*msn
color hist_col  = hist_sc>=0.0 ? (hist_sc[1]<hist_sc?col_grow_above:col_fall_above) : (hist_sc[1]<hist_sc?col_grow_below:col_fall_below)

// ═══════════════════════════════════════════════════════════════════════════════
// §7  QA VOLUME BUBBLES
// ═══════════════════════════════════════════════════════════════════════════════
var string _qa_ltf = switch
    timeframe.isseconds  => "1S"
    timeframe.isintraday => "1"
    timeframe.isdaily    => "5"
    =>                      "60"

[_, _, _, qa_last_d] = taLib.requestVolumeDelta(_qa_ltf)
bool  qa_dok = not na(qa_last_d)
float qa_nd  = nz(qa_last_d)
float qa_ad  = math.abs(qa_nd)

float vSS=ta.percentile_linear_interpolation(volume,qa_short_len,qa_small_pct)
float vSM=ta.percentile_linear_interpolation(volume,qa_mid_len,  qa_small_pct)
float vSL=ta.percentile_linear_interpolation(volume,qa_long_len, qa_small_pct)
float vMS=ta.percentile_linear_interpolation(volume,qa_short_len,qa_med_pct)
float vMM=ta.percentile_linear_interpolation(volume,qa_mid_len,  qa_med_pct)
float vML=ta.percentile_linear_interpolation(volume,qa_long_len, qa_med_pct)
float vBS=ta.percentile_linear_interpolation(volume,qa_short_len,qa_big_pct)
float vBM=ta.percentile_linear_interpolation(volume,qa_mid_len,  qa_big_pct)
float vBL=ta.percentile_linear_interpolation(volume,qa_long_len, qa_big_pct)
float dSS=ta.percentile_linear_interpolation(qa_ad,qa_short_len,qa_small_pct)
float dSM=ta.percentile_linear_interpolation(qa_ad,qa_mid_len,  qa_small_pct)
float dSL=ta.percentile_linear_interpolation(qa_ad,qa_long_len, qa_small_pct)
float dMS=ta.percentile_linear_interpolation(qa_ad,qa_short_len,qa_med_pct)
float dMM=ta.percentile_linear_interpolation(qa_ad,qa_mid_len,  qa_med_pct)
float dML=ta.percentile_linear_interpolation(qa_ad,qa_long_len, qa_med_pct)
float dBS=ta.percentile_linear_interpolation(qa_ad,qa_short_len,qa_big_pct)
float dBM=ta.percentile_linear_interpolation(qa_ad,qa_mid_len,  qa_big_pct)
float dBL=ta.percentile_linear_interpolation(qa_ad,qa_long_len, qa_big_pct)

bool qa_vbig  = f_qa_cons(volume>=vBS,volume>=vBM,volume>=vBL,qa_consensus)
bool qa_vmed  = not qa_vbig and f_qa_cons(volume>=vMS,volume>=vMM,volume>=vML,qa_consensus)
bool qa_vsm   = not qa_vbig and not qa_vmed and f_qa_cons(volume>=vSS,volume>=vSM,volume>=vSL,qa_consensus)
bool qa_dbig  = f_qa_cons(qa_ad>=dBS,qa_ad>=dBM,qa_ad>=dBL,qa_consensus)
bool qa_dmed  = not qa_dbig and f_qa_cons(qa_ad>=dMS,qa_ad>=dMM,qa_ad>=dML,qa_consensus)
bool qa_dsm   = not qa_dbig and not qa_dmed and f_qa_cons(qa_ad>=dSS,qa_ad>=dSM,qa_ad>=dSL,qa_consensus)
string qa_em  = qa_dok ? qa_detect : "Volume Only"
bool qa_big   = f_qa_detect(qa_vbig, qa_dbig, qa_em)
bool qa_med   = not qa_big and f_qa_detect(qa_vmed or qa_vbig, qa_dmed or qa_dbig, qa_em)
bool qa_small = not qa_big and not qa_med and f_qa_detect(qa_vsm or qa_vmed or qa_vbig, qa_dsm or qa_dmed or qa_dbig, qa_em)

bool qa_is_buy  = switch qa_classify
    "Candle Direction" => close >= open
    "Delta Direction"  => qa_dok ? qa_nd > 0.0 : close >= open
    =>                    qa_dok ? (close >= open and qa_nd > 0.0) : close >= open
bool qa_is_sell = switch qa_classify
    "Candle Direction" => close < open
    "Delta Direction"  => qa_dok ? qa_nd < 0.0 : close < open
    =>                    qa_dok ? (close < open and qa_nd < 0.0) : close < open

bool qa_vs = qa_small and qa_show_sm
bool qa_vm = qa_med   and qa_show_md
bool qa_vb = qa_big   and qa_show_bg
bool qa_av = qa_vs or qa_vm or qa_vb

float qa_y = na
if qa_av
    if qa_is_sell
        qa_y := upper
    else if qa_is_buy
        qa_y := lower
    else
        qa_y := 0.0

color qa_col   = qa_is_buy ? qa_c_buy : qa_is_sell ? qa_c_sell : qa_c_mix
float qa_avol  = ta.sma(volume, qa_mid_len)
float qa_ratio = qa_avol > 0.0 ? volume / qa_avol : 0.0

// ═══════════════════════════════════════════════════════════════════════════════
// §8  ICEBERG + VWAP REJECTION
// ═══════════════════════════════════════════════════════════════════════════════
float _vma50  = ta.sma(volume, 50)
float _mass   = _vma50 > 0.0 ? volume/_vma50 : 1.0
float _mmean  = ta.vwma(close, 50)
float _mdev   = ta.stdev(close, 50)*1.5
float _mvah   = _mmean + _mdev
float _mval   = _mmean - _mdev
float _tolz   = _mdev*0.1
bool _ice_vah = math.abs(close-_mvah)<=_tolz and _mass>1.8 and close<=_mvah
bool _ice_val = math.abs(close-_mval)<=_tolz and _mass>1.8 and close>=_mval
bool _rej_top = high>_mvah and close<_mvah and not _ice_vah
bool _rej_bot = low <_mval and close>_mval and not _ice_val
bool _is_ice  = _ice_vah or _ice_val

// ═══════════════════════════════════════════════════════════════════════════════
// §9  AMT BUBBLE PATTERN ENGINE
// ═══════════════════════════════════════════════════════════════════════════════
float _brng   = math.max(high-low, syminfo.mintick)
float _bwup   = (high-math.max(close,open))/_brng
float _bwdn   = (math.min(close,open)-low)/_brng
float _bbvol  = volume*((close-low)/_brng)
float _bsvol  = volume*((high-close)/_brng)
bool  _bbull  = close >= open
bool  _bact   = i_show_bub and volume > _vma50*_bub_thr

string _apat = ""
float  _ay   = na
color  _acol = na
string _atxt = "●"
string _astl = label.style_label_center
bool   _rej_bull = false
bool   _rej_bear = false

if _bact
    if _bbull
        if i_bub_rej and _bwup >= i_wick_rej
            _apat:="REJECTION SELL"
            _ay:=upper
            _acol:=color.new(#9C27B0,0)
            _atxt:="▼"
            _astl:=label.style_label_down
            _rej_bear:=true
        else if i_bub_cnt and _bsvol>_bbvol*(i_vol_dom*0.8) and _bwup>=i_wick_abs
            _apat:="ABS CONTRARIAN"
            _ay:=0.0
            _acol:=color.new(#009688,0)
            _astl:=label.style_label_center
        else if i_bub_abs and _bbvol>_bsvol*i_vol_dom and _bwdn>=i_wick_abs
            _apat:="ABSORPTION BUY"
            _ay:=lower
            _acol:=color.new(#7B1FA2,20)
            _atxt:="▲"
            _astl:=label.style_label_up
        else if i_bub_acc
            _apat:="ACCEPTANCE BUY"
            _ay:=lower+math.abs(lower)*0.15
            _acol:=color.new(#00BCD4,0)
            _atxt:="▲"
            _astl:=label.style_label_up
    else
        if i_bub_rej and _bwdn >= i_wick_rej
            _apat:="REJECTION BUY"
            _ay:=lower
            _acol:=color.new(#009688,0)
            _atxt:="▲"
            _astl:=label.style_label_up
            _rej_bull:=true
        else if i_bub_cnt and _bbvol>_bsvol*(i_vol_dom*0.8) and _bwdn>=i_wick_abs
            _apat:="ABS CONTRARIAN"
            _ay:=0.0
            _acol:=color.new(#9C27B0,0)
            _astl:=label.style_label_center
        else if i_bub_abs and _bsvol>_bbvol*i_vol_dom and _bwup>=i_wick_abs
            _apat:="ABSORPTION SELL"
            _ay:=upper
            _acol:=color.new(#00BCD4,60)
            _atxt:="▼"
            _astl:=label.style_label_down
        else if i_bub_acc
            _apat:="ACCEPTANCE SELL"
            _ay:=upper-upper*0.15
            _acol:=color.new(#E91E63,0)
            _atxt:="▼"
            _astl:=label.style_label_down

// ═══════════════════════════════════════════════════════════════════════════════
// §10  MTF MLMI
// ═══════════════════════════════════════════════════════════════════════════════
[c1,maq1,mas1,rq1,rs1] = request.security(syminfo.tickerid, i_mtf_tf1, [close,ta.wma(close,5),ta.wma(close,20),ta.wma(ta.rsi(close,5),i_mtf_mw),ta.wma(ta.rsi(close,20),i_mtf_mw)], barmerge.gaps_off, barmerge.lookahead_off)
[c2,maq2,mas2,rq2,rs2] = request.security(syminfo.tickerid, i_mtf_tf2, [close,ta.wma(close,5),ta.wma(close,20),ta.wma(ta.rsi(close,5),i_mtf_mw),ta.wma(ta.rsi(close,20),i_mtf_mw)], barmerge.gaps_off, barmerge.lookahead_off)

var mlmi_tf1 = MLMIData.new(array.new_float(1,0.0),array.new_float(1,0.0),array.new_float(1,0.0),array.new_float(1,0.0))
var mlmi_tf2 = MLMIData.new(array.new_float(1,0.0),array.new_float(1,0.0),array.new_float(1,0.0),array.new_float(1,0.0))

bool pos1 = ta.crossover(maq1,mas1)
bool neg1 = ta.crossunder(maq1,mas1)
if pos1 or neg1
    mlmi_tf1.storeTrade(rs1, rq1, c1)

bool pos2 = ta.crossover(maq2,mas2)
bool neg2 = ta.crossunder(maq2,mas2)
if pos2 or neg2
    mlmi_tf2.storeTrade(rs2, rq2, c2)

float pred1 = mlmi_tf1.knnPredict(rs1, rq1, i_mtf_k)
float up1   = ta.highest(pred1, 2000)
float lo1   = ta.lowest(pred1,  2000)
float up1_  = up1 - ta.ema(ta.stdev(pred1,200),200)
float lo1_  = lo1 + ta.ema(ta.stdev(pred1,200),200)

float pred2 = mlmi_tf2.knnPredict(rs2, rq2, i_mtf_k)
float up2   = ta.highest(pred2, 2000)
float lo2   = ta.lowest(pred2,  2000)
float up2_  = up2 - ta.ema(ta.stdev(pred2,200),200)
float lo2_  = lo2 + ta.ema(ta.stdev(pred2,200),200)

bool mtf1_ob = not na(pred1) and not na(up1_) and pred1 >= up1_
bool mtf1_os = not na(pred1) and not na(lo1_) and pred1 <= lo1_
bool mtf2_ob = not na(pred2) and not na(up2_) and pred2 >= up2_
bool mtf2_os = not na(pred2) and not na(lo2_) and pred2 <= lo2_

// MTF Confluence: Base TF + up to 2 higher TFs
// Single  = Base TF only
// Double  = Base TF + exactly ONE of TF1 / TF2
// Triple  = Base TF + TF1 + TF2 (all three aligned)
bool mtf_ob_any    = i_mtf_en and cur_ob
bool mtf_ob_double = i_mtf_en and cur_ob and (mtf1_ob or mtf2_ob) and not (mtf1_ob and mtf2_ob)
bool mtf_ob_triple = i_mtf_en and cur_ob and mtf1_ob and mtf2_ob
bool mtf_os_any    = i_mtf_en and cur_os
bool mtf_os_double = i_mtf_en and cur_os and (mtf1_os or mtf2_os) and not (mtf1_os and mtf2_os)
bool mtf_os_triple = i_mtf_en and cur_os and mtf1_os and mtf2_os

float _ds    = pane_range*0.12
float _ob_m5 = upper-(_ds*0.5)
float _ob_h1 = upper-(_ds*1.5)
float _ob_h4 = upper-(_ds*2.5)
float _os_m5 = lower+(_ds*0.5)
float _os_h1 = lower+(_ds*1.5)
float _os_h4 = lower+(_ds*2.5)
float _lbl_off = pane_range*i_mtf_offset
float _ob_y    = upper+_lbl_off
float _os_y    = lower-_lbl_off

// Dynamic TF names for MTF labels
string _tf1n = f_tf_name(i_mtf_tf1)
string _tf2n = f_tf_name(i_mtf_tf2)

// ═══════════════════════════════════════════════════════════════════════════════
// §11  DIVERGENCE
// ═══════════════════════════════════════════════════════════════════════════════
float _ph = ta.pivothigh(high, i_div_lb, i_div_lb)
float _pl = ta.pivotlow(low,   i_div_lb, i_div_lb)

var float _prev_ph      = na
var float _prev_mlmi_ph = na
var float _prev_pl      = na
var float _prev_mlmi_pl = na
bool _bear_div = false
bool _bull_div = false

if not na(_ph)
    float _mlmi_at_ph = prediction[i_div_lb]
    if not na(_prev_ph) and not na(_prev_mlmi_ph)
        _bear_div := _ph > _prev_ph and _mlmi_at_ph < _prev_mlmi_ph
    _prev_ph := _ph
    _prev_mlmi_ph := _mlmi_at_ph

if not na(_pl)
    float _mlmi_at_pl = prediction[i_div_lb]
    if not na(_prev_pl) and not na(_prev_mlmi_pl)
        _bull_div := _pl < _prev_pl and _mlmi_at_pl > _prev_mlmi_pl
    _prev_pl := _pl
    _prev_mlmi_pl := _mlmi_at_pl

bool div_bear = i_div_en and _bear_div
bool div_bull = i_div_en and _bull_div
float _div_bear_y = div_bear ? prediction[i_div_lb] : na
float _div_bull_y = div_bull ? prediction[i_div_lb] : na

// ═══════════════════════════════════════════════════════════════════════════════
// §12  DELTA TABLE — DATA
// ═══════════════════════════════════════════════════════════════════════════════
// CONFIRMED BARS — use taLib delta (accurate, updates on bar close)
// LIVE BAR (col 0) — ALWAYS use wick proxy (updates every tick, real-time)
//   Reason: taLib.requestVolumeDelta returns last CLOSED LTF bar delta
//           → appears frozen between LTF closes (up to 1 min lag on M5)
//   Wick proxy updates on every price tick → true real-time for live column

float _wbrng  = math.max(high - low, syminfo.mintick)
float _wbvol  = volume * ((close - low)  / _wbrng)   // estimated buy volume
float _wsvol  = volume * ((high - close) / _wbrng)   // estimated sell volume

// Confirmed bar delta (taLib when available)
float _bar_dlt  = qa_dok ? nz(qa_last_d) : _wbvol - _wsvol
float _bar_maxd = _wbvol           // max buy pressure proxy
float _bar_mind = -_wsvol          // max sell pressure proxy (negative)

// Live bar delta (wick proxy — updates every tick)
float _live_dlt  = _wbvol - _wsvol
float _live_maxd = _wbvol
float _live_mind = -_wsvol
float _live_vol  = volume

// Rolling arrays for confirmed bars
var array<float> _dt_dlt  = array.new_float(0)
var array<float> _dt_maxd = array.new_float(0)
var array<float> _dt_mind = array.new_float(0)
var array<float> _dt_vol  = array.new_float(0)
var array<float> _dt_cumd = array.new_float(0)
var float        _dt_cum  = 0.0
var int          _dt_sess = -1

// Session reset for CumDelta
int _sess_now = _s_lon ? 2 : _s_ny ? 3 : 1
if i_dtbl_sess_reset and _sess_now != _dt_sess and (_sess_now == 2 or _sess_now == 3)
    _dt_cum  := 0.0
    _dt_sess := _sess_now

// Push confirmed bar data
if barstate.isconfirmed
    _dt_cum += _bar_dlt
    array.push(_dt_dlt,  _bar_dlt)
    array.push(_dt_maxd, _bar_maxd)
    array.push(_dt_mind, _bar_mind)
    array.push(_dt_vol,  volume)
    array.push(_dt_cumd, _dt_cum)
    int _keep = math.max(i_dtbl_n, i_dtbl_cum_lb) + 10
    if array.size(_dt_dlt) > _keep
        array.shift(_dt_dlt)
        array.shift(_dt_maxd)
        array.shift(_dt_mind)
        array.shift(_dt_vol)
        array.shift(_dt_cumd)

// Live CumDelta (confirmed sum + current wick proxy)
float _live_cum = _dt_cum + _live_dlt

// ═══════════════════════════════════════════════════════════════════════════════
// §13  DELTA SIGNAL LOGIC
// ═══════════════════════════════════════════════════════════════════════════════
// BUY:  OS_TF2 + CumDelta > thresh + MaxDlt > thresh + MinDlt > -thresh
// SELL: OB_TF2 + CumDelta < -thresh + MinDlt < -thresh + MaxDlt < thresh
// Uses live values so signal reacts to current bar real-time

bool _sig_buy  = i_sig_en and i_sig_show_b and mtf2_os and _live_cum > i_sig_thresh and _live_maxd > i_sig_thresh and _live_mind > -i_sig_thresh
bool _sig_sell = i_sig_en and i_sig_show_s and mtf2_ob and _live_cum < -i_sig_thresh and _live_mind < -i_sig_thresh and _live_maxd < i_sig_thresh

// ═══════════════════════════════════════════════════════════════════════════════
// §13b  SMI CONFLUENCE SIGNAL
// ═══════════════════════════════════════════════════════════════════════════════
// BUY:  Triple OS  + SMI crosses above SMI EMA + both below prediction & prediction_ma
// SELL: Triple OB  + SMI crosses below SMI EMA + both above prediction & prediction_ma

bool _smi_xup = ta.crossover(smi_sc, smi_esc)
bool _smi_xdn = ta.crossunder(smi_sc, smi_esc)

bool _smi_below_mlmi = smi_sc < prediction and smi_sc < prediction_ma and smi_esc < prediction and smi_esc < prediction_ma
bool _smi_above_mlmi = smi_sc > prediction and smi_sc > prediction_ma and smi_esc > prediction and smi_esc > prediction_ma

bool _conf_buy  = mtf_os_triple and _smi_xup and _smi_below_mlmi
bool _conf_sell = mtf_ob_triple and _smi_xdn and _smi_above_mlmi

// ═══════════════════════════════════════════════════════════════════════════════
// §13c  EHLERS FISHER TRANSFORM
// ═══════════════════════════════════════════════════════════════════════════════
var float _fish_val1 = 0.0
float _fish_hi   = ta.highest(close, i_fish_len)
float _fish_lo   = ta.lowest(close,  i_fish_len)
float _fish_rng  = math.max(_fish_hi - _fish_lo, syminfo.mintick)
float _fish_raw  = 2.0 * ((close - _fish_lo) / _fish_rng - 0.5)
_fish_val1      := math.max(math.min(0.33 * _fish_raw + 0.67 * nz(_fish_val1[1]), 0.999), -0.999)
var float _fisher = 0.0
_fisher := 0.5 * math.log((1.0 + _fish_val1) / (1.0 - _fish_val1)) + 0.5 * nz(_fisher[1])
float _fish_trig = nz(_fisher[1])
// Scale to MLMI pane range (Fisher ≈ ±3 maps to ±60% of pane)
float _fish_sc_pos = upper * 0.6
float _fish_sc_neg = math.abs(lower) * 0.6
float _fish_sc      = i_fish_en ? (_fisher   > 0.0 ? _fisher   / 3.0 * _fish_sc_pos : _fisher   / 3.0 * _fish_sc_neg) : na
float _fish_trig_sc = i_fish_en ? (_fish_trig > 0.0 ? _fish_trig / 3.0 * _fish_sc_pos : _fish_trig / 3.0 * _fish_sc_neg) : na
// v11 ⑯ — OB/OS level lines: computed and now actually plotted (were invisible hlines in v10)
float _fish_ob_sc   = (i_fish_ob  / 3.0) * _fish_sc_pos
float _fish_os_sc   = (i_fish_os  / 3.0) * _fish_sc_neg   // i_fish_os negative, _fish_sc_neg positive → result negative
// Fisher crossover signals
bool _fish_bull_cross = ta.crossover(_fish_sc,  _fish_trig_sc)
bool _fish_bear_cross = ta.crossunder(_fish_sc, _fish_trig_sc)
bool _fish_ob_zone    = i_fish_en and _fisher >= i_fish_ob
bool _fish_os_zone    = i_fish_en and _fisher <= i_fish_os
// Fisher Double Confirmation signals
bool _fish_conf_buy  = i_fish_en and _fish_bull_cross and _fish_os_zone  and cur_os
bool _fish_conf_sell = i_fish_en and _fish_bear_cross and _fish_ob_zone  and cur_ob

// ═══════════════════════════════════════════════════════════════════════════════
// §13d  HURST EXPONENT
// ═══════════════════════════════════════════════════════════════════════════════
float _hurst_h    = i_hurst_en ? f_hurst(close, i_hurst_len) : 0.5
bool  _hurst_trend = i_hurst_en and _hurst_h > i_hurst_trend_thr
bool  _hurst_mr    = i_hurst_en and _hurst_h < i_hurst_mr_thr
bool  _hurst_rand  = i_hurst_en and not _hurst_trend and not _hurst_mr
string _hurst_regime = _hurst_trend ? "TREND" : _hurst_mr ? "MR" : "RANDOM"
color  _hurst_bg_col = _hurst_trend ? col_hurst_trend : _hurst_mr ? col_hurst_mr : col_hurst_rand
// Regime change detection
bool _hurst_trend_new = _hurst_trend and not _hurst_trend[1]
bool _hurst_mr_new    = _hurst_mr    and not _hurst_mr[1]
bool _hurst_rand_new  = _hurst_rand  and not _hurst_rand[1]

// ═══════════════════════════════════════════════════════════════════════════════
// §13e  CVD OSCILLATOR — NORMALIZED (z-score of per-bar wick-weighted delta)
// Re-uses _bar_dlt (from §12) — no new RS call needed
// ═══════════════════════════════════════════════════════════════════════════════
float _cvd_mean  = ta.sma(_bar_dlt, i_cvd_len)
float _cvd_std   = ta.stdev(_bar_dlt, i_cvd_len)
float _cvd_z     = i_cvd_en and _cvd_std > 0.0 ? (_bar_dlt - _cvd_mean) / _cvd_std : 0.0

// Scale CVD z-score to MLMI pane (±3σ maps to ±15% of pane range)
float _cvd_scale = pane_range * 0.15 / 3.0
float _cvd_sc    = i_cvd_en ? _cvd_z * _cvd_scale : na

// MLMI/CVD Divergence
// Bear div: MLMI bullish (prediction > prediction_ma) but CVD is negative and strong
// Bull div: MLMI bearish (prediction < prediction_ma) but CVD is positive and strong
bool _cvd_div_bear = i_cvd_en and i_cvd_show_div and prediction > prediction_ma and _cvd_z < -i_cvd_div_thr
bool _cvd_div_bull = i_cvd_en and i_cvd_show_div and prediction < prediction_ma and _cvd_z >  i_cvd_div_thr

color _cvd_bar_col = _cvd_div_bear ? col_cvd_div :
     _cvd_div_bull ? col_cvd_div :
     _cvd_z >= 0.0 ? col_cvd_pos : col_cvd_neg

// ═══════════════════════════════════════════════════════════════════════════════
// §14  PLOTS
// ═══════════════════════════════════════════════════════════════════════════════
// RE10140-FIX ①: 5 series-color bgcolor() calls (10u) merged into 1 (2u). All 5
// conditions preserved; overlapping backgrounds now resolve by priority instead of
// transparency-stacking. Priority: Fix > Rejection > MTF stack > Hurst > Session.
bgcolor(
     i_show_fix and _in_fix                       ? col_fix_win :
     i_hl_rej and _rej_bull                       ? color.new(#009688,85) :
     i_hl_rej and _rej_bear                       ? color.new(#9C27B0,85) :
     mtf_ob_triple                                ? col_mtf_c2 :
     mtf_ob_double                                ? col_mtf_c1 :
     mtf_ob_any                                   ? col_mtf_c0 :
     mtf_os_triple                                ? col_mtf_c3 :
     mtf_os_double                                ? col_mtf_c4 :
     mtf_os_any                                   ? col_mtf_c5 :
     i_hurst_en and i_hurst_show_bg               ? _hurst_bg_col :
     i_show_sess and _s_asia                      ? col_sess_asia :
     i_show_sess and _s_lon                       ? col_sess_lon :
     i_show_sess and _s_ny                        ? col_sess_ny :
     na, title="Confluence BG (merged)")

pred_plot = plot(prediction,    "MLMI Prediction", color=col_mlmi_pred)
plot(prediction_ma, "MLMI WMA", color=col_mlmi_wma)
hline(0, "Mid Level",           color=col_mlmi_mid)
ch_u = plot(upper, color=na, editable=false, display=display.none)
ch_l = plot(lower, color=na, editable=false, display=display.none)
ch_m = plot(0,     color=na, editable=false, display=display.none)
// RE10140-FIX ⑤: Upper/Lower channel fills flattened gradient→flat (range already spanned
// full half-pane, so visual is near-identical). 2u → saves 2u toward the 64u budget.
fill(ch_m, ch_u, color=color.new(color.lime,80), title="Upper Fill")
fill(ch_m, ch_l, color=color.new(color.red,80),  title="Lower Fill")
fill(pred_plot, ch_m, top_value=upper, bottom_value=upper_, top_color=color.new(color.lime,0), bottom_color=color.new(color.green,100), title="OB Gradient")
fill(pred_plot, ch_m, top_value=lower_, bottom_value=lower, top_color=color.new(color.red,100), bottom_color=color.new(color.red,0), title="OS Gradient")

smi_ob_p = plot(upper_, "SMI OB", color=col_smi_band)
smi_os_p = plot(lower_, "SMI OS", color=col_smi_band)
fill(smi_ob_p, smi_os_p, title="SMI BG", color=color.new(color.blue,95))
smi_p = plot(smi_sc,  "SMI Line", color=col_smi_line, linewidth=1)
smi_ep= plot(smi_esc, "SMI EMA",  color=col_smi_ema,  linewidth=1)
// RE10140-FIX ⑥: 2 SMI band gradient fills (4u) merged into 1 sign-based fill (2u).
// Green above mid / red below mid — clean filled-oscillator look, saves 2u.
fill(smi_p, ch_m, color=smi_sc >= 0.0 ? color.new(#4caf4f,75) : color.new(color.red,75), title="SMI Fill")

plot(i_show_macd ? hist_sc : na, "MACD Hist",   style=plot.style_columns, color=hist_col)
plot(i_show_macd ? macd_sc : na, "MACD Line",   color=col_macd,           linewidth=1)
plot(i_show_macd ? sig_sc  : na, "Signal Line", color=col_sig_macd,       linewidth=1)

// RE10140-FIX ②: QA volume bubbles → label drawings (free of 64u plot budget).
// Tier preserved via label size. Note: labels are capped at max_labels_count=500,
// so on very deep history only the most-recent bubbles render (fine for live M5).
if qa_vs or qa_vm or qa_vb
    label.new(bar_index, qa_y, "", yloc=yloc.price, style=label.style_circle, color=qa_col, size=(qa_vb ? size.huge : qa_vm ? size.large : size.normal))

// RE10140-FIX ③: Iceberg / rejection markers → label drawings (free of 64u budget;
// these were text-bearing plotshapes = 2u each). Same glyphs, same y-anchors.
if i_show_ice and _is_ice
    label.new(bar_index, 0.0,   "Ice", yloc=yloc.price, style=label.style_label_up,   color=color.new(color.white,100), textcolor=color.new(#00BCD4,0), size=size.small)
if i_show_ice and _rej_top
    label.new(bar_index, upper, "🟨",  yloc=yloc.price, style=label.style_label_down, color=color.new(color.white,100), textcolor=color.new(#FFD700,0), size=size.small)
if i_show_ice and _rej_bot
    label.new(bar_index, lower, "🟨",  yloc=yloc.price, style=label.style_label_up,   color=color.new(color.white,100), textcolor=color.new(#FFD700,0), size=size.small)

plotshape(i_mtf_en and cur_ob  ? _ob_m5 : na, "Base OB", shape.circle, location.absolute, color.new(#FF5252,0), size=size.tiny)
plotshape(i_mtf_en and mtf1_ob ? _ob_h1 : na, "TF1 OB",  shape.circle, location.absolute, color.new(#FF1744,0), size=size.tiny)
plotshape(i_mtf_en and mtf2_ob ? _ob_h4 : na, "TF2 OB",  shape.circle, location.absolute, color.new(#B71C1C,0), size=size.tiny)
plotshape(i_mtf_en and cur_os  ? _os_m5 : na, "Base OS", shape.circle, location.absolute, color.new(#69F0AE,0), size=size.tiny)
plotshape(i_mtf_en and mtf1_os ? _os_h1 : na, "TF1 OS",  shape.circle, location.absolute, color.new(#00E676,0), size=size.tiny)
plotshape(i_mtf_en and mtf2_os ? _os_h4 : na, "TF2 OS",  shape.circle, location.absolute, color.new(#00796B,0), size=size.tiny)

// v11 ⑯ — Fisher OB/OS Level Lines: now rendered via plot() (were invisible hlines in v10)
// i_fish_show_lvl toggle controls visibility correctly
plot(i_fish_en and i_fish_show_lvl ? _fish_ob_sc : na, "Fisher OB Level",
     color=color.new(col_fish_ob, 30), linewidth=1, style=plot.style_line)
plot(i_fish_en and i_fish_show_lvl ? _fish_os_sc : na, "Fisher OS Level",
     color=color.new(col_fish_ob, 30), linewidth=1, style=plot.style_line)

// Fisher plots
p_fish     = plot(i_fish_en ? _fish_sc      : na, "Fisher",   color=col_fish_line, linewidth=1)
p_fish_trg = plot(i_fish_en ? _fish_trig_sc : na, "FishTrig", color=col_fish_trig, linewidth=1)
fill(p_fish, p_fish_trg, color=_fish_bull_cross ? color.new(col_fish_line,85) : _fish_bear_cross ? color.new(col_fish_trig,85) : na, title="Fisher Fill")

// RE10140-FIX ④: Fisher OB/OS zone-entry markers → label drawings (free of 64u budget).
// Drawn on the Fisher line (_fish_sc) at the entry bar; fires only on zone transition.
if i_fish_en and i_fish_show_lvl and _fish_ob_zone and not _fish_ob_zone[1]
    label.new(bar_index, _fish_sc, "", yloc=yloc.price, style=label.style_circle, color=color.new(col_fish_ob,0), size=size.tiny)
if i_fish_en and i_fish_show_lvl and _fish_os_zone and not _fish_os_zone[1]
    label.new(bar_index, _fish_sc, "", yloc=yloc.price, style=label.style_circle, color=color.new(col_fish_ob,0), size=size.tiny)

// CVD Oscillator plot
plot(i_cvd_en ? _cvd_sc : na, "CVD Z-Score", style=plot.style_columns, color=_cvd_bar_col, linewidth=1)

// ═══════════════════════════════════════════════════════════════════════════════
// §15  LABELS
// ═══════════════════════════════════════════════════════════════════════════════
if qa_av and qa_show_lbl
    string _fv  = f_qa_fmtn(volume)
    string _fd  = f_qa_fmtn(qa_nd)
    int    _qlvl= qa_vb?3:qa_vm?2:1
    string _qls = _qlvl==3?"BIG":_qlvl==2?"MEDIUM":"SMALL"
    string _qss = qa_is_buy?"BUY":qa_is_sell?"SELL":"MIXED"
    bool _dpS   = _qlvl==3?qa_ad>=dBS:_qlvl==2?qa_ad>=dMS:qa_ad>=dSS
    bool _dpM   = _qlvl==3?qa_ad>=dBM:_qlvl==2?qa_ad>=dMM:qa_ad>=dSM
    bool _dpL   = _qlvl==3?qa_ad>=dBL:_qlvl==2?qa_ad>=dML:qa_ad>=dSL
    string _dw  = (_dpS?"S":"·")+" "+(_dpM?"M":"·")+" "+(_dpL?"L":"·")
    string _tip = "── "+_qls+" "+_qss+" ──\nVol: "+_fv+"\nDelta: "+(qa_dok?_fd:"n/a")+"\nRatio: "+str.tostring(qa_ratio,"#.##")+"x\nWindows: ["+_dw+"]\nFix: "+(_in_fix?_fix_nm:"none")+"  Sess: "+(_s_lon?"LON":_s_ny?"NY":"ASIA")
    label.new(bar_index, qa_y, _fv, style=label.style_label_center, color=color.new(chart.fg_color,100), textcolor=chart.fg_color, size=size.small, tooltip=_tip)

var array<label> _albs = array.new_label()
if _bact and _apat != "" and not na(_ay)
    float  _bi = volume/math.max(_vma50,1.0)
    string _bs = _bi>_bub_thr*5.0?size.large:_bi>_bub_thr*3.0?size.normal:_bi>_bub_thr*1.5?size.small:size.tiny
    string _bt = _apat+"\nVol: "+str.tostring(_bi,"#.1")+"x SMA50\nSess: "+(_s_lon?"LON":_s_ny?"NY":"ASIA")+"  Fix: "+(_in_fix?_fix_nm:"off")
    array.push(_albs, label.new(bar_index, _ay, style=_astl, color=_acol, size=_bs, text=_atxt, textcolor=color.white, tooltip=_bt))
    if array.size(_albs) > 300
        label.delete(array.shift(_albs))

if i_sig_en
    if _sig_buy
        string _bt = "DELTA BUY\nOS "+_tf2n+": YES\nCumDelta: "+f_dtbl_fmt(_live_cum)+"\nMaxDelta: "+f_dtbl_fmt(_live_maxd)+"\nMinDelta: "+f_dtbl_fmt(_live_mind)+"\nThresh: "+str.tostring(i_sig_thresh,"#.0")
        label.new(bar_index, lower-pane_range*0.04, "▲ BUY", style=label.style_label_up, color=color.new(col_sig_buy,15), textcolor=color.white, size=size.tiny, tooltip=_bt)
    if _sig_sell
        string _st = "DELTA SELL\nOB "+_tf2n+": YES\nCumDelta: "+f_dtbl_fmt(_live_cum)+"\nMinDelta: "+f_dtbl_fmt(_live_mind)+"\nMaxDelta: "+f_dtbl_fmt(_live_maxd)+"\nThresh: "+str.tostring(i_sig_thresh,"#.0")
        label.new(bar_index, upper+pane_range*0.04, "▼ SELL", style=label.style_label_down, color=color.new(col_sig_sell,15), textcolor=color.white, size=size.normal, tooltip=_st)

if _conf_buy
    string _cbt = "SMI CONFLUENCE BUY ◆\nOS TRIPLE ("+_tf0_display+"+"+_tf1n+"+"+_tf2n+"): YES\nSMI cross ↑ below both MLMI lines\nSMI: "+str.tostring(smi_sc,"#.##")+"  EMA: "+str.tostring(smi_esc,"#.##")+"\nMLMI Pred: "+str.tostring(prediction,"#.##")+"  WMA: "+str.tostring(prediction_ma,"#.##")
    label.new(bar_index, lower-pane_range*0.10, "◆ CONF BUY", style=label.style_label_up, color=color.new(#00E676,0), textcolor=color.white, size=size.small, tooltip=_cbt)

if _conf_sell
    string _cst = "SMI CONFLUENCE SELL ◆\nOB TRIPLE ("+_tf0_display+"+"+_tf1n+"+"+_tf2n+"): YES\nSMI cross ↓ above both MLMI lines\nSMI: "+str.tostring(smi_sc,"#.##")+"  EMA: "+str.tostring(smi_esc,"#.##")+"\nMLMI Pred: "+str.tostring(prediction,"#.##")+"  WMA: "+str.tostring(prediction_ma,"#.##")
    label.new(bar_index, upper+pane_range*0.10, "◆ CONF SELL", style=label.style_label_down, color=color.new(#FF1744,0), textcolor=color.white, size=size.large, tooltip=_cst)

if i_mtf_lbl and i_mtf_en
    // v11 ⑰ — dynamic TF names: _tf0_display replaces hardcoded "M5"
    if mtf_ob_triple
        label.new(bar_index, _ob_y, "▼ OB:"+_tf0_display+"+"+_tf1n+"+"+_tf2n, style=label.style_label_down, color=col_mtf_c2, textcolor=color.white, size=size.normal, tooltip="OB TRIPLE\n"+_tf0_display+"+"+_tf1n+"+"+_tf2n+" all overbought — high-probability reversal")
    else if mtf_ob_double
        string _th = mtf1_ob?_tf1n:_tf2n
        label.new(bar_index, _ob_y, "▼ OB:"+_tf0_display+"+"+_th, style=label.style_label_down, color=col_mtf_c1, textcolor=color.white, size=size.small, tooltip="OB DOUBLE — "+_tf0_display+" + "+_th+" overbought")
    if mtf_os_triple
        label.new(bar_index, _os_y, "▲ OS:"+_tf0_display+"+"+_tf1n+"+"+_tf2n, style=label.style_label_up, color=col_mtf_c3, textcolor=color.white, size=size.normal, tooltip="OS TRIPLE\n"+_tf0_display+"+"+_tf1n+"+"+_tf2n+" all oversold — high-probability reversal")
    else if mtf_os_double
        string _th = mtf1_os?_tf1n:_tf2n
        label.new(bar_index, _os_y, "▲ OS:"+_tf0_display+"+"+_th, style=label.style_label_up, color=col_mtf_c4, textcolor=color.white, size=size.small, tooltip="OS DOUBLE — "+_tf0_display+" + "+_th+" oversold")

if i_div_show_lbl
    if div_bear
        label.new(bar_index-i_div_lb, _div_bear_y+pane_range*0.02, "DIV BEAR", style=label.style_none, color=color.new(color.white,100), textcolor=col_div_bear, size=size.tiny)
    if div_bull
        label.new(bar_index-i_div_lb, _div_bull_y-pane_range*0.02, "DIV BULL", style=label.style_none, color=color.new(color.white,100), textcolor=col_div_bull, size=size.tiny)

if i_show_fix and _in_fix
    label.new(bar_index, upper*0.96, "✦ "+_fix_nm, style=label.style_none, color=color.new(color.white,100), textcolor=color.new(#FFD700,0), size=size.tiny)

// Fisher labels
if i_fish_en
    if _fish_conf_buy
        label.new(bar_index, lower - pane_range*0.18, "◈ FISH BUY",
             style=label.style_label_up, color=color.new(col_fish_line,10),
             textcolor=color.white, size=size.small,
             tooltip="Fisher CONF BUY\nFisher OS (≤−φ) + Bull Cross + MLMI OS\nFisher: "+str.tostring(_fisher,"#.###")+"  Trigger: "+str.tostring(_fish_trig,"#.###")+"\nφ threshold: "+str.tostring(i_fish_os,"#.###"))
    if _fish_conf_sell
        label.new(bar_index, upper + pane_range*0.18, "◈ FISH SELL",
             style=label.style_label_down, color=color.new(col_fish_trig,10),
             textcolor=color.white, size=size.small,
             tooltip="Fisher CONF SELL\nFisher OB (≥+φ) + Bear Cross + MLMI OB\nFisher: "+str.tostring(_fisher,"#.###")+"  Trigger: "+str.tostring(_fish_trig,"#.###")+"\nφ threshold: "+str.tostring(i_fish_ob,"#.###"))

// Hurst label (barstate.islast)
var label _hurst_lbl = na
if i_hurst_en and i_hurst_show_lbl and barstate.islast
    label.delete(_hurst_lbl)
    string _htip = "Hurst Exponent (R/S)\nH = "+str.tostring(_hurst_h,"#.###")+"\nRegime: "+_hurst_regime+"\n──────────────\nH > "+str.tostring(i_hurst_trend_thr,"#.##")+" → TREND  (trust Breakout cond_G/H)\nH < "+str.tostring(i_hurst_mr_thr,"#.##")+"  → MR     (trust MR cond_A/B/C/D)\nH ≈ 0.5 → RANDOM (reduce position size)"
    _hurst_lbl := label.new(bar_index, upper + pane_range*0.25,
         "H:"+str.tostring(_hurst_h,"#.##")+" "+_hurst_regime,
         style=label.style_label_left,
         color=color.new(_hurst_trend ? #00E676 : _hurst_mr ? #00BCD4 : #607D8B, 20),
         textcolor=color.white, size=size.small, tooltip=_htip)

// DC Period label (barstate.islast)
var label _dc_smi_lbl = na
if i_asmi_en and i_asmi_show_dc and barstate.islast
    label.delete(_dc_smi_lbl)
    _dc_smi_lbl := label.new(bar_index, upper + pane_range*0.35,
         "DC:"+str.tostring(_dc10)+"b  K:"+str.tostring(smi_k_eff)+" D:"+str.tostring(lengthD)+"(fixed)",
         style=label.style_label_left, color=color.new(#0D1117,75),
         textcolor=color.new(#00BCD4,0), size=size.tiny)

// CVD divergence labels
if i_cvd_en and i_cvd_show_div
    if _cvd_div_bear and not _cvd_div_bear[1]
        label.new(bar_index, lower - pane_range*0.25, "↕ CVD DIV BEAR",
             style=label.style_label_up, color=color.new(col_cvd_div,15),
             textcolor=color.white, size=size.tiny,
             tooltip="CVD/MLMI Bearish Divergence\nMLMI Bullish but CVD Z="+str.tostring(_cvd_z,"#.##")+" (negative/strong)\nSuggests hidden selling pressure undermining bullish MLMI")
    if _cvd_div_bull and not _cvd_div_bull[1]
        label.new(bar_index, upper + pane_range*0.25, "↕ CVD DIV BULL",
             style=label.style_label_down, color=color.new(col_cvd_div,15),
             textcolor=color.white, size=size.tiny,
             tooltip="CVD/MLMI Bullish Divergence\nMLMI Bearish but CVD Z="+str.tostring(_cvd_z,"#.##")+" (positive/strong)\nSuggests hidden buying pressure undermining bearish MLMI")

// ═══════════════════════════════════════════════════════════════════════════════
// §16  DELTA / VOLUME TABLE  (barstate.islast — rebuilds every tick)
// ═══════════════════════════════════════════════════════════════════════════════
// COLUMNS: history bars (oldest→newest) + live bar (● 0)
//   confirmed cols use taLib delta | live col (●0) uses wick proxy (real-time)
// ROWS:
//   0: BAR header (-n...-1, ●0)
//   1: Delta      (confirmed=taLib, live=wick proxy)
//   2: MaxDelta   (if show_mx) — buy vol proxy
//   3: MinDelta   (if show_mx) — sell vol proxy (negative)
//   4: CumDelta   (always)     — running sum, resets at session open if enabled
//   5: Volume     (if show_v)
//   6: Dlt/Vol%   (if show_r)
// Row count: 3 (header+delta+cum) + (show_mx?2:0) + (show_v?1:0) + (show_r?1:0)

// RE-FIX ②: hoist ta.sma out of the conditional table block (must run every bar)
float _vol_sma_mid = ta.sma(volume, qa_mid_len)

var table _dtbl = table.new(position.middle_right, 1, 1, bgcolor=color.new(color.black,100))

if barstate.islast and i_dtbl_en
    int _n  = math.min(i_dtbl_n, array.size(_dt_dlt))
    if _n < 1
        _n := 1
    int _nr = 3 + (i_dtbl_show_mx ? 2 : 0) + (i_dtbl_show_v ? 1 : 0) + (i_dtbl_show_r ? 1 : 0)
    int _nc = _n + 1

    string _tpos = switch i_dtbl_pos
        "middle_left"   => position.middle_left
        "middle_center" => position.middle_center
        "bottom_left"   => position.bottom_left
        "bottom_center" => position.bottom_center
        "bottom_right"  => position.bottom_right
        =>                 position.middle_right
    string _tsz = i_dtbl_sz == "Small" ? size.small : size.tiny

    table.delete(_dtbl)
    _dtbl := table.new(_tpos, _nc, _nr,
         bgcolor=color.new(#0D1117,78), border_color=color.new(#37474F,45),
         border_width=1, frame_color=color.new(#455A64,55), frame_width=1)

    // Compute rolling stdev of delta for highlight threshold
    int   _lb_use = math.min(array.size(_dt_dlt), i_dtbl_cum_lb)
    float _dsum = 0.0
    float _dsq  = 0.0
    for _i = 0 to _lb_use - 1
        float _v = array.get(_dt_dlt, array.size(_dt_dlt)-1-_i)
        _dsum += _v
        _dsq  += _v*_v
    float _dmean = _lb_use > 0 ? _dsum/_lb_use : 0.0
    float _dvar  = _lb_use > 1 ? (_dsq-_dsum*_dsum/_lb_use)/(_lb_use-1) : 1.0
    float _dstd  = math.sqrt(math.max(_dvar,0.0))
    float _thr_p = _dmean + _dstd*i_dtbl_thresh
    float _thr_n = _dmean - _dstd*i_dtbl_thresh

    int _r = 0

    // Helper inline functions for cell value retrieval
    // confirmed index: col _c in [1.._n-1] maps to arr index array.size-_n+_c-1
    // col _n = live bar

    // ── ROW 0: BAR HEADER ────────────────────────────────────────────────────
    table.cell(_dtbl, 0, _r, "BAR", text_color=color.new(#90A4AE,0), text_size=_tsz, bgcolor=color.new(#1E2A3A,88), text_halign=text.align_left)
    for _c = 1 to _n
        bool   _il  = (_c == _n)
        string _lbl = _il ? "● 0" : "-"+str.tostring(_n-_c)
        table.cell(_dtbl, _c, _r, _lbl, text_color=_il?color.new(#FFD700,0):color.new(#546E7A,0), text_size=_tsz, bgcolor=_il?color.new(#2A2200,85):color.new(#1E2A3A,88), text_halign=text.align_right)
    _r += 1

    // ── ROW 1: DELTA ─────────────────────────────────────────────────────────
    table.cell(_dtbl, 0, _r, "Delta", text_color=color.new(#B0BEC5,0), text_size=_tsz, bgcolor=color.new(#162032,88), text_halign=text.align_left)
    for _c = 1 to _n
        bool  _il  = (_c == _n)
        int   _ai  = array.size(_dt_dlt)-_n+_c-1
        float _v   = _il ? _live_dlt : (_ai>=0 and _ai<array.size(_dt_dlt) ? array.get(_dt_dlt,_ai) : na)
        if not na(_v)
            bool  _hi  = _v >= _thr_p or _v <= _thr_n
            color _tc  = _v >= 0.0 ? col_dtbl_pos : col_dtbl_neg
            color _bg  = _il ? color.new(#2A2200,80) : (_hi ? (_v>=0.0?color.new(col_dtbl_pos,70):color.new(col_dtbl_neg,70)) : color.new(#0D1117,85))
            table.cell(_dtbl, _c, _r, f_dtbl_fmt(_v), text_color=_tc, bgcolor=_bg, text_size=_tsz, text_halign=text.align_right)
        else
            table.cell(_dtbl, _c, _r, "--", text_color=col_dtbl_neu, bgcolor=color.new(#0D1117,88), text_size=_tsz, text_halign=text.align_right)
    _r += 1

    // ── ROWS 2-3: MAXDELTA / MINDELTA ────────────────────────────────────────
    if i_dtbl_show_mx
        // MaxDelta
        table.cell(_dtbl, 0, _r, "MaxDlt", text_color=color.new(#B0BEC5,0), text_size=_tsz, bgcolor=color.new(#0F2318,88), text_halign=text.align_left)
        for _c = 1 to _n
            bool  _il = (_c == _n)
            int   _ai = array.size(_dt_maxd)-_n+_c-1
            float _v  = _il ? _live_maxd : (_ai>=0 and _ai<array.size(_dt_maxd) ? array.get(_dt_maxd,_ai) : na)
            if not na(_v)
                table.cell(_dtbl, _c, _r, f_dtbl_fmt(_v), text_color=color.new(col_dtbl_pos,15), bgcolor=_il?color.new(#2A2200,80):color.new(#0F2318,88), text_size=_tsz, text_halign=text.align_right)
            else
                table.cell(_dtbl, _c, _r, "--", text_color=col_dtbl_neu, bgcolor=color.new(#0D1117,88), text_size=_tsz, text_halign=text.align_right)
        _r += 1

        // MinDelta
        table.cell(_dtbl, 0, _r, "MinDlt", text_color=color.new(#B0BEC5,0), text_size=_tsz, bgcolor=color.new(#230F0F,88), text_halign=text.align_left)
        for _c = 1 to _n
            bool  _il = (_c == _n)
            int   _ai = array.size(_dt_mind)-_n+_c-1
            float _v  = _il ? _live_mind : (_ai>=0 and _ai<array.size(_dt_mind) ? array.get(_dt_mind,_ai) : na)
            if not na(_v)
                table.cell(_dtbl, _c, _r, f_dtbl_fmt(_v), text_color=color.new(col_dtbl_neg,15), bgcolor=_il?color.new(#2A2200,80):color.new(#230F0F,88), text_size=_tsz, text_halign=text.align_right)
            else
                table.cell(_dtbl, _c, _r, "--", text_color=col_dtbl_neu, bgcolor=color.new(#0D1117,88), text_size=_tsz, text_halign=text.align_right)
        _r += 1

    // ── ROW: CUMDELTA (always shown) ──────────────────────────────────────────
    string _cum_lbl = i_dtbl_sess_reset ? "CumDlt*" : "CumDlt"
    table.cell(_dtbl, 0, _r, _cum_lbl, text_color=color.new(#FFD700,0), text_size=_tsz, bgcolor=color.new(#1A1A10,88), text_halign=text.align_left)
    for _c = 1 to _n
        bool  _il = (_c == _n)
        int   _ai = array.size(_dt_cumd)-_n+_c-1
        float _v  = _il ? _live_cum : (_ai>=0 and _ai<array.size(_dt_cumd) ? array.get(_dt_cumd,_ai) : na)
        if not na(_v)
            color _tc = _v >= 0.0 ? color.new(col_dtbl_pos,5) : color.new(col_dtbl_neg,5)
            color _bg = _il ? color.new(#2A2200,78) : (_v>=0.0?color.new(#1B3A2A,80):color.new(#3A1B1B,80))
            table.cell(_dtbl, _c, _r, f_dtbl_fmt(_v), text_color=_tc, bgcolor=_bg, text_size=_tsz, text_halign=text.align_right)
        else
            table.cell(_dtbl, _c, _r, "--", text_color=col_dtbl_neu, bgcolor=color.new(#0D1117,88), text_size=_tsz, text_halign=text.align_right)
    _r += 1

    // ── ROW: VOLUME ───────────────────────────────────────────────────────────
    if i_dtbl_show_v
        float _vmean = _vol_sma_mid
        table.cell(_dtbl, 0, _r, "Volume", text_color=color.new(#B0BEC5,0), text_size=_tsz, bgcolor=color.new(#162032,88), text_halign=text.align_left)
        for _c = 1 to _n
            bool  _il = (_c == _n)
            int   _ai = array.size(_dt_vol)-_n+_c-1
            float _v  = _il ? _live_vol : (_ai>=0 and _ai<array.size(_dt_vol) ? array.get(_dt_vol,_ai) : na)
            if not na(_v)
                float _vr = _vmean > 0.0 ? _v/_vmean : 1.0
                color _tc = _vr >= 2.0 ? #FFD700 : _vr >= 1.5 ? color.new(#FFD700,40) : color.new(#607D8B,0)
                color _bg = _il ? color.new(#2A2200,78) : (_vr>=2.0?color.new(#2A2200,78):color.new(#0D1117,88))
                table.cell(_dtbl, _c, _r, f_dtbl_fmtv(_v), text_color=_tc, bgcolor=_bg, text_size=_tsz, text_halign=text.align_right)
            else
                table.cell(_dtbl, _c, _r, "--", text_color=col_dtbl_neu, bgcolor=color.new(#0D1117,88), text_size=_tsz, text_halign=text.align_right)
        _r += 1

    // ── ROW: DELTA/VOL% ───────────────────────────────────────────────────────
    if i_dtbl_show_r
        table.cell(_dtbl, 0, _r, "Dlt/V%", text_color=color.new(#B0BEC5,0), text_size=_tsz, bgcolor=color.new(#162032,88), text_halign=text.align_left)
        for _c = 1 to _n
            bool  _il  = (_c == _n)
            int   _di  = array.size(_dt_dlt)-_n+_c-1
            int   _vi  = array.size(_dt_vol)-_n+_c-1
            float _dv  = _il ? _live_dlt : (_di>=0 and _di<array.size(_dt_dlt) ? array.get(_dt_dlt,_di) : na)
            float _vv  = _il ? _live_vol : (_vi>=0 and _vi<array.size(_dt_vol) ? array.get(_dt_vol,_vi) : na)
            if not na(_dv) and not na(_vv)
                float _rat = _vv > 0.0 ? _dv/_vv*100.0 : 0.0
                color _tc  = _rat >= 0.0 ? color.new(col_dtbl_pos,20) : color.new(col_dtbl_neg,20)
                color _bg  = _il ? color.new(#2A2200,80) : (_rat>=0.0?color.new(#1B3A2A,80):color.new(#3A1B1B,80))
                table.cell(_dtbl, _c, _r, str.tostring(_rat,"#.0")+"%", text_color=_tc, bgcolor=_bg, text_size=_tsz, text_halign=text.align_right)
            else
                table.cell(_dtbl, _c, _r, "--", text_color=col_dtbl_neu, bgcolor=color.new(#0D1117,88), text_size=_tsz, text_halign=text.align_right)

// ═══════════════════════════════════════════════════════════════════════════════
// §17  ALERTS
// ═══════════════════════════════════════════════════════════════════════════════
alertcondition(mtf_ob_triple, title="[QC_MoConf v11] OB TRIPLE",         message="MoConf v11 — Overbought Triple (Base+TF1+TF2). High-probability reversal DOWN.")
alertcondition(mtf_os_triple, title="[QC_MoConf v11] OS TRIPLE",         message="MoConf v11 — Oversold Triple (Base+TF1+TF2). High-probability reversal UP.")
alertcondition(mtf_ob_double, title="[QC_MoConf v11] OB DOUBLE",         message="MoConf v11 — Overbought Double confirmation.")
alertcondition(mtf_os_double, title="[QC_MoConf v11] OS DOUBLE",         message="MoConf v11 — Oversold Double confirmation.")
alertcondition(div_bear,      title="[QC_MoConf v11] DIV BEAR",          message="MoConf v11 — Bearish Divergence: price HH, MLMI LH.")
alertcondition(div_bull,      title="[QC_MoConf v11] DIV BULL",          message="MoConf v11 — Bullish Divergence: price LL, MLMI HL.")
alertcondition(qa_vb,         title="[QC_MoConf v11] QA BIG CLUSTER",    message="MoConf v11 — QuantAlgo BIG volume cluster.")
alertcondition(_rej_bull or _rej_bear, title="[QC_MoConf v11] AMT REJECTION", message="MoConf v11 — AMT Rejection pattern.")
alertcondition(_in_am and not _in_am[1], title="[QC_MoConf v11] LBMA AM FIX", message="MoConf v11 — LBMA AM Fix window opened.")
alertcondition(_in_pm and not _in_pm[1], title="[QC_MoConf v11] LBMA PM FIX", message="MoConf v11 — LBMA PM Fix window opened.")
alertcondition(_sig_buy  and not _sig_buy[1],  title="[QC_MoConf v11] DELTA BUY",  message="MoConf v11 — OS TF2 + Delta confluence BUY signal.")
alertcondition(_sig_sell and not _sig_sell[1], title="[QC_MoConf v11] DELTA SELL", message="MoConf v11 — OB TF2 + Delta confluence SELL signal.")
alertcondition(_conf_buy  and not _conf_buy[1],  title="[QC_MoConf v11] SMI CONF BUY",  message="MoConf v11 — OS TRIPLE (Base+TF1+TF2) + SMI cross↑ below both MLMI lines: High-probability BUY confluence.")
alertcondition(_conf_sell and not _conf_sell[1], title="[QC_MoConf v11] SMI CONF SELL", message="MoConf v11 — OB TRIPLE (Base+TF1+TF2) + SMI cross↓ above both MLMI lines: High-probability SELL confluence.")
alertcondition(_fish_conf_buy  and not _fish_conf_buy[1],  title="[QC_MoConf v11] FISHER CONF BUY",  message="MoConf v11 — Fisher OS(≤−φ) + Bull Cross + MLMI OS. High-prob BUY confluence.")
alertcondition(_fish_conf_sell and not _fish_conf_sell[1], title="[QC_MoConf v11] FISHER CONF SELL", message="MoConf v11 — Fisher OB(≥+φ) + Bear Cross + MLMI OB. High-prob SELL confluence.")
alertcondition(_hurst_trend_new, title="[QC_MoConf v11] HURST TREND MODE",   message="MoConf v11 — Hurst H>threshold: Market entered TREND regime. Favour Breakout signals.")
alertcondition(_hurst_mr_new,    title="[QC_MoConf v11] HURST MR MODE",      message="MoConf v11 — Hurst H<threshold: Market entered MEAN-REVERT regime. Favour MR signals.")
alertcondition(_cvd_div_bear and not _cvd_div_bear[1], title="[QC_MoConf v11] CVD DIV BEAR", message="MoConf v11 — MLMI Bull but CVD negative: Hidden selling pressure detected.")
alertcondition(_cvd_div_bull and not _cvd_div_bull[1], title="[QC_MoConf v11] CVD DIV BULL", message="MoConf v11 — MLMI Bear but CVD positive: Hidden buying pressure detected.")

// ═══════════════════════════════════════════════════════════════════════════════
// ⑱  MOMENTUM STATE — Expansion / Fade + Fast-RSI   [v12]  DISPLAY-ONLY
// ─────────────────────────────────────────────────────────────────────────────
// The momentum-pane's answer to the "3 primitives" framework:
//  • EXPANDING = SMI accelerating with the move → breakout / trend continuation.
//  • FADING    = SMI decelerating / rolling over → "exit when momentum disappears"
//                (the breakout exit the transcript names) + potential MR trigger.
//  • FAST-RSI  = short-period RSI OB/OS = mean-reversion timing (Connors-style).
// Uses only label + table  → 0 plot-budget (QC already hit RE10140 once). Nothing
// feeds any signal / gate / score — pure momentum-context readout.
// ═══════════════════════════════════════════════════════════════════════════════
var string G_MST = "⑱ MOMENTUM STATE — Expansion / Fade + Fast-RSI"
i_mst_on    = input.bool(true, "Enable Momentum State module", group=G_MST, tooltip="Is momentum EXPANDING (trend/breakout continuation), FADING (breakout exit — 'exit when momentum disappears'), plus a fast-RSI OB/OS for mean-reversion timing. DISPLAY-ONLY.")
i_mst_tbl   = input.bool(true, "  Show Momentum table", group=G_MST)
i_mst_tpos  = input.string("top_right", "  Table position", group=G_MST, options=["top_left","top_center","top_right","middle_left","middle_right","bottom_left","bottom_center","bottom_right"])
i_mst_mark  = input.bool(true, "  Show Fade / Fast-RSI markers on pane", group=G_MST)
i_frsi_len  = input.int(2,     "  Fast-RSI length", group=G_MST, minval=1, maxval=50, tooltip="Short RSI for MR timing (Connors RSI-2 style). M5 default 2.")
i_frsi_ob   = input.float(90.0,"  Fast-RSI OB", group=G_MST, minval=50.0, maxval=100.0)
i_frsi_os   = input.float(10.0,"  Fast-RSI OS", group=G_MST, minval=0.0,  maxval=50.0)
i_mst_sz    = input.string("Small", "  Text size", group=G_MST, options=["Tiny","Small","Normal"])
col_mst_exp = input.color(color.new(#00E676,0), "  Expanding-up color", group=G_MST)
col_mst_dn  = input.color(color.new(#FF1744,0), "  Bearish color", group=G_MST)
col_mst_fade= input.color(color.new(#FF9800,0), "  Fading color", group=G_MST)

float _mst_slope = smi_sc - smi_sc[1]
bool  _mst_bull  = smi_sc > smi_esc
bool  _mst_bear  = smi_sc < smi_esc
bool  _mst_accel = math.abs(_mst_slope) > math.abs(nz(_mst_slope[1]))
int   _mst_state = not i_mst_on ? 0 : (_mst_bull and _mst_slope > 0.0 and _mst_accel) ? 1 : (_mst_bear and _mst_slope < 0.0 and _mst_accel) ? -1 : (_mst_bull or _mst_bear) ? 2 : 0
bool  _mst_exit_long  = i_mst_on and _smi_xdn and nz(smi_sc[1]) > 0.0
bool  _mst_exit_short = i_mst_on and _smi_xup and nz(smi_sc[1]) < 0.0

float _frsi      = ta.rsi(close, i_frsi_len)
bool  _frsi_ob   = _frsi >= i_frsi_ob
bool  _frsi_os   = _frsi <= i_frsi_os
bool  _frsi_ob_f = i_mst_on and _frsi_ob and not _frsi_ob[1]
bool  _frsi_os_f = i_mst_on and _frsi_os and not _frsi_os[1]

string _mst_szr = switch i_mst_sz
    "Tiny"   => size.tiny
    "Normal" => size.normal
    =>          size.small
float _mst_yo = math.abs(nz(upper_, 100.0) - nz(lower_, -100.0)) * 0.07

var array<label> _mst_lbs = array.new_label()
if i_mst_on and i_mst_mark and _mst_exit_long
    array.push(_mst_lbs, label.new(bar_index, smi_sc + _mst_yo, "fade▼", style=label.style_label_down, color=color.new(col_mst_fade, 78), textcolor=col_mst_fade, size=_mst_szr, tooltip="Momentum FADING from bull — SMI rolled under its EMA while positive.\nBreakout-exit read ('exit when momentum disappears'), or MR-short timing."))
    if array.size(_mst_lbs) > 200
        label.delete(array.shift(_mst_lbs))
if i_mst_on and i_mst_mark and _mst_exit_short
    array.push(_mst_lbs, label.new(bar_index, smi_sc - _mst_yo, "fade▲", style=label.style_label_up, color=color.new(col_mst_fade, 78), textcolor=col_mst_fade, size=_mst_szr, tooltip="Momentum FADING from bear — SMI rolled above its EMA while negative.\nBreakout-exit read, or MR-long timing."))
    if array.size(_mst_lbs) > 200
        label.delete(array.shift(_mst_lbs))
if i_mst_on and i_mst_mark and _frsi_os_f
    array.push(_mst_lbs, label.new(bar_index, smi_sc - _mst_yo, "R-OS", style=label.style_label_up, color=color.new(col_mst_exp, 80), textcolor=col_mst_exp, size=_mst_szr, tooltip="Fast-RSI(" + str.tostring(i_frsi_len) + ") oversold " + str.tostring(_frsi, "#") + " ≤ " + str.tostring(i_frsi_os, "#") + " — mean-reversion BUY timing."))
    if array.size(_mst_lbs) > 200
        label.delete(array.shift(_mst_lbs))
if i_mst_on and i_mst_mark and _frsi_ob_f
    array.push(_mst_lbs, label.new(bar_index, smi_sc + _mst_yo, "R-OB", style=label.style_label_down, color=color.new(col_mst_dn, 80), textcolor=col_mst_dn, size=_mst_szr, tooltip="Fast-RSI(" + str.tostring(i_frsi_len) + ") overbought " + str.tostring(_frsi, "#") + " ≥ " + str.tostring(i_frsi_ob, "#") + " — mean-reversion SELL timing."))
    if array.size(_mst_lbs) > 200
        label.delete(array.shift(_mst_lbs))

_mst_pos = switch i_mst_tpos
    "top_left"      => position.top_left
    "top_center"    => position.top_center
    "middle_left"   => position.middle_left
    "middle_right"  => position.middle_right
    "bottom_left"   => position.bottom_left
    "bottom_center" => position.bottom_center
    "bottom_right"  => position.bottom_right
    =>                 position.top_right
var table _mst_t = table.new(_mst_pos, 2, 5, border_width=1, frame_color=color.new(#2c3440, 0), frame_width=1)
if barstate.islast
    table.clear(_mst_t, 0, 0, 1, 4)
    if i_mst_on and i_mst_tbl
        color _bg   = color.new(#161B22, 0)
        color _lbc  = #6b7686
        string _sst = _mst_state == 1 ? "EXPANDING ↑" : _mst_state == -1 ? "EXPANDING ↓" : _mst_state == 2 ? "FADING" : "FLAT"
        color  _scl = _mst_state == 1 ? col_mst_exp : _mst_state == -1 ? col_mst_dn : _mst_state == 2 ? col_mst_fade : #9aa4b2
        string _exs = _mst_exit_long ? "FADE ▼" : _mst_exit_short ? "FADE ▲" : "—"
        color  _exc = (_mst_exit_long or _mst_exit_short) ? col_mst_fade : #9aa4b2
        string _rst = _frsi_ob ? "OB " + str.tostring(_frsi, "#") : _frsi_os ? "OS " + str.tostring(_frsi, "#") : str.tostring(_frsi, "#")
        color  _rcl = _frsi_ob ? col_mst_dn : _frsi_os ? col_mst_exp : #9aa4b2
        table.cell(_mst_t, 0, 0, "MOMENTUM", text_color=#8b98a8, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 1, 0, "", bgcolor=_bg)
        table.cell(_mst_t, 0, 1, "state",   text_color=_lbc, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 1, 1, _sst,      text_color=_scl, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 0, 2, "SMI slope", text_color=_lbc, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 1, 2, str.tostring(_mst_slope, "#.#"), text_color=#c9d1d9, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 0, 3, "fast-RSI" + str.tostring(i_frsi_len), text_color=_lbc, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 1, 3, _rst,      text_color=_rcl, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 0, 4, "exit",    text_color=_lbc, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)
        table.cell(_mst_t, 1, 4, _exs,      text_color=_exc, text_size=_mst_szr, bgcolor=_bg, text_halign=text.align_left)

// == END QC_MomentumConfluence v12.0 | CE:0 RE:0 | +MomentumState(Exp/Fade/FastRSI) ==