OMNI-GOD-UNIFIED.pine (15).txt
Copy
// This source code is subject to the terms of the Mozilla Public License 2.0
// © OMNI-GOD ∞ [UNIFIED MASTER SUITE — Gold & BTC Adaptive]
// Merged from: OMNI-TITAN v10 GOD EDITION + OMNI-GOD3 HYBRID v9 QUANTUM + kim-TITAN v9 ASCENSION
//
// ── BUGS FIXED IN THIS MERGE (vs the 3 originals) ─────────────────────────────
// 1. `abs()` / `round()` are NOT Pine functions → replaced with math.abs / math.round
// 2. Function defined INSIDE an if-block (drawFieldForZone) → moved to global scope
// 3. `lookahead=barmerge.lookahead_on` removed (Pine v6 removed that parameter;
// the non-repaint pattern is now request.security(sym, "D", high[1]))
// 4. NFP detection used dayofweek == 6 (Saturday) → fixed to 5 (Friday)
// 5. Elastic EMA length built with float math fed to ta.ema(int) → pure int bands
// 6. Breaker arrays grew unbounded → capped at 8
// 7. Radar (top-right) and Analysis table (top-right) overlapped → Analysis moved top-left
// 8. float→int implicit conversions (color transparency, lengths) eliminated
// 9. TP2+SL same-bar double exit race (if/if) → if/else-if exit ladder
// 10. Dark pool levels now persist (var) instead of resetting each bar
// ═══════════════════════════════════════════════════════════════════════════════
//@version=6
indicator("GOD Conviction Engine (automatic)", shorttitle="GOD CE", overlay=true, max_labels_count=500, max_boxes_count=500, max_lines_count=500, max_bars_back=500)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 1: ENUMS & INPUTS ║
// ╚═══════════════════════════════════════════════════════════════════╝
enum RiskProfile
Aggressive = "Aggressive (65% Confluence)"
Standard = "Standard (70% Confluence)"
Strict = "Strict (80% Confluence)"
string GRP_UI = "━━━ VISUAL TOGGLES ━━━"
string GRP_CORE = "━━━ CORE ENGINE & BASELINE ━━━"
string GRP_MACRO = "━━━ MACRO CORRELATION (DXY / NDX) ━━━"
string GRP_ELAST = "━━━ ELASTIC EMA & REGIME ━━━"
string GRP_LIQ = "━━━ LIQUIDITY, ASIAN & SESSIONS ━━━"
string GRP_SMC = "━━━ SMC QUANTUM ZONES & TIERS ━━━"
string GRP_IND = "━━━ MACD / RSI / OSCILLATORS ━━━"
string GRP_ADV = "━━━ WYCKOFF / DARK POOL / ASOC ━━━"
string GRP_RISK = "━━━ RISK, SIZING & HYDRA TRAIL ━━━"
string GRP_ALERT = "━━━ ALERTS & WEBHOOKS ━━━"
string GRP_LAYOUT = "━━━ LAYOUT (TABLE PLACEMENT) ━━━"
string GRP_PATTERN = "━━━ PATTERN — H&S + ICHIMOKU ━━━"
string GRP_ROUTE = "━━━ STRATEGY ROUTER ━━━"
// ── Visual Toggles ──
bool showEmas = input.bool(true, "Show Trend EMAs", group=GRP_UI)
bool showHma = input.bool(true, "Show HMA 50 Baseline", group=GRP_UI)
bool showElastic = input.bool(true, "Show Elastic EMA", group=GRP_UI)
bool showOB = input.bool(true, "Show Order Blocks (OB)", group=GRP_UI)
bool showFVG = input.bool(true, "Show Fair Value Gaps (FVG)", group=GRP_UI)
bool showBreakers = input.bool(true, "Show Breaker Blocks (RBS/SBR)", group=GRP_UI)
bool showSweeps = input.bool(true, "Show Liquidity Sweeps", group=GRP_UI)
bool showAsian = input.bool(true, "Show Asian Range Box (00-07 UTC)", group=GRP_UI)
bool showLevels = input.bool(true, "Show Key OHLC & Pivot Lines", group=GRP_UI)
bool showPD = input.bool(true, "Show PD Array Bands", group=GRP_UI)
bool showVwap = input.bool(true, "Show VWAP Line", group=GRP_UI)
bool showMktStr = input.bool(true, "Show Structure (BOS/CHoCH/Swings)", group=GRP_UI)
bool showQuantum = input.bool(true, "Show Quantum Field Gravity", group=GRP_UI)
bool showDarkPool = input.bool(true, "Show Dark Pool Levels", group=GRP_UI)
bool showLiqHeat = input.bool(true, "Show Liquidity Heat Projection", group=GRP_UI)
bool showHeatmap = input.bool(true, "Show Confluence Heatmap", group=GRP_UI)
bool showNarrative = input.bool(true, "Show Narrative Voice Label", group=GRP_UI)
bool showHUD = input.bool(true, "Show Execution HUD (position: Layout group)", group=GRP_UI)
bool showRadar = input.bool(true, "Show Confluence Radar (position: Layout group)", group=GRP_UI)
bool showAnalysis = input.bool(true, "Show Analysis Table (position: Layout group)", group=GRP_UI)
bool showRegime = input.bool(true, "Show Regime Label On Chart", group=GRP_UI)
bool showKillzones = input.bool(false, "Show Session Backgrounds", group=GRP_UI)
// ── Core Engine ──
int fastLen = input.int(9, "Fast EMA", group=GRP_CORE, minval=1)
int medLen = input.int(21, "Medium EMA", group=GRP_CORE, minval=1)
int slowLen = input.int(50, "Slow EMA", group=GRP_CORE, minval=1)
int trendLen = input.int(200, "Trend EMA", group=GRP_CORE, minval=1)
int hmaLen = input.int(50, "Non-Lag Baseline (HMA)", group=GRP_CORE, minval=5)
int atrLen = input.int(14, "ATR Length", group=GRP_CORE, minval=1)
int adxLen = input.int(14, "ADX Length", group=GRP_CORE, minval=1)
int adxMin = input.int(18, "ADX Min Strength", group=GRP_CORE, minval=5)
int zLen = input.int(20, "Z-Score Window", group=GRP_CORE, minval=5)
int volLen = input.int(20, "Volume SMA Length", group=GRP_CORE, minval=5)
bool useVolImb = input.bool(true, "Volume Imbalance (2.0x SMA)", group=GRP_CORE)
bool useVolConf = input.bool(false, "Require Volume Confirmation", group=GRP_CORE)
bool useKillzone = input.bool(false, "Killzone Session Filter (London+NY)",group=GRP_CORE)
bool useBtcWeekend = input.bool(true, "BTC Weekend Thin-Liquidity Filter", group=GRP_CORE, tooltip="Sat/Sun: +3% stricter thresholds, 15% wider SL buffers (BTC only)")
// ── Pattern recognition — Head & Shoulders + Ichimoku ──
bool showIchimoku = input.bool(true, "Ichimoku: Show Clouds (Daily + TF)", group=GRP_PATTERN, tooltip="Classic 9/26/52. Cloud = the Kumo surrounding the current bar")
bool useIchimoku = input.bool(true, "Ichimoku: Score Cloud Context", group=GRP_PATTERN)
bool showHs = input.bool(true, "H&S: Show Pattern Labels & Neckline", group=GRP_PATTERN)
bool useHs = input.bool(true, "H&S: Score Neckline Breaks", group=GRP_PATTERN)
int hsAtrTol = input.int(2, "H&S Shoulder Tolerance (×ATR)", group=GRP_PATTERN, minval=1, maxval=5)
// ── Macro Correlation ──
bool useMacro = input.bool(true, "Enable Macro Engine", group=GRP_MACRO)
bool autoMacro = input.bool(true, "Auto-Detect Macro Asset", group=GRP_MACRO, tooltip="DXY for Gold (inverse), NDX for BTC (direct)")
string manualMacro = input.string("TVC:DXY", "Manual Macro Symbol", group=GRP_MACRO)
// ── Elastic Trends & Regime ──
bool useElastic = input.bool(true, "Enable Elastic EMA (Hurst-driven)", group=GRP_ELAST)
int minElastic = input.int(5, "Min Elastic Length", group=GRP_ELAST, minval=3, maxval=20)
int maxElastic = input.int(55, "Max Elastic Length", group=GRP_ELAST, minval=10, maxval=100)
float regTrendTh = input.float(0.20, "Trend Slope Threshold", group=GRP_ELAST, step=0.05, minval=0.05)
int regimeLen = input.int(20, "Regime Lookback (BB/Slope)", group=GRP_ELAST, minval=10, maxval=50)
// ── Liquidity, Asian & Sessions ──
int swingLen = input.int(10, "Swing Pivot Length", group=GRP_LIQ, minval=3, maxval=30)
float eqTolerance = input.float(0.30, "EQH/EQL ATR Tolerance", group=GRP_LIQ, step=0.05, minval=0.05)
bool useWeekOpen = input.bool(true, "Use Weekly Open Level", group=GRP_LIQ)
// ── SMC Quantum Zones & Tiers ──
int maxOBs = input.int(5, "Max Active Order Blocks", group=GRP_SMC, minval=1, maxval=10)
int maxFVGs = input.int(5, "Max Active FVGs", group=GRP_SMC, minval=1, maxval=10)
int fvgMinPct = input.int(20, "FVG Min Size (% ATR)", group=GRP_SMC, minval=5, maxval=100)
int zoneMaxAge = input.int(120, "Zone Max Age (bars)", group=GRP_SMC, minval=20, maxval=500)
float qGravity = input.float(0.50, "Quantum Gravity Steepness", group=GRP_SMC, step=0.1, minval=0.1, maxval=2.0)
float qCorePct = input.float(0.35, "Zone Core Probability (0-1)", group=GRP_SMC, step=0.05, minval=0.1, maxval=0.9, tooltip="Central band of a zone with highest reaction probability. Core taps score higher than shadow taps.")
bool useLiqHier = input.bool(true, "Enable Liquidity Tiers", group=GRP_SMC)
int maxLiqTier = input.int(4, "Max Tier Level", group=GRP_SMC, minval=1, maxval=4)
bool showTierTags = input.bool(true, "Show Tier Badges on Zones", group=GRP_SMC)
// ── Oscillators ──
int macdFast = input.int(12, "MACD Fast", group=GRP_IND, minval=1)
int macdSlow = input.int(26, "MACD Slow", group=GRP_IND, minval=1)
int macdSig = input.int(9, "MACD Signal", group=GRP_IND, minval=1)
int rsiLen = input.int(14, "RSI Length", group=GRP_IND, minval=2)
int rsiOBLvl = input.int(70, "RSI Overbought", group=GRP_IND, minval=55, maxval=90)
int rsiOSLvl = input.int(30, "RSI Oversold", group=GRP_IND, minval=10, maxval=45)
// ── Wyckoff / Dark Pool / ASOC ──
bool useWyckoff = input.bool(true, "Enable Wyckoff Phase Detector", group=GRP_ADV)
bool useVolDiv = input.bool(true, "Enable Volume Divergence", group=GRP_ADV)
bool useDarkPool = input.bool(true, "Calculate Dark Pool Levels", group=GRP_ADV)
bool useASOC = input.bool(true, "Enable ASOC Self-Optimization", group=GRP_ADV)
int asocWindow = input.int(20, "ASOC Sample Window (Trades)", group=GRP_ADV, minval=5, maxval=50)
float optStep = input.float(1.0, "ASOC Adjust Step (%)", group=GRP_ADV, minval=0.1, step=0.1, tooltip="Hybrid self-opt: per completed sample, WR<45% tightens threshold by this, WR>70% loosens it")
bool useFactorGate = input.bool(true, "Require Min Distinct Factors", group=GRP_ADV, tooltip="Blocks entries where one dominant factor inflates the score")
int minFactors = input.int(4, "Min Distinct Factors to Enter", group=GRP_ADV, minval=2, maxval=10)
// ── Risk & Hydra Execution ──
RiskProfile userRisk = input.enum(RiskProfile.Standard, "Execution Strictness", group=GRP_RISK)
float slMult = input.float(1.0, "SL ATR Buffer Multiplier", group=GRP_RISK, minval=0.1, step=0.1)
float trailATR = input.float(1.2, "Hydra Trail ATR Distance", group=GRP_RISK, minval=0.2, step=0.1, tooltip="Phase-1 trail: how many ATR below the bar's close the stop sits once a trade is +0.3R. 0.6 = old very-tight behavior, 1.2–1.5 = one bar of room")
float tp1RR = input.float(1.5, "TP1 Risk:Reward", group=GRP_RISK, minval=0.5, step=0.25)
float tp2RR = input.float(3.0, "TP2 Risk:Reward", group=GRP_RISK, minval=1.0, step=0.5)
bool usePVS = input.bool(true, "PVS Mode: TP2 partial, TP3 runner exit", group=GRP_RISK)
float accBalance = input.float(10000.0, "Account Balance ($)", group=GRP_RISK, minval=100.0)
float riskPct = input.float(1.0, "Risk % Per Trade", group=GRP_RISK, minval=0.1, maxval=10.0, step=0.1)
float tickValue = input.float(1.0, "Tick/Point Value", group=GRP_RISK, minval=0.0, step=0.1)
bool useHydra = input.bool(true, "Enable Hydra 4-Phase Trailing Stop", group=GRP_RISK)
bool moveBEat50 = input.bool(true, "Move to BE at 0.5R Gain", group=GRP_RISK)
bool useProfitLock = input.bool(true, "Profit-Lock Trail (0.5R retrace)", group=GRP_RISK, tooltip="After +1.5R, locks profit if price retraces 0.5R from the peak")
float beOffsetR = input.float(0.10, "TP1 BE Lock Offset (R)", group=GRP_RISK, step=0.05, minval=0.0, tooltip="At TP1 the stop moves to entry + this many R (0 = plain BE)")
bool useSpikeShield = input.bool(true, "Spike Shield (no entries on vol spikes)", group=GRP_RISK)
// ── Strategy Router (dynamic playbook selection) ──
bool useRouter = input.bool(true, "Enable Strategy Router", group=GRP_ROUTE, tooltip="ON: router picks the playbook per market state — MOMENTUM / MEAN-REV / POST-SPIKE / BREAKOUT / FLAT. OFF: legacy single-strategy score mode")
int modeDwell = input.int(2, "Mode switch dwell (bars)", group=GRP_ROUTE, minval=1, maxval=10, tooltip="Candidate mode must persist this many confirmed bars before the router switches")
bool alertOnMode = input.bool(false, "Alert on strategy-mode change", group=GRP_ROUTE)
bool showGeoLines = input.bool(false, "Show geometry lines (support/resistance trendlines)", group=GRP_ROUTE, tooltip="Optional overlay of the detected trendlines. Off by default — the geometry drives decisions, not decoration")
float spikeAtrMult = input.float(2.2, "Spike Shield ATR Multiple", group=GRP_RISK, step=0.1, minval=1.0)
// ── Alerts ──
bool alertOnSignal = input.bool(true, "Alert: New Setup Signal", group=GRP_ALERT)
bool alertOnEntry = input.bool(true, "Alert: Order Execution", group=GRP_ALERT)
bool alertOnZone = input.bool(true, "Alert: Zone Entry/Tap", group=GRP_ALERT)
bool alertOnExit = input.bool(true, "Alert: TP / BE / SL Exit", group=GRP_ALERT)
// ── Layout — dynamic table placement ──
string hudPos = input.string("bottom_right", "Execution HUD Position", options=["bottom_right", "bottom_left", "top_right", "top_left"], group=GRP_LAYOUT)
string radarPos = input.string("top_right", "Confluence Radar Position", options=["bottom_right", "bottom_left", "top_right", "top_left"], group=GRP_LAYOUT)
string analysisPos = input.string("top_left", "Analysis Table Position", options=["bottom_right", "bottom_left", "top_right", "top_left"], group=GRP_LAYOUT)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 2: SYMBOL DETECTION & ADAPTIVE PARAMETERS ║
// ╚═══════════════════════════════════════════════════════════════════╝
string tickerID = str.upper(syminfo.ticker)
bool isGold = str.contains(tickerID, "XAU") or str.contains(tickerID, "GOLD")
bool isBTC = str.contains(tickerID, "BTC")
string symbolMode = isGold ? "GOLD 🥇" : isBTC ? "BTC ₿" : "STANDARD"
float symImpulse = isGold ? 0.85 : isBTC ? 1.60 : 1.0
float symFvgScl = isGold ? 0.75 : isBTC ? 1.50 : 1.0
float symSlBuf = isGold ? 1.00 : isBTC ? 1.40 : 1.0
float symOhlcW = isGold ? 2.00 : isBTC ? 0.60 : 1.0
float symSweepW = isGold ? 1.00 : isBTC ? 1.40 : 1.0
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 3: SESSION / KILLZONE & GOLD TIME AWARENESS ║
// ╚═══════════════════════════════════════════════════════════════════╝
int utcHour = hour(time, "UTC")
int utcDay = dayofweek(time, "UTC")
int utcDayMonth = dayofmonth(time, "UTC")
bool inAsian = utcHour >= 0 and utcHour < 7
bool inLondon = utcHour >= 7 and utcHour < 12
bool inLondonKZ = utcHour >= 7 and utcHour < 10
bool inNY = utcHour >= 12 and utcHour < 17
bool inNYKZ = utcHour >= 12 and utcHour < 15
bool inNYClose = utcHour >= 17 and utcHour < 20
bool inKillzone = inLondonKZ or inNYKZ
bool sessionOk = not useKillzone or not timeframe.isintraday or inKillzone
// London Fix window (15:00 UTC) — manipulation warning for Gold
bool inLondonFix = isGold and utcHour == 15
// NFP: first Friday of the month (dayofweek: 0=Sunday … 5=Friday)
bool isNfpDay = isGold and utcDay == 5 and utcDayMonth <= 7
bool cautionMode = isNfpDay or inLondonFix
// BTC trades 24/7 — Saturday/Sunday liquidity is thin: wider stops, stricter entries
bool btcWeekend = isBTC and (utcDay == 0 or utcDay == 6)
bgcolor(showKillzones and timeframe.isintraday and inAsian ? color.new(color.purple, 94) : na, title="Asian Session")
bgcolor(showKillzones and timeframe.isintraday and inLondon ? color.new(color.blue, 92) : na, title="London Session")
bgcolor(showKillzones and timeframe.isintraday and inNY ? color.new(color.orange, 92) : na, title="NY Session")
bgcolor(showKillzones and timeframe.isintraday and inNYClose ? color.new(color.gray, 94) : na, title="NY Close")
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 4: TIMEFRAME INTELLIGENCE & ADAPTIVE HTF ║
// ╚═══════════════════════════════════════════════════════════════════╝
int tfMult = timeframe.multiplier
bool isScalp = timeframe.isintraday and tfMult <= 5
bool isIntraday = timeframe.isintraday and tfMult > 5 and tfMult <= 60
string tfLabel = isScalp ? "SCALP" : isIntraday ? "INTRADAY" : "SWING"
string htf = isScalp ? "60" : isIntraday ? "240" : "W"
float regAtrMult = isScalp ? 0.8 : isIntraday ? 1.2 : 2.0
string biasTF1 = isScalp ? "15" : isIntraday ? "60" : "240"
string biasTF2 = isScalp ? "60" : isIntraday ? "240" : "1D"
string biasTF3 = isScalp ? "240" : isIntraday ? "1D" : "1W"
string tf1Label = isScalp ? "15m" : isIntraday ? "1H" : "4H"
string tf2Label = isScalp ? "1H" : isIntraday ? "4H" : "Daily"
string tf3Label = isScalp ? "4H" : isIntraday ? "Daily" : "Weekly"
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 5: PURE HELPER FUNCTIONS ║
// ╚═══════════════════════════════════════════════════════════════════╝
// Sigmoid activation for quantum field gravity
f_sigmoid(float x, float steepness) =>
1.0 / (1.0 + math.exp(-steepness * x))
// Map a layout input to a Pine position constant
f_pos(string p) =>
p == "top_left" ? position.top_left : p == "top_right" ? position.top_right : p == "bottom_left" ? position.bottom_left : position.bottom_right
// Hurst-driven elastic EMA length — pure INT math (ta.ema needs int length)
// Trending (high Hurst) → short, responsive EMA · Choppy (low Hurst) → long, smooth EMA
f_elasticLen(float hVal) =>
int el = useElastic ? (hVal > 0.66 ? minElastic : hVal > 0.34 ? (minElastic + maxElastic) / 2 : maxElastic) : fastLen
el
// Multi-TF bias payload
f_biasData() =>
float bE50 = ta.ema(close, 50)
float bE200 = ta.ema(close, 200)
bool bBull = close > bE50 and bE50 > bE200
bool bBear = close < bE50 and bE50 < bE200
float bDist = (close - bE50) / math.max(bE50, 0.001) * 100.0
[bBull, bBear, bDist]
// HTF impulse / order block payload
f_obData() =>
float oAtr = ta.rma(ta.tr, atrLen)
bool oBull = (close - open) > oAtr * 1.5 and close > high[1] and close[1] < open[1]
bool oBear = (open - close) > oAtr * 1.5 and close < low[1] and close[1] > open[1]
[oBull, oBear, high[1], low[1]]
// Macro payload
f_macroData() =>
float mC = close
float mE50 = ta.ema(close, 50)
float mE200 = ta.ema(close, 200)
[mC, mE50, mE200]
// Radar row helper
f_radar_row(table tbl, int row, string factor, string status, bool isPass) =>
table.cell(tbl, 0, row, factor, bgcolor=color.rgb(10, 10, 10), text_color=color.gray, text_size=size.small)
table.cell(tbl, 1, row, status, bgcolor=isPass ? color.new(color.green, 75) : color.new(color.red, 75), text_color=color.white, text_size=size.small)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 6: MACRO CORRELATION ENGINE (DXY / NDX) ║
// ═══════════════════════════════════════════════════════════════════╝
string activeMacroSym = autoMacro ? (isGold ? "TVC:DXY" : isBTC ? "NASDAQ:NDX" : manualMacro) : manualMacro
string macroName = str.contains(activeMacroSym, "DXY") ? "DXY" : str.contains(activeMacroSym, "NDX") ? "NDX" : "MACRO"
[macroClose, macroEma50, macroEma200] = request.security(activeMacroSym, timeframe.period, f_macroData(), ignore_invalid_symbol=true)
bool macroValid = useMacro and not na(macroClose) and not na(macroEma50)
// Inverse for Gold: DXY DOWN = GOLD BULLISH. Direct for BTC: NDX UP = BTC BULLISH.
bool isInverseMacro = isGold or (not isBTC and str.contains(activeMacroSym, "DXY"))
bool macroBullish = macroValid and (isInverseMacro ? macroClose < macroEma50 and macroEma50 < macroEma200 : macroClose > macroEma50 and macroEma50 > macroEma200)
bool macroBearish = macroValid and (isInverseMacro ? macroClose > macroEma50 and macroEma50 > macroEma200 : macroClose < macroEma50 and macroEma50 < macroEma200)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 7: CORE ENGINE, VOLATILITY & 4-STATE REGIME ║
// ╚═══════════════════════════════════════════════════════════════════╝
float atr = ta.rma(ta.tr, atrLen)
float atrAvg = ta.sma(atr, 20)
float atrRatio = atr / math.max(atrAvg, 0.001)
bool lowVol = atrRatio < 0.70
bool highVol = atrRatio > 1.50
float emaF = ta.ema(close, fastLen)
float emaM = ta.ema(close, medLen)
float emaS = ta.ema(close, slowLen)
float emaT = ta.ema(close, trendLen)
float hmaBase = ta.hma(close, hmaLen)
[_diP, _diM, adxLine] = ta.dmi(adxLen, adxLen)
bool adxOk = adxLine > adxMin
float volSma = ta.sma(volume, volLen)
bool volImbalance = useVolImb ? volume > volSma * 2.0 : volume > volSma * 1.2
bool volStrong = volume > volSma * 1.2
bool volOk = not useVolConf or volStrong
float zBase = ta.sma(close, zLen)
float zDev = ta.stdev(close, zLen)
float zScore = zDev > 0 ? (close - zBase) / zDev : 0.0
bool bullStack = emaF > emaM and emaM > emaS and close > emaT and close > hmaBase
bool bearStack = emaF < emaM and emaM < emaS and close < emaT and close < hmaBase
// ── Regime detection (Hurst persistence + BB width + ADX + slope) ──
int regimeSlopeLen = math.max(regimeLen / 2, 5)
float bbMid = ta.sma(close, regimeLen)
float bbDev = ta.stdev(close, regimeLen)
float bbWidth = 2 * bbDev / math.max(bbMid, 0.001)
float hurst = math.log(ta.atr(10) / math.max(ta.atr(20), 0.0000001)) / math.log(2)
float hurstNorm = math.max(0.0, math.min(1.0, (hurst + 1.0) / 2.0))
float slope = ta.linreg(close, regimeSlopeLen, 0) - ta.linreg(close, regimeSlopeLen, 1)
float slopeNorm = slope / math.max(atr, 0.001)
bool highADX = adxLine > 25
bool lowADX = adxLine < 18
bool wideBB = bbWidth > ta.sma(bbWidth, regimeLen) * 1.3
bool narrowBB = bbWidth < ta.sma(bbWidth, regimeLen) * 0.7
bool priceAbove = close > emaS
bool priceBelow = close < emaS
string regimeStr = "◆ RANGING"
color regimeCol = color.new(color.yellow, 50)
if highADX and priceAbove and slopeNorm > regTrendTh
regimeStr := "▲ TRENDING BULL"
regimeCol := color.new(color.green, 50)
else if highADX and priceBelow and slopeNorm < -regTrendTh
regimeStr := "▼ TRENDING BEAR"
regimeCol := color.new(color.red, 50)
else if highVol and wideBB and not highADX
regimeStr := "⚡ VOLATILE"
regimeCol := color.new(color.orange, 50)
else if lowVol and narrowBB and lowADX
regimeStr := "🌀 CHOPPY"
regimeCol := color.new(color.gray, 50)
else if highVol and highADX and (priceAbove or priceBelow) and (slopeNorm > 0.3 or slopeNorm < -0.3)
regimeStr := "🚀 BREAKOUT"
regimeCol := color.new(color.blue, 50)
else if (ta.cross(close, emaS) or ta.cross(close, emaT)) and math.abs(slopeNorm) < 0.1
regimeStr := "↺ REVERSAL"
regimeCol := color.new(color.purple, 50)
bool regimeVolatile = regimeStr == "⚡ VOLATILE" or regimeStr == "🚀 BREAKOUT"
string volRegimeStr = lowVol ? "LOW VOL 🤏 (Chop)" : highVol ? "HIGH VOL ⚡ (Expand)" : "NORMAL VOL"
if showRegime
var label regimeLabel = na
if barstate.islast
if not na(regimeLabel)
label.delete(regimeLabel)
regimeLabel := label.new(bar_index, high + atr * 1.5, regimeStr, color=regimeCol, textcolor=color.white, style=label.style_label_down, size=size.small)
// ── Elastic EMA (Hurst-driven) ──
// Pine v6: ta.ema() requires a SIMPLE int length. A runtime (series) length is
// implemented as an explicit EMA recursion with a dynamic smoothing alpha:
// alpha = 2 / (len + 1) ; ema = alpha*close + (1-alpha)*ema[1]
int elasticLen = f_elasticLen(hurstNorm)
var float emaElastic = na
float elAlpha = 2.0 / (elasticLen + 1.0)
emaElastic := na(emaElastic[1]) ? close : elAlpha * close + (1.0 - elAlpha) * emaElastic[1]
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 8: PD ARRAY & KEY INSTITUTIONAL LEVELS ║
// ╚═══════════════════════════════════════════════════════════════════╝
float rangeH = ta.highest(high, 50)
float rangeL = ta.lowest(low, 50)
float rangeEQ = (rangeH + rangeL) * 0.5
bool inPremium = close > rangeEQ
bool inDiscount = close < rangeEQ
// Pine v6: no lookahead parameter — high[1] on "D" is the non-repaint previous-day high
float pdHigh = request.security(syminfo.tickerid, "D", high[1])
float pdLow = request.security(syminfo.tickerid, "D", low[1])
float pdClose = request.security(syminfo.tickerid, "D", close[1])
float pwHigh = request.security(syminfo.tickerid, "W", high[1])
float pwLow = request.security(syminfo.tickerid, "W", low[1])
// Current week's open — key institutional reference (especially BTC)
float weekOpen = request.security(syminfo.tickerid, "W", open)
float dailyPivot = not na(pdHigh) and not na(pdLow) and not na(pdClose) ? (pdHigh + pdLow + pdClose) / 3.0 : na
bool abovePivot = not na(dailyPivot) and close >= dailyPivot
float recentSwingLow = ta.lowest(low, 5)
float recentSwingHigh = ta.highest(high, 5)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 9: MULTI-TIMEFRAME BIAS ENGINE ║
// ╚═══════════════════════════════════════════════════════════════════╝
[tf1Bull, tf1Bear, tf1Dist] = request.security(syminfo.tickerid, biasTF1, f_biasData())
[tf2Bull, tf2Bear, tf2Dist] = request.security(syminfo.tickerid, biasTF2, f_biasData())
[tf3Bull, tf3Bear, tf3Dist] = request.security(syminfo.tickerid, biasTF3, f_biasData())
int biasScore = 0
if tf1Bull
biasScore += 1
if tf2Bull
biasScore += 1
if tf3Bull
biasScore += 1
if tf1Bear
biasScore -= 1
if tf2Bear
biasScore -= 1
if tf3Bear
biasScore -= 1
string overallBias = biasScore >= 2 ? "BULLISH" : biasScore <= -2 ? "BEARISH" : "MIXED"
color biasBg = biasScore >= 2 ? color.new(color.green, 45) : biasScore <= -2 ? color.new(color.red, 45) : color.new(color.orange, 48)
string biasEmoji = biasScore >= 2 ? "▲ BULLISH" : biasScore <= -2 ? "▼ BEARISH" : "◆ MIXED"
string tf1Str = (tf1Bull ? "▲ BULL " : tf1Bear ? "▼ BEAR " : "◆ FLAT ") + str.tostring(nz(tf1Dist), "#.##") + "% vs EMA50"
string tf2Str = (tf2Bull ? "▲ BULL " : tf2Bear ? "▼ BEAR " : "◆ FLAT ") + str.tostring(nz(tf2Dist), "#.##") + "% vs EMA50"
string tf3Str = (tf3Bull ? "▲ BULL " : tf3Bear ? "▼ BEAR " : "◆ FLAT ") + str.tostring(nz(tf3Dist), "#.##") + "% vs EMA50"
color tf1Col = tf1Bull ? color.new(color.green, 55) : tf1Bear ? color.new(color.red, 55) : color.new(color.orange, 55)
color tf2Col = tf2Bull ? color.new(color.green, 55) : tf2Bear ? color.new(color.red, 55) : color.new(color.orange, 55)
color tf3Col = tf3Bull ? color.new(color.green, 55) : tf3Bear ? color.new(color.red, 55) : color.new(color.orange, 55)
// ── HTF Pivots ──
float htfPvH = request.security(syminfo.tickerid, biasTF2, ta.pivothigh(high, swingLen, swingLen))
float htfPvL = request.security(syminfo.tickerid, biasTF2, ta.pivotlow(low, swingLen, swingLen))
var float lastHtfPH = na
var float lastHtfPL = na
if not na(htfPvH)
lastHtfPH := htfPvH
if not na(htfPvL)
lastHtfPL := htfPvL
// ── HTF Order Block feed ──
[htfBullImp, htfBearImp, htfObH, htfObL] = request.security(syminfo.tickerid, htf, f_obData())
bool isNewHtfBar = ta.change(time(htf)) != 0
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 10: ASIAN RANGE & LIQUIDITY GEOMETRY ║
// ╚═══════════════════════════════════════════════════════════════════╝
var float asianHigh = na
var float asianLow = na
var int asianStart = na
var box asianBox = na
if inAsian and not inAsian[1]
asianHigh := high
asianLow := low
asianStart := bar_index
if showAsian and timeframe.isintraday
if not na(asianBox)
box.delete(asianBox)
asianBox := box.new(bar_index, high, bar_index + 40, low, border_color=color.new(color.purple, 50), bgcolor=color.new(color.purple, 92), border_style=line.style_dashed)
else if inAsian
asianHigh := math.max(nz(asianHigh, high), high)
asianLow := math.min(nz(asianLow, low), low)
if showAsian and timeframe.isintraday and not na(asianBox)
box.set_top(asianBox, asianHigh)
box.set_bottom(asianBox, asianLow)
box.set_right(asianBox, bar_index + 20)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 11: MARKET STRUCTURE — BOS / CHoCH & SWINGS ║
// ╚═══════════════════════════════════════════════════════════════════╝
float ph = ta.pivothigh(high, swingLen, swingLen)
float pl = ta.pivotlow(low, swingLen, swingLen)
var float lastPH = na
var float lastPH2 = na
var float lastPL = na
var float lastPL2 = na
var int structState = 0
if not na(ph)
lastPH2 := lastPH
lastPH := ph
if not na(pl)
lastPL2 := lastPL
lastPL := pl
// BOS / CHoCH with displacement confirmation
bool bullBOS = not na(lastPL2) and not na(lastPH) and close > lastPH and (close - open) > atr * 0.3
bool bearBOS = not na(lastPH2) and not na(lastPL) and close < lastPL and (open - close) > atr * 0.3
bool bullCHoCH = not na(lastPL2) and not na(lastPH2) and close > lastPH2 and (close - open) > atr * 0.3
bool bearCHoCH = not na(lastPH2) and not na(lastPL2) and close < lastPL2 and (open - close) > atr * 0.3
if bullBOS or bullCHoCH
structState := 1
if bearBOS or bearCHoCH
structState := -1
bool isHH = not na(lastPH) and not na(lastPH2) and lastPH > lastPH2
bool isHL = not na(lastPL) and not na(lastPL2) and lastPL > lastPL2
bool isLH = not na(lastPH) and not na(lastPH2) and lastPH < lastPH2
bool isLL = not na(lastPL) and not na(lastPL2) and lastPL < lastPL2
bool bullStruct = structState == 1
bool bearStruct = structState == -1
string structStr = "◆ RANGING"
if bullStruct
structStr := "▲ BULLISH (BOS Confirmed)"
else if bearStruct
structStr := "▼ BEARISH (BOS Confirmed)"
else if bullCHoCH
structStr := "▲ CHoCH Bullish"
else if bearCHoCH
structStr := "▼ CHoCH Bearish"
else if isHH
structStr := "▲ HH — awaiting HL"
else if isHL
structStr := "▲ HL — awaiting HH"
else if isLH
structStr := "▼ LH — awaiting LL"
else if isLL
structStr := "▼ LL — awaiting LH"
color structCol = bullStruct ? color.new(color.green, 50) : bearStruct ? color.new(color.red, 50) : color.new(color.orange, 55)
// ── EQH / EQL — equal highs/lows = stacked stop-liquidity pools ──
bool isEQL = not na(lastPL) and not na(lastPL2) and math.abs(lastPL - lastPL2) <= atr * eqTolerance
bool isEQH = not na(lastPH) and not na(lastPH2) and math.abs(lastPH - lastPH2) <= atr * eqTolerance
float eqlLevel = (lastPL + lastPL2) / 2
float eqhLevel = (lastPH + lastPH2) / 2
bool nearEQL = isEQL and math.abs(close - eqlLevel) <= atr * 0.5
bool nearEQH = isEQH and math.abs(close - eqhLevel) <= atr * 0.5
if showMktStr and barstate.isconfirmed
if isHH
label.new(bar_index[swingLen], lastPH + atr * 0.4, "HH", textcolor=color.new(color.lime, 20), style=label.style_none, size=size.tiny)
if isHL
label.new(bar_index[swingLen], lastPL - atr * 0.4, "HL", textcolor=color.new(color.lime, 20), style=label.style_none, size=size.tiny)
if isLH
label.new(bar_index[swingLen], lastPH + atr * 0.4, "LH", textcolor=color.new(color.red, 20), style=label.style_none, size=size.tiny)
if isLL
label.new(bar_index[swingLen], lastPL - atr * 0.4, "LL", textcolor=color.new(color.red, 20), style=label.style_none, size=size.tiny)
if bullBOS
label.new(bar_index, high + atr * 0.6, "BOS ▲", textcolor=color.new(color.teal, 10), style=label.style_none, size=size.tiny)
if bearBOS
label.new(bar_index, low - atr * 0.6, "BOS ▼", textcolor=color.new(color.maroon, 10), style=label.style_none, size=size.tiny)
// ── Liquidity Sweeps (swing + Asian range) ──
bool bslSweep = not na(lastPH) and high > lastPH and close < lastPH and close < open
bool sslSweep = not na(lastPL) and low < lastPL and close > lastPL and close > open
bool sweepAsianH = not na(asianHigh) and high > asianHigh and close < asianHigh and close < open
bool sweepAsianL = not na(asianLow) and low < asianLow and close > asianLow and close > open
bool validBuySweep = sslSweep or sweepAsianL
bool validSellSweep = bslSweep or sweepAsianH
if showSweeps and barstate.isconfirmed
if validBuySweep
label.new(bar_index, low - atr * 0.6, "SSL ⚡", color=color.new(color.green, 8), textcolor=color.white, style=label.style_label_up, size=size.small)
if validSellSweep
label.new(bar_index, high + atr * 0.6, "BSL ⚡", color=color.new(color.red, 8), textcolor=color.white, style=label.style_label_down, size=size.small)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 12: OSCILLATORS — MACD / RSI / VWAP ENGINES ║
// ╚═══════════════════════════════════════════════════════════════════╝
[macdLine, macdSignal, macdHist] = ta.macd(close, macdFast, macdSlow, macdSig)
bool macdAbove = macdLine > macdSignal
bool macdBelow = macdLine < macdSignal
bool macdCross = ta.crossover(macdLine, macdSignal)
bool macdDeadX = ta.crossunder(macdLine, macdSignal)
bool macdHistUp = macdHist > macdHist[1]
bool bullDiv = low[1] < low[5] and macdLine[1] > macdLine[5] and low[1] == ta.lowest(low, 5)
bool bearDiv = high[1] > high[5] and macdLine[1] < macdLine[5] and high[1] == ta.highest(high, 5)
string macdStr = "◆ NEUTRAL"
if macdCross
macdStr := "▲ BULL CROSS ⚡"
else if macdDeadX
macdStr := "▼ BEAR CROSS ⚡"
else if bullDiv
macdStr := "▲ BULL DIVERGENCE"
else if bearDiv
macdStr := "▼ BEAR DIVERGENCE"
else if macdAbove and macdHistUp
macdStr := "▲ BULL STRONG"
else if macdAbove
macdStr := "▲ BULL"
else if macdBelow and not macdHistUp
macdStr := "▼ BEAR STRONG"
else if macdBelow
macdStr := "▼ BEAR"
color macdDispCol = color.new(color.orange, 48)
if macdAbove or macdCross or bullDiv
macdDispCol := color.new(color.green, 45)
else if macdBelow or macdDeadX or bearDiv
macdDispCol := color.new(color.red, 45)
float rsiVal = ta.rsi(close, rsiLen)
bool rsiOB = rsiVal >= rsiOBLvl
bool rsiOS = rsiVal <= rsiOSLvl
bool rsiBullRange = rsiVal > 50 and rsiVal < rsiOBLvl
bool rsiBearRange = rsiVal < 50 and rsiVal > rsiOSLvl
bool rsiBullDiv = low[1] < low[5] and rsiVal[1] > rsiVal[5] and low[1] == ta.lowest(low, 5)
bool rsiBearDiv = high[1] > high[5] and rsiVal[1] < rsiVal[5] and high[1] == ta.highest(high, 5)
string rsiStr = str.tostring(math.round(rsiVal)) + " — "
if rsiOB
rsiStr := rsiStr + "OVERBOUGHT ⚠"
else if rsiOS
rsiStr := rsiStr + "OVERSOLD ⚠"
else if rsiBullDiv
rsiStr := rsiStr + "BULL DIVERGENCE"
else if rsiBearDiv
rsiStr := rsiStr + "BEAR DIVERGENCE"
else if rsiBullRange
rsiStr := rsiStr + "BULLISH RANGE"
else if rsiBearRange
rsiStr := rsiStr + "BEARISH RANGE"
else
rsiStr := rsiStr + "NEUTRAL"
color rsiDispCol = color.new(color.orange, 55)
if rsiOS or rsiBullDiv
rsiDispCol := color.new(color.green, 45)
else if rsiOB or rsiBearDiv
rsiDispCol := color.new(color.red, 45)
else if rsiBullRange
rsiDispCol := color.new(color.green, 58)
else if rsiBearRange
rsiDispCol := color.new(color.red, 58)
float vwapVal = ta.vwap(hlc3)
bool aboveVwap = close > vwapVal
bool vwapCross = ta.crossover(close, vwapVal)
bool vwapDeadX = ta.crossunder(close, vwapVal)
string vwapStr = "—"
color vwapCol = color.rgb(22, 22, 22)
if timeframe.isintraday
if vwapCross
vwapStr := "▲ RECLAIMED VWAP"
vwapCol := color.new(color.green, 40)
else if vwapDeadX
vwapStr := "▼ LOST VWAP"
vwapCol := color.new(color.red, 40)
else if aboveVwap
vwapStr := "▲ ABOVE VWAP"
vwapCol := color.new(color.green, 55)
else
vwapStr := "▼ BELOW VWAP"
vwapCol := color.new(color.red, 55)
else
vwapStr := "N/A (Daily+)"
vwapCol := color.rgb(22, 22, 22)
// ── Volume value band (intraday) — VWAP of highs/lows = institutional value area ──
float vwapHigh = ta.vwap(high)
float vwapLow = ta.vwap(low)
float oiLevel = (vwapHigh + vwapLow) / 2
bool aboveValue = timeframe.isintraday and close > vwapHigh
bool belowValue = timeframe.isintraday and close < vwapLow
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ PATTERN ENGINES — ICHIMOKU KINKO HYO + HEAD & SHOULDERS ║
// ╚═══════════════════════════════════════════════════════════════════╝
// ── Ichimoku (classic 9/26/52). Current cloud = spans projected from 26 bars ago ──
f_ichimoku() =>
float iTk = (ta.highest(high, 9) + ta.lowest(low, 9)) / 2
float iKj = (ta.highest(high, 26) + ta.lowest(low, 26)) / 2
float iSpA = (iTk + iKj) / 2
float iSpB = (ta.highest(high, 52) + ta.lowest(low, 52)) / 2
[math.max(iSpA[26], iSpB[26]), math.min(iSpA[26], iSpB[26]), iTk, iKj]
[ichiTopC, ichiBotC, ichiTk, ichiKj] = f_ichimoku()
[dayKumoTop, dayKumoBot, dayTk, dayKj] = request.security(syminfo.tickerid, "D", f_ichimoku())
bool tkKjGold = ta.crossover(ichiTk, ichiKj)
bool tkKjDeath = ta.crossunder(ichiTk, ichiKj)
string dayKumoState = na(dayKumoTop) ? "N/A" : close > dayKumoTop ? "▲ ABOVE CLOUD" : close < dayKumoBot ? "▼ BELOW CLOUD" : "◆ IN CLOUD"
string tfKumoState = na(ichiTopC) ? "N/A" : close > ichiTopC ? "▲ ABOVE" : close < ichiBotC ? "▼ BELOW" : "◆ IN"
// ── Head & Shoulders state machine — pivot-confirmed, non-repainting, PURE (no drawing — callable in security) ──
// state: 0 none · 1 LS+HEAD confirmed · 2 complete (neckline watched) · 3 neckline broken
f_hs_top() =>
var int tState = 0
var float tLS = na
var float tHead = na
var float tRS = na
var int tLSbar = 0
var int tHeadBar = 0
var int tRSbar = 0
var float tNL1 = na
var int tNL1bar = 0
var float tNL2 = na
var int tNL2bar = 0
var int tBrokeBar = 0
var array<float> phV = array.new_float()
var array<int> phB = array.new_int()
float ph = ta.pivothigh(high, swingLen, swingLen)
if not na(ph)
array.unshift(phV, ph)
array.unshift(phB, bar_index - swingLen)
if array.size(phV) > 4
array.pop(phV)
array.pop(phB)
float nlNow = na
bool lsNow = false
bool rsNow = false
bool brkNow = false
bool rstNow = false
// Invalidation & expiry
bool resetNow = false
if tState == 1
if not na(ph) and ph > tHead
resetNow := true
else if bar_index - tHeadBar > 150
resetNow := true
if tState == 2
if close > tHead
resetNow := true
else if bar_index - tRSbar > 120
resetNow := true
if tState == 3 and bar_index - tBrokeBar > 10
resetNow := true
if resetNow
tState := 0
tLS := na
tHead := na
tRS := na
tNL1 := na
tNL2 := na
tBrokeBar := 0
rstNow := true
// 0 → 1 : new head forms above a prior swing (left shoulder)
if tState == 0 and array.size(phV) >= 2
float headC = array.get(phV, 0)
float lsC = array.get(phV, 1)
if headC > lsC + atr * 0.5
tState := 1
tLS := lsC
tLSbar := array.get(phB, 1)
tHead := headC
tHeadBar := array.get(phB, 0)
int kA = bar_index - tHeadBar
int kB = bar_index - tLSbar
tNL1 := low[kA]
tNL1bar := tHeadBar
for kk = kA + 1 to kB - 1
if low[kk] < tNL1
tNL1 := low[kk]
tNL1bar := bar_index - kk
lsNow := true
// 1 → 2 : right shoulder below head, close to left shoulder
if tState == 1 and not na(ph) and ph < tHead - atr * 0.5
if math.abs(ph - tLS) <= atr * hsAtrTol
tState := 2
tRS := ph
tRSbar := bar_index - swingLen
int kA2 = bar_index - tRSbar
int kB2 = bar_index - tHeadBar
tNL2 := low[kA2]
tNL2bar := tRSbar
for kk = kA2 + 1 to kB2 - 1
if low[kk] < tNL2
tNL2 := low[kk]
tNL2bar := bar_index - kk
rsNow := true
// 2 / 3 : live neckline + break watch
if tState == 2 or tState == 3
float nlSlope = (tNL2 - tNL1) / math.max(tNL2bar - tNL1bar, 1)
nlNow := tNL2 + nlSlope * (bar_index - tNL2bar)
if tState == 2 and close < nlNow
tState := 3
tBrokeBar := bar_index
brkNow := true
[tState, nlNow, tBrokeBar, tLS, tHead, tRS, tLSbar, tHeadBar, tRSbar, tNL1, tNL1bar, lsNow, rsNow, brkNow, rstNow]
// Mirror — inverse Head & Shoulders (bullish) — pure
f_hs_bottom() =>
var int bState = 0
var float bLS = na
var float bHead = na
var float bRS = na
var int bLSbar = 0
var int bHeadBar = 0
var int bRSbar = 0
var float bNL1 = na
var int bNL1bar = 0
var float bNL2 = na
var int bNL2bar = 0
var int bBrokeBar = 0
var array<float> plV = array.new_float()
var array<int> plB = array.new_int()
float pl = ta.pivotlow(low, swingLen, swingLen)
if not na(pl)
array.unshift(plV, pl)
array.unshift(plB, bar_index - swingLen)
if array.size(plV) > 4
array.pop(plV)
array.pop(plB)
float nlNow = na
bool lsNow = false
bool rsNow = false
bool brkNow = false
bool rstNow = false
// Invalidation & expiry
bool resetNow = false
if bState == 1
if not na(pl) and pl < bHead
resetNow := true
else if bar_index - bHeadBar > 150
resetNow := true
if bState == 2
if close < bHead
resetNow := true
else if bar_index - bRSbar > 120
resetNow := true
if bState == 3 and bar_index - bBrokeBar > 10
resetNow := true
if resetNow
bState := 0
bLS := na
bHead := na
bRS := na
bNL1 := na
bNL2 := na
bBrokeBar := 0
rstNow := true
// 0 → 1 : new head forms below a prior swing (left shoulder)
if bState == 0 and array.size(plV) >= 2
float headC = array.get(plV, 0)
float lsC = array.get(plV, 1)
if headC < lsC - atr * 0.5
bState := 1
bLS := lsC
bLSbar := array.get(plB, 1)
bHead := headC
bHeadBar := array.get(plB, 0)
int kA = bar_index - bHeadBar
int kB = bar_index - bLSbar
bNL1 := high[kA]
bNL1bar := bHeadBar
for kk = kA + 1 to kB - 1
if high[kk] > bNL1
bNL1 := high[kk]
bNL1bar := bar_index - kk
lsNow := true
// 1 → 2 : right shoulder above head, close to left shoulder
if bState == 1 and not na(pl) and pl > bHead + atr * 0.5
if math.abs(pl - bLS) <= atr * hsAtrTol
bState := 2
bRS := pl
bRSbar := bar_index - swingLen
int kA2 = bar_index - bRSbar
int kB2 = bar_index - bHeadBar
bNL2 := high[kA2]
bNL2bar := bRSbar
for kk = kA2 + 1 to kB2 - 1
if high[kk] > bNL2
bNL2 := high[kk]
bNL2bar := bar_index - kk
rsNow := true
// 2 / 3 : live neckline + break watch
if bState == 2 or bState == 3
float nlSlope = (bNL2 - bNL1) / math.max(bNL2bar - bNL1bar, 1)
nlNow := bNL2 + nlSlope * (bar_index - bNL2bar)
if bState == 2 and close > nlNow
bState := 3
bBrokeBar := bar_index
brkNow := true
[bState, nlNow, bBrokeBar, bLS, bHead, bRS, bLSbar, bHeadBar, bRSbar, bNL1, bNL1bar, lsNow, rsNow, brkNow, rstNow]
// H&S stage text for tables
f_hsStage(int st, float nl, string brokeGlyph) =>
st == 1 ? "LS+Head forming" : st == 2 ? "NL " + str.tostring(nl, "#.##") : "BROKEN " + brokeGlyph
[hsT_state, hsT_nl, hsT_broke, hsT_ls, hsT_head, hsT_rs, hsT_lsB, hsT_headB, hsT_rsB, hsT_nl1, hsT_nl1B, hsT_lsNow, hsT_rsNow, hsT_brkNow, hsT_rstNow] = f_hs_top()
[hsB_state, hsB_nl, hsB_broke, hsB_ls, hsB_head, hsB_rs, hsB_lsB, hsB_headB, hsB_rsB, hsB_nl1, hsB_nl1B, hsB_lsNow, hsB_rsNow, hsB_brkNow, hsB_rstNow] = f_hs_bottom()
[hsD_Tst, hsD_Tnl, hsD_Tbrk, hsD_Tls, hsD_Thead, hsD_Trs, hsD_TlsB, hsD_TheadB, hsD_TrsB, hsD_Tnl1, hsD_Tnl1B, hsD_TlsNow, hsD_TheadNow, hsD_TbrkNow, hsD_TrstNow] = request.security(syminfo.tickerid, "D", f_hs_top())
[hsD_Bst, hsD_Bnl, hsD_Bbrk, hsD_Bls, hsD_Bhead, hsD_Brs, hsD_BlsB, hsD_BheadB, hsD_BrsB, hsD_Bnl1, hsD_Bnl1B, hsD_BlsNow, hsD_BheadNow, hsD_BbrkNow, hsD_BrstNow] = request.security(syminfo.tickerid, "D", f_hs_bottom())
// ── H&S alerts (global scope — security expressions cannot alert) ──
if alertOnSignal
if hsT_brkNow
alert("⚠ H&S TOP — Neckline broken | " + syminfo.ticker, alert.freq_once_per_bar)
if hsB_brkNow
alert("✔ H&S BOTTOM — Neckline broken | " + syminfo.ticker, alert.freq_once_per_bar)
if hsD_TbrkNow
alert("⚠ DAILY H&S TOP — Neckline broken | " + syminfo.ticker, alert.freq_once_per_bar)
if hsD_BbrkNow
alert("✔ DAILY H&S BOTTOM — Neckline broken | " + syminfo.ticker, alert.freq_once_per_bar)
f_drawHsVisuals() =>
// ── H&S visuals — native TF (event-driven LS/HEAD/RS labels + live neckline) ──
var label nLsT = na
var label nHeadT = na
var label nRsT = na
var line nNlT = na
var label nLsB = na
var label nHeadB = na
var label nRsB = na
var line nNlB = na
if hsT_rstNow
if not na(nLsT)
label.delete(nLsT)
if not na(nHeadT)
label.delete(nHeadT)
if not na(nRsT)
label.delete(nRsT)
if not na(nNlT)
line.delete(nNlT)
nLsT := na
nHeadT := na
nRsT := na
nNlT := na
if hsB_rstNow
if not na(nLsB)
label.delete(nLsB)
if not na(nHeadB)
label.delete(nHeadB)
if not na(nRsB)
label.delete(nRsB)
if not na(nNlB)
line.delete(nNlB)
nLsB := na
nHeadB := na
nRsB := na
nNlB := na
if showHs
if hsT_lsNow
if not na(nLsT)
label.delete(nLsT)
if not na(nHeadT)
label.delete(nHeadT)
nLsT := label.new(hsT_lsB, hsT_ls, "LS", style=label.style_label_up, color=color.new(color.orange, 10), textcolor=color.white, size=size.tiny)
nHeadT := label.new(hsT_headB, hsT_head, "HEAD", style=label.style_label_up, color=color.new(color.red, 10), textcolor=color.white, size=size.tiny)
if hsT_rsNow
nRsT := label.new(hsT_rsB, hsT_rs, "RS", style=label.style_label_up, color=color.new(color.orange, 10), textcolor=color.white, size=size.tiny)
if hsB_lsNow
if not na(nLsB)
label.delete(nLsB)
if not na(nHeadB)
label.delete(nHeadB)
nLsB := label.new(hsB_lsB, hsB_ls, "LS", style=label.style_label_down, color=color.new(color.teal, 10), textcolor=color.white, size=size.tiny)
nHeadB := label.new(hsB_headB, hsB_head, "HEAD", style=label.style_label_down, color=color.new(color.green, 10), textcolor=color.white, size=size.tiny)
if hsB_rsNow
nRsB := label.new(hsB_rsB, hsB_rs, "RS", style=label.style_label_down, color=color.new(color.teal, 10), textcolor=color.white, size=size.tiny)
if hsT_state == 2 or hsT_state == 3
if na(nNlT)
nNlT := line.new(hsT_nl1B, hsT_nl1, bar_index, hsT_nl, color=color.new(color.yellow, 0), style=line.style_dashed, width=2)
else
line.set_xy2(nNlT, bar_index, hsT_nl)
if hsB_state == 2 or hsB_state == 3
if na(nNlB)
nNlB := line.new(hsB_nl1B, hsB_nl1, bar_index, hsB_nl, color=color.new(color.yellow, 0), style=line.style_dashed, width=2)
else
line.set_xy2(nNlB, bar_index, hsB_nl)
// ── H&S visuals — daily pattern (series-based neckline + status tag) ──
var line dNlT = na
var line dNlB = na
var label dTag = na
if showHs and barstate.islast
if not na(dNlT)
line.delete(dNlT)
dNlT := na
if not na(dNlB)
line.delete(dNlB)
dNlB := na
if not na(dTag)
label.delete(dTag)
dTag := na
if hsD_Tst >= 2 and not na(hsD_Tnl) and not na(hsD_Tnl[40])
dNlT := line.new(bar_index - 40, hsD_Tnl[40], bar_index + 10, hsD_Tnl, color=color.new(color.yellow, 0), style=line.style_dashed, width=2)
if hsD_Bst >= 2 and not na(hsD_Bnl) and not na(hsD_Bnl[40])
dNlB := line.new(bar_index - 40, hsD_Bnl[40], bar_index + 10, hsD_Bnl, color=color.new(color.yellow, 0), style=line.style_dashed, width=2)
if hsD_Tst >= 1 or hsD_Bst >= 1
string dTxt = ""
if hsD_Tst > 0
dTxt := "DAILY H&S TOP " + (hsD_Tst == 3 ? "BROKEN ✖" : "forming")
if hsD_Bst > 0
dTxt := dTxt + (dTxt == "" ? "" : " · ") + "DAILY H&S BOT " + (hsD_Bst == 3 ? "BROKEN ✔" : "forming")
dTag := label.new(bar_index, high, dTxt, style=label.style_label_down, color=color.new(color.yellow, 25), textcolor=color.black, size=size.tiny)
f_drawHsVisuals()
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 13: INSTITUTIONAL FOOTPRINT & DARK POOL LEVELS ║
// ╚═══════════════════════════════════════════════════════════════════╝
float delta = (close - open) * volume / math.max(high - low, 0.001)
float cumDelta = ta.cum(delta)
float deltaAvg = ta.sma(cumDelta, 50)
float deltaZ = (cumDelta - deltaAvg) / math.max(ta.stdev(cumDelta, 50), 0.001)
bool aggressiveBuy = delta > 0 and volume > volSma * 1.5
bool aggressiveSell = delta < 0 and volume > volSma * 1.5
bool absorption = math.abs(close - open) < atr * 0.2 and volume > volSma * 1.3
string footprintStr = "Neutral"
color footprintCol = color.rgb(22, 22, 22)
if absorption
footprintStr := "Absorption ⚡ (Reversal)"
footprintCol := color.new(color.purple, 50)
else if aggressiveBuy
footprintStr := "Aggressive Buying 🟢"
footprintCol := color.new(color.green, 45)
else if aggressiveSell
footprintStr := "Aggressive Selling 🔴"
footprintCol := color.new(color.red, 45)
else if deltaZ > 1.5
footprintStr := "Accumulation 📈"
footprintCol := color.new(color.blue, 45)
else if deltaZ < -1.5
footprintStr := "Distribution 📉"
footprintCol := color.new(color.orange, 45)
// Dark pool levels — persist (var) so proximity scoring works between extremes
var float dpBuyLevel = na
var float dpSellLevel = na
if useDarkPool
if deltaZ > 1.8
dpBuyLevel := close
if deltaZ < -1.8
dpSellLevel := close
var line dpLineBuy = na
var line dpLineSell = na
if showDarkPool and barstate.islast
if not na(dpLineBuy)
line.delete(dpLineBuy)
if not na(dpLineSell)
line.delete(dpLineSell)
if not na(dpBuyLevel)
dpLineBuy := line.new(bar_index - 30, dpBuyLevel, bar_index + 15, dpBuyLevel, color=color.new(color.blue, 40), style=line.style_dashed, width=2)
if not na(dpSellLevel)
dpLineSell := line.new(bar_index - 30, dpSellLevel, bar_index + 15, dpSellLevel, color=color.new(color.orange, 40), style=line.style_dashed, width=2)
// ── Sentiment vector (-1 .. +1) ──
float sentiment = 0.0
if rsiVal > 50
sentiment += 0.2
else if rsiVal < 50
sentiment -= 0.2
if macdAbove
sentiment += 0.2
else if macdBelow
sentiment -= 0.2
if adxLine > 25 and close > emaS
sentiment += 0.3
else if adxLine > 25 and close < emaS
sentiment -= 0.3
if bullStruct
sentiment += 0.3
else if bearStruct
sentiment -= 0.3
sentiment := math.max(-1.0, math.min(1.0, sentiment))
string sentStr = "Neutral"
color sentCol = color.rgb(30, 30, 30)
if sentiment > 0.5
sentStr := "Extreme Greed 🤑"
sentCol := color.new(color.green, 45)
else if sentiment > 0.2
sentStr := "Bullish"
sentCol := color.new(color.green, 65)
else if sentiment < -0.5
sentStr := "Extreme Fear 😨"
sentCol := color.new(color.red, 45)
else if sentiment < -0.2
sentStr := "Bearish"
sentCol := color.new(color.red, 65)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 14: WYCKOFF PHASE & VOLUME DIVERGENCE ║
// ╚═══════════════════════════════════════════════════════════════════╝
bool volBullDiv = useVolDiv and low[1] < low[5] and volume[1] < volume[5] and low[1] == ta.lowest(low, 5)
bool volBearDiv = useVolDiv and high[1] > high[5] and volume[1] < volume[5] and high[1] == ta.highest(high, 5)
bool wyckSpring = useWyckoff and not na(lastPL2) and low < lastPL2 and close > lastPL2 and volume > volSma * 1.3 and inDiscount
bool wyckSOS = useWyckoff and close > rangeH[1] and volume > volSma * 1.5 and bullStruct
bool wyckUTAD = useWyckoff and high > rangeH and close < rangeEQ and volume < volSma * 0.8 and inPremium
bool wyckLPSY = useWyckoff and not na(lastPH) and high < lastPH and volume < volSma * 0.8 and bearStruct
string wyckStr = "N/A"
color wyckCol = color.gray
if wyckSpring
wyckStr := "ACCUMULATION: Spring"
wyckCol := color.new(color.green, 50)
else if wyckSOS
wyckStr := "MARKUP: SOS"
wyckCol := color.new(color.teal, 50)
else if wyckUTAD
wyckStr := "DISTRIBUTION: UTAD"
wyckCol := color.new(color.orange, 50)
else if wyckLPSY
wyckStr := "DISTRIBUTION: LPSY"
wyckCol := color.new(color.red, 50)
else if inDiscount and lowVol
wyckStr := "ACCUMULATION: Building"
wyckCol := color.new(color.blue, 60)
else if inPremium and lowVol
wyckStr := "DISTRIBUTION: Building"
wyckCol := color.new(color.maroon, 60)
else if bullStruct
wyckStr := "MARKUP: Phase"
wyckCol := color.new(color.green, 70)
else if bearStruct
wyckStr := "MARKDOWN: Phase"
wyckCol := color.new(color.red, 70)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 15: CANDLESTICK PATTERN ENGINE ║
// ╚═══════════════════════════════════════════════════════════════════╝
float candleBody = math.abs(close - open)
float upperWick = high - math.max(close, open)
float lowerWick = math.min(close, open) - low
float totalRange = high - low
bool bullPin = totalRange > atr * 0.3 and lowerWick > candleBody * 2.0 and lowerWick > upperWick * 2.0
bool bearPin = totalRange > atr * 0.3 and upperWick > candleBody * 2.0 and upperWick > lowerWick * 2.0
bool bullEngulf = close > open and close[1] < open[1] and close > open[1] and open < close[1] and candleBody > math.abs(close[1] - open[1])
bool bearEngulf = close < open and close[1] > open[1] and close < open[1] and open > close[1] and candleBody > math.abs(close[1] - open[1])
bool insideBar = high[1] < high[2] and low[1] > low[2]
bool ibBullBreak = insideBar[1] and close > high[1]
bool ibBearBreak = insideBar[1] and close < low[1]
bool doji = totalRange > atr * 0.2 and candleBody < atr * 0.08
string candleStr = "No Pattern"
if bullEngulf or ibBullBreak
candleStr := "▲ Bull Engulf / IB Break"
else if bullPin
candleStr := "▲ Bullish Pin Bar"
else if bearEngulf or ibBearBreak
candleStr := "▼ Bear Engulf / IB Break"
else if bearPin
candleStr := "▼ Bearish Pin Bar"
else if doji
candleStr := "◆ Doji (Indecision)"
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 16: SMC QUANTUM ZONES — OB / FVG / BREAKERS ║
// ╚═══════════════════════════════════════════════════════════════════╝
// ── Liquidity tier classifier (Retail → CTA → Inst → Central Bank) ──
f_liqTier(float level) =>
int tier = 0
if useLiqHier
float dist = math.abs(close - level) / math.max(atr, 0.001)
if dist < 0.3
tier := 1
if (not na(pdHigh) and math.abs(level - pdHigh) < atr * 0.5) or (not na(pdLow) and math.abs(level - pdLow) < atr * 0.5)
tier := 2
if (not na(pwHigh) and math.abs(level - pwHigh) < atr * 0.8) or (not na(pwLow) and math.abs(level - pwLow) < atr * 0.8)
tier := 3
if math.abs(level - math.round(level / 50.0) * 50.0) < 0.05 or math.abs(level - math.round(level / 1000.0) * 1000.0) < 0.05
tier := 4
if tier > maxLiqTier
tier := maxLiqTier
tier
f_tierString(int t) =>
t == 4 ? "🟥 CB" : t == 3 ? "🟧 Inst" : t == 2 ? "🟩 CTA" : t == 1 ? "🟦 Retail" : "—"
// ── Zone arrays ──
var array<float> bObH = array.new<float>()
var array<float> bObL = array.new<float>()
var array<int> bObBar = array.new<int>()
var array<bool> bObOk = array.new<bool>()
var array<box> bObBox = array.new<box>()
var array<float> bObStr = array.new<float>()
var array<int> bObTouch = array.new<int>()
var array<float> rObH = array.new<float>()
var array<float> rObL = array.new<float>()
var array<int> rObBar = array.new<int>()
var array<bool> rObOk = array.new<bool>()
var array<box> rObBox = array.new<box>()
var array<float> rObStr = array.new<float>()
var array<int> rObTouch = array.new<int>()
var array<float> bFvgH = array.new<float>()
var array<float> bFvgL = array.new<float>()
var array<int> bFvgBar = array.new<int>()
var array<bool> bFvgOk = array.new<bool>()
var array<box> bFvgBox = array.new<box>()
var array<float> bFvgStr = array.new<float>()
var array<float> rFvgH = array.new<float>()
var array<float> rFvgL = array.new<float>()
var array<int> rFvgBar = array.new<int>()
var array<bool> rFvgOk = array.new<bool>()
var array<box> rFvgBox = array.new<box>()
var array<float> rFvgStr = array.new<float>()
var array<float> rbsH = array.new<float>()
var array<float> rbsL = array.new<float>()
var array<bool> rbsOk = array.new<bool>()
var array<box> rbsBox = array.new<box>()
var array<int> rbsState = array.new<int>()
var array<float> sbrH = array.new<float>()
var array<float> sbrL = array.new<float>()
var array<bool> sbrOk = array.new<bool>()
var array<box> sbrBox = array.new<box>()
var array<int> sbrState = array.new<int>()
var array<line> zoneLines = array.new<line>()
// ── Zone adders ──
addOB(array<float> H, array<float> L, array<int> Bar, array<bool> Ok, array<box> Bx, array<float> Str, array<int> Touch,
float top, float bot, int bIdx, float strength, color borderC, color bgC, int bw) =>
if array.size(H) >= maxOBs
if array.size(Bx) > 0
box.delete(array.shift(Bx))
array.shift(H)
array.shift(L)
array.shift(Bar)
array.shift(Ok)
array.shift(Str)
array.shift(Touch)
array.push(H, top)
array.push(L, bot)
array.push(Bar, bIdx)
array.push(Ok, true)
array.push(Str, strength)
array.push(Touch, 0)
int tier = f_liqTier(top)
if showOB
array.push(Bx, box.new(bIdx, top, bIdx + 40, bot, border_color=borderC, bgcolor=bgC, border_width=bw))
if showTierTags and tier > 0
label.new(bIdx + 20, (top + bot) / 2, f_tierString(tier), color=color.new(color.gray, 50), textcolor=color.white, style=label.style_label_center, size=size.tiny)
addFVG(array<float> H, array<float> L, array<int> Bar, array<bool> Ok, array<box> Bx, array<float> Str,
float top, float bot, int bIdx, color bgC) =>
if array.size(H) >= maxFVGs
if array.size(Bx) > 0
box.delete(array.shift(Bx))
array.shift(H)
array.shift(L)
array.shift(Bar)
array.shift(Ok)
array.shift(Str)
array.push(H, top)
array.push(L, bot)
array.push(Bar, bIdx)
array.push(Ok, true)
array.push(Str, 70.0)
if showFVG
array.push(Bx, box.new(bIdx, top, bIdx + 40, bot, border_color=na, bgcolor=bgC))
// Quantum field line drawer (global scope — Pine forbids functions inside control blocks)
drawFieldForZone(array<float> H, array<float> L, array<bool> Ok, array<float> Str, color col) =>
if array.size(H) > 0
for i = 0 to array.size(H) - 1
if array.get(Ok, i)
float top = array.get(H, i)
float bot = array.get(L, i)
float strV = array.get(Str, i) / 100.0
float mid = (top + bot) / 2.0
float width = atr * 0.5 * (1.0 + strV)
for j = -2 to 2
float offset = (j / 2.0) * width
float y1 = mid + offset - atr * 0.1
float y2 = mid + offset + atr * 0.1
line l = line.new(bar_index - 5, y1, bar_index + 5, y2, color=color.new(col, 30), width=1, style=line.style_dotted)
array.push(zoneLines, l)
// ── Impulse detection & zone creation ──
float localImpThresh = atr * regAtrMult * symImpulse * 1.2
bool bullImpulse = (close - open) > localImpThresh and close > high[1] and close[1] < open[1]
bool bearImpulse = (open - close) > localImpThresh and close < low[1] and close[1] > open[1]
if htfBullImp and isNewHtfBar
addOB(bObH, bObL, bObBar, bObOk, bObBox, bObStr, bObTouch, htfObH, htfObL, bar_index, 100.0, color.new(color.lime, 5), color.new(color.lime, 72), 2)
else if bullImpulse
float bStr = math.min((close - open) / atr * 25.0 + 50.0, 100.0)
addOB(bObH, bObL, bObBar, bObOk, bObBox, bObStr, bObTouch, high[1], low[1], bar_index[1], bStr, color.new(color.lime, 30), color.new(color.lime, 88), 1)
if htfBearImp and isNewHtfBar
addOB(rObH, rObL, rObBar, rObOk, rObBox, rObStr, rObTouch, htfObH, htfObL, bar_index, 100.0, color.new(color.red, 5), color.new(color.red, 72), 2)
else if bearImpulse
float rStr = math.min((open - close) / atr * 25.0 + 50.0, 100.0)
addOB(rObH, rObL, rObBar, rObOk, rObBox, rObStr, rObTouch, high[1], low[1], bar_index[1], rStr, color.new(color.red, 30), color.new(color.red, 88), 1)
// ── Breaker blocks (RBS/SBR), capped at 8 ──
if not na(ph) and close > ph
float half = atr * 0.5
if array.size(rbsH) >= 8
if array.size(rbsBox) > 0
box.delete(array.shift(rbsBox))
array.shift(rbsH)
array.shift(rbsL)
array.shift(rbsOk)
array.shift(rbsState)
array.push(rbsH, ph)
array.push(rbsL, ph - half)
array.push(rbsOk, true)
array.push(rbsState, 0)
if showBreakers
array.push(rbsBox, box.new(bar_index[swingLen], ph, bar_index + 40, ph - half, border_color=color.new(color.blue, 35), bgcolor=color.new(color.blue, 88), border_width=1))
if not na(pl) and close < pl
float half = atr * 0.5
if array.size(sbrH) >= 8
if array.size(sbrBox) > 0
box.delete(array.shift(sbrBox))
array.shift(sbrH)
array.shift(sbrL)
array.shift(sbrOk)
array.shift(sbrState)
array.push(sbrH, pl + half)
array.push(sbrL, pl)
array.push(sbrOk, true)
array.push(sbrState, 0)
if showBreakers
array.push(sbrBox, box.new(bar_index[swingLen], pl + half, bar_index + 40, pl, border_color=color.new(color.orange, 35), bgcolor=color.new(color.orange, 88), border_width=1))
// ── FVG detection ──
float fvgMin = atr * (fvgMinPct / 100.0) * symFvgScl
bool bFvgNew = low > high[2] and (low - high[2]) >= fvgMin
bool rFvgNew = high < low[2] and (low[2] - high) >= fvgMin
if bFvgNew
addFVG(bFvgH, bFvgL, bFvgBar, bFvgOk, bFvgBox, bFvgStr, low, high[2], bar_index[1], color.new(color.teal, 83))
if rFvgNew
addFVG(rFvgH, rFvgL, rFvgBar, rFvgOk, rFvgBox, rFvgStr, low[2], high, bar_index[1], color.new(color.fuchsia, 83))
// ── OB hit detection, touch decay & quantum gravity ──
bool bObHit = false
bool bObCoreTap = false
float bObNearTop = na
float bObNearBot = na
float bObMinDist = 1e10
float bObZoneGravity = 0.0
if array.size(bObH) > 0
for i = 0 to array.size(bObH) - 1
if array.get(bObOk, i)
float t = array.get(bObH, i)
float b = array.get(bObL, i)
int cb = array.get(bObBar, i)
if showOB and i < array.size(bObBox)
box.set_right(array.get(bObBox, i), bar_index + 40)
if low <= t and close >= b
bObHit := true
// Quantum core band — central probability zone of the OB
float hz = t - b
if hz > 0
float coreBot = b + hz * (1.0 - qCorePct) / 2.0
float coreTop = b + hz * (1.0 + qCorePct) / 2.0
if close >= coreBot and close <= coreTop
bObCoreTap := true
if showOB and i < array.size(bObBox)
box.set_bgcolor(array.get(bObBox, i), color.new(color.lime, 45))
int touches = array.get(bObTouch, i) + 1
array.set(bObTouch, i, touches)
if touches >= 3
array.set(bObStr, i, array.get(bObStr, i) * 0.7)
float dist = math.abs(close - t) / math.max(atr, 0.001)
bObZoneGravity += f_sigmoid(-dist, qGravity) * (array.get(bObStr, i) / 100.0)
if close < b or (bar_index - cb) > zoneMaxAge
array.set(bObOk, i, false)
if showOB and i < array.size(bObBox)
box.set_bgcolor(array.get(bObBox, i), color.new(color.gray, 94))
box.set_border_color(array.get(bObBox, i), na)
float d = math.abs(close - t)
if d < bObMinDist
bObMinDist := d
bObNearTop := t
bObNearBot := b
bool rObHit = false
bool rObCoreTap = false
float rObNearTop = na
float rObNearBot = na
float rObMinDist = 1e10
float rObZoneGravity = 0.0
if array.size(rObH) > 0
for i = 0 to array.size(rObH) - 1
if array.get(rObOk, i)
float t = array.get(rObH, i)
float b = array.get(rObL, i)
int cb = array.get(rObBar, i)
if showOB and i < array.size(rObBox)
box.set_right(array.get(rObBox, i), bar_index + 40)
if high >= b and close <= t
rObHit := true
// Quantum core band — central probability zone of the OB
float hz = t - b
if hz > 0
float coreBot = b + hz * (1.0 - qCorePct) / 2.0
float coreTop = b + hz * (1.0 + qCorePct) / 2.0
if close >= coreBot and close <= coreTop
rObCoreTap := true
if showOB and i < array.size(rObBox)
box.set_bgcolor(array.get(rObBox, i), color.new(color.red, 45))
int touches = array.get(rObTouch, i) + 1
array.set(rObTouch, i, touches)
if touches >= 3
array.set(rObStr, i, array.get(rObStr, i) * 0.7)
float dist = math.abs(close - b) / math.max(atr, 0.001)
rObZoneGravity += f_sigmoid(-dist, qGravity) * (array.get(rObStr, i) / 100.0)
if close > t or (bar_index - cb) > zoneMaxAge
array.set(rObOk, i, false)
if showOB and i < array.size(rObBox)
box.set_bgcolor(array.get(rObBox, i), color.new(color.gray, 94))
box.set_border_color(array.get(rObBox, i), na)
float d = math.abs(close - b)
if d < rObMinDist
rObMinDist := d
rObNearTop := t
rObNearBot := b
// ── FVG hit detection & nearest tracking ──
bool bFvgHit = false
float bFvgNearTop = na
float bFvgNearBot = na
float bFvgMinDist = 1e10
if array.size(bFvgH) > 0
for i = 0 to array.size(bFvgH) - 1
if array.get(bFvgOk, i)
float t = array.get(bFvgH, i)
float b = array.get(bFvgL, i)
int cb = array.get(bFvgBar, i)
if showFVG and i < array.size(bFvgBox)
box.set_right(array.get(bFvgBox, i), bar_index + 40)
if low <= t and close >= b
bFvgHit := true
if close < b or (bar_index - cb) > zoneMaxAge
array.set(bFvgOk, i, false)
if showFVG and i < array.size(bFvgBox)
box.set_bgcolor(array.get(bFvgBox, i), color.new(color.gray, 94))
float d = math.abs(close - t)
if d < bFvgMinDist
bFvgMinDist := d
bFvgNearTop := t
bFvgNearBot := b
bool rFvgHit = false
float rFvgNearTop = na
float rFvgNearBot = na
float rFvgMinDist = 1e10
if array.size(rFvgH) > 0
for i = 0 to array.size(rFvgH) - 1
if array.get(rFvgOk, i)
float t = array.get(rFvgH, i)
float b = array.get(rFvgL, i)
int cb = array.get(rFvgBar, i)
if showFVG and i < array.size(rFvgBox)
box.set_right(array.get(rFvgBox, i), bar_index + 40)
if high >= b and close <= t
rFvgHit := true
if close > t or (bar_index - cb) > zoneMaxAge
array.set(rFvgOk, i, false)
if showFVG and i < array.size(rFvgBox)
box.set_bgcolor(array.get(rFvgBox, i), color.new(color.gray, 94))
float d = math.abs(close - t)
if d < rFvgMinDist
rFvgMinDist := d
rFvgNearTop := t
rFvgNearBot := b
// ── Breaker hit detection ──
bool rbsHit = false
float rbsNearTop = na
if array.size(rbsH) > 0
for i = 0 to array.size(rbsH) - 1
if array.get(rbsOk, i)
float t = array.get(rbsH, i)
float b = array.get(rbsL, i)
if showBreakers and i < array.size(rbsBox)
box.set_right(array.get(rbsBox, i), bar_index + 40)
if low <= t and close >= b
rbsHit := true
rbsNearTop := t
array.set(rbsState, i, 1)
if close < b or (bar_index - 10) > zoneMaxAge
array.set(rbsOk, i, false)
if showBreakers and i < array.size(rbsBox)
box.set_bgcolor(array.get(rbsBox, i), color.new(color.gray, 94))
box.set_border_color(array.get(rbsBox, i), na)
bool sbrHit = false
float sbrNearTop = na
if array.size(sbrH) > 0
for i = 0 to array.size(sbrH) - 1
if array.get(sbrOk, i)
float t = array.get(sbrH, i)
float b = array.get(sbrL, i)
if showBreakers and i < array.size(sbrBox)
box.set_right(array.get(sbrBox, i), bar_index + 40)
if high >= b and close <= t
sbrHit := true
sbrNearTop := t
array.set(sbrState, i, 1)
if close > t or (bar_index - 10) > zoneMaxAge
array.set(sbrOk, i, false)
if showBreakers and i < array.size(sbrBox)
box.set_bgcolor(array.get(sbrBox, i), color.new(color.gray, 94))
box.set_border_color(array.get(sbrBox, i), na)
// ── Quantum field gravity visual ──
if showQuantum
if array.size(zoneLines) > 0
for i = 0 to array.size(zoneLines) - 1
line.delete(array.get(zoneLines, i))
array.clear(zoneLines)
drawFieldForZone(bObH, bObL, bObOk, bObStr, color.green)
drawFieldForZone(rObH, rObL, rObOk, rObStr, color.red)
drawFieldForZone(bFvgH, bFvgL, bFvgOk, bFvgStr, color.teal)
drawFieldForZone(rFvgH, rFvgL, rFvgOk, rFvgStr, color.fuchsia)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 17: ASOC SELF-OPTIMIZATION & DYNAMIC THRESHOLD ║
// ╚═══════════════════════════════════════════════════════════════════╝
var array<bool> asocResults = array.new_bool(20, true)
// Hybrid ASOC state [0]=persistent offset(%) [1]=wins in sample [2]=trades in sample
var array<float> adaptState = array.new_float(3, 0.0)
// ── SELF-MEASURING MODES — per-mode track record feeds back into the router ──
// Losing modes get stricter entry thresholds; modes <35% WR after 8+ trades are bypassed
var int tradeMode = 0
var array<float> modePerf = array.new<float>(12) // per mode: [trades, wins]
f_modePerfAdd(int m, bool win) =>
int base = math.min(5, math.max(0, m)) * 2
array.set(modePerf, base, array.get(modePerf, base) + 1.0)
array.set(modePerf, base + 1, array.get(modePerf, base + 1) + (win ? 1.0 : 0.0))
f_modePerfN(int m) =>
array.get(modePerf, math.min(5, math.max(0, m)) * 2)
f_modePerfWr(int m) =>
float n = f_modePerfN(m)
n >= 5 ? array.get(modePerf, math.min(5, math.max(0, m)) * 2 + 1) / n : 0.5
f_modeThreshAdj(int m) =>
f_modePerfN(m) >= 5 ? math.max(-10.0, math.min(10.0, (0.5 - f_modePerfWr(m)) * 40.0)) : 0.0
f_modeViable(int m) =>
float n = f_modePerfN(m)
n < 8 or f_modePerfWr(m) >= 0.35
// HYBRID SELF-OPT — v10 ASOC rolling results + HYBRID v9 step-tuner, merged.
// Every asocWindow closed trades: WR < 45% → threshold +optStep (stricter);
// WR > 70% → threshold -optStep (looser). Offset persists until another sample flips it.
f_record_trade(bool isWin) =>
f_modePerfAdd(tradeMode, isWin)
if useASOC
array.unshift(asocResults, isWin)
if array.size(asocResults) > asocWindow
array.pop(asocResults)
float off = array.get(adaptState, 0)
float wins = array.get(adaptState, 1)
float trades = array.get(adaptState, 2)
trades := trades + 1.0
if isWin
wins := wins + 1.0
if trades >= asocWindow
float wRate = wins / trades * 100.0
if wRate < 45.0
off := math.min(off + optStep, 10.0)
else if wRate > 70.0
off := math.max(off - optStep, -10.0)
trades := 0.0
wins := 0.0
array.set(adaptState, 0, off)
array.set(adaptState, 1, wins)
array.set(adaptState, 2, trades)
float asocWins = 0.0
for i = 0 to array.size(asocResults) - 1
if array.get(asocResults, i)
asocWins += 1.0
float asocWinRate = asocWins / math.max(array.size(asocResults), 1) * 100.0
float adaptOffset = array.get(adaptState, 0)
float adaptTrades = array.get(adaptState, 2)
// Base threshold by risk profile → regime-adjusted → hybrid ASOC offset (clamp 55–92)
float baseThresh = userRisk == RiskProfile.Aggressive ? 65.0 : userRisk == RiskProfile.Strict ? 80.0 : 70.0
float threshAdj = 0.0
if regimeStr == "▲ TRENDING BULL" or regimeStr == "▼ TRENDING BEAR"
threshAdj := -5.0
else if regimeStr == "🌀 CHOPPY"
threshAdj := 8.0
else if regimeStr == "🚀 BREAKOUT"
threshAdj := -3.0
else if regimeStr == "↺ REVERSAL"
threshAdj := 2.0
float confThreshold = math.max(55.0, math.min(92.0, baseThresh + threshAdj))
float dynThreshold = math.max(55.0, math.min(92.0, confThreshold + (useASOC ? adaptOffset : 0.0)))
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 18: 20-FACTOR CONFLUENCE SCORING ENGINE ║
// ═══════════════════════════════════════════════════════════════════╝
float buyScore = 0.0
float sellScore = 0.0
string buyWhy = "Awaiting Setup..."
string sellWhy = "Awaiting Setup..."
bool atBuyZone = bObHit or bFvgHit or rbsHit
bool atSellZone = rObHit or rFvgHit or sbrHit
// 1. Macro alignment (max 20)
if useMacro and macroValid
if macroBullish
buyScore += 20.0
buyWhy := "Macro Aligned"
else if macroBearish
buyScore -= 25.0
if macroBearish
sellScore += 20.0
sellWhy := "Macro Aligned"
else if macroBullish
sellScore -= 25.0
// 2. Trend stack & HMA baseline (max 18)
if bullStack
buyScore += 12.0
if close > hmaBase
buyScore += 6.0
if bearStack
sellScore += 12.0
if close < hmaBase
sellScore += 6.0
// 3. Elastic EMA (max 6)
if close > emaElastic
buyScore += 6.0
else
sellScore += 6.0
// 4. Multi-TF bias (max 15)
if tf3Bull
buyScore += 8.0
if tf2Bull
buyScore += 4.0
if tf1Bull
buyScore += 3.0
if tf3Bear
sellScore += 8.0
if tf2Bear
sellScore += 4.0
if tf1Bear
sellScore += 3.0
// 5. Liquidity sweeps & Asian range (max 25, symbol-weighted)
if validBuySweep and volImbalance
buyScore += 25.0 * symSweepW
buyWhy := "SSL Sweep + Vol Imbalance"
else if validBuySweep
buyScore += 18.0 * symSweepW
buyWhy := "SSL Sweep"
if validSellSweep and volImbalance
sellScore += 25.0 * symSweepW
sellWhy := "BSL Sweep + Vol Imbalance"
else if validSellSweep
sellScore += 18.0 * symSweepW
sellWhy := "BSL Sweep"
// 6. SMC quantum zones — OB gravity (max 22). Core-band taps score above shadow taps.
if bObHit or bObZoneGravity > 0.4
float qBase = bObHit ? (bObCoreTap ? 18.0 : 12.0) : 8.0
float qBonus = math.min(22.0, qBase + bObZoneGravity * 10.0)
buyScore += qBonus
buyWhy := buyWhy == "Awaiting Setup..." ? (bObCoreTap ? "Bull OB Core Tap" : "Bull OB Gravity") : buyWhy + " + OB"
if rObHit or rObZoneGravity > 0.4
float qBase = rObHit ? (rObCoreTap ? 18.0 : 12.0) : 8.0
float qBonus = math.min(22.0, qBase + rObZoneGravity * 10.0)
sellScore += qBonus
sellWhy := sellWhy == "Awaiting Setup..." ? (rObCoreTap ? "Bear OB Core Tap" : "Bear OB Gravity") : sellWhy + " + OB"
// 6b. FVG hits (max 10)
if bFvgHit and inDiscount
buyScore += 10.0
buyWhy := buyWhy == "Awaiting Setup..." ? "Bull FVG" : buyWhy + " + FVG"
else if bFvgHit
buyScore += 6.0
if rFvgHit and inPremium
sellScore += 10.0
sellWhy := sellWhy == "Awaiting Setup..." ? "Bear FVG" : sellWhy + " + FVG"
else if rFvgHit
sellScore += 6.0
// 7. Breaker blocks (max 10)
if rbsHit and inDiscount
buyScore += 10.0
buyWhy := buyWhy == "Awaiting Setup..." ? "RBS Breaker" : buyWhy + " + RBS"
else if rbsHit
buyScore += 6.0
if sbrHit and inPremium
sellScore += 10.0
sellWhy := sellWhy == "Awaiting Setup..." ? "SBR Breaker" : sellWhy + " + SBR"
else if sbrHit
sellScore += 6.0
// 8. Premium / Discount equilibrium (max 8)
if inDiscount
buyScore += 8.0
if inPremium
sellScore += 8.0
// 9. ADX trend strength (max 6)
if adxOk
buyScore += 6.0
sellScore += 6.0
// 10. MACD & divergence (max 8)
if macdAbove
buyScore += 4.0
if macdBelow
sellScore += 4.0
if macdCross or bullDiv
buyScore += 4.0
if macdDeadX or bearDiv
sellScore += 4.0
// 11. RSI & extreme reversals (max 8)
if rsiOS and inDiscount
buyScore += 8.0
if rsiOB and inPremium
sellScore += 8.0
if rsiBullDiv
buyScore += 4.0
if rsiBearDiv
sellScore += 4.0
// 12. Dark pool proximity (max 6)
if not na(dpBuyLevel) and math.abs(close - dpBuyLevel) < atr * 0.6
buyScore += 6.0
if not na(dpSellLevel) and math.abs(close - dpSellLevel) < atr * 0.6
sellScore += 6.0
// 13. Market structure (max 8)
if bullStruct
buyScore += 8.0
if bearStruct
sellScore += 8.0
// 14. VWAP (intraday, max 5)
if timeframe.isintraday
if aboveVwap
buyScore += 5.0
else
sellScore += 5.0
// 15. Wyckoff & volume divergence (max 8)
if volBullDiv and inDiscount
buyScore += 4.0
if volBearDiv and inPremium
sellScore += 4.0
if wyckSpring
buyScore += 4.0
if wyckUTAD
sellScore += 4.0
// 16. Z-score extremes (max 10)
if zScore <= -1.8
buyScore += 10.0
if zScore >= 1.8
sellScore += 10.0
// 17. OHLC confluence at institutional levels (symbol-weighted)
float ohlcBuf = atr * 0.4
float ohlcPts = 5.0 * symOhlcW
if not na(pdLow) and math.abs(low - pdLow) < ohlcBuf
buyScore += ohlcPts
if not na(pdHigh) and math.abs(high - pdHigh) < ohlcBuf
sellScore += ohlcPts
if not na(pwLow) and math.abs(low - pwLow) < ohlcBuf
buyScore += ohlcPts * 0.5
if not na(pwHigh) and math.abs(high - pwHigh) < ohlcBuf
sellScore += ohlcPts * 0.5
if not na(dailyPivot) and math.abs(close - dailyPivot) < ohlcBuf
buyScore += 3.0 * symOhlcW
sellScore += 3.0 * symOhlcW
// 18. Institutional footprint (max 8)
if aggressiveBuy and inDiscount
buyScore += 5.0
if aggressiveSell and inPremium
sellScore += 5.0
if absorption and atBuyZone
buyScore += 3.0
if absorption and atSellZone
sellScore += 3.0
// 19. Sentiment vector — contrarian overlay (max 6)
if sentiment < -0.4 and atBuyZone
buyScore += 6.0
if sentiment > 0.4 and atSellZone
sellScore += 6.0
// 20. Candle pattern at zone (max 5)
if (bullPin or bullEngulf or ibBullBreak) and atBuyZone
buyScore += 5.0
if (bearPin or bearEngulf or ibBearBreak) and atSellZone
sellScore += 5.0
// 21. EQH/EQL stacked liquidity pools (max 4)
if nearEQL and inDiscount
buyScore += 4.0
buyWhy := buyWhy == "Awaiting Setup..." ? "EQL Liquidity Pool" : buyWhy + " + EQL"
if nearEQH and inPremium
sellScore += 4.0
sellWhy := sellWhy == "Awaiting Setup..." ? "EQH Liquidity Pool" : sellWhy + " + EQH"
// 22. Weekly open reference — two-sided (max 3)
if useWeekOpen and not na(weekOpen) and math.abs(close - weekOpen) < atr * 0.4
buyScore += 3.0
sellScore += 3.0
// 23. Volume value band — institutional value area (intraday, max 2)
if timeframe.isintraday
if belowValue and inDiscount
buyScore += 2.0
if aboveValue and inPremium
sellScore += 2.0
// 24. Daily Ichimoku cloud context (max 5)
if useIchimoku and not na(dayKumoTop)
if close > dayKumoTop
buyScore += 3.0
buyWhy := buyWhy == "Awaiting Setup..." ? "Daily Above Kumo" : buyWhy + " + Kumo"
else if close < dayKumoBot
sellScore += 3.0
sellWhy := sellWhy == "Awaiting Setup..." ? "Daily Below Kumo" : sellWhy + " + Kumo"
if math.abs(close - dayKumoTop) <= atr * 0.3 and close >= dayKumoTop
buyScore += 2.0
if math.abs(close - dayKumoBot) <= atr * 0.3 and close <= dayKumoBot
sellScore += 2.0
// 25. TK/KJ cross on current TF (max 3)
if useIchimoku
if tkKjGold
buyScore += 3.0
buyWhy := buyWhy == "Awaiting Setup..." ? "TK/KJ Golden Cross" : buyWhy + " + TKX"
if tkKjDeath
sellScore += 3.0
sellWhy := sellWhy == "Awaiting Setup..." ? "TK/KJ Death Cross" : sellWhy + " + TKX"
// 26. H&S neckline breaks (max 8)
if useHs
if hsT_state == 3 and bar_index - hsT_broke <= 5
sellScore += 8.0
sellWhy := sellWhy == "Awaiting Setup..." ? "H&S Neckline Break" : sellWhy + " + H&S"
if hsB_state == 3 and bar_index - hsB_broke <= 5
buyScore += 8.0
buyWhy := buyWhy == "Awaiting Setup..." ? "H&S Bottom Break" : buyWhy + " + H&S"
if hsD_Tst == 3
sellScore += 6.0
sellWhy := sellWhy == "Awaiting Setup..." ? "Daily H&S Break" : sellWhy + " + dH&S"
if hsD_Bst == 3
buyScore += 6.0
buyWhy := buyWhy == "Awaiting Setup..." ? "Daily H&S Bottom" : buyWhy + " + dH&S"
buyScore := math.max(0.0, math.min(100.0, buyScore))
sellScore := math.max(0.0, math.min(100.0, sellScore))
// ── Distinct factor families — entry-quality gate (blocks 1-factor blowups) ──
int buyFactors = 0
if useMacro and macroValid and macroBullish
buyFactors += 1
if bullStack
buyFactors += 1
if close > hmaBase
buyFactors += 1
if close > emaElastic
buyFactors += 1
if tf3Bull
buyFactors += 1
if tf2Bull
buyFactors += 1
if tf1Bull
buyFactors += 1
if validBuySweep
buyFactors += 1
if bObHit or bObZoneGravity > 0.4
buyFactors += 1
if bFvgHit
buyFactors += 1
if rbsHit
buyFactors += 1
if inDiscount
buyFactors += 1
if adxOk
buyFactors += 1
if macdAbove
buyFactors += 1
if macdCross or bullDiv
buyFactors += 1
if rsiOS and inDiscount
buyFactors += 1
if rsiBullDiv
buyFactors += 1
if not na(dpBuyLevel) and math.abs(close - dpBuyLevel) < atr * 0.6
buyFactors += 1
if bullStruct
buyFactors += 1
if timeframe.isintraday and aboveVwap
buyFactors += 1
if (volBullDiv and inDiscount) or wyckSpring
buyFactors += 1
if zScore <= -1.8
buyFactors += 1
if (not na(pdLow) and math.abs(low - pdLow) < ohlcBuf) or (not na(pwLow) and math.abs(low - pwLow) < ohlcBuf) or (not na(dailyPivot) and math.abs(close - dailyPivot) < ohlcBuf)
buyFactors += 1
if (aggressiveBuy and inDiscount) or (absorption and atBuyZone)
buyFactors += 1
if sentiment < -0.4 and atBuyZone
buyFactors += 1
if (bullPin or bullEngulf or ibBullBreak) and atBuyZone
buyFactors += 1
if nearEQL and inDiscount
buyFactors += 1
if useWeekOpen and not na(weekOpen) and math.abs(close - weekOpen) < atr * 0.4
buyFactors += 1
if timeframe.isintraday and belowValue and inDiscount
buyFactors += 1
if useIchimoku and not na(dayKumoTop) and close > dayKumoTop
buyFactors += 1
if useIchimoku and tkKjGold
buyFactors += 1
if useHs and hsB_state == 3 and bar_index - hsB_broke <= 5
buyFactors += 1
if useHs and hsD_Bst == 3
buyFactors += 1
int sellFactors = 0
if useMacro and macroValid and macroBearish
sellFactors += 1
if bearStack
sellFactors += 1
if close < hmaBase
sellFactors += 1
if close < emaElastic
sellFactors += 1
if tf3Bear
sellFactors += 1
if tf2Bear
sellFactors += 1
if tf1Bear
sellFactors += 1
if validSellSweep
sellFactors += 1
if rObHit or rObZoneGravity > 0.4
sellFactors += 1
if rFvgHit
sellFactors += 1
if sbrHit
sellFactors += 1
if inPremium
sellFactors += 1
if adxOk
sellFactors += 1
if macdBelow
sellFactors += 1
if macdDeadX or bearDiv
sellFactors += 1
if rsiOB and inPremium
sellFactors += 1
if rsiBearDiv
sellFactors += 1
if not na(dpSellLevel) and math.abs(close - dpSellLevel) < atr * 0.6
sellFactors += 1
if bearStruct
sellFactors += 1
if timeframe.isintraday and not aboveVwap
sellFactors += 1
if (volBearDiv and inPremium) or wyckUTAD
sellFactors += 1
if zScore >= 1.8
sellFactors += 1
if (not na(pdHigh) and math.abs(high - pdHigh) < ohlcBuf) or (not na(pwHigh) and math.abs(high - pwHigh) < ohlcBuf) or (not na(dailyPivot) and math.abs(close - dailyPivot) < ohlcBuf)
sellFactors += 1
if (aggressiveSell and inPremium) or (absorption and atSellZone)
sellFactors += 1
if sentiment > 0.4 and atSellZone
sellFactors += 1
if (bearPin or bearEngulf or ibBearBreak) and atSellZone
sellFactors += 1
if nearEQH and inPremium
sellFactors += 1
if useWeekOpen and not na(weekOpen) and math.abs(close - weekOpen) < atr * 0.4
sellFactors += 1
if timeframe.isintraday and aboveValue and inPremium
sellFactors += 1
if useIchimoku and not na(dayKumoBot) and close < dayKumoBot
sellFactors += 1
if useIchimoku and tkKjDeath
sellFactors += 1
if useHs and hsT_state == 3 and bar_index - hsT_broke <= 5
sellFactors += 1
if useHs and hsD_Tst == 3
sellFactors += 1
bool buyGateOk = not useFactorGate or buyFactors >= minFactors
bool sellGateOk = not useFactorGate or sellFactors >= minFactors
getGrade(float score, float thresh) =>
float diff = score - thresh
diff >= 20.0 ? "A+ GOD" : diff >= 10.0 ? "A APEX" : diff >= 0.0 ? "B PRIME" : "C WEAK"
// ── Counter-bias & regime threshold gates ──
bool strongBear = biasScore == -3
bool strongBull = biasScore == 3
float buyThresh = dynThreshold
float sellThresh = dynThreshold
if userRisk != RiskProfile.Aggressive
if strongBear
buyThresh := math.min(buyThresh + 8.0, 95.0)
if strongBull
sellThresh := math.min(sellThresh + 8.0, 95.0)
if regimeStr == "🌀 CHOPPY" or lowVol
buyThresh := math.min(buyThresh + 5.0, 95.0)
sellThresh := math.min(sellThresh + 5.0, 95.0)
if regimeVolatile
buyThresh := math.max(buyThresh - 5.0, 50.0)
sellThresh := math.max(sellThresh - 5.0, 50.0)
if useBtcWeekend and btcWeekend
buyThresh := math.min(buyThresh + 3.0, 95.0)
sellThresh := math.min(sellThresh + 3.0, 95.0)
bool isCounterBiasBuy = overallBias == "BEARISH" and buyScore >= buyThresh
bool isCounterBiasSell = overallBias == "BULLISH" and sellScore >= sellThresh
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 19: TRADE STATE MACHINE, SIZING & HYDRA TRAIL ║
// ╚═══════════════════════════════════════════════════════════════════╝
var int tradeState = 0
var float entryPx = na
var float slPx = na
var float tp1Px = na
var float tp2Px = na
var float tp3Px = na
var float riskR = na
var bool tp1Done = false
var bool beActive = false
var int tradeBars = 0
var int hydraPhase = 0
var float maxProfitR = 0.0
var string catalyst = "—"
var bool isCounterBias = false
var float peakConv = 0.0
var int convLowBars = 0
var bool convSoft = false
var box tpViz = na
var box slViz = na
var line entryViz = na
// Position sizing
float slDist = math.abs(nz(entryPx, close) - nz(slPx, close))
float riskDollars = accBalance * (riskPct / 100.0)
float posUnits = slDist > 0 ? riskDollars / (slDist * math.max(tickValue, 0.001)) : 0.0
bool canTrade = not cautionMode or userRisk == RiskProfile.Aggressive
float dynSlBuf = (regimeVolatile ? slMult * 1.5 : slMult) * (useBtcWeekend and btcWeekend ? 1.15 : 1.0)
bool spikeBlocked = useSpikeShield and atrRatio > spikeAtrMult
// ╔══════════════════════════════════════════════════════════════════╗
// ║ V-REVERSAL DETECTOR — spike → reclaim (the gold "V") ║
// ║ vBot/vTop = aggressive V-reversal · vBotBreak/vTopBreak = ║
// ║ V-then-breakout (close reclaims the pre-spike extreme) ║
// ╚══════════════════════════════════════════════════════════════════╝
int vLook = 12
float vSpikeA = 2.5
int vWin = 3
float volC = volSma > 0 ? volume / volSma : 1.0
float vPreHigh = ta.highest(high, vLook + vWin)
float vPreLow = ta.lowest(low, vLook + vWin)
float vSpikeBot = ta.lowest(low, vLook)
float vSpikeTop = ta.highest(high, vLook)
float vDrop = vPreHigh - vSpikeBot
float vPump = vSpikeTop - vPreLow
int barsBot = ta.barssince(low <= vSpikeBot)
int barsTop = ta.barssince(high >= vSpikeTop)
bool vBotTrig = vDrop >= vSpikeA * atr and barsBot >= 1 and barsBot <= vWin and close - vSpikeBot >= 0.5 * vDrop and close > open and volC >= 1.2
bool vTopTrig = vPump >= vSpikeA * atr and barsTop >= 1 and barsTop <= vWin and vSpikeTop - close >= 0.5 * vPump and close < open and volC >= 1.2
bool vBotBreak = vDrop >= vSpikeA * atr and barsBot >= 1 and barsBot <= vWin and close > vPreHigh and volC >= 1.2
bool vTopBreak = vPump >= vSpikeA * atr and barsTop >= 1 and barsTop <= vWin and close < vPreLow and volC >= 1.2
// ── SESSION SENSE — instrument-aware clock (UTC exchange time) ──
bool isWeekend = isBTC and (dayofweek(time) == 0 or dayofweek(time) == 6)
bool isGoldWeekday = not isBTC and dayofweek(time) >= 1 and dayofweek(time) <= 5
bool isAsia = isGoldWeekday and hour(time) < 6
bool isLondon = isGoldWeekday and hour(time) >= 7 and hour(time) < 12
bool isGoldActive = isGoldWeekday and hour(time) >= 7 and hour(time) < 17
// ╔══════════════════════════════════════════════════════════════════╗
// ║ LINE GEOMETRY — trendlines · triangles · wedges · ranges ║
// ║ swing pivots → least-squares lines → pattern + breakout ║
// ╚══════════════════════════════════════════════════════════════════╝
// Least-squares fit over the last `cnt` pivots. x = bars since oldest fit pivot.
// Returns [slope per bar, value at oldest pivot, mean |residual|]
f_fitLine(array<float> P, array<int> B, int cnt) =>
int start = array.size(P) - cnt
float sx = 0.0
float sy = 0.0
float sxy = 0.0
float sxx = 0.0
int x0 = array.get(B, start)
for i = start to array.size(P) - 1
float x = array.get(B, i) - x0
float y = array.get(P, i)
sx += x
sy += y
sxy += x * y
sxx += x * x
float slope = 0.0
float b0 = 0.0
float denom = cnt * sxx - sx * sx
if denom > 0
slope := (cnt * sxy - sx * sy) / denom
b0 := (sy - slope * sx) / cnt
float resid = 0.0
for i = start to array.size(P) - 1
float x = array.get(B, i) - x0
resid += math.abs(array.get(P, i) - (slope * x + b0))
[slope, b0, resid / cnt]
var array<float> geoPlP = array.new<float>()
var array<int> geoPlB = array.new<int>()
var array<float> geoPhP = array.new<float>()
var array<int> geoPhB = array.new<int>()
float pivHi = ta.pivothigh(3, 3)
float pivLo = ta.pivotlow(3, 3)
if barstate.isconfirmed
if not na(pivHi)
array.push(geoPhP, pivHi)
array.push(geoPhB, bar_index - 3)
if array.size(geoPhP) > 8
array.shift(geoPhP)
array.shift(geoPhB)
if not na(pivLo)
array.push(geoPlP, pivLo)
array.push(geoPlB, bar_index - 3)
if array.size(geoPlP) > 8
array.shift(geoPlP)
array.shift(geoPlB)
int PAT_NONE = 0
int PAT_TRI = 1
int PAT_WDOW = 2
int PAT_WUP = 3
int PAT_ATRI = 4
int PAT_DTRI = 5
int PAT_RANGE = 6
bool hasSup = array.size(geoPlP) >= 3
bool hasRes = array.size(geoPhP) >= 3
float supSlope = na
float supNow = na
float supResid = na
if hasSup
int scnt = math.min(4, array.size(geoPlP))
[s0, b0, r0] = f_fitLine(geoPlP, geoPlB, scnt)
supSlope := s0
supNow := s0 * (bar_index - array.get(geoPlB, array.size(geoPlP) - scnt)) + b0
supResid := r0
bool supClean = hasSup and supResid < atr * 0.75
bool supTrend = supClean and supSlope > atr * 0.05
bool supTest = hasSup and low <= supNow and close > supNow
float resSlope = na
float resNow = na
float resResid = na
if hasRes
int rcnt = math.min(4, array.size(geoPhP))
[s1, b1, r1] = f_fitLine(geoPhP, geoPhB, rcnt)
resSlope := s1
resNow := s1 * (bar_index - array.get(geoPhP, array.size(geoPhP) - rcnt)) + b1
resResid := r1
bool resClean = hasRes and resResid < atr * 0.75
bool resTrendUp = resClean and resSlope > atr * 0.05
bool resTest = hasRes and high >= resNow and close < resNow
float gapNow = hasSup and hasRes and supClean and resClean ? resNow - supNow : na
float gapSlope = hasSup and hasRes ? resSlope - supSlope : na
float barsApex = not na(gapNow) and not na(gapSlope) and gapSlope < 0 and gapNow > 0 ? gapNow / -gapSlope : na
int pattern = PAT_NONE
if hasSup and hasRes and supClean and resClean and gapNow > atr * 0.5
bool supRise = supSlope > atr * 0.02
bool supFall = supSlope < -atr * 0.02
bool resRise = resSlope > atr * 0.02
bool resFall = resSlope < -atr * 0.02
if not (supRise or supFall) and not (resRise or resFall)
pattern := PAT_RANGE
else if gapSlope < -atr * 0.01 and not na(barsApex) and barsApex >= 5 and barsApex <= 150
if not (resRise or resFall) and supRise
pattern := PAT_ATRI
else if not (supRise or supFall) and resFall
pattern := PAT_DTRI
else if resFall and supRise
pattern := PAT_TRI
else if resFall and supFall
pattern := PAT_WDOW
else if resRise and supRise
pattern := PAT_WUP
string patternName = "—"
if pattern == PAT_TRI
patternName := "SYM TRIANGLE"
else if pattern == PAT_WDOW
patternName := "FALLING WEDGE"
else if pattern == PAT_WUP
patternName := "RISING WEDGE"
else if pattern == PAT_ATRI
patternName := "ASCENDING TRI"
else if pattern == PAT_DTRI
patternName := "DESCENDING TRI"
else if pattern == PAT_RANGE
patternName := "RANGE BOX"
bool patBreakLong = pattern != PAT_NONE and close > resNow and close[1] < resNow - resSlope and volume > volSma
bool patBreakShort = pattern != PAT_NONE and close < supNow and close[1] > supNow - supSlope and volume > volSma
// ── ASIA RANGE BREAK — gold consolidates Asia (00–06 UTC), breaks in London ──
var float asiaHi = na
var float asiaLo = na
var int asiaDay = 1
int dNow = dayofmonth(time)
if dNow != asiaDay
asiaHi := na
asiaLo := na
asiaDay := dNow
if isAsia
asiaHi := na(asiaHi) ? high : math.max(asiaHi, high)
asiaLo := na(asiaLo) ? low : math.min(asiaLo, low)
bool asiaValid = isGoldWeekday and not na(asiaHi) and asiaHi > asiaLo
bool asiaBreakLong = asiaValid and isLondon and close > asiaHi and close[1] <= asiaHi and volume > volSma
bool asiaBreakShort = asiaValid and isLondon and close < asiaLo and close[1] >= asiaLo and volume > volSma
// Optional geometry overlay (off by default)
var line geoSupL = na
var line geoResL = na
if showGeoLines
if hasSup and supClean
if na(geoSupL)
geoSupL := line.new(bar_index - 20, supNow - supSlope * 20, bar_index, supNow, color=color.new(color.green, 25), width=1)
else
line.set_xy1(geoSupL, bar_index - 20, supNow - supSlope * 20)
line.set_xy2(geoSupL, bar_index, supNow)
else if not na(geoSupL)
line.delete(geoSupL)
geoSupL := na
if hasRes and resClean
if na(geoResL)
geoResL := line.new(bar_index - 20, resNow - resSlope * 20, bar_index, resNow, color=color.new(color.red, 25), width=1)
else
line.set_xy1(geoResL, bar_index - 20, resNow - resSlope * 20)
line.set_xy2(geoResL, bar_index, resNow)
else if not na(geoResL)
line.delete(geoResL)
geoResL := na
// ╔══════════════════════════════════════════════════════════════════╗
// ║ STRATEGY ROUTER — picks the playbook per market state ║
// ║ 0 FLAT · 1 MOMENTUM · 2 MEAN-REV · 3 POST-SPIKE · 4 BREAKOUT ║
// ╚══════════════════════════════════════════════════════════════════╝
int M_FLAT = 0
int M_MOM = 1
int M_MEAN = 2
int M_POST = 3
int M_BRK = 4
int M_LEG = 5 // legacy single-strategy (useRouter off)
// Per-mode playbook rules. Returns [allowLong, allowShort, allowCounter, tp1R, tp2R, tp3R, slCapAtr, runnerAtr, threshOff]
f_modeRules(int m, float t1, float t2, string rg) =>
bool aL = true
bool aS = true
bool ctr = true
float f1 = t1
float f2 = t2
float f3 = t2 + 1.5
float cap = 2.0
float run = 1.2
float off = 0.0
if m == M_MOM
ctr := false
aL := rg == "▲ TRENDING BULL"
aS := rg == "▼ TRENDING BEAR"
else if m == M_MEAN
f1 := math.min(f1, 0.75)
f2 := math.min(f2, 1.5)
f3 := f2 + 1.0
cap := 1.5
run := 0.8
off := -5.0
else if m == M_POST
f1 := math.min(f1, 0.5)
f2 := math.min(f2, 1.0)
f3 := f2 + 0.75
cap := 1.25
run := 1.0
off := -5.0
else if m == M_BRK
f1 := math.min(f1, 1.0)
f2 := math.min(f2, 2.0)
f3 := f2 + 1.0
cap := 1.5
run := 1.0
off := -10.0
[aL, aS, ctr, f1, f2, f3, cap, run, off]
float range30 = ta.highest(high, 30) - ta.lowest(low, 30)
bool squeeze30 = range30 < atr * 6.0
bool adxUp = adxLine > nz(adxLine[3])
int candMode = M_FLAT
if useRouter
if not (volOk and sessionOk and canTrade)
candMode := M_FLAT
else if atrRatio > spikeAtrMult
candMode := M_POST
else if vBotTrig or vTopTrig or vBotBreak or vTopBreak
candMode := M_POST
else if patBreakLong or patBreakShort
candMode := M_BRK
else if asiaBreakLong or asiaBreakShort
candMode := M_BRK
else if (squeeze30 and adxUp) and f_modeViable(M_BRK)
candMode := M_BRK
else if (regimeStr == "▲ TRENDING BULL" or regimeStr == "▼ TRENDING BEAR") and adxOk and f_modeViable(M_MOM)
candMode := M_MOM
else if f_modeViable(M_MEAN)
candMode := M_MEAN
else
candMode := M_LEG
else
candMode := volOk and sessionOk and canTrade ? M_LEG : M_FLAT
var int mode = 0
var int modeCand = 0
var int modeHold = 0
if barstate.isconfirmed
modeHold := candMode == modeCand ? modeHold + 1 : 1
if candMode != modeCand
modeCand := candMode
if modeHold >= modeDwell and modeCand != mode
mode := modeCand
string modeName = "LEGACY"
if mode == M_FLAT
modeName := "FLAT (no-trade)"
else if mode == M_MOM
modeName := "MOMENTUM"
else if mode == M_MEAN
modeName := "MEAN-REV"
else if mode == M_POST
modeName := "POST-SPIKE"
else if mode == M_BRK
modeName := "BREAKOUT"
// Playbook gate: does the active mode's setup agree with this side right now?
f_modeSignal(int m, bool isLong) =>
bool ok = true
if m == M_MOM
float val = timeframe.isintraday ? vwapVal : hmaBase
ok := isLong ? (low[1] <= val + atr * 0.75 or low[2] <= val + atr * 0.75 or low[3] <= val + atr * 0.75) : (high[1] >= val - atr * 0.75 or high[2] >= val - atr * 0.75 or high[3] >= val - atr * 0.75)
else if m == M_MEAN
ok := isLong ? (inDiscount and (validBuySweep or atBuyZone or rsiOS)) : (inPremium and (validSellSweep or atSellZone or rsiOB))
else if m == M_POST
ok := isLong ? (vBotTrig or vBotBreak) : (vTopTrig or vTopBreak)
else if m == M_BRK
ok := isLong ? ((close > ta.highest(high, 30)[1] and volume > volSma) or patBreakLong or asiaBreakLong) : ((close < ta.lowest(low, 30)[1] and volume > volSma) or patBreakShort or asiaBreakShort)
ok
if useRouter and alertOnMode and barstate.isconfirmed and mode != mode[1]
alert("🧭 MODE → " + modeName + " | " + syminfo.ticker, alert.freq_once_per_bar)
// ── GOD CONVICTION ENGINE — live directional confluence (0..100) ──
// (uses existing pdHigh/pdLow prev-day levels declared above)
f_conviction(bool isLong) =>
float c = 50.0
if isGoldActive
c += 3.0
if isWeekend
c -= 5.0
if isLong
if regimeStr == "▲ TRENDING BULL"
c += isBTC ? 14.0 : 12.0
if regimeStr == "▼ TRENDING BEAR"
c -= 12.0
if close > (timeframe.isintraday ? vwapVal : hmaBase)
c += 8.0
else
c -= 5.0
else
if regimeStr == "▼ TRENDING BEAR"
c += isBTC ? 14.0 : 12.0
if regimeStr == "▲ TRENDING BULL"
c -= 12.0
if close < (timeframe.isintraday ? vwapVal : hmaBase)
c += 8.0
else
c -= 5.0
if adxOk and adxLine > nz(adxLine[3])
c += 8.0
if isLong
if emaF > emaS
c += 8.0
else
c -= 8.0
else
if emaF < emaS
c += 8.0
else
c -= 8.0
if isLong
if macroBullish
c += 8.0
if macroBearish
c -= 8.0
else
if macroBearish
c += 8.0
if macroBullish
c -= 8.0
if isLong
if validBuySweep or vBotTrig
c += 8.0
if validSellSweep
c -= 10.0
else
if validSellSweep or vTopTrig
c += 8.0
if validBuySweep
c -= 10.0
if isLong
if hsB_state >= 2
c += 8.0
if hsT_state >= 2
c -= 8.0
else
if hsT_state >= 2
c += 8.0
if hsB_state >= 2
c -= 8.0
if isLong
if rsiOS
c += 5.0
if rsiOB
c -= 5.0
else
if rsiOB
c += 5.0
if rsiOS
c -= 5.0
if isLong
if supTrend and supTest
c += 8.0
if patBreakLong
c += 8.0
if asiaBreakLong
c += 8.0
if supTrend and close < supNow
c -= 10.0
if resClean and resSlope < 0 and high >= resNow and close < resNow
c -= 8.0
if pattern == PAT_WDOW
c += 5.0
if pattern == PAT_WUP
c -= 5.0
else
if resTrendUp and resTest
c += 8.0
if patBreakShort
c += 8.0
if asiaBreakShort
c += 8.0
if resTrendUp and close > resNow
c -= 10.0
if supClean and supSlope > 0 and low <= supNow and close > supNow
c -= 8.0
if pattern == PAT_WUP
c += 5.0
if pattern == PAT_WDOW
c -= 5.0
float rndStep = isBTC ? 500.0 : 100.0
float rnd = math.round(close / rndStep) * rndStep
if math.abs(close - rnd) < atr * 0.5
if isLong and close < rnd
c -= 5.0
if isLong and close > rnd
c += 5.0
if not isLong and close > rnd
c -= 5.0
if not isLong and close < rnd
c += 5.0
if not na(pdHigh) and not na(pdLow)
if isLong and close > pdHigh
c += 5.0
if isLong and close < pdLow
c -= 5.0
if not isLong and close < pdLow
c += 5.0
if not isLong and close > pdHigh
c -= 5.0
if not na(pwHigh) and not na(pwLow)
if isLong and close > pwHigh
c += 5.0
if isLong and close < pwLow
c -= 5.0
if not isLong and close < pwLow
c += 5.0
if not isLong and close > pwHigh
c -= 5.0
math.max(0.0, math.min(100.0, c))
float convNow = tradeState == 1 ? f_conviction(true) : tradeState == -1 ? f_conviction(false) : f_conviction(buyScore >= sellScore)
bool goLong = false
bool goShort = false
[modeAL, modeAS, modeCTR, modeT1, modeT2, modeT3, modeCap, modeRun, modeOff] = f_modeRules(useRouter ? mode : M_LEG, tp1RR, tp2RR, regimeStr)
float modeThreshB = math.max(math.min(buyThresh + modeOff + f_modeThreshAdj(mode), 95.0), 50.0)
float modeThreshS = math.max(math.min(sellThresh + modeOff + f_modeThreshAdj(mode), 95.0), 50.0)
var float posRunAtr = 1.2
if tradeState == 0 and barstate.isconfirmed and volOk and sessionOk and canTrade
bool modeOpen = useRouter ? mode > M_FLAT : true
bool playLong = useRouter ? f_modeSignal(mode, true) : true
bool playShort = useRouter ? f_modeSignal(mode, false) : true
bool spikeOk2 = (useRouter and mode == M_POST) or not spikeBlocked
bool buyOk = modeAL and modeOpen and playLong and spikeOk2 and buyScore >= modeThreshB and buyGateOk
bool sellOk = modeAS and modeOpen and playShort and spikeOk2 and sellScore >= modeThreshS and sellGateOk
if not modeCTR
if isCounterBiasBuy
buyOk := false
if isCounterBiasSell
sellOk := false
if buyOk and (not sellOk or buyScore >= sellScore + 5.0)
goLong := true
else if sellOk and (not buyOk or sellScore >= buyScore + 5.0)
goShort := true
// ── LONG ENTRY ──
if goLong
tradeState := 1
entryPx := close
slPx := math.max(recentSwingLow - atr * dynSlBuf * symSlBuf, close - atr * modeCap)
riskR := entryPx - slPx
tp1Px := entryPx + riskR * modeT1
tp2Px := entryPx + riskR * modeT2
tp3Px := usePVS ? entryPx + riskR * modeT3 : na
posRunAtr := modeRun
peakConv := convNow
convLowBars := 0
convSoft := false
tp1Done := false
beActive := false
tradeBars := 0
hydraPhase := 0
maxProfitR := 0.0
catalyst := buyWhy
tradeMode := mode
isCounterBias := isCounterBiasBuy
if not na(tpViz)
box.delete(tpViz)
if not na(slViz)
box.delete(slViz)
if not na(entryViz)
line.delete(entryViz)
tpViz := box.new(bar_index, usePVS ? tp3Px : tp2Px, bar_index + 1, entryPx, bgcolor=color.new(color.teal, 88), border_color=color.new(color.teal, 35))
slViz := box.new(bar_index, entryPx, bar_index + 1, slPx, bgcolor=color.new(color.red, 88), border_color=color.new(color.red, 35))
entryViz := line.new(bar_index - 3, entryPx, bar_index + 1, entryPx, color=color.new(color.white, 40), style=line.style_dashed)
string grNote = "\n[" + getGrade(buyScore, buyThresh) + "]"
string cbNote = isCounterBiasBuy ? "\n⚡ Counter-Bias" : ""
string tp3Str = usePVS ? "\nTP3: " + str.tostring(tp3Px, "#.##") : ""
label.new(bar_index, slPx - atr * 1.5,
text="🟢 LONG\nUnits: " + str.tostring(posUnits, "#.##") + "\nEntry: " + str.tostring(entryPx, "#.##") + "\nSL: " + str.tostring(slPx, "#.##") + "\nTP1: " + str.tostring(tp1Px, "#.##") + "\nTP2: " + str.tostring(tp2Px, "#.##") + tp3Str + "\n" + buyWhy + grNote + cbNote + "\nScore: " + str.tostring(buyScore, "#") + "%\n🧭 " + modeName,
color=color.rgb(8, 38, 18), textcolor=color.white, style=label.style_label_up, size=size.normal)
if alertOnEntry
alert("🟢 LONG | " + syminfo.ticker + (isCounterBiasBuy ? " ⚡COUNTER" : "") + " | Entry: " + str.tostring(entryPx, "#.##") + " | Units: " + str.tostring(posUnits, "#.##") + " | " + buyWhy, alert.freq_once_per_bar)
// ── SHORT ENTRY ──
if goShort
tradeState := -1
entryPx := close
slPx := math.min(recentSwingHigh + atr * dynSlBuf * symSlBuf, close + atr * modeCap)
riskR := slPx - entryPx
tp1Px := entryPx - riskR * modeT1
tp2Px := entryPx - riskR * modeT2
tp3Px := usePVS ? entryPx - riskR * modeT3 : na
posRunAtr := modeRun
peakConv := convNow
convLowBars := 0
convSoft := false
tp1Done := false
beActive := false
tradeBars := 0
hydraPhase := 0
maxProfitR := 0.0
catalyst := sellWhy
tradeMode := mode
isCounterBias := isCounterBiasSell
if not na(tpViz)
box.delete(tpViz)
if not na(slViz)
box.delete(slViz)
if not na(entryViz)
line.delete(entryViz)
tpViz := box.new(bar_index, entryPx, bar_index + 1, usePVS ? tp3Px : tp2Px, bgcolor=color.new(color.teal, 88), border_color=color.new(color.teal, 35))
slViz := box.new(bar_index, slPx, bar_index + 1, entryPx, bgcolor=color.new(color.red, 88), border_color=color.new(color.red, 35))
entryViz := line.new(bar_index - 3, entryPx, bar_index + 1, entryPx, color=color.new(color.white, 40), style=line.style_dashed)
string grNote = "\n[" + getGrade(sellScore, sellThresh) + "]"
string cbNote = isCounterBiasSell ? "\n⚡ Counter-Bias" : ""
string tp3Str = usePVS ? "\nTP3: " + str.tostring(tp3Px, "#.##") : ""
label.new(bar_index, slPx + atr * 1.5,
text="🔴 SHORT\nUnits: " + str.tostring(posUnits, "#.##") + "\nEntry: " + str.tostring(entryPx, "#.##") + "\nSL: " + str.tostring(slPx, "#.##") + "\nTP1: " + str.tostring(tp1Px, "#.##") + "\nTP2: " + str.tostring(tp2Px, "#.##") + tp3Str + "\n" + sellWhy + grNote + cbNote + "\nScore: " + str.tostring(sellScore, "#") + "%\n🧭 " + modeName,
color=color.rgb(38, 8, 8), textcolor=color.white, style=label.style_label_down, size=size.normal)
if alertOnEntry
alert("🔴 SHORT | " + syminfo.ticker + (isCounterBiasSell ? " ⚡COUNTER" : "") + " | Entry: " + str.tostring(entryPx, "#.##") + " | Units: " + str.tostring(posUnits, "#.##") + " | " + sellWhy, alert.freq_once_per_bar)
// ── GOD CONVICTION — LONG (live soft/hard invalidation + stop adaptation) ──
if tradeState == 1 and barstate.isconfirmed
if convNow > peakConv
peakConv := convNow
convLowBars := convNow < 20.0 ? convLowBars + 1 : 0
if convNow < 40.0
float softTarget = entryPx - 0.5 * riskR
if softTarget > slPx
slPx := softTarget
convSoft := true
if not na(slViz)
box.set_bottom(slViz, slPx)
if peakConv - convNow >= 20.0 and close > entryPx
float midPx = (entryPx + close) / 2
if midPx > slPx
slPx := midPx
if not na(slViz)
box.set_bottom(slViz, slPx)
if convLowBars >= 2
tradeState := 0
f_record_trade(beActive)
label.new(bar_index, close, "❄ INVALIDATED — Conv " + str.tostring(convNow, "#"), color=color.new(color.purple, 10), textcolor=color.white, style=label.style_label_up, size=size.small)
if alertOnExit
alert("❄ CONVICTION EXIT | LONG | " + syminfo.ticker + " @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
// ── LONG POSITION MANAGEMENT ──
string exitNote = ""
if tradeState == 1 and barstate.isconfirmed
tradeBars += 1
float curR = (close - entryPx) / math.max(riskR, 0.001)
if curR > maxProfitR
maxProfitR := curR
// HYDRA 4-phase trailing
if useHydra and riskR > 0
if not tp1Done
if curR >= 0.3
float tPrice = close - atr * trailATR
if tPrice > slPx
slPx := tPrice
hydraPhase := 1
else
if not beActive
beActive := true
slPx := entryPx
hydraPhase := 2
else
if curR >= 1.5
float runner = close - atr * slMult * posRunAtr
if runner > slPx
slPx := runner
hydraPhase := 3
// Profit lock: after +1.5R, never give back more than 0.5R from the peak
if useProfitLock and maxProfitR >= 1.5
float lockPx = entryPx + (maxProfitR - 0.5) * riskR
if lockPx > slPx
slPx := lockPx
else if moveBEat50 and curR >= 0.5 and slPx < entryPx
slPx := entryPx
hydraPhase := 2
if not na(tpViz)
box.set_right(tpViz, bar_index + 1)
if not na(slViz)
box.set_right(slViz, bar_index + 1)
box.set_bottom(slViz, slPx)
if not na(entryViz)
line.set_x2(entryViz, bar_index + 1)
// Intelligent BE: pre-TP1 defense at +0.75R on strong conviction
if not tp1Done and close - entryPx >= riskR * 0.75 and convNow >= 60.0 and slPx < entryPx
slPx := entryPx + 0.1 * riskR
beActive := true
if not na(slViz)
box.set_bottom(slViz, slPx)
if not tp1Done and high >= tp1Px
tp1Done := true
beActive := true
if convNow >= 70.0
slPx := entryPx + 0.25 * riskR
else if convNow >= 50.0
slPx := entryPx + beOffsetR * riskR
else
slPx := entryPx
if not na(slViz)
box.set_bottom(slViz, slPx)
label.new(bar_index, tp1Px + atr * 0.3, "✅ TP1 + BE Lock", color=color.new(color.teal, 20), textcolor=color.white, style=label.style_label_down, size=size.tiny)
// Exit ladder — if/else-if so TP and SL can never both fire on one bar
if usePVS
if high >= tp3Px
tradeState := 0
exitNote := "TP3 HIT 🚀"
f_record_trade(true)
label.new(bar_index, tp3Px + atr * 0.5, "🎯 TP3 HIT", color=color.new(color.teal, 10), textcolor=color.white, style=label.style_label_down, size=size.normal)
if alertOnExit
alert("🚀 TP3 | " + syminfo.ticker + " LONG @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
else if low <= slPx
tradeState := 0
exitNote := beActive ? "STOPPED AT BE 🛡️" : "STOP LOSS ❌"
f_record_trade(beActive)
label.new(bar_index, slPx - atr * 0.5, exitNote, color=color.new(color.red, 10), textcolor=color.white, style=label.style_label_up, size=size.normal)
if alertOnExit
alert(exitNote + " | " + syminfo.ticker + " LONG @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
else if high >= tp2Px
label.new(bar_index, tp2Px + atr * 0.3, "✅ TP2 (runner on)", color=color.new(color.teal, 20), textcolor=color.white, style=label.style_label_down, size=size.tiny)
else
if high >= tp2Px
tradeState := 0
exitNote := "TP2 HIT ✅"
f_record_trade(true)
label.new(bar_index, tp2Px + atr * 0.5, "🎯 TP2 HIT", color=color.new(color.teal, 10), textcolor=color.white, style=label.style_label_down, size=size.normal)
if alertOnExit
alert("🎯 TP2 | " + syminfo.ticker + " LONG @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
else if low <= slPx
tradeState := 0
exitNote := beActive ? "STOPPED AT BE 🛡️" : "STOP LOSS ❌"
f_record_trade(beActive)
label.new(bar_index, slPx - atr * 0.5, exitNote, color=color.new(color.red, 10), textcolor=color.white, style=label.style_label_up, size=size.normal)
if alertOnExit
alert(exitNote + " | " + syminfo.ticker + " LONG @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
// ── GOD CONVICTION — SHORT (live soft/hard invalidation + stop adaptation) ──
if tradeState == -1 and barstate.isconfirmed
if convNow > peakConv
peakConv := convNow
convLowBars := convNow < 20.0 ? convLowBars + 1 : 0
if convNow < 40.0
float softTarget = entryPx + 0.5 * riskR
if softTarget < slPx
slPx := softTarget
convSoft := true
if not na(slViz)
box.set_top(slViz, slPx)
if peakConv - convNow >= 20.0 and close < entryPx
float midPx = (entryPx + close) / 2
if midPx < slPx
slPx := midPx
if not na(slViz)
box.set_top(slViz, slPx)
if convLowBars >= 2
tradeState := 0
f_record_trade(beActive)
label.new(bar_index, close, "❄ INVALIDATED — Conv " + str.tostring(convNow, "#"), color=color.new(color.purple, 10), textcolor=color.white, style=label.style_label_down, size=size.small)
if alertOnExit
alert("❄ CONVICTION EXIT | SHORT | " + syminfo.ticker + " @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
// ── SHORT POSITION MANAGEMENT ──
if tradeState == -1 and barstate.isconfirmed
tradeBars += 1
float curR = (entryPx - close) / math.max(riskR, 0.001)
if curR > maxProfitR
maxProfitR := curR
// HYDRA 4-phase trailing
if useHydra and riskR > 0
if not tp1Done
if curR >= 0.3
float tPrice = close + atr * trailATR
if tPrice < slPx
slPx := tPrice
hydraPhase := 1
else
if not beActive
beActive := true
slPx := entryPx
hydraPhase := 2
else
if curR >= 1.5
float runner = close + atr * slMult * posRunAtr
if runner < slPx
slPx := runner
hydraPhase := 3
// Profit lock: after +1.5R, never give back more than 0.5R from the peak
if useProfitLock and maxProfitR >= 1.5
float lockPx = entryPx - (maxProfitR - 0.5) * riskR
if lockPx < slPx
slPx := lockPx
else if moveBEat50 and curR >= 0.5 and slPx > entryPx
slPx := entryPx
hydraPhase := 2
if not na(tpViz)
box.set_right(tpViz, bar_index + 1)
if not na(slViz)
box.set_right(slViz, bar_index + 1)
box.set_top(slViz, slPx)
if not na(entryViz)
line.set_x2(entryViz, bar_index + 1)
// Intelligent BE: pre-TP1 defense at -0.75R on strong conviction
if not tp1Done and entryPx - close >= riskR * 0.75 and convNow >= 60.0 and slPx > entryPx
slPx := entryPx - 0.1 * riskR
beActive := true
if not na(slViz)
box.set_top(slViz, slPx)
if not tp1Done and low <= tp1Px
tp1Done := true
beActive := true
if convNow >= 70.0
slPx := entryPx - 0.25 * riskR
else if convNow >= 50.0
slPx := entryPx - beOffsetR * riskR
else
slPx := entryPx
if not na(slViz)
box.set_top(slViz, slPx)
label.new(bar_index, tp1Px - atr * 0.3, "✅ TP1 + BE Lock", color=color.new(color.teal, 20), textcolor=color.white, style=label.style_label_up, size=size.tiny)
if usePVS
if low <= tp3Px
tradeState := 0
exitNote := "TP3 HIT 🚀"
f_record_trade(true)
label.new(bar_index, tp3Px - atr * 0.5, "🎯 TP3 HIT", color=color.new(color.teal, 10), textcolor=color.white, style=label.style_label_up, size=size.normal)
if alertOnExit
alert("🚀 TP3 | " + syminfo.ticker + " SHORT @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
else if high >= slPx
tradeState := 0
exitNote := beActive ? "STOPPED AT BE 🛡️" : "STOP LOSS ❌"
f_record_trade(beActive)
label.new(bar_index, slPx + atr * 0.5, exitNote, color=color.new(color.red, 10), textcolor=color.white, style=label.style_label_down, size=size.normal)
if alertOnExit
alert(exitNote + " | " + syminfo.ticker + " SHORT @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
else if low <= tp2Px
label.new(bar_index, tp2Px - atr * 0.3, "✅ TP2 (runner on)", color=color.new(color.teal, 20), textcolor=color.white, style=label.style_label_up, size=size.tiny)
else
if low <= tp2Px
tradeState := 0
exitNote := "TP2 HIT ✅"
f_record_trade(true)
label.new(bar_index, tp2Px - atr * 0.5, "🎯 TP2 HIT", color=color.new(color.teal, 10), textcolor=color.white, style=label.style_label_up, size=size.normal)
if alertOnExit
alert("🎯 TP2 | " + syminfo.ticker + " SHORT @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
else if high >= slPx
tradeState := 0
exitNote := beActive ? "STOPPED AT BE 🛡️" : "STOP LOSS ❌"
f_record_trade(beActive)
label.new(bar_index, slPx + atr * 0.5, exitNote, color=color.new(color.red, 10), textcolor=color.white, style=label.style_label_down, size=size.normal)
if alertOnExit
alert(exitNote + " | " + syminfo.ticker + " SHORT @ " + str.tostring(close, "#.##"), alert.freq_once_per_bar)
// ── Pending signals (post-state-machine) ──
bool pendingBuy = modeAL and buyScore >= modeThreshB and buyGateOk and not spikeBlocked and tradeState == 0 and volOk and sessionOk and canTrade
bool pendingSell = modeAS and sellScore >= modeThreshS and sellGateOk and not spikeBlocked and tradeState == 0 and volOk and sessionOk and canTrade
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 20: SIGNAL & ZONE ALERTS ║
// ╚═══════════════════════════════════════════════════════════════════╝
if alertOnSignal and barstate.isconfirmed
if pendingBuy
alert("⚠ BUY | " + syminfo.ticker + " [" + symbolMode + "] | " + str.tostring(buyScore, "#") + "% | " + buyWhy + " | Regime: " + regimeStr, alert.freq_once_per_bar)
if pendingSell
alert("⚠ SELL | " + syminfo.ticker + " [" + symbolMode + "] | " + str.tostring(sellScore, "#") + "% | " + sellWhy + " | Regime: " + regimeStr, alert.freq_once_per_bar)
if alertOnZone and tradeState == 0 and barstate.isconfirmed
if bObHit or bFvgHit or rbsHit
alert("📍 BUY ZONE | " + syminfo.ticker, alert.freq_once_per_bar)
if rObHit or rFvgHit or sbrHit
alert("📍 SELL ZONE | " + syminfo.ticker, alert.freq_once_per_bar)
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 21: S&R CLUSTER WEIGHT ENGINE ║
// ╚═══════════════════════════════════════════════════════════════════╝
srClusterWeight(float lev) =>
float cd = atr * 0.65
int w = 0
if not na(pdHigh) and math.abs(lev - pdHigh) <= cd
w += 3
if not na(pdLow) and math.abs(lev - pdLow) <= cd
w += 3
if not na(pwHigh) and math.abs(lev - pwHigh) <= cd
w += 3
if not na(pwLow) and math.abs(lev - pwLow) <= cd
w += 3
if not na(dailyPivot) and math.abs(lev - dailyPivot) <= cd
w += 3
if not na(bObNearTop) and math.abs(lev - bObNearTop) <= cd
w += 2
if not na(bObNearBot) and math.abs(lev - bObNearBot) <= cd
w += 2
if not na(rObNearTop) and math.abs(lev - rObNearTop) <= cd
w += 2
if not na(rObNearBot) and math.abs(lev - rObNearBot) <= cd
w += 2
if not na(bFvgNearTop) and math.abs(lev - bFvgNearTop) <= cd
w += 2
if not na(bFvgNearBot) and math.abs(lev - bFvgNearBot) <= cd
w += 2
if not na(rFvgNearTop) and math.abs(lev - rFvgNearTop) <= cd
w += 2
if not na(rFvgNearBot) and math.abs(lev - rFvgNearBot) <= cd
w += 2
if not na(lastPH) and math.abs(lev - lastPH) <= cd
w += 2
if not na(lastPL) and math.abs(lev - lastPL) <= cd
w += 2
if not na(lastHtfPH) and math.abs(lev - lastHtfPH) <= cd
w += 2
if not na(lastHtfPL) and math.abs(lev - lastHtfPL) <= cd
w += 2
if not na(rbsNearTop) and math.abs(lev - rbsNearTop) <= cd
w += 2
if not na(sbrNearTop) and math.abs(lev - sbrNearTop) <= cd
w += 2
if useWeekOpen and not na(weekOpen) and math.abs(lev - weekOpen) <= cd
w += 2
if timeframe.isintraday and not na(oiLevel) and math.abs(lev - oiLevel) <= cd
w += 2
if math.abs(lev - rangeH) <= cd
w += 1
if math.abs(lev - rangeL) <= cd
w += 1
w
srStars(int w) =>
w >= 5 ? "★★★" : w >= 3 ? "★★" : "★"
srBgLabel(int w, bool isSup) =>
isSup ? (w >= 5 ? color.rgb(8, 45, 18) : w >= 3 ? color.rgb(10, 28, 14) : color.rgb(10, 18, 12)) : (w >= 5 ? color.rgb(45, 8, 8) : w >= 3 ? color.rgb(28, 10, 10) : color.rgb(18, 10, 10))
srBgValue(int w, bool isSup) =>
isSup ? (w >= 5 ? color.rgb(12, 60, 24) : w >= 3 ? color.rgb(14, 38, 18) : color.rgb(14, 24, 16)) : (w >= 5 ? color.rgb(60, 12, 12) : w >= 3 ? color.rgb(38, 14, 14) : color.rgb(24, 14, 14))
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 22: EXECUTION HUD (BOTTOM-RIGHT) ║
// ╚═══════════════════════════════════════════════════════════════════╝
f_drawHud() =>
if showHUD
color DR = color.rgb(10, 10, 10)
color MR = color.rgb(18, 18, 18)
color TC = color.new(color.white, 8)
color LC = color.new(color.gray, 15)
var table execHUD = na
var string hudPosUsed = ""
if na(execHUD) or hudPos != hudPosUsed
if not na(execHUD)
table.delete(execHUD)
execHUD := table.new(f_pos(hudPos), 2, 26, border_width=1, border_color=color.new(color.gray, 65), frame_color=color.new(color.gray, 55), frame_width=1)
hudPosUsed := hudPos
if barstate.islast
color stateBg = color.rgb(15, 15, 15)
string stateStr = "● SCANNING"
if tradeState == 1
stateBg := color.rgb(0, 52, 20)
stateStr := "▲ LONG ACTIVE"
else if tradeState == -1
stateBg := color.rgb(52, 0, 0)
stateStr := "▼ SHORT ACTIVE"
else if pendingBuy
stateBg := color.rgb(0, 38, 14)
stateStr := "⚠ BUY SIGNAL"
else if pendingSell
stateBg := color.rgb(38, 0, 0)
stateStr := "⚠ SELL SIGNAL"
else if exitNote != ""
stateStr := "LAST: " + exitNote
string liveRR = "—"
color rrCol = MR
if tradeState != 0 and not na(riskR) and riskR > 0
float pnl = tradeState == 1 ? close - entryPx : entryPx - close
float rr = pnl / riskR
liveRR := (rr >= 0 ? "+" : "") + str.tostring(rr, "#.##") + "R | " + str.tostring(tradeBars) + "b"
rrCol := rr >= 0 ? color.new(color.green, 50) : color.new(color.red, 50)
string hydraStr = "—"
if tradeState != 0
hydraStr := hydraPhase == 0 ? "Phase 0: Initial" : hydraPhase == 1 ? "Phase 1: Tight Trail" : hydraPhase == 2 ? "Phase 2: BE Locked" : "Phase 3: Runner"
string tp1Str = "—"
if tradeState != 0
if tp1Done
tp1Str := "✅ Secured (BE On)"
else
tp1Str := "⏳ En Route"
string sessionStr = "Off-Session"
color sessionCol = color.rgb(22, 22, 22)
if not timeframe.isintraday
sessionStr := "Daily+ (N/A)"
sessionCol := color.new(color.gray, 60)
else if inLondon
sessionStr := "🔵 London (07-12)"
sessionCol := color.new(color.blue, 52)
else if inNY
sessionStr := "🟠 New York (12-17)"
sessionCol := color.new(color.orange, 52)
else if inAsian
sessionStr := "🟣 Asian (00-07)"
sessionCol := color.new(color.purple, 60)
string cautionStr = cautionMode ? "⚠ CAUTION MODE" : spikeBlocked ? "🛡 SPIKE SHIELD" : (useBtcWeekend and btcWeekend) ? "🌙 THIN (BTC WE)" : "✅ Normal"
string macroHudStr = "N/A"
color macroHudCol = color.rgb(22, 22, 22)
if macroValid
if macroBullish
macroHudStr := isInverseMacro ? "✅ Favors Longs" : "✅ Favors Longs"
macroHudCol := color.new(color.green, 70)
else if macroBearish
macroHudStr := "✅ Favors Shorts"
macroHudCol := color.new(color.red, 70)
else
macroHudStr := "◆ Neutral"
string zoneStr = "Between Zones"
color zoneCol = color.rgb(22, 22, 22)
if atBuyZone
zoneStr := "⚡ IN BUY ZONE"
zoneCol := color.new(color.green, 50)
else if atSellZone
zoneStr := "⚡ IN SELL ZONE"
zoneCol := color.new(color.red, 50)
string buyZoneStr = "No Zone"
if not na(bObNearTop)
buyZoneStr := "OB @ " + str.tostring(bObNearTop, "#.##")
else if not na(bFvgNearTop)
buyZoneStr := "FVG @ " + str.tostring(bFvgNearTop, "#.##")
else if not na(rbsNearTop)
buyZoneStr := "RBS @ " + str.tostring(rbsNearTop, "#.##")
string sellZoneStr = "No Zone"
if not na(rObNearTop) and rObNearTop > close
sellZoneStr := "OB @ " + str.tostring(rObNearTop, "#.##")
else if not na(rFvgNearTop) and rFvgNearTop > close
sellZoneStr := "FVG @ " + str.tostring(rFvgNearTop, "#.##")
else if not na(sbrNearTop) and sbrNearTop > close
sellZoneStr := "SBR @ " + str.tostring(sbrNearTop, "#.##")
string liqStr = "No Sweep"
color liqCol = MR
if sslSweep
liqStr := "▲ SSL SWEPT"
liqCol := color.new(color.green, 45)
else if bslSweep
liqStr := "▼ BSL SWEPT"
liqCol := color.new(color.red, 45)
string pdStr = "EQUILIBRIUM ↔"
if inPremium
pdStr := "PREMIUM ↑"
else if inDiscount
pdStr := "DISCOUNT ↓"
string biasAgreStr = "—"
color biasAgreCol = color.rgb(22, 22, 22)
if tradeState == 1
if overallBias == "BULLISH"
biasAgreStr := "✅ ALIGNED"
biasAgreCol := color.new(color.green, 50)
else if overallBias == "BEARISH"
biasAgreStr := "⚡ COUNTER"
biasAgreCol := color.new(color.orange, 45)
else
biasAgreStr := "◆ MIXED"
else if tradeState == -1
if overallBias == "BEARISH"
biasAgreStr := "✅ ALIGNED"
biasAgreCol := color.new(color.green, 50)
else if overallBias == "BULLISH"
biasAgreStr := "⚡ COUNTER"
biasAgreCol := color.new(color.orange, 45)
else
biasAgreStr := "◆ MIXED"
string catStr = "Awaiting..."
if tradeState != 0
catStr := catalyst
else if pendingBuy
catStr := buyWhy
else if pendingSell
catStr := sellWhy
string confStr = "B:" + str.tostring(buyScore, "#") + "% (" + str.tostring(buyFactors) + "F) S:" + str.tostring(sellScore, "#") + "% (" + str.tostring(sellFactors) + "F)"
color confBg = MR
if buyScore >= buyThresh
confBg := color.new(color.green, 40)
else if sellScore >= sellThresh
confBg := color.new(color.red, 40)
string gradeStr = tradeState != 0 ? getGrade(tradeState == 1 ? buyScore : sellScore, dynThreshold) : "—"
string asocStr = useASOC ? str.tostring(asocWinRate, "#") + "% WR | Adj " + (adaptOffset >= 0 ? "+" : "") + str.tostring(adaptOffset, "#.#") + "% | " + str.tostring(adaptTrades, "#") + "/" + str.tostring(asocWindow) + " tr" : "Off"
table.cell(execHUD, 0, 0, "OMNI-GOD ∞ UNIFIED", bgcolor=color.rgb(5, 5, 5), text_color=TC, text_size=size.small)
table.cell(execHUD, 1, 0, stateStr, bgcolor=stateBg, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 1, "Mode / Timeframe", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 1, symbolMode + " | " + tfLabel, bgcolor=MR, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 2, "Regime", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 2, regimeStr + " | " + volRegimeStr, bgcolor=regimeCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 3, "Session / Caution", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 3, sessionStr + " | " + cautionStr, bgcolor=sessionCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 4, "Macro (" + macroName + ")", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 4, macroHudStr, bgcolor=macroHudCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 5, "Overall Bias", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 5, biasEmoji, bgcolor=biasBg, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 6, "Market Structure", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 6, structStr, bgcolor=structCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 7, "Wyckoff Phase", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 7, wyckStr, bgcolor=wyckCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 8, "MACD", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 8, macdStr, bgcolor=macdDispCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 9, "RSI (" + str.tostring(rsiLen) + ")", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 9, rsiStr, bgcolor=rsiDispCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 10, "VWAP", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 10, vwapStr, bgcolor=vwapCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 11, "Sentiment", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 11, sentStr, bgcolor=sentCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 12, "Footprint", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 12, footprintStr, bgcolor=footprintCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 13, "Candle", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 13, candleStr, bgcolor=MR, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 14, "Liquidity / PD", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 14, liqStr + " | " + pdStr + (nearEQL ? " | ⭑EQL Pool" : nearEQH ? " | ⭑EQH Pool" : ""), bgcolor=liqCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 15, "Zone Status", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 15, zoneStr + " | B:" + buyZoneStr + " S:" + sellZoneStr, bgcolor=zoneCol, text_color=TC, text_size=size.tiny)
table.cell(execHUD, 0, 16, "Confluence", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 16, confStr, bgcolor=confBg, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 17, "ASOC Threshold", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 17, asocStr, bgcolor=MR, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 18, "Position Sizing", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 18, str.tostring(posUnits, "#.##") + " units ($" + str.tostring(riskDollars, "#") + " risk)", bgcolor=MR, text_color=color.teal, text_size=size.small)
table.cell(execHUD, 0, 19, "Live R:R | TP1", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 19, liveRR + " | Max +" + str.tostring(maxProfitR, "#.##") + "R | " + tp1Str, bgcolor=rrCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 20, "Hydra Trail", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 20, hydraStr, bgcolor=MR, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 21, "Bias Agreement", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 21, biasAgreStr, bgcolor=biasAgreCol, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 22, "Setup Grade", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 22, gradeStr, bgcolor=MR, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 23, "Catalyst", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 23, catStr, bgcolor=MR, text_color=TC, text_size=size.tiny)
string asianStr = "N/A"
if not na(asianHigh) and not na(asianLow)
asianStr := str.tostring(asianLow, "#.##") + " - " + str.tostring(asianHigh, "#.##") + " | " + str.tostring(math.abs(asianHigh - asianLow) / math.max(atr, 0.001), "#.##") + " ATR | " + str.tostring(bar_index - nz(asianStart, bar_index)) + "b"
string valueStr = "N/A (Daily+)"
if timeframe.isintraday
valueStr := belowValue ? "BELOW BAND ⬇ (Value Low)" : aboveValue ? "ABOVE BAND ⬆ (Value High)" : "IN VALUE BAND ⇄"
table.cell(execHUD, 0, 24, "Asian Range", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 24, asianStr, bgcolor=MR, text_color=TC, text_size=size.small)
table.cell(execHUD, 0, 25, "Vol Value (VWAP)", bgcolor=DR, text_color=LC, text_size=size.small)
table.cell(execHUD, 1, 25, valueStr, bgcolor=MR, text_color=TC, text_size=size.small)
f_drawHud()
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 23: CONFLUENCE RADAR (TOP-RIGHT) ║
// ═══════════════════════════════════════════════════════════════════╝
f_drawRadar() =>
if showRadar
var table radarTbl = na
var string radarPosUsed = ""
if na(radarTbl) or radarPos != radarPosUsed
if not na(radarTbl)
table.delete(radarTbl)
radarTbl := table.new(f_pos(radarPos), 2, 13, border_width=1, border_color=color.new(color.gray, 65), frame_color=color.new(color.gray, 55), frame_width=1)
radarPosUsed := radarPos
if barstate.islast
f_radar_row(radarTbl, 0, "━━ CONFLUENCE RADAR ━━", "━━ " + symbolMode + " ━━", true)
f_radar_row(radarTbl, 1, "Macro (" + macroName + ")", macroBullish ? "Favors Longs" : macroBearish ? "Favors Shorts" : "Neutral", macroBullish or macroBearish)
f_radar_row(radarTbl, 2, "Trend Stack (EMA)", bullStack ? "Bullish" : bearStack ? "Bearish" : "Mixed", bullStack or bearStack)
f_radar_row(radarTbl, 3, "Baseline (HMA 50)", close > hmaBase ? "Above" : "Below", true)
f_radar_row(radarTbl, 4, "Elastic EMA (" + str.tostring(elasticLen) + ")", close > emaElastic ? "Bullish" : "Bearish", true)
f_radar_row(radarTbl, 5, "Multi-TF Bias", overallBias, overallBias != "MIXED")
f_radar_row(radarTbl, 6, "Liquidity / EQ Pool", validBuySweep or validSellSweep ? "Swept ⚡" : nearEQL or nearEQH ? "EQ Pool 🎯" : "None", validBuySweep or validSellSweep or nearEQL or nearEQH)
f_radar_row(radarTbl, 7, "SMC Zone / Breaker", atBuyZone or atSellZone ? "Tap" : "None", atBuyZone or atSellZone)
f_radar_row(radarTbl, 8, "ADX Strength", adxOk ? "Strong" : "Weak", adxOk)
f_radar_row(radarTbl, 9, "Wyckoff Phase", wyckStr, wyckStr != "N/A")
f_radar_row(radarTbl, 10, "Score vs Threshold", (buyScore >= dynThreshold or sellScore >= dynThreshold) ? "EXECUTE" : "WAIT", buyScore >= dynThreshold or sellScore >= dynThreshold)
f_radar_row(radarTbl, 11, "Ichimoku (Daily)", dayKumoState, not na(dayKumoTop))
f_radar_row(radarTbl, 12, "H&S Pattern", hsT_state == 3 ? "TOP BROKEN ✖" : hsB_state == 3 ? "BOTTOM BROKEN ✔" : hsT_state >= 1 or hsB_state >= 1 ? "Forming" : hsD_Tst == 3 ? "DAILY TOP ✖" : hsD_Bst == 3 ? "DAILY BOTTOM ✔" : "None", hsT_state >= 1 or hsB_state >= 1 or hsD_Tst >= 1 or hsD_Bst >= 1)
f_drawRadar()
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 24: ANALYSIS TABLE (TOP-LEFT) ║
// ╚═══════════════════════════════════════════════════════════════════╝
f_drawAnalysis() =>
if showAnalysis
color HDR = color.rgb(5, 5, 5)
color DR2 = color.rgb(10, 10, 10)
color MR2 = color.rgb(18, 18, 18)
color TC2 = color.new(color.white, 8)
color LC2 = color.new(color.gray, 15)
color CUR_BG = color.rgb(18, 18, 35)
color PIV_BG = color.rgb(30, 28, 8)
var table atbl = na
var string atblPosUsed = ""
if na(atbl) or analysisPos != atblPosUsed
if not na(atbl)
table.delete(atbl)
atbl := table.new(f_pos(analysisPos), 2, 26, border_width=1, border_color=color.new(color.gray, 65), frame_color=color.new(color.gray, 55), frame_width=1)
atblPosUsed := analysisPos
if barstate.islast
// ── Resistance / Support candidates ──
array<float> resCands = array.from(
not na(pdHigh) and pdHigh > close ? pdHigh : na,
not na(pwHigh) and pwHigh > close ? pwHigh : na,
not na(dailyPivot) and dailyPivot > close ? dailyPivot : na,
not na(lastPH) and lastPH > close ? lastPH : na,
not na(lastHtfPH) and lastHtfPH > close ? lastHtfPH : na,
not na(rObNearBot) and rObNearBot > close ? rObNearBot : na,
not na(rFvgNearTop) and rFvgNearTop > close ? rFvgNearTop : na,
not na(sbrNearTop) and sbrNearTop > close ? sbrNearTop : na,
useWeekOpen and not na(weekOpen) and weekOpen > close ? weekOpen : na,
timeframe.isintraday and not na(oiLevel) and oiLevel > close ? oiLevel : na,
not na(rangeH) and rangeH > close ? rangeH : na)
array<float> supCands = array.from(
not na(pdLow) and pdLow < close ? pdLow : na,
not na(pwLow) and pwLow < close ? pwLow : na,
not na(dailyPivot) and dailyPivot < close ? dailyPivot : na,
not na(lastPL) and lastPL < close ? lastPL : na,
not na(lastHtfPL) and lastHtfPL < close ? lastHtfPL : na,
not na(bObNearTop) and bObNearTop < close ? bObNearTop : na,
not na(bFvgNearTop) and bFvgNearTop < close ? bFvgNearTop : na,
not na(rbsNearTop) and rbsNearTop < close ? rbsNearTop : na,
useWeekOpen and not na(weekOpen) and weekOpen < close ? weekOpen : na,
timeframe.isintraday and not na(oiLevel) and oiLevel < close ? oiLevel : na,
not na(rangeL) and rangeL < close ? rangeL : na)
// ── 3 nearest resistances ──
float res1 = na
float res2 = na
float res3 = na
int resW1 = 0
int resW2 = 0
int resW3 = 0
if array.size(resCands) > 0
for i = 0 to array.size(resCands) - 1
float lev = array.get(resCands, i)
if not na(lev)
int w = srClusterWeight(lev)
if na(res1) or lev < res1
res3 := res2
resW3 := resW2
res2 := res1
resW2 := resW1
res1 := lev
resW1 := w
else if (na(res2) or lev < res2) and math.abs(lev - res1) > atr * 0.4
res3 := res2
resW3 := resW2
res2 := lev
resW2 := w
else if (na(res3) or lev < res3) and not na(res2) and math.abs(lev - res2) > atr * 0.4
res3 := lev
resW3 := w
// ── 3 nearest supports ──
float sup1 = na
float sup2 = na
float sup3 = na
int supW1 = 0
int supW2 = 0
int supW3 = 0
if array.size(supCands) > 0
for i = 0 to array.size(supCands) - 1
float lev = array.get(supCands, i)
if not na(lev)
int w = srClusterWeight(lev)
if na(sup1) or lev > sup1
sup3 := sup2
supW3 := supW2
sup2 := sup1
supW2 := supW1
sup1 := lev
supW1 := w
else if (na(sup2) or lev > sup2) and math.abs(lev - sup1) > atr * 0.4
sup3 := sup2
supW3 := supW2
sup2 := lev
supW2 := w
else if (na(sup3) or lev > sup3) and not na(sup2) and math.abs(lev - sup2) > atr * 0.4
sup3 := lev
supW3 := w
bool res1IsPivot = not na(res1) and not na(dailyPivot) and math.abs(res1 - dailyPivot) < atr * 0.6
bool res2IsPivot = not na(res2) and not na(dailyPivot) and math.abs(res2 - dailyPivot) < atr * 0.6
bool res3IsPivot = not na(res3) and not na(dailyPivot) and math.abs(res3 - dailyPivot) < atr * 0.6
bool sup1IsPivot = not na(sup1) and not na(dailyPivot) and math.abs(sup1 - dailyPivot) < atr * 0.6
bool sup2IsPivot = not na(sup2) and not na(dailyPivot) and math.abs(sup2 - dailyPivot) < atr * 0.6
bool sup3IsPivot = not na(sup3) and not na(dailyPivot) and math.abs(sup3 - dailyPivot) < atr * 0.6
string res3LStr = not na(res3) ? srStars(resW3) + " Resistance" : "—"
string res3VStr = not na(res3) ? str.tostring(res3, "#.##") + (res3IsPivot ? " ⭕" : "") : "—"
string res2LStr = not na(res2) ? srStars(resW2) + " Resistance" : "—"
string res2VStr = not na(res2) ? str.tostring(res2, "#.##") + (res2IsPivot ? " ⭕" : "") : "—"
string res1LStr = not na(res1) ? srStars(resW1) + " Resistance" + (res1IsPivot ? " ⭕" : "") : "—"
string res1VStr = not na(res1) ? str.tostring(res1, "#.##") : "—"
string sup1LStr = not na(sup1) ? srStars(supW1) + " Support" + (sup1IsPivot ? " ⭕" : "") : "—"
string sup1VStr = not na(sup1) ? str.tostring(sup1, "#.##") : "—"
string sup2LStr = not na(sup2) ? srStars(supW2) + " Support" : "—"
string sup2VStr = not na(sup2) ? str.tostring(sup2, "#.##") : "—"
string sup3LStr = not na(sup3) ? srStars(supW3) + " Support" : "—"
string sup3VStr = not na(sup3) ? str.tostring(sup3, "#.##") + (sup3IsPivot ? " ⭕" : "") : "—"
string lastStr = "◆ LAST " + str.tostring(close, "#.##")
string prefLine = "—"
string altLine = "—"
string comment = "—"
if overallBias == "BEARISH"
if not na(res1)
prefLine := "Downside prevails below " + str.tostring(res1, "#.##")
if not na(sup1) and not na(sup2)
prefLine := prefLine + ", targets " + str.tostring(sup1, "#.##") + " & " + str.tostring(sup2, "#.##")
else if not na(sup1)
prefLine := prefLine + ", target " + str.tostring(sup1, "#.##")
else
prefLine := "Bearish — sell rallies to resistance"
if not na(res1) and not na(res2)
altLine := "Above " + str.tostring(res1, "#.##") + ", look for " + str.tostring(res2, "#.##")
else
altLine := "Break above resistance → bearish invalidated"
else if overallBias == "BULLISH"
if not na(sup1)
prefLine := "Upside prevails above " + str.tostring(sup1, "#.##")
if not na(res1) and not na(res2)
prefLine := prefLine + ", targets " + str.tostring(res1, "#.##") + " & " + str.tostring(res2, "#.##")
else if not na(res1)
prefLine := prefLine + ", target " + str.tostring(res1, "#.##")
else
prefLine := "Bullish — buy dips to support"
if not na(sup1) and not na(sup2)
altLine := "Below " + str.tostring(sup1, "#.##") + ", look for " + str.tostring(sup2, "#.##")
else
altLine := "Break below support → bullish invalidated"
else
string sStr = not na(sup1) ? str.tostring(sup1, "#.##") : "?"
string rStr = not na(res1) ? str.tostring(res1, "#.##") : "?"
prefLine := "Ranging between " + sStr + " and " + rStr
if not na(res1)
altLine := "Break " + rStr + " bullish | Break " + sStr + " bearish"
else
altLine := "Await directional breakout"
// ── AUTO-GENERATED BRIEFING — assembles itself from live state ──
string cDir = overallBias == "BULLISH" ? "BULLISH bias" : overallBias == "BEARISH" ? "BEARISH bias" : "MIXED bias"
cDir := cDir + " (" + str.tostring(math.abs(biasScore)) + "/3 TF" + (math.abs(biasScore) == 1 ? "" : "s") + " aligned)"
string cConf = "Conf B " + str.tostring(buyScore, "#") + "%/" + str.tostring(buyFactors) + "F vs S " + str.tostring(sellScore, "#") + "%/" + str.tostring(sellFactors) + "F"
string cDriver = "none above threshold yet"
if buyScore >= dynThreshold
cDriver := buyWhy
else if sellScore >= dynThreshold
cDriver := sellWhy
string cCloud = na(dayKumoTop) ? "" : close > dayKumoTop ? " | Above daily Kumo" : close < dayKumoBot ? " | Below daily Kumo" : " | In daily Kumo (chop)"
string cPat = ""
if hsT_state >= 1
cPat := "H&S TOP " + (hsT_state == 3 ? "BROKEN ✖" : "forming · NL " + str.tostring(hsT_nl, "#.##"))
if hsB_state >= 1
cPat := cPat + (cPat == "" ? "" : " · ") + "H&S BOT " + (hsB_state == 3 ? "BROKEN ✔" : "forming · NL " + str.tostring(hsB_nl, "#.##"))
if hsD_Tst == 3
cPat := cPat + (cPat == "" ? "" : " · ") + "dH&S TOP broken"
if hsD_Bst == 3
cPat := cPat + (cPat == "" ? "" : " · ") + "dH&S BOT broken"
if cPat == "" and nearEQL
cPat := "EQL pool at " + str.tostring(eqlLevel, "#.##")
if cPat == "" and nearEQH
cPat := "EQH pool at " + str.tostring(eqhLevel, "#.##")
string cRisk = ""
if cautionMode
cRisk := cRisk + " ⚠Caution"
if spikeBlocked
cRisk := cRisk + " 🛡Spike"
if not adxOk
cRisk := cRisk + " weak-mom"
if useBtcWeekend and btcWeekend
cRisk := cRisk + " thin-wknd"
if isCounterBias
cRisk := cRisk + " counter-bias"
if lowVol
cRisk := cRisk + " low-vol"
string cLevels = "R " + (not na(res1) ? str.tostring(res1, "#.##") : "?") + " | S " + (not na(sup1) ? str.tostring(sup1, "#.##") : "?")
comment := cDir + " | " + cConf + " | Driver: " + cDriver + cCloud
string cRead = cLevels + " | " + (cPat == "" ? "no active pattern" : cPat) + cRisk
color res3LBG = srBgLabel(resW3, false)
color res3VBG = srBgValue(resW3, false)
color res2LBG = srBgLabel(resW2, false)
color res2VBG = srBgValue(resW2, false)
color res1LBG = srBgLabel(resW1, false)
color res1VBG = srBgValue(resW1, false)
color sup1LBG = srBgLabel(supW1, true)
color sup1VBG = srBgValue(supW1, true)
color sup2LBG = srBgLabel(supW2, true)
color sup2VBG = srBgValue(supW2, true)
color sup3LBG = srBgLabel(supW3, true)
color sup3VBG = srBgValue(supW3, true)
if res1IsPivot
res1LBG := PIV_BG
res1VBG := PIV_BG
if sup1IsPivot
sup1LBG := PIV_BG
sup1VBG := PIV_BG
string alignStr = "DIVERGING (0/3)"
int absScore = math.abs(biasScore)
if absScore == 3
alignStr := "STRONG (3/3)"
else if absScore == 2
alignStr := "ALIGNED (2/3)"
else if absScore == 1
alignStr := "WEAK (1/3)"
table.cell(atbl, 0, 0, "ANALYSIS " + tfLabel, bgcolor=HDR, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 0, symbolMode, bgcolor=HDR, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 1, "Intraday (" + tf1Label + ")", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 1, tf1Str, bgcolor=tf1Col, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 2, "Short Term (" + tf2Label + ")", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 2, tf2Str, bgcolor=tf2Col, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 3, "Medium Term (" + tf3Label + ")", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 3, tf3Str, bgcolor=tf3Col, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 4, "Alignment", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 4, alignStr, bgcolor=MR2, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 5, "MACD", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 5, macdStr, bgcolor=macdDispCol, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 6, "RSI (" + str.tostring(rsiLen) + ")", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 6, rsiStr, bgcolor=rsiDispCol, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 7, "VWAP", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 7, vwapStr, bgcolor=vwapCol, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 8, "Wyckoff", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 8, wyckStr, bgcolor=wyckCol, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 9, "── LEVELS ──", bgcolor=HDR, text_color=color.new(color.gray, 60), text_size=size.tiny)
table.cell(atbl, 1, 9, "── ⭕ = Daily Pivot ──", bgcolor=HDR, text_color=color.new(color.gray, 60), text_size=size.tiny)
table.cell(atbl, 0, 10, res3LStr, bgcolor=res3LBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 10, res3VStr, bgcolor=res3VBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 11, res2LStr, bgcolor=res2LBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 11, res2VStr, bgcolor=res2VBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 12, res1LStr, bgcolor=res1LBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 12, res1VStr, bgcolor=res1VBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 13, lastStr, bgcolor=CUR_BG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 13, abovePivot ? "▲ Above Pivot" : "▼ Below Pivot", bgcolor=abovePivot ? color.new(color.green, 55) : color.new(color.red, 55), text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 14, sup1LStr, bgcolor=sup1LBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 14, sup1VStr, bgcolor=sup1VBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 15, sup2LStr, bgcolor=sup2LBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 15, sup2VStr, bgcolor=sup2VBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 16, sup3LStr, bgcolor=sup3LBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 1, 16, sup3VStr, bgcolor=sup3VBG, text_color=TC2, text_size=size.small)
table.cell(atbl, 0, 17, "── ANALYSIS ──", bgcolor=HDR, text_color=color.new(color.gray, 60), text_size=size.tiny)
table.cell(atbl, 1, 17, "────────────────", bgcolor=HDR, text_color=color.new(color.gray, 60), text_size=size.tiny)
table.cell(atbl, 0, 18, "Our Preference", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 18, prefLine, bgcolor=biasBg, text_color=TC2, text_size=size.tiny)
table.cell(atbl, 0, 19, "Alternative", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 19, altLine, bgcolor=MR2, text_color=TC2, text_size=size.tiny)
table.cell(atbl, 0, 20, "Comment", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 20, comment + " | Th: " + str.tostring(dynThreshold, "#") + "%", bgcolor=MR2, text_color=TC2, text_size=size.tiny)
string hsTFLine = "—"
if hsT_state > 0 or hsB_state > 0
hsTFLine := (hsT_state > 0 ? "TOP " + f_hsStage(hsT_state, hsT_nl, "✖") : "") + (hsB_state > 0 ? " · BOT " + f_hsStage(hsB_state, hsB_nl, "✔") : "")
string hsDFLine = "—"
if hsD_Tst > 0 or hsD_Bst > 0
hsDFLine := (hsD_Tst > 0 ? "TOP " + f_hsStage(hsD_Tst, hsD_Tnl, "✖") : "") + (hsD_Bst > 0 ? " · BOT " + f_hsStage(hsD_Bst, hsD_Bnl, "✔") : "")
table.cell(atbl, 0, 21, "Ichimoku (Daily)", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 21, dayKumoState + " | " + str.tostring(dayKumoBot, "#.##") + "–" + str.tostring(dayKumoTop, "#.##"), bgcolor=MR2, text_color=TC2, text_size=size.tiny)
table.cell(atbl, 0, 22, "Ichimoku (TF)", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 22, tfKumoState + " | TK" + (ichiTk > ichiKj ? ">" : "<") + "KJ" + (tkKjGold ? " ✚X" : tkKjDeath ? " ✖X" : ""), bgcolor=MR2, text_color=TC2, text_size=size.tiny)
table.cell(atbl, 0, 23, "H&S (Daily)", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 23, hsDFLine, bgcolor=MR2, text_color=TC2, text_size=size.tiny)
table.cell(atbl, 0, 24, "H&S (TF)", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 24, hsTFLine, bgcolor=MR2, text_color=TC2, text_size=size.tiny)
table.cell(atbl, 0, 25, "Auto-Read", bgcolor=DR2, text_color=LC2, text_size=size.small)
table.cell(atbl, 1, 25, cRead, bgcolor=MR2, text_color=TC2, text_size=size.tiny)
f_drawAnalysis()
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ SECTION 25: PLOTS, LINES, HEATMAP & NARRATIVE ║
// ╚═══════════════════════════════════════════════════════════════════╝
plot(showEmas ? emaF : na, "Fast EMA", color=color.new(color.aqua, 15), linewidth=1)
plot(showEmas ? emaM : na, "Med EMA", color=color.new(color.orange, 15), linewidth=2)
plot(showEmas ? emaS : na, "Slow EMA", color=color.new(color.red, 15), linewidth=2)
plot(showEmas ? emaT : na, "Trend EMA", color=color.new(color.white, 55), linewidth=1)
plot(showHma ? hmaBase : na, "HMA Baseline", color=color.new(color.purple, 20), linewidth=2)
plot(showElastic and useElastic ? emaElastic : na, "Elastic EMA", color=color.new(color.cyan, 30), linewidth=2, style=plot.style_dotted)
plot(showVwap and timeframe.isintraday ? vwapVal : na, "VWAP", color=color.new(color.white, 22), linewidth=2)
plot(showPD ? rangeH : na, "Range High", color=color.new(color.red, 72), style=plot.style_stepline)
plot(showPD ? rangeL : na, "Range Low", color=color.new(color.green, 72), style=plot.style_stepline)
plot(showPD ? rangeEQ : na, "Range EQ", color=color.new(color.gray, 78), style=plot.style_stepline)
// Confluence heatmap background (tiered opacity — int-safe)
// NOTE: bgcolor() is only allowed at global scope, so the color is resolved inline here
float heatHgtB = (buyScore - buyThresh) / math.max(100.0 - buyThresh, 1.0)
float heatHgtS = (sellScore - sellThresh) / math.max(100.0 - sellThresh, 1.0)
color heatColor = showHeatmap and tradeState == 0 and buyScore >= buyThresh ? (heatHgtB >= 0.6 ? color.new(color.green, 60) : heatHgtB >= 0.3 ? color.new(color.green, 75) : color.new(color.green, 88)) : showHeatmap and tradeState == 0 and sellScore >= sellThresh ? (heatHgtS >= 0.6 ? color.new(color.red, 60) : heatHgtS >= 0.3 ? color.new(color.red, 75) : color.new(color.red, 88)) : na
f_drawClouds() =>
// ── Ichimoku clouds — daily + current TF ──
var line cldDT = na
var line cldDB = na
var linefill cldDF = na
var line cldCT = na
var line cldCB = na
var linefill cldCF = na
if showIchimoku and barstate.islast
if not na(cldDT)
line.delete(cldDT)
if not na(cldDB)
line.delete(cldDB)
if not na(cldDF)
linefill.delete(cldDF)
if not na(cldCT)
line.delete(cldCT)
if not na(cldCB)
line.delete(cldCB)
if not na(cldCF)
linefill.delete(cldCF)
if not na(dayKumoTop) and not na(dayKumoBot)
cldDT := line.new(bar_index - 60, dayKumoTop[60], bar_index + 30, dayKumoTop, color=color.new(color.gray, 25), style=line.style_solid, width=1)
cldDB := line.new(bar_index - 60, dayKumoBot[60], bar_index + 30, dayKumoBot, color=color.new(color.gray, 25), style=line.style_solid, width=1)
cldDF := linefill.new(cldDT, cldDB, color.new(color.gray, 85))
if not na(ichiTopC) and not na(ichiBotC)
cldCT := line.new(bar_index - 60, ichiTopC[60], bar_index + 30, ichiTopC, color=color.new(color.aqua, 20), style=line.style_solid, width=1)
cldCB := line.new(bar_index - 60, ichiBotC[60], bar_index + 30, ichiBotC, color=color.new(color.aqua, 20), style=line.style_solid, width=1)
cldCF := linefill.new(cldCT, cldCB, color.new(color.aqua, 88))
f_drawClouds()
bgcolor(heatColor, title="Confluence Heatmap")
f_drawLevels() =>
// Key OHLC & pivot lines (redrawn on last bar)
var line _pdH = na
var line _pdL = na
var line _pwH = na
var line _pwL = na
var line _pvL = na
var line _wkO = na
if showLevels and barstate.islast
if not na(_pdH)
line.delete(_pdH)
if not na(_pdL)
line.delete(_pdL)
if not na(_pwH)
line.delete(_pwH)
if not na(_pwL)
line.delete(_pwL)
if not na(_pvL)
line.delete(_pvL)
if not na(_wkO)
line.delete(_wkO)
if not na(pdHigh)
_pdH := line.new(bar_index - 30, pdHigh, bar_index + 15, pdHigh, color=color.new(color.orange, 35), style=line.style_dashed, width=1)
if not na(pdLow)
_pdL := line.new(bar_index - 30, pdLow, bar_index + 15, pdLow, color=color.new(color.orange, 35), style=line.style_dashed, width=1)
if not na(pwHigh)
_pwH := line.new(bar_index - 30, pwHigh, bar_index + 15, pwHigh, color=color.new(color.yellow, 50), style=line.style_dotted, width=1)
if not na(pwLow)
_pwL := line.new(bar_index - 30, pwLow, bar_index + 15, pwLow, color=color.new(color.yellow, 50), style=line.style_dotted, width=1)
if not na(dailyPivot)
_pvL := line.new(bar_index - 30, dailyPivot, bar_index + 15, dailyPivot, color=color.new(color.white, 38), style=line.style_dotted, width=2)
if useWeekOpen and not na(weekOpen)
_wkO := line.new(bar_index - 30, weekOpen, bar_index + 15, weekOpen, color=color.new(color.green, 35), style=line.style_dotted, width=1)
f_drawLevels()
f_drawLiqHeat() =>
// Liquidity heat projection above/below last swings
var box liqAboveBox = na
var box liqBelowBox = na
if showLiqHeat and barstate.islast
if not na(liqAboveBox)
box.delete(liqAboveBox)
if not na(liqBelowBox)
box.delete(liqBelowBox)
if not na(lastPH)
liqAboveBox := box.new(bar_index - 5, lastPH, bar_index + 20, lastPH + atr * 0.6, bgcolor=color.new(color.red, 92), border_color=na)
if not na(lastPL)
liqBelowBox := box.new(bar_index - 5, lastPL - atr * 0.6, bar_index + 20, lastPL, bgcolor=color.new(color.green, 92), border_color=na)
f_drawLiqHeat()
f_drawSweeps() =>
// Sweep reference lines
if showSweeps and barstate.isconfirmed
if bslSweep
line.new(bar_index - swingLen, lastPH, bar_index, lastPH, color=color.new(color.red, 40), style=line.style_dashed)
if sslSweep
line.new(bar_index - swingLen, lastPL, bar_index, lastPL, color=color.new(color.green, 40), style=line.style_dashed)
f_drawSweeps()
f_drawNarrative() =>
// ── NARRATIVE VOICE ──
if showNarrative and barstate.islast
string convStr = str.tostring(convNow, "#") + (convNow > nz(convNow[1]) ? " ▲" : convNow < nz(convNow[1]) ? " ▼" : " ·")
string perfTag = ""
if f_modePerfN(mode) >= 5
perfTag := " " + str.tostring(f_modePerfWr(mode) * 100, "#") + "% WR (" + str.tostring(f_modePerfN(mode), "#") + ")"
string narrative = "🧭 " + modeName + perfTag + " · " + symbolMode + " · Conv " + convStr
if tradeState == 1
string rStr = riskR > 0 ? str.tostring((close - entryPx) / riskR, "#.##") + "R" : "—"
narrative := narrative + " · 🚀 LONG " + (close >= entryPx ? "+" : "") + rStr + " · " + (tp1Done ? (slPx > entryPx ? "BE + Profit Lock" : "BE Locked") : "En Route") + (convSoft ? " · ⚠ Soft Invalidation" : "") + (isCounterBias ? " · ⚡ C-Bias" : "") + " · " + catalyst
else if tradeState == -1
string rStr = riskR > 0 ? str.tostring((entryPx - close) / riskR, "#.##") + "R" : "—"
narrative := narrative + " · 🚀 SHORT " + (close <= entryPx ? "+" : "") + rStr + " · " + (tp1Done ? (slPx < entryPx ? "BE + Profit Lock" : "BE Locked") : "En Route") + (convSoft ? " · ⚠ Soft Invalidation" : "") + (isCounterBias ? " · ⚡ C-Bias" : "") + " · " + catalyst
else
string ctx = "Equilibrium"
if cautionMode
ctx := "⚠ " + (isNfpDay ? "NFP Day" : "London Fix")
else if spikeBlocked
ctx := "🛡 Spike Shield"
else if vBotTrig
ctx := "⚡ V-bottom " + str.tostring(nz(barsBot)) + "b ago"
else if vTopTrig
ctx := "⚡ V-top " + str.tostring(nz(barsTop)) + "b ago"
else if pattern != PAT_NONE
ctx := "📐 " + patternName + (not na(barsApex) ? " · apex ~" + str.tostring(barsApex, "#") + "b" : "")
else if isWeekend
ctx := "🌙 WEEKEND · thin flow"
else if asiaBreakLong
ctx := "🌏 ASIA RANGE BREAK ↑"
else if asiaBreakShort
ctx := "🌏 ASIA RANGE BREAK ↓"
else if useHs and ((hsT_state == 3 and bar_index - hsT_broke <= 5) or (hsB_state == 3 and bar_index - hsB_broke <= 5))
ctx := "⚠ H&S " + (hsT_state == 3 ? "TOP BROKEN" : "BOTTOM BROKEN")
else if pendingBuy
ctx := "🟢 BUY READY " + str.tostring(buyScore, "#") + "% · " + buyWhy
else if pendingSell
ctx := "🔴 SELL READY " + str.tostring(sellScore, "#") + "% · " + sellWhy
else if squeeze30
ctx := "🟡 Compression — breakout watch"
else if regimeVolatile
ctx := "🟣 Volatile expansion"
else if inPremium
ctx := "Premium zone — favor shorts"
else if inDiscount
ctx := "Discount zone — favor longs"
narrative := narrative + " · " + ctx
var label narLabel = na
if not na(narLabel)
label.delete(narLabel)
narrative := "🧭 " + modeName + " · " + narrative
narLabel := label.new(bar_index + 1, close, narrative, color=color.rgb(8, 8, 12), textcolor=color.white, style=label.style_label_left, size=size.small, textalign=text.align_left)
f_drawNarrative()
// ╔═══════════════════════════════════════════════════════════════════╗
// ║ END OF SCRIPT ║
// ╚═══════════════════════════════════════════════════════════════════╝
Show all 3,696 lines