```html Rell SMI v4.7 Premium - Indicator Command Site
Rell SMI v4.7 Premium
Indicator command site
Rell SMI v4.7 FULL · Market brain command center

Context first. Signals second.

Rell SMI v4.7 Premium is a TradingView market intelligence system built to read structure, liquidity, momentum, VWAP position, trend strength, volume pressure, and session participation as one coordinated decision layer.

Instead of blindly firing buy and sell prompts, it grades the environment, measures confluence, highlights trap conditions, and turns noisy chart action into a readable market state.

TradingView ready Copy Pine code Teaching-friendly explanations
Installation

Setup in TradingView

Four steps to load the system onto your chart. The entire raw Pine Script is available at the bottom of this command center.

1

Copy the source code

Scroll down to the Algorithm section at the bottom of this page and hit "Copy Pine Code" to copy the raw script to your clipboard.

2

Open TradingView

Launch your target chart. Open the "Pine Editor" tab at the bottom of the TradingView interface and select "Open > New blank indicator".

3

Paste and save

Delete any existing code in the editor. Paste the Rell SMI script you copied. Hit "Save" and name the file "Rell SMI v4.7 Premium".

4

Add to chart

Click the "Add to chart" button in the top right of the Pine Editor panel. The system will now execute on your live feed.

Dashboard glossary

Read the board without guessing.

These explanation cards mirror the style in your screenshots so someone can understand the output quickly instead of staring at labels with no context.

Bias

Which side has more confluence. This is not a blind entry button. It is the directional lean created by combined evidence.

Quality

A+, A, B, C, D, Trap Risk, or No Trade. This section translates raw confirmation into a usable execution grade.

Liquidity

BSL and SSL pools, sweeps, and reclaim behavior. This is stop-hunt context and often explains why price moved the way it did.

Structure

BOS signals continuation. CHoCH warns that control may be shifting. This is your backbone context.

FVG / OB

Shows where price may rebalance, mitigate, or react around institutional imbalance and order block zones.

Session

Shows whether London or New York participation is active so you can avoid treating dead conditions like real opportunity.

Ten analysis engines

Every engine has a job.

The edge comes from how the engines reinforce or contradict one another across timeframes. The cards below give you the exact educational feel your mockups were aiming for.

1. Market structure

Uses BOS, CHoCH, swing logic, internal structure, higher-timeframe bias, and alignment checks.

  • Defines bullish, bearish, or conflicting structure.
  • Raises caution when HTF and LTF disagree.
  • Forms the backbone of directional bias.

2. Liquidity

Tracks SSL, BSL, equal highs and lows, sweep events, and reclaim behavior.

  • Detects stop hunts and reversal conditions.
  • Heavily influences sentiment weighting.
  • Separates real continuation from engineered traps.

3. Order blocks

Scores displacement-backed institutional zones and fresh retests.

  • Distinguishes fresh from stale zones.
  • Checks directional alignment before weighting.
  • Improves retest quality assessment.

4. Fair value gaps

Maps imbalance, mitigation, inversion behavior, and continuation logic.

  • Identifies inefficient price delivery.
  • Reads support and resistance behavior inside gaps.
  • Tracks whether mitigation confirms or weakens context.

5. Momentum

Built around compression, squeeze behavior, acceleration, and expansion shifts.

  • Finds breakout phases before and after release.
  • Flags exhaustion and weakening continuation.
  • Adds timing context to structural bias.

6. Trend strength

Uses ADX and directional movement to classify trend versus chop.

  • Stops trend signals from dominating during ranges.
  • Measures trend quality, not just direction.
  • Improves confluence quality scoring.

7. VWAP deviation

Reads VWAP, standard deviation, and premium or discount positioning.

  • Measures fair value versus extension.
  • Flags exhaustion at premium extremes.
  • Supports continuation logic after discount reclaim.

8. Volume pressure

Estimates buyer and seller aggression with delta-style logic.

  • Exposes weak breakouts and absorption.
  • Confirms when pressure matches price action.
  • Improves fakeout detection.

9. Session participation

Uses London, New York, killzones, RVOL, and participation filters.

  • Filters weak sessions and dead liquidity windows.
  • Discourages forcing trades in poor conditions.
  • Measures whether the move has real participation.

10. Auto adaptation

Changes behavior depending on the asset class being traded.

  • Crypto reacts faster to sweeps and momentum shifts.
  • Forex favors cleaner structure weighting.
  • Stocks and futures emphasize session and volume more heavily.
Setup quality framework

Grades that force realism.

The grading system avoids vague labels and instead forces a realistic reading of probability, risk, alignment, and participation.

A+

Premium setup

Strong multi-engine agreement with favorable context, timing, and structure.

  • Best quality execution window.
  • Strong confirmation stack.
A

High probability

Clean alignment with strong conditions, though not at peak quality.

  • Usually actionable.
  • Still respect invalidation.
B

Tradable

Solid, but often handled best after a retest or extra confirmation.

  • Usable with discipline.
  • Not full size by default.
C

Riskier valid setup

Real idea, but exposed to conflict, weaker participation, or incomplete alignment.

  • Lower conviction.
  • Demand tighter control.
D

Weak edge

Low conviction environment where discipline matters more than action.

  • Often better skipped.
  • Thin confluence.
TRAP RISK

Danger state

Possible exhaustion, reclaim, reversal, weak breakout, or engineered liquidity event.

  • High caution.
  • Designed to stop impulsive entries.

Source Code

The Algorithm

Save and add to chart

Name it Rell SMI v4.7 Premium, save the script, then click Add to Chart to load the full dashboard.

Open TradingView
Pine preview · install-ready Pine block Ready
//@version=5
indicator("Rell SMI v4.7 Premium", "RSMI47P", overlay=true, max_lines_count=500, max_boxes_count=500, max_labels_count=500, max_bars_back=2000)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Utils
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

f_pos(posTxt) =>
    switch posTxt
        "Top Left" => position.top_left
        "Top Right" => position.top_right
        "Bottom Left" => position.bottom_left
        "Bottom Right" => position.bottom_right
        => position.top_right

f_size(s) =>
    switch s
        "Tiny" => size.tiny
        "Small" => size.small
        "Normal" => size.normal
        "Large" => size.large
        "Huge" => size.huge
        => size.small

f_solid(c) =>
    color.rgb(color.r(c), color.g(c), color.b(c))

f_pct(x) =>
    str.tostring(x, "#.##") + "%"

f_prob(x) =>
    str.tostring(math.round(x * 100.0)) + "%"

f_score_text(bull, bear, maxv) =>
    "Bull " + str.tostring(bull) + "/" + str.tostring(maxv) + " | Bear " + str.tostring(bear) + "/" + str.tostring(maxv)

f_sentiment_bar(_pct) =>
    filled = int(math.round(_pct / 10.0))
    empty = 10 - filled
    str.repeat("█", filled) + str.repeat("░", empty)

f_trend_text(c, e50, e200) =>
    string result = "Range"
    if c > e50 and e50 > e200
        result := "Bull"
    else if c < e50 and e50 < e200
        result := "Bear"
    result

f_trend_color(t) =>
    t == "Bull" ? color.lime : t == "Bear" ? color.red : color.gray

f_clamp(v, lo, hi) =>
    math.max(lo, math.min(hi, v))

transparent = color.new(color.white, 100)
atr = ta.atr(14)
atrLong = ta.atr(50)
ema20 = ta.ema(close, 20)
ema50 = ta.ema(close, 50)
ema200 = ta.ema(close, 200)
volMa = ta.sma(volume, 20)
rvol = volume / math.max(volMa, 1)

pipSize = syminfo.type == "forex" ? syminfo.mintick * 10.0 : syminfo.mintick
atrPips = atr / pipSize

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Inputs
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

gMarket = "Market / Presets"
marketMode = input.string("Auto", "Market Mode", options=["Auto", "Crypto", "Forex", "Stocks", "Futures/Indices", "Custom"], group=gMarket)
applyPresetOverrides = input.bool(true, "Apply Preset Overrides", group=gMarket)
allowAutoDetect = input.bool(true, "Allow Auto Detect When Auto", group=gMarket)
customLabel = input.string("Custom", "Custom Label", group=gMarket)

gDash = "Dashboard Premium"
showDashboard = input.bool(true, "Show Dashboard", group=gDash)
dashMode = input.string("Balanced", "Dashboard Mode", options=["Focused", "Balanced", "Full"], group=gDash)
scannerMode = input.string("Compact", "Scanner Mode", options=["Off", "Compact", "Full"], group=gDash)
dashPosition = input.string("Top Right", "Position", options=["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group=gDash)
dashTextSize = input.string("Small", "Text Size", options=["Tiny", "Small", "Normal", "Large", "Huge"], group=gDash)
dashTheme = input.string("Glass", "Theme", options=["Glass", "Carbon", "Midnight", "Neon"], group=gDash)
dashOpacity = input.int(10, "Panel Opacity", minval=0, maxval=100, group=gDash)
dashBorderOpacity = input.int(70, "Border Opacity", minval=0, maxval=100, group=gDash)
dashUseChartTint = input.bool(false, "Chart Background Tint", group=gDash)

gScanner = "Multi-Market Scanner"
scannerTf = input.timeframe("15", "Scanner Timeframe", group=gScanner)
scan1 = input.symbol("COINBASE:BTCUSD", "Scanner Symbol 1", group=gScanner)
scan2 = input.symbol("OANDA:EURUSD", "Scanner Symbol 2", group=gScanner)
scan3 = input.symbol("NASDAQ:AAPL", "Scanner Symbol 3", group=gScanner)

gMTF = "MTF Backend Context"
useMTFBackend = input.bool(true, "Use MTF Backend Brain", group=gMTF)
mtfLiqTf = input.timeframe("15", "MTF Liquidity TF", group=gMTF)
mtfContextTf = input.timeframe("60", "HTF Context TF", group=gMTF)
mtfMacroTf = input.timeframe("240", "Macro Context TF", group=gMTF)
mtfPivotLen = input.int(5, "MTF Pivot Length", minval=2, maxval=20, group=gMTF)
mtfLiqToleranceAtr = input.float(0.15, "MTF Equal H/L Tolerance ATR", minval=0.02, maxval=0.50, step=0.01, group=gMTF)
mtfMinFvgQuality = input.float(0.55, "Minimum MTF FVG Quality", minval=0.25, maxval=0.90, step=0.05, group=gMTF)


gMain = "Main Controls"
showEMAs = input.bool(false, "Show EMA 20 / 50 / 200", group=gMain)
showDWM = input.bool(true, "Show Daily Open + Previous Day High/Low", group=gMain)
includeDWMInScore = input.bool(true, "Include DWM In Score", group=gMain)

gFVG = "Fair Value Gaps + Quality"
showFVG = input.bool(true, "Show FVGs", group=gFVG)
fvgThreshold = input.float(0.0, "Minimum Gap %", minval=0, step=0.1, group=gFVG)
fvgUseATR = input.bool(true, "Use ATR Gap Filter", group=gFVG)
fvgAtrMult = input.float(0.20, "ATR Filter Multiplier", minval=0.01, step=0.01, group=gFVG)
fvgRequireDisplacement = input.bool(true, "Require Displacement Candle", group=gFVG)
dispBodyAtr = input.float(0.55, "Min Body ATR x", minval=0.1, step=0.05, group=gFVG)
dispBodyRatio = input.float(0.55, "Min Body / Range", minval=0.2, maxval=0.95, step=0.05, group=gFVG)
fvgExtend = input.int(40, "Extend Bars", minval=1, group=gFVG)
fvgMaxZones = input.int(20, "Max Active FVGs", minval=1, maxval=100, group=gFVG)
fvgMaxAge = input.int(250, "Auto Delete After Bars", minval=25, maxval=2000, group=gFVG)
fvgInvalidation = input.string("Full Fill", "Delete FVG When", options=["Full Fill", "Close Through", "Midpoint Touch", "Any Touch"], group=gFVG)
fvgShrinkOnFill = input.bool(true, "Shrink Partially Filled", group=gFVG)
showFvgMidline = input.bool(true, "Show CE / Midline", group=gFVG)
trackIFVG = input.bool(true, "Track IFVG / Inversions", group=gFVG)
ifvgReqDisplacement = input.bool(true, "IFVG Requires Displacement", group=gFVG)
ifvgReqStructure = input.bool(true, "IFVG Requires CHoCH/BOS", group=gFVG)
fvgClearWhenOff = input.bool(true, "Clear All When Disabled", group=gFVG)

bullFvgColor = input.color(color.new(#089981, 80), "Bullish FVG", group=gFVG)
bearFvgColor = input.color(color.new(#f23645, 80), "Bearish FVG", group=gFVG)
ifvgBullColor = input.color(color.new(#7e57c2, 78), "Bullish IFVG", group=gFVG)
ifvgBearColor = input.color(color.new(#f48fb1, 78), "Bearish IFVG", group=gFVG)

gStructure = "Structure / CHoCH / BOS"
showStructure = input.bool(true, "Show BOS / CHoCH Labels", group=gStructure)
internalLength = input.int(3, "Internal Swing Length", minval=2, maxval=20, group=gStructure)
swingLength = input.int(7, "Swing Length", minval=2, maxval=50, group=gStructure)
msUseClose = input.bool(true, "Use Close For Break", group=gStructure)
htfStructureTf = input.timeframe("60", "HTF Bias TF", group=gStructure)
showStructureLabels = input.bool(true, "Show Structure Labels", group=gStructure)
bullStructureColor = input.color(color.lime, "Bull Structure", group=gStructure)
bearStructureColor = input.color(color.red, "Bear Structure", group=gStructure)

gLiq = "Liquidity Pools"
showLiquidity = input.bool(true, "Show Liquidity Pools", group=gLiq)
liqContactAmount = input.int(2, "Zone Contact Amount", minval=2, group=gLiq)
liqGapCount = input.int(5, "Bars Between Contacts", minval=0, group=gLiq)
liqWaitBars = input.int(10, "Confirmation Bars", minval=1, group=gLiq)
liqSweepConfirm = input.string("Close Back Inside", "Sweep Confirm", options=["Close Back Inside", "Close Through Zone"], group=gLiq)
liqShowVolLabels = input.bool(true, "Volume Labels", group=gLiq)
liqVolSizeInput = input.string("Small", "Vol Label Size", options=["Tiny", "Small", "Normal", "Large", "Huge"], group=gLiq)
liqDeleteMode = input.string("Never", "Delete Zone", options=["Never", "On Sweep", "On Break"], group=gLiq)
liqMaxZones = input.int(30, "Max Zones", minval=1, maxval=100, group=gLiq)
liqShowSweepLabels = input.bool(true, "Sweep Labels", group=gLiq)
liqBullColor = input.color(color.new(#089981, 80), "Sell-Side SSL Zone", group=gLiq)
liqBearColor = input.color(color.new(#f23645, 80), "Buy-Side BSL Zone", group=gLiq)

gOB = "Backend Order Blocks"
useOB = input.bool(true, "Use OB Backend Confluence", group=gOB)
obLookback = input.int(10, "OB Lookback", minval=3, maxval=30, group=gOB)
obMaxAge = input.int(100, "OB Freshness Bars", minval=20, maxval=500, group=gOB)

gVP = "Backend Volume Profile"
useVP = input.bool(true, "Use POC / Value Area Backend", group=gVP)
vpLookback = input.int(160, "VP Lookback Bars", minval=50, maxval=500, group=gVP)
vpBins = input.int(48, "VP Bins", minval=20, maxval=120, group=gVP)
vpValueAreaPct = input.int(68, "Value Area %", minval=50, maxval=90, group=gVP)

gOR = "Opening Range"
showOR = input.bool(false, "Show Opening Range Lines", group=gOR)
useORBackend = input.bool(true, "Use Opening Range Backend", group=gOR)
orSession = input.session("0930-1000", "Opening Range Session", group=gOR)
orColor = input.color(color.new(color.yellow, 20), "OR Color", group=gOR)

gKZ = "Killzones"
showKillzones = input.bool(true, "Show Killzones", group=gKZ)
kzTimezone = input.string("America/New_York", "Timezone", group=gKZ)
kzTransparency = input.int(85, "Box Transparency", minval=0, maxval=100, group=gKZ)
kzShowHL = input.bool(true, "Draw KZ High/Low", group=gKZ)
kzMaxDays = input.int(3, "Days Limit", minval=1, group=gKZ)
kzEnableOnCrypto = input.bool(false, "Allow Killzones On Crypto", group=gKZ)

useAsia = input.bool(true, "Asia", inline="AS", group=gKZ)
asiaSession = input.session("2000-0000", "", inline="AS", group=gKZ)
asiaColor = input.color(color.blue, "", inline="AS", group=gKZ)

useLondon = input.bool(true, "London", inline="LO", group=gKZ)
londonSession = input.session("0200-0500", "", inline="LO", group=gKZ)
londonColor = input.color(color.red, "", inline="LO", group=gKZ)

useNYAM = input.bool(true, "NY AM", inline="NYA", group=gKZ)
nyamSession = input.session("0930-1100", "", inline="NYA", group=gKZ)
nyamColor = input.color(#089981, "", inline="NYA", group=gKZ)

useNYPM = input.bool(true, "NY PM", inline="NYP", group=gKZ)
nypmSession = input.session("1330-1600", "", inline="NYP", group=gKZ)
nypmColor = input.color(color.purple, "", inline="NYP", group=gKZ)

gTime = "Time / Volume Filters"
useTimeFilter = input.bool(true, "Use Time Blackout Filter", group=gTime)
avoidOpenMin = input.int(5, "Skip First N Minutes NY Open", minval=0, maxval=30, group=gTime)
avoidCloseMin = input.int(10, "Skip Last N Minutes NY Close", minval=0, maxval=30, group=gTime)
avoidLunch = input.bool(true, "Skip NY Lunch Chop", group=gTime)
rvolMin = input.float(0.8, "Minimum RVOL Quality", minval=0.1, step=0.1, group=gTime)
volCompressPct = input.float(0.70, "Compression Threshold", minval=0.4, maxval=0.95, step=0.05, group=gTime)
volExpandPct = input.float(1.30, "Expansion Threshold", minval=1.05, maxval=2.0, step=0.05, group=gTime)

gTL = "Trendlines With Breaks"
showTrendlines = input.bool(true, "Show Trendlines", group=gTL)
tlLength = input.int(14, "Swing Lookback", minval=2, group=gTL)
tlSlopeMult = input.float(1.0, "Slope Mult", minval=0, step=0.1, group=gTL)
tlCalcMethod = input.string("Atr", "Slope Method", options=["Atr", "Stdev", "Linreg"], group=gTL)
tlBackpaint = input.bool(true, "Backpaint", group=gTL)
tlShowBreaks = input.bool(true, "Break Labels", group=gTL)
tlShowExtended = input.bool(true, "Extended Lines", group=gTL)
tlUpColor = input.color(color.teal, "Up TL Color", group=gTL)
tlDownColor = input.color(color.red, "Down TL Color", group=gTL)

gAlerts = "Alerts"
alertFVG = input.bool(true, "FVG Alerts", group=gAlerts)
alertStructure = input.bool(true, "Structure Alerts", group=gAlerts)
alertLiquidity = input.bool(true, "Liquidity Alerts", group=gAlerts)
alertConfluence = input.bool(true, "Confluence Alerts", group=gAlerts)
confluenceThreshold = input.int(5, "Confluence Score", minval=2, maxval=15, group=gAlerts)

gBrain = "v4.7 Premium Backend"
useBrain43 = input.bool(true, "Use v4.3 Brain Engine", group=gBrain)
useAutoThreshold43 = input.bool(true, "Auto Confluence Threshold", group=gBrain)
useMomentumBrain43 = input.bool(true, "Momentum Brain", group=gBrain)
useVwapBrain43 = input.bool(true, "VWAP Deviation Brain", group=gBrain)
useVolumeBrain43 = input.bool(true, "Volume Pressure Brain", group=gBrain)
useTrendBrain43 = input.bool(true, "ADX / DI Trend Brain", group=gBrain)
showSentiment43 = input.bool(true, "Show Sentiment Row", group=gBrain)
usePremium46 = input.bool(true, "Use v4.7 Premium Backend", group=gBrain)
useIT3Backend47 = input.bool(true, "Use IT3 Backend Filter", group=gBrain)
enableIT3Alerts46 = input.bool(true, "Backend IT3 Alerts", group=gBrain)
showBTCycle46 = input.bool(true, "Show BTC Cycle Context", group=gBrain)
showWorkflow461 = input.bool(true, "Show Workflow Row", group=gBrain)
useStrictAction461 = input.bool(true, "Strict Action Guard", group=gBrain)

gGov47 = "v4.7 Risk Governance"
useGovernance47 = input.bool(true, "Use Risk Governance Layer", group=gGov47)
showGovernanceRows47 = input.bool(true, "Show Risk / Research Rows", group=gGov47)
researchStatus47 = input.string("Forward Test", "Research Status", options=["Approved", "Forward Test", "Watchlist", "Rejected", "Untrained"], group=gGov47)
drawdownInputMode47 = input.string("Auto", "Drawdown Mode", options=["Auto", "Normal", "Caution", "Recovery", "Lockout"], group=gGov47)
dailyLossCount47 = input.int(0, "Daily Loss Count", minval=0, maxval=20, group=gGov47)
weeklyLossCount47 = input.int(0, "Weekly Loss Count", minval=0, maxval=50, group=gGov47)
losingStreak47 = input.int(0, "Current Losing Streak", minval=0, maxval=20, group=gGov47)
maxDailyLosses47 = input.int(3, "Lockout After Daily Losses", minval=1, maxval=10, group=gGov47)
reducedRiskPct47 = input.float(0.25, "Reduced Risk %", minval=0.0, maxval=2.0, step=0.05, group=gGov47)
normalRiskPct47 = input.float(0.50, "Normal Risk %", minval=0.0, maxval=2.0, step=0.05, group=gGov47)
premiumRiskPct47 = input.float(0.75, "Premium Risk %", minval=0.0, maxval=2.0, step=0.05, group=gGov47)
maxRiskCapPct47 = input.float(1.00, "Hard Risk Cap %", minval=0.0, maxval=3.0, step=0.05, group=gGov47)
useKellyReference47 = input.bool(false, "Cap With Fractional Kelly Reference", group=gGov47)
kellyWinRate47 = input.float(52.0, "Kelly Win Rate %", minval=1.0, maxval=99.0, step=0.5, group=gGov47)
kellyRR47 = input.float(2.0, "Kelly Reward/Risk", minval=0.25, maxval=10.0, step=0.25, group=gGov47)
correlationOverride47 = input.string("Auto", "Correlation Risk", options=["Auto", "Low", "Medium", "High"], group=gGov47)


//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Market Presets
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

autoProfile = syminfo.type == "crypto" ? "Crypto" : syminfo.type == "forex" ? "Forex" : syminfo.type == "stock" ? "Stocks" : syminfo.type == "futures" ? "Futures/Indices" : "Custom"
profile = marketMode == "Auto" and allowAutoDetect ? autoProfile : marketMode == "Custom" ? customLabel : marketMode

isCrypto = profile == "Crypto"
isForex = profile == "Forex"
isStocks = profile == "Stocks"
isFut = profile == "Futures/Indices"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// v4.2 Adaptive Market Engine
// Faster crypto reaction logic + smarter auto presets
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

fastCalc = input.bool(true, "Fast Calculation", group=gAlerts)

effConfluence = applyPresetOverrides ? (isCrypto ? math.min(confluenceThreshold, 3) : isStocks ? math.max(confluenceThreshold, 5) : isFut ? math.max(confluenceThreshold, 5) : confluenceThreshold) : confluenceThreshold

effFvgAtrMult = applyPresetOverrides ? (isCrypto ? math.max(fvgAtrMult, 0.15) : isStocks ? math.max(fvgAtrMult, 0.18) : fvgAtrMult) : fvgAtrMult

effLiqWait = applyPresetOverrides ? (isCrypto ? math.min(liqWaitBars, 3) : isStocks ? math.max(liqWaitBars, 8) : liqWaitBars) : liqWaitBars

effLiqGap = applyPresetOverrides ? (isCrypto ? math.min(liqGapCount, 2) : isStocks ? math.max(liqGapCount, 4) : liqGapCount) : liqGapCount

effSwingLength = applyPresetOverrides ? (isCrypto ? math.min(swingLength, 5) : swingLength) : swingLength

effInternalLength = applyPresetOverrides ? (isCrypto ? math.min(internalLength, 2) : internalLength) : internalLength

effRvolMin = applyPresetOverrides ? (isCrypto ? math.min(rvolMin, 0.6) : rvolMin) : rvolMin

effShowKillzones = showKillzones and (not isCrypto or kzEnableOnCrypto)

// Fast crypto liquidity sweep logic
bullishSweepFast = low < low[1] and close > low[1]
bearishSweepFast = high > high[1] and close < high[1]

cryptoSweepBull = isCrypto and bullishSweepFast
cryptoSweepBear = isCrypto and bearishSweepFast


var string adaptiveModeText = isCrypto ? "Crypto Fast Engine" : isForex ? "Forex Precision" : isStocks ? "Stocks Volume" : isFut ? "Futures OR Engine" : "Custom"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MTF Backend Brain: Liquidity / Structure / FVG / Premium-Discount
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

f_mtf_liq(_len, _tolAtr) =>
    ph = ta.pivothigh(high, _len, _len)
    pl = ta.pivotlow(low, _len, _len)
    a = ta.atr(14)

    var float prevH = na
    var float prevL = na
    var float bsl = na
    var float ssl = na

    if not na(ph)
        if not na(prevH) and math.abs(ph - prevH) <= a * _tolAtr
            bsl := math.avg(ph, prevH)
        prevH := ph

    if not na(pl)
        if not na(prevL) and math.abs(pl - prevL) <= a * _tolAtr
            ssl := math.avg(pl, prevL)
        prevL := pl

    bslSweep = not na(bsl) and high > bsl and close < bsl
    sslSweep = not na(ssl) and low < ssl and close > ssl

    bslArea = not na(bsl) and bsl > close
    sslArea = not na(ssl) and ssl < close

    code = sslSweep ? 2 : bslSweep ? -2 : (bslArea and sslArea ? 3 : sslArea ? 1 : bslArea ? -1 : 0)

    if bslSweep
        bsl := na
    if sslSweep
        ssl := na

    [code, bsl, ssl]

f_mtf_struct(_len, _useClose) =>
    ph = ta.pivothigh(high, _len, _len)
    pl = ta.pivotlow(low, _len, _len)

    var float h = na
    var float l = na
    var bool hLive = false
    var bool lLive = false
    var int dir = 0

    if not na(ph)
        h := ph
        hLive := true

    if not na(pl)
        l := pl
        lLive := true

    bH = _useClose ? close : high
    bL = _useClose ? close : low

    bullBreak = hLive and not na(h) and bH > h
    bearBreak = lLive and not na(l) and bL < l

    event = 0

    if bullBreak
        event := dir == -1 ? 2 : 1
        dir := 1
        hLive := false

    if bearBreak
        event := dir == 1 ? -2 : -1
        dir := -1
        lLive := false

    [dir, event]

f_mtf_pd(_len) =>
    ph = ta.pivothigh(high, _len, _len)
    pl = ta.pivotlow(low, _len, _len)

    var float h = na
    var float l = na

    if not na(ph)
        h := ph
    if not na(pl)
        l := pl

    have = not na(h) and not na(l)
    mid = have ? math.avg(h, l) : na
    code = not have ? 0 : close < mid ? -1 : close > mid ? 1 : 0
    [code, mid]

f_mtf_disp(_ofs, _wantBull, _bodyAtr, _bodyRatio) =>
    a = ta.atr(14)
    body = math.abs(close[_ofs] - open[_ofs])
    rng = math.max(high[_ofs] - low[_ofs], syminfo.mintick)
    big = body / math.max(a, syminfo.mintick) >= _bodyAtr
    solid = body / rng >= _bodyRatio
    dir = _wantBull ? close[_ofs] > open[_ofs] : close[_ofs] < open[_ofs]
    big and solid and dir

f_mtf_fvg(_atrMult, _bodyAtr, _bodyRatio, _pdLen) =>
    a = ta.atr(14)
    vma = ta.sma(volume, 20)
    rv = volume / math.max(vma, 1)
    e20 = ta.ema(close, 20)
    e50 = ta.ema(close, 50)
    e200 = ta.ema(close, 200)

    ph = ta.pivothigh(high, _pdLen, _pdLen)
    pl = ta.pivotlow(low, _pdLen, _pdLen)

    var float rangeH = na
    var float rangeL = na

    if not na(ph)
        rangeH := ph
    if not na(pl)
        rangeL := pl

    haveRangeLocal = not na(rangeH) and not na(rangeL)
    midRange = haveRangeLocal ? math.avg(rangeH, rangeL) : na

    bullGapLocal = low - high[2]
    bearGapLocal = low[2] - high

    bullDispLocal = f_mtf_disp(1, true, _bodyAtr, _bodyRatio)
    bearDispLocal = f_mtf_disp(1, false, _bodyAtr, _bodyRatio)

    newBull = not na(high[2]) and low > high[2] and close[1] > high[2] and bullGapLocal >= a * _atrMult and bullDispLocal
    newBear = not na(low[2]) and high < low[2] and close[1] < low[2] and bearGapLocal >= a * _atrMult and bearDispLocal

    var float top = na
    var float bot = na
    var int dir = 0
    var float q = 0.0
    var int state = 0

    if newBull
        top := low
        bot := high[2]
        dir := 1
        sizeScore = f_clamp((top - bot) / math.max(a, syminfo.mintick), 0.0, 2.0) / 2.0
        trendScore = e20 > e50 ? 1.0 : 0.25
        htfScore = close > e200 ? 0.85 : 0.35
        volScore = rv >= 1.5 ? 1.0 : rv >= 0.8 ? 0.65 : 0.25
        pdScore = not haveRangeLocal ? 0.50 : math.avg(top, bot) < midRange ? 1.0 : 0.25
        q := f_clamp(sizeScore * 0.25 + trendScore * 0.20 + htfScore * 0.20 + volScore * 0.15 + pdScore * 0.20, 0.05, 0.95)
        state := 0

    if newBear
        top := low[2]
        bot := high
        dir := -1
        sizeScore = f_clamp((top - bot) / math.max(a, syminfo.mintick), 0.0, 2.0) / 2.0
        trendScore = e20 < e50 ? 1.0 : 0.25
        htfScore = close < e200 ? 0.85 : 0.35
        volScore = rv >= 1.5 ? 1.0 : rv >= 0.8 ? 0.65 : 0.25
        pdScore = not haveRangeLocal ? 0.50 : math.avg(top, bot) > midRange ? 1.0 : 0.25
        q := f_clamp(sizeScore * 0.25 + trendScore * 0.20 + htfScore * 0.20 + volScore * 0.15 + pdScore * 0.20, 0.05, 0.95)
        state := 0

    inside = dir == 1 ? (high >= bot and low <= top) : dir == -1 ? (high >= bot and low <= top) : false
    ce = dir != 0 ? math.avg(top, bot) : na
    ceTap = dir == 1 ? low <= ce : dir == -1 ? high >= ce : false

    if ceTap and state == 0
        state := 1

    closeThrough = dir == 1 ? close < bot : dir == -1 ? close > top : false
    fullFill = dir == 1 ? low <= bot : dir == -1 ? high >= top : false
    ifvg = fullFill and closeThrough ? -dir : 0

    if ifvg != 0
        dir := ifvg
        state := 3

    [dir, q, inside ? 1 : 0, ceTap ? 1 : 0, ifvg]

f_liq_code_text(_code) =>
    _code == 2 ? "SSL Swept" :
     _code == -2 ? "BSL Swept" :
     _code == 3 ? "BSL/SSL Areas" :
     _code == 1 ? "SSL Below" :
     _code == -1 ? "BSL Above" :
     "No HTF Liq"

f_event_text(_event) =>
    _event == 2 ? "Bull CHoCH" :
     _event == 1 ? "Bull BOS" :
     _event == -2 ? "Bear CHoCH" :
     _event == -1 ? "Bear BOS" :
     "No Event"

f_dir_text(_dir) =>
    _dir == 1 ? "Bull" :
     _dir == -1 ? "Bear" :
     "Neutral"

f_pd_text(_code) =>
    _code == -1 ? "Discount" :
     _code == 1 ? "Premium" :
     "Equilibrium"

[mtfLiqCode, mtfBsl, mtfSsl] = request.security(syminfo.tickerid, mtfLiqTf, f_mtf_liq(mtfPivotLen, mtfLiqToleranceAtr), barmerge.gaps_off, barmerge.lookahead_off)
[ctxStructDir, ctxStructEvent] = request.security(syminfo.tickerid, mtfContextTf, f_mtf_struct(mtfPivotLen, msUseClose), barmerge.gaps_off, barmerge.lookahead_off)
[macroStructDir, macroStructEvent] = request.security(syminfo.tickerid, mtfMacroTf, f_mtf_struct(mtfPivotLen, msUseClose), barmerge.gaps_off, barmerge.lookahead_off)
[ctxPdCode, ctxPdMid] = request.security(syminfo.tickerid, mtfContextTf, f_mtf_pd(mtfPivotLen), barmerge.gaps_off, barmerge.lookahead_off)
[ctxFvgDir, ctxFvgQ, ctxFvgTouch, ctxCeTap, ctxIfvgDir] = request.security(syminfo.tickerid, mtfContextTf, f_mtf_fvg(effFvgAtrMult, dispBodyAtr, dispBodyRatio, mtfPivotLen), barmerge.gaps_off, barmerge.lookahead_off)

mtfBullLiq = mtfLiqCode == 2 or mtfLiqCode == 1
mtfBearLiq = mtfLiqCode == -2 or mtfLiqCode == -1
mtfBullStruct = ctxStructDir == 1 or ctxStructEvent == 1 or ctxStructEvent == 2
mtfBearStruct = ctxStructDir == -1 or ctxStructEvent == -1 or ctxStructEvent == -2
macroBull = macroStructDir == 1
macroBear = macroStructDir == -1
mtfBullFvg = ctxFvgDir == 1 and ctxFvgQ >= mtfMinFvgQuality
mtfBearFvg = ctxFvgDir == -1 and ctxFvgQ >= mtfMinFvgQuality

mtfBullScore = useMTFBackend ? ((mtfLiqCode == 2 ? 2 : mtfLiqCode == 1 ? 1 : 0) + (ctxStructEvent == 2 ? 2 : ctxStructEvent == 1 ? 1 : ctxStructDir == 1 ? 1 : 0) + (macroBull ? 1 : 0) + (ctxPdCode == -1 ? 1 : 0) + (mtfBullFvg ? 1 : 0) + (ctxIfvgDir == 1 ? 1 : 0)) : 0
mtfBearScore = useMTFBackend ? ((mtfLiqCode == -2 ? 2 : mtfLiqCode == -1 ? 1 : 0) + (ctxStructEvent == -2 ? 2 : ctxStructEvent == -1 ? 1 : ctxStructDir == -1 ? 1 : 0) + (macroBear ? 1 : 0) + (ctxPdCode == 1 ? 1 : 0) + (mtfBearFvg ? 1 : 0) + (ctxIfvgDir == -1 ? 1 : 0)) : 0

mtfContextText = useMTFBackend ? mtfLiqTf + " " + f_liq_code_text(mtfLiqCode) + " | " + mtfContextTf + " " + f_dir_text(ctxStructDir) : "Off"
mtfDetailText = useMTFBackend ? "FVG " + f_dir_text(ctxFvgDir) + " " + f_prob(ctxFvgQ) + " | " + f_pd_text(ctxPdCode) : "Off"
// v4.6.2 color fix for MTF backend row.
color mtfContextColor462 = not useMTFBackend ? color.gray : mtfLiqCode > 0 or ctxStructDir > 0 ? color.lime : mtfLiqCode < 0 or ctxStructDir < 0 ? color.red : color.silver
color mtfDetailColor462 = not useMTFBackend ? color.gray : ctxFvgDir > 0 or ctxPdCode < 0 ? color.lime : ctxFvgDir < 0 or ctxPdCode > 0 ? color.red : color.silver



//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// DWM + EMAs
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

dayOpen = request.security(syminfo.tickerid, "D", open, barmerge.gaps_off, barmerge.lookahead_on)
prevDayHigh = request.security(syminfo.tickerid, "D", high[1], barmerge.gaps_off, barmerge.lookahead_on)
prevDayLow = request.security(syminfo.tickerid, "D", low[1], barmerge.gaps_off, barmerge.lookahead_on)

plot(showEMAs ? ema20 : na, "EMA 20", color=color.new(color.aqua, 0), linewidth=1)
plot(showEMAs ? ema50 : na, "EMA 50", color=color.new(color.yellow, 0), linewidth=1)
plot(showEMAs ? ema200 : na, "EMA 200", color=color.new(color.orange, 0), linewidth=2)

plot(showDWM ? dayOpen : na, "Daily Open", color=color.new(color.white, 40), linewidth=1, style=plot.style_linebr)
plot(showDWM ? prevDayHigh : na, "Previous Day High", color=color.new(color.red, 35), linewidth=1, style=plot.style_linebr)
plot(showDWM ? prevDayLow : na, "Previous Day Low", color=color.new(color.lime, 35), linewidth=1, style=plot.style_linebr)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Time / Volatility Regime
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

nyHour = hour(time, kzTimezone)
nyMinute = minute(time, kzTimezone)
nyMins = nyHour * 60 + nyMinute
openBlackout = nyMins >= 570 and nyMins < 570 + avoidOpenMin
closeBlackout = nyMins >= 960 - avoidCloseMin and nyMins < 960
lunchBlackout = avoidLunch and nyMins >= 690 and nyMins < 810
inBlackout = useTimeFilter and (openBlackout or closeBlackout or lunchBlackout)

volRatio = atr / math.max(atrLong, syminfo.mintick)
volCompressed = volRatio < volCompressPct
volExpanded = volRatio > volExpandPct

// v4.6.2 volatility fix: ATR ratio + short/long ATR behavior.
// The old regime stayed Normal too often across markets. This makes states more responsive.
volAtrFast462 = ta.sma(atr, 14)
volAtrSlow462 = ta.sma(atr, 100)
volAtrRatio462 = volAtrSlow462 != 0.0 ? volAtrFast462 / volAtrSlow462 : 1.0
volVeryLow462 = volAtrRatio462 < 0.72
volLow462 = volAtrRatio462 >= 0.72 and volAtrRatio462 < 0.88
volHigh462 = volAtrRatio462 > 1.18 and volAtrRatio462 <= 1.45
volExtreme462 = volAtrRatio462 > 1.45
volRegime = volVeryLow462 ? "Very Low" : volLow462 ? "Low" : volCompressed ? "Compressed" : volExtreme462 ? "Extreme" : volHigh462 ? "High" : volExpanded ? "Expanding" : "Normal"
rvolState = rvol >= 1.5 ? "High" : rvol >= effRvolMin ? "OK" : rvol >= 0.5 ? "Low" : "Dead"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Killzones
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

f_killzone(bool enabled, string sess, string name, color col) =>
    inSess = enabled and effShowKillzones and timeframe.isintraday and not na(time(timeframe.period, sess, kzTimezone))
    startSess = inSess and not inSess[1]
    endSess = not inSess and inSess[1]

    var box curBox = na
    var float sHigh = na
    var float sLow = na
    var array<box> boxes = array.new<box>()
    var array<line> hiLines = array.new<line>()
    var array<line> loLines = array.new<line>()

    if startSess
        sHigh := high
        sLow := low
        curBox := box.new(left=time, top=high, right=time, bottom=low, xloc=xloc.bar_time, bgcolor=color.new(col, kzTransparency), border_color=color.new(col, 55), text=name, text_color=color.new(col, 0))
        array.unshift(boxes, curBox)
        if array.size(boxes) > kzMaxDays
            old = array.pop(boxes)
            box.delete(old)

    if inSess
        sHigh := math.max(nz(sHigh, high), high)
        sLow := math.min(nz(sLow, low), low)
        if not na(curBox)
            box.set_right(curBox, time)
            box.set_top(curBox, sHigh)
            box.set_bottom(curBox, sLow)

    if endSess and kzShowHL and not na(sHigh) and not na(sLow)
        hi = line.new(time[1], sHigh, time, sHigh, xloc=xloc.bar_time, extend=extend.right, color=col, style=line.style_dotted)
        lo = line.new(time[1], sLow, time, sLow, xloc=xloc.bar_time, extend=extend.right, color=col, style=line.style_dotted)
        array.unshift(hiLines, hi)
        array.unshift(loLines, lo)
        if array.size(hiLines) > kzMaxDays
            oldH = array.pop(hiLines)
            oldL = array.pop(loLines)
            line.delete(oldH)
            line.delete(oldL)

    inSess

inAsia = f_killzone(useAsia, asiaSession, "Asia", asiaColor)
inLondon = f_killzone(useLondon, londonSession, "London", londonColor)
inNYAM = f_killzone(useNYAM, nyamSession, "NY AM", nyamColor)
inNYPM = f_killzone(useNYPM, nypmSession, "NY PM", nypmColor)

currentSession = inAsia ? "Asia" : inLondon ? "London" : inNYAM ? "NY AM" : inNYPM ? "NY PM" : "None"
killzoneActive = inLondon or inNYAM or inNYPM

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Structure: Internal + Swing BOS / CHoCH
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

breakHighPrice = msUseClose ? close : high
breakLowPrice = msUseClose ? close : low

iPh = ta.pivothigh(high, internalLength, internalLength)
iPl = ta.pivotlow(low, internalLength, internalLength)
sPh = ta.pivothigh(high, effSwingLength, effSwingLength)
sPl = ta.pivotlow(low, effSwingLength, effSwingLength)

var float intHigh = na
var float intLow = na
var int intHighBar = na
var int intLowBar = na
var bool intHighLive = false
var bool intLowLive = false
var int internalTrend = 0

var float swHigh = na
var float swLow = na
var int swHighBar = na
var int swLowBar = na
var bool swHighLive = false
var bool swLowLive = false
var int swingTrend = 0

if not na(iPh)
    intHigh := iPh
    intHighBar := bar_index - internalLength
    intHighLive := true

if not na(iPl)
    intLow := iPl
    intLowBar := bar_index - internalLength
    intLowLive := true

if not na(sPh)
    swHigh := sPh
    swHighBar := bar_index - effSwingLength
    swHighLive := true

if not na(sPl)
    swLow := sPl
    swLowBar := bar_index - effSwingLength
    swLowLive := true

internalBullBreak = intHighLive and not na(intHigh) and breakHighPrice > intHigh
internalBearBreak = intLowLive and not na(intLow) and breakLowPrice < intLow
swingBullBreak = swHighLive and not na(swHigh) and breakHighPrice > swHigh
swingBearBreak = swLowLive and not na(swLow) and breakLowPrice < swLow

internalBullCHoCH = internalBullBreak and internalTrend == -1
internalBearCHoCH = internalBearBreak and internalTrend == 1
swingBullCHoCH = swingBullBreak and swingTrend == -1
swingBearCHoCH = swingBearBreak and swingTrend == 1

internalBullBOS = internalBullBreak and not internalBullCHoCH
internalBearBOS = internalBearBreak and not internalBearCHoCH
swingBullBOS = swingBullBreak and not swingBullCHoCH
swingBearBOS = swingBearBreak and not swingBearCHoCH

var string structureEvent = "None"

if internalBullBreak
    structureEvent := internalBullCHoCH ? "Int Bull CHoCH" : "Int Bull BOS"
    internalTrend := 1
    intHighLive := false

if internalBearBreak
    structureEvent := internalBearCHoCH ? "Int Bear CHoCH" : "Int Bear BOS"
    internalTrend := -1
    intLowLive := false

if swingBullBreak
    structureEvent := swingBullCHoCH ? "Swing Bull CHoCH" : "Swing Bull BOS"
    swingTrend := 1
    swHighLive := false
    if showStructure and showStructureLabels
        label.new(bar_index, swHigh, swingBullCHoCH ? "Bull CHoCH" : "Bull BOS", style=label.style_label_down, color=transparent, textcolor=bullStructureColor, size=size.small)

if swingBearBreak
    structureEvent := swingBearCHoCH ? "Swing Bear CHoCH" : "Swing Bear BOS"
    swingTrend := -1
    swLowLive := false
    if showStructure and showStructureLabels
        label.new(bar_index, swLow, swingBearCHoCH ? "Bear CHoCH" : "Bear BOS", style=label.style_label_up, color=transparent, textcolor=bearStructureColor, size=size.small)

bullStructureBreak = internalBullBreak or swingBullBreak
bearStructureBreak = internalBearBreak or swingBearBreak
bullCHoCH = internalBullCHoCH or swingBullCHoCH
bearCHoCH = internalBearCHoCH or swingBearCHoCH
bullBOS = internalBullBOS or swingBullBOS
bearBOS = internalBearBOS or swingBearBOS

f_htf_structure(_len, _useClose) =>
    ph = ta.pivothigh(high, _len, _len)
    pl = ta.pivotlow(low, _len, _len)
    var float h = na
    var float l = na
    var bool hLive = false
    var bool lLive = false
    var int dir = 0
    if not na(ph)
        h := ph
        hLive := true
    if not na(pl)
        l := pl
        lLive := true
    bH = _useClose ? close : high
    bL = _useClose ? close : low
    if hLive and not na(h) and bH > h
        dir := 1
        hLive := false
    if lLive and not na(l) and bL < l
        dir := -1
        lLive := false
    dir

htfDir = request.security(syminfo.tickerid, htfStructureTf, f_htf_structure(effSwingLength, msUseClose), barmerge.gaps_off, barmerge.lookahead_off)
htfBiasText = htfDir == 1 ? "Bullish" : htfDir == -1 ? "Bearish" : "Neutral"

haveRange = not na(swHigh) and not na(swLow)
rangeHi = haveRange ? math.max(swHigh, swLow) : na
rangeLo = haveRange ? math.min(swHigh, swLow) : na
rangeMid = haveRange ? math.avg(rangeHi, rangeLo) : na
inDiscount = haveRange and close < rangeMid
inPremium = haveRange and close > rangeMid
pdState = not haveRange ? "Unknown" : inDiscount ? "Discount" : inPremium ? "Premium" : "Equilibrium"

strongWeakState = swingTrend == 1 ? "Weak High / Strong Low" : swingTrend == -1 ? "Strong High / Weak Low" : "Unknown"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Displacement + OB Backend
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

isDisp(ofs, wantBull) =>
    bodyAbs = math.abs(close[ofs] - open[ofs])
    rng = math.max(high[ofs] - low[ofs], syminfo.mintick)
    big = bodyAbs / math.max(atr, syminfo.mintick) >= dispBodyAtr
    solid = bodyAbs / rng >= dispBodyRatio
    dir = wantBull ? close[ofs] > open[ofs] : close[ofs] < open[ofs]
    big and solid and dir

curBullDisp = isDisp(0, true)
curBearDisp = isDisp(0, false)
prevBullDisp = isDisp(1, true)
prevBearDisp = isDisp(1, false)

var array<float> obTop = array.new<float>()
var array<float> obBot = array.new<float>()
var array<bool> obBull = array.new<bool>()
var array<int> obBorn = array.new<int>()

if useOB and curBullDisp
    for k = 1 to obLookback
        if close[k] < open[k]
            array.unshift(obTop, high[k])
            array.unshift(obBot, low[k])
            array.unshift(obBull, true)
            array.unshift(obBorn, bar_index - k)
            break

if useOB and curBearDisp
    for k = 1 to obLookback
        if close[k] > open[k]
            array.unshift(obTop, high[k])
            array.unshift(obBot, low[k])
            array.unshift(obBull, false)
            array.unshift(obBorn, bar_index - k)
            break

if array.size(obTop) > 20
    array.pop(obTop)
    array.pop(obBot)
    array.pop(obBull)
    array.pop(obBorn)

var float lastBullBreakerTop = na
var float lastBullBreakerBot = na
var int lastBullBreakerBorn = na
var float lastBearBreakerTop = na
var float lastBearBreakerBot = na
var int lastBearBreakerBorn = na

obRetestBull = false
obRetestBear = false
obBrokenBull = false
obBrokenBear = false

if useOB and array.size(obTop) > 0
    for i = array.size(obTop) - 1 to 0
        t = array.get(obTop, i)
        b = array.get(obBot, i)
        isB = array.get(obBull, i)
        born = array.get(obBorn, i)
        fresh = bar_index - born <= obMaxAge

        if fresh and isB and low <= t and high >= b and close > b
            obRetestBull := true
        if fresh and not isB and high >= b and low <= t and close < t
            obRetestBear := true

        broken = isB ? close < b : close > t
        if broken
            if isB
                obBrokenBull := true
                lastBullBreakerTop := t
                lastBullBreakerBot := b
                lastBullBreakerBorn := bar_index
            else
                obBrokenBear := true
                lastBearBreakerTop := t
                lastBearBreakerBot := b
                lastBearBreakerBorn := bar_index
            array.remove(obTop, i)
            array.remove(obBot, i)
            array.remove(obBull, i)
            array.remove(obBorn, i)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// FVG Engine: Quality + CE + IFVG
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

type FvgZone
    box bx
    line mid
    float top
    float bot
    bool bull
    int born
    float q
    int state

var array<FvgZone> fvgZones = array.new<FvgZone>()

f_delete_fvg(FvgZone z) =>
    box.delete(z.bx)
    if not na(z.mid)
        line.delete(z.mid)

calcFvgQuality(fvgSize, fvgMid, isBull) =>
    sizeScore = f_clamp(fvgSize / math.max(atr, syminfo.mintick), 0.0, 2.0) / 2.0
    trendScore = isBull ? (ema20 > ema50 ? 1.0 : 0.25) : (ema20 < ema50 ? 1.0 : 0.25)
    htfScore = isBull ? (htfDir == 1 ? 1.0 : htfDir == 0 ? 0.55 : 0.20) : (htfDir == -1 ? 1.0 : htfDir == 0 ? 0.55 : 0.20)
    volScore = rvol >= 1.5 ? 1.0 : rvol >= effRvolMin ? 0.65 : 0.25
    pdScore = not haveRange ? 0.50 : isBull ? (fvgMid < rangeMid ? 1.0 : 0.25) : (fvgMid > rangeMid ? 1.0 : 0.25)
    structScore = isBull ? (bullStructureBreak ? 1.0 : 0.45) : (bearStructureBreak ? 1.0 : 0.45)
    kzScore = killzoneActive ? 0.75 : 0.45
    volRegScore = volExpanded ? 0.85 : volCompressed ? 0.25 : 0.60
    q = sizeScore * 0.16 + trendScore * 0.14 + htfScore * 0.16 + volScore * 0.12 + pdScore * 0.16 + structScore * 0.14 + kzScore * 0.06 + volRegScore * 0.06
    f_clamp(q, 0.05, 0.95)

f_add_fvg(bool isBull, float top, float bot, color col, float q) =>
    labelText = f_prob(q)
    b = box.new(left=bar_index - 2, top=top, right=bar_index + fvgExtend, bottom=bot, bgcolor=col, border_color=color.new(f_solid(col), 65), text=labelText, text_color=color.new(f_solid(col), 0), text_size=size.tiny, text_halign=text.align_right, text_valign=text.align_top)
    line midLine = na
    if showFvgMidline
        m = math.avg(top, bot)
        midLine := line.new(bar_index - 2, m, bar_index + fvgExtend, m, color=color.new(f_solid(col), 0), style=line.style_dotted)
    z = FvgZone.new(b, midLine, top, bot, isBull, bar_index, q, 0)
    array.unshift(fvgZones, z)
    if array.size(fvgZones) > fvgMaxZones
        old = array.pop(fvgZones)
        f_delete_fvg(old)

if not showFVG and fvgClearWhenOff and barstate.islast and array.size(fvgZones) > 0
    for i = array.size(fvgZones) - 1 to 0
        oldZ = array.get(fvgZones, i)
        f_delete_fvg(oldZ)
        array.remove(fvgZones, i)

bullGap = low - high[2]
bearGap = low[2] - high

gapFilterBull = not na(high[2]) and ((bullGap / high[2]) * 100 >= fvgThreshold)
gapFilterBear = not na(low[2]) and ((bearGap / high) * 100 >= fvgThreshold)

atrFilterBull = not fvgUseATR or bullGap >= atr * effFvgAtrMult
atrFilterBear = not fvgUseATR or bearGap >= atr * effFvgAtrMult

dispFilterBull = not fvgRequireDisplacement or prevBullDisp
dispFilterBear = not fvgRequireDisplacement or prevBearDisp

bullFVG = showFVG and not na(high[2]) and low > high[2] and close[1] > high[2] and gapFilterBull and atrFilterBull and dispFilterBull
bearFVG = showFVG and not na(low[2]) and high < low[2] and close[1] < low[2] and gapFilterBear and atrFilterBear and dispFilterBear

if bullFVG
    mid = math.avg(low, high[2])
    q = calcFvgQuality(bullGap, mid, true)
    f_add_fvg(true, low, high[2], bullFvgColor, q)

if bearFVG
    mid = math.avg(low[2], high)
    q = calcFvgQuality(bearGap, mid, false)
    f_add_fvg(false, low[2], high, bearFvgColor, q)

priceInBullFVG = false
priceInBearFVG = false
bullCETap = false
bearCETap = false
bullIFVG = false
bearIFVG = false
activeBullFVGs = 0
activeBearFVGs = 0
bestBullFvgQ = 0.0
bestBearFvgQ = 0.0
bullFvgMitigatedNow = false
bearFvgMitigatedNow = false

if showFVG and array.size(fvgZones) > 0
    for i = array.size(fvgZones) - 1 to 0
        z = array.get(fvgZones, i)

        box.set_right(z.bx, bar_index + fvgExtend)
        if not na(z.mid)
            line.set_x2(z.mid, bar_index + fvgExtend)

        overlaps = high >= z.bot and low <= z.top
        midNow = math.avg(z.top, z.bot)

        if overlaps
            if z.bull
                priceInBullFVG := true
                bestBullFvgQ := math.max(bestBullFvgQ, z.q)
            else
                priceInBearFVG := true
                bestBearFvgQ := math.max(bestBearFvgQ, z.q)

        ceTap = z.bull ? low <= midNow : high >= midNow
        if ceTap and z.state == 0
            z.state := 1
            if z.bull
                bullCETap := true
            else
                bearCETap := true

        if fvgShrinkOnFill and overlaps and z.state != 3
            if z.bull and low < z.top and low > z.bot
                z.top := low
                box.set_top(z.bx, z.top)
            if not z.bull and high > z.bot and high < z.top
                z.bot := high
                box.set_bottom(z.bx, z.bot)
            if not na(z.mid)
                nm = math.avg(z.top, z.bot)
                line.set_y1(z.mid, nm)
                line.set_y2(z.mid, nm)

        fullFill = z.bull ? low <= z.bot : high >= z.top
        closeThrough = z.bull ? close < z.bot : close > z.top
        ifvgDispOk = not ifvgReqDisplacement or (z.bull ? curBearDisp : curBullDisp)
        ifvgStructOk = not ifvgReqStructure or (z.bull ? bearStructureBreak : bullStructureBreak)
        validIFVG = trackIFVG and fullFill and closeThrough and ifvgDispOk and ifvgStructOk and z.state != 3

        mitigated = fvgInvalidation == "Any Touch" ? overlaps : fvgInvalidation == "Midpoint Touch" ? ceTap : fvgInvalidation == "Close Through" ? closeThrough : fullFill
        expired = bar_index - z.born > fvgMaxAge

        if validIFVG
            z.bull := not z.bull
            z.state := 3
            if z.bull
                bullIFVG := true
                box.set_bgcolor(z.bx, ifvgBullColor)
                box.set_border_color(z.bx, color.new(f_solid(ifvgBullColor), 40))
            else
                bearIFVG := true
                box.set_bgcolor(z.bx, ifvgBearColor)
                box.set_border_color(z.bx, color.new(f_solid(ifvgBearColor), 40))
            box.set_text(z.bx, "IFVG " + f_prob(z.q))
            array.set(fvgZones, i, z)
        else if mitigated or expired
            bullFvgMitigatedNow := z.bull and mitigated
            bearFvgMitigatedNow := (not z.bull) and mitigated
            f_delete_fvg(z)
            array.remove(fvgZones, i)
        else
            array.set(fvgZones, i, z)
            if z.bull
                activeBullFVGs += 1
            else
                activeBearFVGs += 1

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Liquidity Pools
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

type LiqData
    float h
    float t
    float b
    float l
    int bi

type LiqZone
    box bx
    line ln
    label lab
    float vol
    bool buySide
    bool swept

liqVolSize = f_size(liqVolSizeInput)
liqInvis = color.rgb(0, 0, 0, 100)

liqTop = math.max(open, close)
liqBot = math.min(open, close)
liqCur = LiqData.new(high, liqTop, liqBot, low, bar_index)

var LiqData hst = LiqData.new(high, liqTop, liqBot, low, bar_index)
var LiqData lst = LiqData.new(high, liqTop, liqBot, low, bar_index)

var int hCount = 0
var int lCount = 0
var int lastHighWick = bar_index
var int lastLowWick = bar_index
var int bsHighWick = 0
var int bsLowWick = 0
var float hiVol = 0.0
var float loVol = 0.0
var int lastBSLLeft = na
var int lastSSLLeft = na

var array<LiqZone> bslZones = array.new<LiqZone>()
var array<LiqZone> sslZones = array.new<LiqZone>()

f_del_liq(LiqZone z) =>
    box.delete(z.bx)
    line.delete(z.ln)
    if not na(z.lab)
        label.delete(z.lab)

f_add_liq(bool buySide, int leftBar, float top, float bot, float vol) =>
    col = buySide ? liqBearColor : liqBullColor
    lvl = buySide ? bot : top
    tag = buySide ? "BSL" : "SSL"
    bx = box.new(left=leftBar, top=top, right=bar_index, bottom=bot, bgcolor=col, border_color=color.new(f_solid(col), 85))
    ln = line.new(leftBar, lvl, bar_index, lvl, color=color.new(f_solid(col), 0), style=line.style_dotted)
    txt = liqShowVolLabels ? tag + " " + str.tostring(vol, format.volume) : tag
    lb = label.new(bar_index, lvl, txt, color=liqInvis, textcolor=color.new(f_solid(col), 0), style=label.style_label_right, size=liqVolSize, textalign=text.align_right)
    z = LiqZone.new(bx, ln, lb, vol, buySide, false)
    if buySide
        array.unshift(bslZones, z)
        if array.size(bslZones) > liqMaxZones
            old = array.pop(bslZones)
            f_del_liq(old)
    else
        array.unshift(sslZones, z)
        if array.size(sslZones) > liqMaxZones
            old = array.pop(sslZones)
            f_del_liq(old)

if showLiquidity
    if (high > hst.h) and ((liqTop > hst.h) or (liqTop < hst.t))
        if hCount > 1
            lst := liqCur
            loVol := 0
            lCount := 0
        hst := liqCur
        hiVol := 0
        hCount := 1
        lastHighWick := bar_index

    if (low < lst.l) and ((liqBot < lst.l) or (liqBot > lst.b))
        if lCount > 1
            hst := liqCur
            hiVol := 0
            hCount := 0
        lst := liqCur
        loVol := 0
        lCount := 1
        lastLowWick := bar_index

    h_wick = high > hst.t and liqTop <= hst.t
    l_wick = low < lst.b and liqBot >= lst.b

    if h_wick[1] and hst.t[1] == hst.t[2] and bsHighWick > effLiqGap
        hCount += 1
        lastHighWick := bar_index[1]

    if l_wick[1] and lst.b[1] == lst.b[2] and bsLowWick > effLiqGap
        lCount += 1
        lastLowWick := bar_index[1]

    bsHighWick := math.abs(lastHighWick - bar_index)
    bsLowWick := math.abs(lastLowWick - bar_index)

    if high > hst.h
        hst.h := high

    if low < lst.l
        lst.l := low

    rng = math.max(high - low, syminfo.mintick)
    hiVol += nz((math.max(high - hst.t, 0) / rng) * volume)
    loVol += nz((math.max(lst.b - low, 0) / rng) * volume)

    makeBSL = hCount >= liqContactAmount and ta.crossover(bsHighWick, effLiqWait) and close < hst.t and hst.bi != lastBSLLeft
    if makeBSL
        f_add_liq(true, hst.bi, hst.h, hst.t, hiVol)
        lastBSLLeft := hst.bi

    makeSSL = lCount >= liqContactAmount and ta.crossover(bsLowWick, effLiqWait) and close > lst.b and lst.bi != lastSSLLeft
    if makeSSL
        f_add_liq(false, lst.bi, lst.b, lst.l, loVol)
        lastSSLLeft := lst.bi

bullLiquiditySweep = false
bearLiquiditySweep = false
var string lastLiqEvent = "None"

if showLiquidity and array.size(bslZones) > 0
    for i = array.size(bslZones) - 1 to 0
        z = array.get(bslZones, i)
        top = box.get_top(z.bx)
        bot = box.get_bottom(z.bx)
        lvl = bot

        box.set_right(z.bx, bar_index)
        line.set_x2(z.ln, bar_index)

        if not na(z.lab)
            label.set_x(z.lab, bar_index)
            label.set_y(z.lab, lvl)
            label.set_text(z.lab, liqShowVolLabels ? "BSL " + str.tostring(z.vol, format.volume) : "BSL")

        sweep = liqSweepConfirm == "Close Through Zone" ? (high > top and close < bot) : (high > top and close < top)

        if sweep and not z.swept
            bearLiquiditySweep := true
            z.swept := true
            lastLiqEvent := "BSL Sweep"
            if liqShowSweepLabels
                label.new(bar_index, high, "BSL Sweep", style=label.style_label_down, color=transparent, textcolor=bearStructureColor, size=size.tiny)

        delZone = (liqDeleteMode == "On Sweep" and sweep) or (liqDeleteMode == "On Break" and close > top)
        array.set(bslZones, i, z)
        if delZone
            f_del_liq(z)
            array.remove(bslZones, i)

if showLiquidity and array.size(sslZones) > 0
    for i = array.size(sslZones) - 1 to 0
        z = array.get(sslZones, i)
        top = box.get_top(z.bx)
        bot = box.get_bottom(z.bx)
        lvl = top

        box.set_right(z.bx, bar_index)
        line.set_x2(z.ln, bar_index)

        if not na(z.lab)
            label.set_x(z.lab, bar_index)
            label.set_y(z.lab, lvl)
            label.set_text(z.lab, liqShowVolLabels ? "SSL " + str.tostring(z.vol, format.volume) : "SSL")

        sweep = liqSweepConfirm == "Close Through Zone" ? (low < bot and close > top) : (low < bot and close > bot)

        if sweep and not z.swept
            bullLiquiditySweep := true
            z.swept := true
            lastLiqEvent := "SSL Sweep"
            if liqShowSweepLabels
                label.new(bar_index, low, "SSL Sweep", style=label.style_label_up, color=transparent, textcolor=bullStructureColor, size=size.tiny)

        delZone = (liqDeleteMode == "On Sweep" and sweep) or (liqDeleteMode == "On Break" and close < bot)
        array.set(sslZones, i, z)
        if delZone
            f_del_liq(z)
            array.remove(sslZones, i)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Opening Range Backend
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

inOR = not na(time(timeframe.period, orSession, kzTimezone))
var float orHigh = na
var float orLow = na
var line orHighLine = na
var line orLowLine = na

if inOR and not inOR[1]
    orHigh := high
    orLow := low

if inOR
    orHigh := math.max(nz(orHigh, high), high)
    orLow := math.min(nz(orLow, low), low)

if showOR and barstate.islast and not na(orHigh) and not na(orLow)
    line.delete(orHighLine)
    line.delete(orLowLine)
    orHighLine := line.new(bar_index - 100, orHigh, bar_index + 20, orHigh, color=orColor, style=line.style_solid, extend=extend.right)
    orLowLine := line.new(bar_index - 100, orLow, bar_index + 20, orLow, color=orColor, style=line.style_solid, extend=extend.right)

orBreakUp = useORBackend and not inOR and not na(orHigh) and close > orHigh
orBreakDown = useORBackend and not inOR and not na(orLow) and close < orLow
orState = inOR ? "Building" : orBreakUp ? "Above OR" : orBreakDown ? "Below OR" : "Inside/None"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Volume Profile Backend: POC / VAH / VAL
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

var array<float> vpVols = array.new_float()
if barstate.isfirst
    for i = 0 to vpBins - 1
        array.push(vpVols, 0.0)

var float vpPoc = na
var float vpVah = na
var float vpVal = na
var string vpState = "Off"

if useVP and barstate.islast
    array.fill(vpVols, 0.0)
    lb = math.min(vpLookback, bar_index + 1)
    vpHi = ta.highest(high, vpLookback)
    vpLo = ta.lowest(low, vpLookback)
    step = math.max((vpHi - vpLo) / math.max(vpBins - 1, 1), syminfo.mintick)

    for i = 0 to lb - 1
        idxRaw = int(math.floor((hlc3[i] - vpLo) / step))
        idx = math.max(0, math.min(vpBins - 1, idxRaw))
        array.set(vpVols, idx, array.get(vpVols, idx) + volume[i])

    maxVol = array.max(vpVols)
    pocIdx = array.indexof(vpVols, maxVol)
    vpPoc := vpLo + step * pocIdx

    totalVol = array.sum(vpVols)
    vaTarget = totalVol * vpValueAreaPct / 100.0
    vaSum = maxVol
    up = pocIdx
    dn = pocIdx

    while vaSum < vaTarget and (up < vpBins - 1 or dn > 0)
        upVol = up < vpBins - 1 ? array.get(vpVols, up + 1) : 0.0
        dnVol = dn > 0 ? array.get(vpVols, dn - 1) : 0.0
        if upVol >= dnVol and up < vpBins - 1
            up += 1
            vaSum += upVol
        else if dn > 0
            dn -= 1
            vaSum += dnVol
        else
            break

    vpVah := vpLo + step * up
    vpVal := vpLo + step * dn
    vpState := close > vpVah ? "Above VAH" : close < vpVal ? "Below VAL" : close > vpPoc ? "Above POC" : close < vpPoc ? "Below POC" : "At POC"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Trendlines With Breaks
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

tlPh = ta.pivothigh(high, tlLength, tlLength)
tlPl = ta.pivotlow(low, tlLength, tlLength)

tlSlope = tlCalcMethod == "Atr" ? ta.atr(tlLength) / tlLength * tlSlopeMult : tlCalcMethod == "Stdev" ? ta.stdev(close, tlLength) / tlLength * tlSlopeMult : math.abs(ta.sma(close * bar_index, tlLength) - ta.sma(close, tlLength) * ta.sma(bar_index, tlLength)) / ta.variance(bar_index, tlLength) / 2 * tlSlopeMult

var float tlUpper = na
var float tlLower = na
var float tlSlopeHigh = na
var float tlSlopeLow = na
var int tlUpState = 0
var int tlDownState = 0

if not na(tlPh)
    tlSlopeHigh := tlSlope
    tlUpper := tlPh
else if not na(tlUpper) and not na(tlSlopeHigh)
    tlUpper := tlUpper - tlSlopeHigh

if not na(tlPl)
    tlSlopeLow := tlSlope
    tlLower := tlPl
else if not na(tlLower) and not na(tlSlopeLow)
    tlLower := tlLower + tlSlopeLow

tlUpState := not na(tlPh) ? 0 : (not na(tlUpper) and not na(tlSlopeHigh) and close > tlUpper - tlSlopeHigh * tlLength ? 1 : tlUpState)
tlDownState := not na(tlPl) ? 0 : (not na(tlLower) and not na(tlSlopeLow) and close < tlLower + tlSlopeLow * tlLength ? 1 : tlDownState)

trendUpBreak = showTrendlines and tlUpState > nz(tlUpState[1])
trendDownBreak = showTrendlines and tlDownState > nz(tlDownState[1])

tlOffset = tlBackpaint ? tlLength : 0
upperPlot = showTrendlines ? (tlBackpaint ? tlUpper : tlUpper - tlSlopeHigh * tlLength) : na
lowerPlot = showTrendlines ? (tlBackpaint ? tlLower : tlLower + tlSlopeLow * tlLength) : na

plot(upperPlot, "Upper TL", color=showTrendlines ? tlUpColor : na, offset=-tlOffset)
plot(lowerPlot, "Lower TL", color=showTrendlines ? tlDownColor : na, offset=-tlOffset)

plotshape(tlShowBreaks and trendUpBreak ? low : na, "Upper Break", shape.labelup, location.absolute, tlUpColor, text="B", textcolor=color.white, size=size.tiny)
plotshape(tlShowBreaks and trendDownBreak ? high : na, "Lower Break", shape.labeldown, location.absolute, tlDownColor, text="B", textcolor=color.white, size=size.tiny)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Confluence + Dynamic Rule Engine
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

trendFilterBull = close > ema50 and ema50 > ema200
trendFilterBear = close < ema50 and ema50 < ema200

dwmBull = includeDWMInScore and showDWM ? ((close > dayOpen ? 1 : 0) + (not na(prevDayHigh) and close > prevDayHigh ? 1 : 0)) : 0
dwmBear = includeDWMInScore and showDWM ? ((close < dayOpen ? 1 : 0) + (not na(prevDayLow) and close < prevDayLow ? 1 : 0)) : 0

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// v4.6 Premium Brain
// Compact hidden data layer. No visual changes.
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

brainSource43 = close

// Squeeze / Momentum engine, compressed from LazyBear-style squeeze logic.
momLen43 = isCrypto ? 18 : isForex ? 20 : 20
bbLen43 = momLen43
kcLen43 = momLen43
bbMult43 = 2.0
kcMult43 = isCrypto ? 1.35 : 1.50

bbBasis43 = ta.sma(brainSource43, bbLen43)
bbDev43 = bbMult43 * ta.stdev(brainSource43, bbLen43)
bbUpper43 = bbBasis43 + bbDev43
bbLower43 = bbBasis43 - bbDev43

kcBasis43 = ta.sma(brainSource43, kcLen43)
kcRange43 = ta.sma(ta.tr(true), kcLen43)
kcUpper43 = kcBasis43 + kcRange43 * kcMult43
kcLower43 = kcBasis43 - kcRange43 * kcMult43

sqzOn43 = bbLower43 > kcLower43 and bbUpper43 < kcUpper43
sqzOff43 = bbLower43 < kcLower43 and bbUpper43 > kcUpper43
momBase43 = brainSource43 - math.avg(math.avg(ta.highest(high, momLen43), ta.lowest(low, momLen43)), ta.sma(brainSource43, momLen43))
momOsc43 = ta.linreg(momBase43, momLen43, 0)
momSig43 = ta.sma(momOsc43, 5)
momStrength43 = momOsc43 - momSig43
momBull43 = useMomentumBrain43 and momOsc43 > momSig43 and momStrength43 > 0
momBear43 = useMomentumBrain43 and momOsc43 < momSig43 and momStrength43 < 0
momExpansion43 = useMomentumBrain43 and sqzOff43 and not sqzOn43

// ADX / DI trend strength engine.
[diPlus43, diMinus43, adx43] = ta.dmi(14, 14)
trendStrong43 = useTrendBrain43 and adx43 >= (isForex ? 18 : isCrypto ? 20 : 22)
trendBull43 = trendStrong43 and diPlus43 > diMinus43
trendBear43 = trendStrong43 and diMinus43 > diPlus43
trendChop43 = useTrendBrain43 and adx43 < (isForex ? 15 : 18)

// VWAP deviation engine. Backend only.
vwap43 = ta.vwap(hlc3)
vwapDev43 = ta.stdev(hlc3 - vwap43, 50)
vwapZ43 = vwapDev43 == 0.0 ? 0.0 : (close - vwap43) / vwapDev43
vwapBull43 = useVwapBrain43 and close > vwap43
vwapBear43 = useVwapBrain43 and close < vwap43
vwapOverHigh43 = useVwapBrain43 and vwapZ43 > 2.0
vwapOverLow43 = useVwapBrain43 and vwapZ43 < -2.0
vwapState43 = not useVwapBrain43 ? "Off" : vwapOverHigh43 ? "VWAP High Ext" : vwapOverLow43 ? "VWAP Low Ext" : vwapBull43 ? "Above VWAP" : vwapBear43 ? "Below VWAP" : "At VWAP"

// Volume pressure engine. Uses candle-position volume split, compressed.
range43 = math.max(high - low, syminfo.mintick)
buyVol43 = volume * (close - low) / range43
sellVol43 = volume - buyVol43
buyPct43 = volume == 0 ? 50.0 : buyVol43 / volume * 100.0
sellPct43 = 100.0 - buyPct43
deltaBull43 = useVolumeBrain43 and buyPct43 >= 55.0
deltaBear43 = useVolumeBrain43 and sellPct43 >= 55.0
deltaState43 = not useVolumeBrain43 ? "Off" : deltaBull43 ? "Buy Pressure" : deltaBear43 ? "Sell Pressure" : "Balanced"

// Auto-threshold modifier. Market type + regime, not one dumb fixed number.
baseThreshold43 = isCrypto ? 3 : isForex ? 5 : isStocks ? 5 : isFut ? 5 : confluenceThreshold
thresholdAdj43 = 0
thresholdAdj43 += volCompressed ? 1 : 0
thresholdAdj43 += trendChop43 ? 1 : 0
thresholdAdj43 += rvol < effRvolMin ? 1 : 0
thresholdAdj43 += momExpansion43 ? -1 : 0
thresholdAdj43 += trendStrong43 ? -1 : 0
effConfluenceAuto = useBrain43 and useAutoThreshold43 ? int(f_clamp(baseThreshold43 + thresholdAdj43, 3, 8)) : effConfluence

brainBull43 = (momBull43 ? 1 : 0) + (trendBull43 ? 1 : 0) + (vwapBull43 and not vwapOverHigh43 ? 1 : 0) + (deltaBull43 ? 1 : 0) + (momExpansion43 and momBull43 ? 1 : 0)
brainBear43 = (momBear43 ? 1 : 0) + (trendBear43 ? 1 : 0) + (vwapBear43 and not vwapOverLow43 ? 1 : 0) + (deltaBear43 ? 1 : 0) + (momExpansion43 and momBear43 ? 1 : 0)

brainState43 = not useBrain43 ? "Brain Off" : brainBull43 > brainBear43 ? "Brain Bull " + str.tostring(brainBull43) + "/5" : brainBear43 > brainBull43 ? "Brain Bear " + str.tostring(brainBear43) + "/5" : "Brain Mixed"
momState43 = not useMomentumBrain43 ? "Mom Off" : sqzOn43 ? "Squeeze" : momBull43 ? "Bull Momentum" : momBear43 ? "Bear Momentum" : "Momentum Flat"

bullFvgActive = priceInBullFVG or bullIFVG or (activeBullFVGs > 0 and bestBullFvgQ >= 0.55)
bearFvgActive = priceInBearFVG or bearIFVG or (activeBearFVGs > 0 and bestBearFvgQ >= 0.55)

bullObActive = obRetestBull and not obBrokenBull
bearObActive = obRetestBear and not obBrokenBear

// v4.6 Weighted Market Sentiment Aggregator
// Uses existing brain variables. No extra visuals beyond dashboard row.
W_STRUCT43 = 3.0
W_LIQ43 = 3.0
W_PD43 = 1.0
W_MOM43 = 2.0
W_TREND43 = 1.0
W_VWAP43 = 1.0
W_FLOW43 = 2.0
W_SESSION43 = 1.0

sentBull43 = 0.0
sentBear43 = 0.0

// 1. Market structure vote
if swingTrend == 1 or htfDir == 1 or macroStructDir == 1
    sentBull43 += W_STRUCT43
if swingTrend == -1 or htfDir == -1 or macroStructDir == -1
    sentBear43 += W_STRUCT43

// 2. Liquidity vote
if bullLiquiditySweep or cryptoSweepBull
    sentBull43 += W_LIQ43
if bearLiquiditySweep or cryptoSweepBear
    sentBear43 += W_LIQ43

// 3. OB / FVG / premium-discount vote
if inDiscount or bullFvgActive or bullObActive
    sentBull43 += W_PD43
if inPremium or bearFvgActive or bearObActive
    sentBear43 += W_PD43

// 4. Momentum vote
if momBull43 and not sqzOn43
    sentBull43 += W_MOM43
if momBear43 and not sqzOn43
    sentBear43 += W_MOM43

// 5. ADX / DI trend vote, only when trend is strong enough
if trendStrong43
    if diPlus43 > diMinus43
        sentBull43 += W_TREND43
    else
        sentBear43 += W_TREND43

// 6. VWAP context vote
if vwapBull43 and not vwapOverHigh43
    sentBull43 += W_VWAP43
if vwapBear43 and not vwapOverLow43
    sentBear43 += W_VWAP43

// 7. Volume pressure vote
if deltaBull43
    sentBull43 += W_FLOW43
if deltaBear43
    sentBear43 += W_FLOW43

// 8. Session / participation vote
preSide43 = sentBull43 > sentBear43 ? 1 : sentBear43 > sentBull43 ? -1 : 0
if killzoneActive and preSide43 == 1
    sentBull43 += W_SESSION43
if killzoneActive and preSide43 == -1
    sentBear43 += W_SESSION43

sentTotal43 = sentBull43 + sentBear43
sentBullPct43 = sentTotal43 > 0 ? sentBull43 / sentTotal43 * 100.0 : 50.0
sentBar43 = f_sentiment_bar(sentBullPct43)
sentColor43 = sentBullPct43 > 60 ? color.new(color.green, 10) : sentBullPct43 < 40 ? color.new(color.red, 10) : color.gray
sentText43 = str.tostring(sentBullPct43, "#") + "% Bull | " + str.tostring(100 - sentBullPct43, "#") + "% Bear"

sessionPoint = effShowKillzones and killzoneActive ? 1 : 0
rvolPoint = rvol >= effRvolMin ? 1 : 0
volPoint = volExpanded ? 1 : volCompressed ? -1 : 0

bullScore = (priceInBullFVG ? 1 : 0) + (bestBullFvgQ >= 0.65 ? 2 : bestBullFvgQ >= 0.45 ? 1 : 0) + (bullStructureBreak ? 2 : 0) + (bullCHoCH ? 2 : 0) + (bullLiquiditySweep ? 2 : 0) + (trendUpBreak ? 1 : 0) + (trendFilterBull ? 1 : 0) + (htfDir == 1 ? 1 : 0) + (inDiscount ? 1 : 0) + (obRetestBull ? 1 : 0) + (bullIFVG ? 1 : 0) + sessionPoint + dwmBull + rvolPoint + volPoint + mtfBullScore

bearScore = (priceInBearFVG ? 1 : 0) + (bestBearFvgQ >= 0.65 ? 2 : bestBearFvgQ >= 0.45 ? 1 : 0) + (bearStructureBreak ? 2 : 0) + (bearCHoCH ? 2 : 0) + (bearLiquiditySweep ? 2 : 0) + (trendDownBreak ? 1 : 0) + (trendFilterBear ? 1 : 0) + (htfDir == -1 ? 1 : 0) + (inPremium ? 1 : 0) + (obRetestBear ? 1 : 0) + (bearIFVG ? 1 : 0) + sessionPoint + dwmBear + rvolPoint + volPoint + mtfBearScore

maxScore = useMTFBackend ? 23 : 15
bias = bullScore >= bearScore + 2 ? "Bullish" : bearScore >= bullScore + 2 ? "Bearish" : "Neutral"

longConfluence = bullScore >= effConfluenceAuto and bullScore > bearScore
shortConfluence = bearScore >= effConfluenceAuto and bearScore > bullScore

conflict = (useMTFBackend and macroStructDir == 1 and swingTrend == -1) ? "4H Bull vs LTF Bear" : (useMTFBackend and macroStructDir == -1 and swingTrend == 1) ? "4H Bear vs LTF Bull" : (htfDir == 1 and swingTrend == -1) ? "HTF Bull vs LTF Bear" : (htfDir == -1 and swingTrend == 1) ? "HTF Bear vs LTF Bull" : (bullScore >= effConfluenceAuto and bearScore >= effConfluenceAuto) ? "Two-Sided Conflict" : "None"

marketBad = inBlackout or (volCompressed and rvol < effRvolMin and not momExpansion43)
side43 = bullScore > bearScore ? 1 : bearScore > bullScore ? -1 : 0
activeScore43 = side43 == 1 ? bullScore + brainBull43 : side43 == -1 ? bearScore + brainBear43 : math.max(bullScore, bearScore)
oppositeBrain43 = side43 == 1 ? brainBear43 : side43 == -1 ? brainBull43 : math.max(brainBull43, brainBear43)

longTrapRisk43 = side43 == 1 and (bearLiquiditySweep or bearStructureBreak or (vwapBear43 and momBear43) or oppositeBrain43 >= 3)
shortTrapRisk43 = side43 == -1 and (bullLiquiditySweep or bullStructureBreak or (vwapBull43 and momBull43) or oppositeBrain43 >= 3)
trapRisk43 = useBrain43 and (longTrapRisk43 or shortTrapRisk43)

quality = marketBad ? "No Trade" : trapRisk43 ? "Trap Risk" : activeScore43 >= 13 ? "A+" : activeScore43 >= 11 ? "A" : activeScore43 >= 8 ? "B" : activeScore43 >= 6 ? "C" : activeScore43 >= effConfluenceAuto ? "D" : "No Trade"
qualityDetail43 = quality == "A+" ? "Premium Setup" : quality == "A" ? "Clean Setup" : quality == "B" ? "Wait Retest" : quality == "C" ? "Risky Valid" : quality == "D" ? "Weak Edge" : quality == "Trap Risk" ? "Opposite Reclaim" : "Conflict / Chop"

bullBreakoutNow = bullBOS or trendUpBreak or orBreakUp
bearBreakoutNow = bearBOS or trendDownBreak or orBreakDown
bullSweepSetup = bullLiquiditySweep or str.contains(lastLiqEvent, "SSL")
bearSweepSetup = bearLiquiditySweep or str.contains(lastLiqEvent, "BSL")

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// v4.6 Premium Backend: staged liquidity, S/D, breakers, S/R, volume break,
// BTC cycle context, and RellQuantBrain IT3 router.
// Backend deep. Dashboard clear. Chart clean.
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// Liquidity staged awareness
// v4.6.2 fix: box-array liquidity can be empty when drawings are hidden/trimmed.
// This fallback uses confirmed swing/pivot + DWM context so the dashboard does not get stuck on No Zones.
bool sslBoxArea46 = array.size(sslZones) > 0
bool bslBoxArea46 = array.size(bslZones) > 0
bool sslSwingArea462 = not na(swLow) or not na(prevDayLow)
bool bslSwingArea462 = not na(swHigh) or not na(prevDayHigh)
bool sslArea46 = sslBoxArea46 or sslSwingArea462
bool bslArea46 = bslBoxArea46 or bslSwingArea462
bool sslNearby46 = false
bool bslNearby46 = false
bool sslReaction46 = false
bool bslReaction46 = false

if sslBoxArea46
    for i = 0 to array.size(sslZones) - 1
        z = array.get(sslZones, i)
        zTop = box.get_top(z.bx)
        zBot = box.get_bottom(z.bx)
        zTouch = high >= zBot and low <= zTop
        zDist = close > zTop ? close - zTop : close < zBot ? zBot - close : 0.0
        sslReaction46 := sslReaction46 or zTouch
        sslNearby46 := sslNearby46 or (zDist <= atr * 0.75)

if bslBoxArea46
    for i = 0 to array.size(bslZones) - 1
        z = array.get(bslZones, i)
        zTop = box.get_top(z.bx)
        zBot = box.get_bottom(z.bx)
        zTouch = high >= zBot and low <= zTop
        zDist = close > zTop ? close - zTop : close < zBot ? zBot - close : 0.0
        bslReaction46 := bslReaction46 or zTouch
        bslNearby46 := bslNearby46 or (zDist <= atr * 0.75)

// Swing / DWM liquidity fallback
float sslRef462 = not na(swLow) ? swLow : prevDayLow
float bslRef462 = not na(swHigh) ? swHigh : prevDayHigh
bool sslSwingNearby462 = not na(sslRef462) and close > sslRef462 and (close - sslRef462) <= atr * 1.25
bool bslSwingNearby462 = not na(bslRef462) and close < bslRef462 and (bslRef462 - close) <= atr * 1.25
bool sslSwingReaction462 = not na(sslRef462) and low <= sslRef462 + atr * 0.15 and close > sslRef462
bool bslSwingReaction462 = not na(bslRef462) and high >= bslRef462 - atr * 0.15 and close < bslRef462

sslNearby46 := sslNearby46 or sslSwingNearby462
bslNearby46 := bslNearby46 or bslSwingNearby462
sslReaction46 := sslReaction46 or sslSwingReaction462
bslReaction46 := bslReaction46 or bslSwingReaction462

string liqState = bullLiquiditySweep ? "SSL Swept" : bearLiquiditySweep ? "BSL Swept" : sslReaction46 ? "SSL Reaction" : bslReaction46 ? "BSL Reaction" : sslNearby46 ? "SSL Nearby" : bslNearby46 ? "BSL Nearby" : sslArea46 ? "SSL Area Below" : bslArea46 ? "BSL Area Above" : "No Zones"
color liqStateColor46 = bullLiquiditySweep or sslReaction46 or sslNearby46 or (sslArea46 and not bslArea46) ? color.lime : bearLiquiditySweep or bslReaction46 or bslNearby46 or (bslArea46 and not sslArea46) ? color.red : sslArea46 or bslArea46 ? color.orange : color.silver

// Supply / demand backend-only intelligence
bool demandReaction46 = obRetestBull or priceInBullFVG or bullCETap or bullIFVG
bool supplyReaction46 = obRetestBear or priceInBearFVG or bearCETap or bearIFVG
bool demandBroken46 = obBrokenBull
bool supplyBroken46 = obBrokenBear
bool demandNearby46 = demandReaction46 or (not na(swLow) and math.abs(close - swLow) <= atr * 1.25) or inDiscount
bool supplyNearby46 = supplyReaction46 or (not na(swHigh) and math.abs(close - swHigh) <= atr * 1.25) or inPremium
int demandStrength46 = int(math.min(10, (demandReaction46 ? 3 : 0) + (bullLiquiditySweep ? 2 : 0) + (inDiscount ? 1 : 0) + (htfDir == 1 ? 2 : 0) + (rvol >= effRvolMin ? 1 : 0) + (priceInBullFVG ? 1 : 0)))
int supplyStrength46 = int(math.min(10, (supplyReaction46 ? 3 : 0) + (bearLiquiditySweep ? 2 : 0) + (inPremium ? 1 : 0) + (htfDir == -1 ? 2 : 0) + (rvol >= effRvolMin ? 1 : 0) + (priceInBearFVG ? 1 : 0)))
string sdState46 = demandBroken46 ? "Demand Broken" : supplyBroken46 ? "Supply Broken" : demandReaction46 ? "Demand " + str.tostring(demandStrength46) + "/10 Reaction" : supplyReaction46 ? "Supply " + str.tostring(supplyStrength46) + "/10 Reaction" : demandNearby46 ? "Demand " + str.tostring(demandStrength46) + "/10 Nearby" : supplyNearby46 ? "Supply " + str.tostring(supplyStrength46) + "/10 Nearby" : "No S/D"
color sdColor46 = demandReaction46 or demandNearby46 ? color.lime : supplyReaction46 or supplyNearby46 ? color.red : color.silver

// Breaker block backend from failed order blocks
bool bullBreakerRetest46 = not na(lastBearBreakerTop) and (bar_index - nz(lastBearBreakerBorn, bar_index)) <= obMaxAge and high >= lastBearBreakerBot and low <= lastBearBreakerTop and close > lastBearBreakerBot
bool bearBreakerRetest46 = not na(lastBullBreakerTop) and (bar_index - nz(lastBullBreakerBorn, bar_index)) <= obMaxAge and high >= lastBullBreakerBot and low <= lastBullBreakerTop and close < lastBullBreakerTop
string breakerState46 = bullBreakerRetest46 ? "Bull Breaker Retest" : bearBreakerRetest46 ? "Bear Breaker Retest" : obBrokenBear ? "Bear OB Failed" : obBrokenBull ? "Bull OB Failed" : "No Breaker"
color breakerColor46 = bullBreakerRetest46 or obBrokenBear ? color.lime : bearBreakerRetest46 or obBrokenBull ? color.red : color.silver

// Support / resistance channel backend
float srBand46 = atr * 0.35
bool inSupportChannel46 = not na(swLow) and low <= swLow + srBand46 and high >= swLow - srBand46
bool inResistanceChannel46 = not na(swHigh) and high >= swHigh - srBand46 and low <= swHigh + srBand46
bool supportBroken46 = not na(swLow) and close < swLow - srBand46
bool resistanceBroken46 = not na(swHigh) and close > swHigh + srBand46
int supportStrength46 = int(math.min(10, (inSupportChannel46 ? 3 : 0) + (sslArea46 ? 1 : 0) + (inDiscount ? 2 : 0) + (htfDir == 1 ? 2 : 0) + (rvol >= effRvolMin ? 1 : 0)))
int resistanceStrength46 = int(math.min(10, (inResistanceChannel46 ? 3 : 0) + (bslArea46 ? 1 : 0) + (inPremium ? 2 : 0) + (htfDir == -1 ? 2 : 0) + (rvol >= effRvolMin ? 1 : 0)))
string srState46 = supportBroken46 ? "Support Broken" : resistanceBroken46 ? "Resistance Broken" : inSupportChannel46 ? "In Support Channel " + str.tostring(supportStrength46) + "/10" : inResistanceChannel46 ? "In Resistance Channel " + str.tostring(resistanceStrength46) + "/10" : "No S/R"
color srColor46 = inSupportChannel46 ? color.lime : inResistanceChannel46 ? color.red : color.silver

// Volume-confirmed breakout filter
bool volBreakUp46 = resistanceBroken46 and rvol >= effRvolMin and (volExpanded or volume > volMa)
bool volBreakDown46 = supportBroken46 and rvol >= effRvolMin and (volExpanded or volume > volMa)
bool weakBreakUp46 = resistanceBroken46 and not volBreakUp46
bool weakBreakDown46 = supportBroken46 and not volBreakDown46
string volBreakState46 = volBreakUp46 ? "Volume Break Up" : volBreakDown46 ? "Volume Break Down" : weakBreakUp46 or weakBreakDown46 ? "Weak Break" : "No Break"
color volBreakColor46 = volBreakUp46 ? color.lime : volBreakDown46 ? color.red : weakBreakUp46 or weakBreakDown46 ? color.orange : color.silver
bool volBreakEvent46 = volBreakUp46 or volBreakDown46
color volRegimeColor462 = volRegime == "Extreme" ? color.red : volRegime == "High" or volRegime == "Expanding" ? color.orange : volRegime == "Compressed" or volRegime == "Low" or volRegime == "Very Low" ? color.aqua : color.silver

// BTC macro/cycle context
bool isBTC46 = str.contains(syminfo.tickerid, "BTC")
btcWma200 = request.security(syminfo.tickerid, "W", ta.sma(close, 200), barmerge.gaps_off, barmerge.lookahead_off)
btcDHigh365 = request.security(syminfo.tickerid, "D", ta.highest(high, 365), barmerge.gaps_off, barmerge.lookahead_off)
btcDLow365 = request.security(syminfo.tickerid, "D", ta.lowest(low, 365), barmerge.gaps_off, barmerge.lookahead_off)
btcDHigh730 = request.security(syminfo.tickerid, "D", ta.highest(high, 730), barmerge.gaps_off, barmerge.lookahead_off)
btcDLow730 = request.security(syminfo.tickerid, "D", ta.lowest(low, 730), barmerge.gaps_off, barmerge.lookahead_off)
btcAth1460 = request.security(syminfo.tickerid, "D", ta.highest(high, 1460), barmerge.gaps_off, barmerge.lookahead_off)
btcPct1Y = (btcDHigh365 - btcDLow365) != 0 ? (close - btcDLow365) / (btcDHigh365 - btcDLow365) : 0.5
btcPct2Y = (btcDHigh730 - btcDLow730) != 0 ? (close - btcDLow730) / (btcDHigh730 - btcDLow730) : 0.5
btcDist200W = not na(btcWma200) and btcWma200 != 0 ? (close - btcWma200) / btcWma200 : na
btcAthDD = not na(btcAth1460) and btcAth1460 != 0 ? (close / btcAth1460 - 1.0) : na
bool btcCycleDiscount46 = isBTC46 and (btcPct2Y < 0.35 or nz(btcDist200W, 0.0) < 0.15 or nz(btcAthDD, 0.0) < -0.45)
bool btcCyclePremium46 = isBTC46 and (btcPct2Y > 0.75 and nz(btcDist200W, 0.0) > 0.80)
string btcCycleState46 = not isBTC46 or not showBTCycle46 ? "Off" : inDiscount and btcCycleDiscount46 ? "Local Discount | Cycle Discount" : inDiscount ? "Local Discount | Cycle Neutral" : inPremium and btcCyclePremium46 ? "Local Premium | Cycle Expensive" : inPremium ? "Local Premium | Cycle Premium" : btcCycleDiscount46 ? "Cycle Discount" : btcCyclePremium46 ? "Cycle Premium" : "Cycle Neutral"
color btcCycleColor46 = str.contains(btcCycleState46, "Discount") ? color.lime : str.contains(btcCycleState46, "Premium") or str.contains(btcCycleState46, "Expensive") ? color.red : color.silver

// RellQuantBrain IT3 multi-symbol forward-test router
bool it3M15_46 = timeframe.isminutes and timeframe.multiplier == 15
bool it3EURUSD46 = str.contains(syminfo.tickerid, "EURUSD")
bool it3USA50046 = str.contains(syminfo.tickerid, "USA500") or str.contains(syminfo.tickerid, "SPX500")
bool it3BTCUSD46 = str.contains(syminfo.tickerid, "BTCUSD")
bool it3Approved46 = useIT3Backend47 and it3M15_46 and (it3EURUSD46 or it3USA50046)
bool it3Watchlist46 = useIT3Backend47 and it3M15_46 and it3BTCUSD46
string it3Side46 = it3Approved46 ? "Long" : it3Watchlist46 ? "Short" : "None"
string it3Profile46 = it3EURUSD46 ? "EURUSD M15 Long Approved" : it3USA50046 ? "USA500 M15 Long Approved" : it3BTCUSD46 ? "BTC M15 Short Watchlist" : it3M15_46 ? "Not Trained" : "Wrong TF"
float it3Threshold46 = 0.70
float it3Slope46 = atr != 0.0 ? (ema20 - ema20[5]) / atr : 0.0
float it3Sqz46 = atr != 0.0 ? ta.stdev(close, 20) / atr : 1.0
float it3VwapDist46 = atr != 0.0 ? (close - ta.vwap(close)) / atr : 0.0
float it3PriorLow46 = ta.lowest(low, 20)[1]
float it3PriorHigh46 = ta.highest(high, 20)[1]
bool it3SslSweep46 = low < it3PriorLow46 and close > it3PriorLow46
bool it3BslRej46 = high > it3PriorHigh46 and close < it3PriorHigh46
int it3BarsSinceSSL46 = ta.barssince(it3SslSweep46)
int it3BarsSinceBSL46 = ta.barssince(it3BslRej46)
float it3Score46 = 0.0
if it3Side46 == "Long"
    if it3Slope46 > 0.05 and it3VwapDist46 < 0.2
        it3Score46 += 40
    if it3Sqz46 < 1.0
        it3Score46 += 30
    if not na(it3BarsSinceSSL46) and it3BarsSinceSSL46 <= 15
        it3Score46 += 30
else if it3Side46 == "Short"
    if it3Slope46 < -0.05 and it3VwapDist46 > -0.2
        it3Score46 += 40
    if it3Sqz46 < 1.0
        it3Score46 += 30
    if not na(it3BarsSinceBSL46) and it3BarsSinceBSL46 <= 15
        it3Score46 += 30
float it3ScorePct46 = it3Score46 / 100.0
bool it3ApprovedLongSignal46 = it3Approved46 and it3Side46 == "Long" and it3ScorePct46 >= it3Threshold46
bool it3WatchSignal46 = it3Watchlist46 and it3ScorePct46 >= it3Threshold46
string it3State46 = not useIT3Backend47 ? "Off" : not it3M15_46 ? "Wrong TF | Use M15" : it3ApprovedLongSignal46 ? "A+ | " + str.tostring(it3ScorePct46 * 100, "#") + "%" : it3WatchSignal46 ? "Watch | " + str.tostring(it3ScorePct46 * 100, "#") + "%" : it3Approved46 ? "Wait | " + str.tostring(it3ScorePct46 * 100, "#") + "%" : it3Watchlist46 ? "Watchlist | " + str.tostring(it3ScorePct46 * 100, "#") + "%" : "Off"
color it3Color46 = it3ApprovedLongSignal46 ? color.lime : it3WatchSignal46 and it3Side46 == "Short" ? color.red : it3WatchSignal46 ? color.orange : it3Watchlist46 ? color.orange : it3Approved46 ? color.silver : color.gray

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// v4.6.2 Trader Workflow / Anti-FOMO Guard
// Purpose: turn the dashboard into a checklist, not a permission slip.
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

bool wfBiasAligned461 = (bias == "Bullish" and htfDir >= 0) or (bias == "Bearish" and htfDir <= 0) or bias == "Neutral"
bool wfLiquidity461 = bullLiquiditySweep or bearLiquiditySweep or sslReaction46 or bslReaction46 or sslNearby46 or bslNearby46
bool wfZone461 = demandReaction46 or supplyReaction46 or bullBreakerRetest46 or bearBreakerRetest46 or inSupportChannel46 or inResistanceChannel46 or priceInBullFVG or priceInBearFVG
bool wfTrigger461 = bullCHoCH or bearCHoCH or bullStructureBreak or bearStructureBreak or volBreakEvent46 or it3ApprovedLongSignal46
bool wfVolume461 = rvol >= effRvolMin or volBreakEvent46 or not useVolumeBrain43
bool wfRiskOk461 = not marketBad and quality != "Trap Risk" and quality != "No Trade"
bool wfIt3Ok461 = not useIT3Backend47 or not it3Approved46 or it3ApprovedLongSignal46

int wfScore461 = (wfBiasAligned461 ? 1 : 0) + (wfLiquidity461 ? 1 : 0) + (wfZone461 ? 1 : 0) + (wfTrigger461 ? 1 : 0) + (wfVolume461 ? 1 : 0) + (wfRiskOk461 ? 1 : 0)

string wfText461 = str.tostring(wfScore461) + "/6 " + (wfScore461 >= 5 ? "Ready" : wfScore461 >= 3 ? "Waiting" : "Incomplete")
color wfColor461 = wfScore461 >= 5 and wfRiskOk461 ? color.lime : wfScore461 >= 3 ? color.orange : color.red

string actionState461 = quality == "No Trade" ? "NO TRADE" : quality == "Trap Risk" ? "TRAP RISK" : marketBad ? "NO TRADE" : useStrictAction461 and wfScore461 < 5 ? "WAIT" : (quality == "A+" or quality == "A") ? "READY" : quality == "B" ? "WAIT RETEST" : "WAIT"
color actionColor461 = actionState461 == "READY" ? color.lime : actionState461 == "TRAP RISK" ? color.red : actionState461 == "NO TRADE" ? color.red : color.orange
string actionNote461 = actionState461 == "READY" ? "Checklist aligned" : actionState461 == "WAIT RETEST" ? "Valid, but wait retest" : actionState461 == "TRAP RISK" ? "Do not chase" : actionState461 == "NO TRADE" ? "Stand down" : "Need trigger"

string playbook = "WAIT"
string guide = "Need sweep, BOS/CHoCH, or FVG retest"
string risk = "No mid-range entry"
string target = bias == "Bullish" ? "Target BSL / EQH / PDH" : bias == "Bearish" ? "Target SSL / EQL / PDL" : "Target range edges"

if marketBad
    playbook := "WAIT"
    guide := inBlackout ? "Time blackout, avoid forced entries" : "Compression/low RVOL, fakeout risk"
    risk := "No trade until expansion"
else if demandReaction46 and (bullCHoCH or bullStructureBreak or trendFilterBull)
    playbook := "LONG Demand Reaction"
    guide := "Demand reaction + bullish structure → wait confirm"
    risk := "SL below demand/sweep | Target BSL"
else if supplyReaction46 and (bearCHoCH or bearStructureBreak or trendFilterBear)
    playbook := "SHORT Supply Reaction"
    guide := "Supply reaction + bearish structure → wait confirm"
    risk := "SL above supply/sweep | Target SSL"
else if bullBreakerRetest46
    playbook := "LONG Breaker Retest"
    guide := "Bear OB failed → retest as support → long"
    risk := "SL below breaker | Target BSL/PDH"
else if bearBreakerRetest46
    playbook := "SHORT Breaker Retest"
    guide := "Bull OB failed → retest as resistance → short"
    risk := "SL above breaker | Target SSL/PDL"
else if volBreakUp46
    playbook := "LONG S/R Breakout Retest"
    guide := "Break resistance with volume → wait retest"
    risk := "SL below broken resistance"
else if volBreakDown46
    playbook := "SHORT S/R Breakdown Retest"
    guide := "Break support with volume → wait retest"
    risk := "SL above broken support"
else if weakBreakUp46 or weakBreakDown46
    playbook := "WAIT: Weak breakout"
    guide := "Volume not confirmed, wait for retest"
    risk := "No chase"
else if isBTC46 and btcCycleDiscount46 and bullLiquiditySweep and bullCHoCH
    playbook := "LONG Cycle Discount Reversal"
    guide := "Cycle discount + SSL sweep + Bull CHoCH → Buy FVG/OB"
    risk := "SL below cycle sweep | Swing target BSL"
else if isBTC46 and btcCyclePremium46 and (bearStructureBreak or priceInBearFVG)
    playbook := "SHORT Cycle Premium Rejection"
    guide := "Cycle premium + Bear BOS/FVG → short retest"
    risk := "SL above premium rejection"
else if (sslArea46 or bslArea46) and not (sslReaction46 or bslReaction46 or bullLiquiditySweep or bearLiquiditySweep)
    playbook := "WAIT: Zone exists"
    guide := "Liquidity area exists, no trigger yet"
    risk := "Wait reaction/sweep"
else if useMTFBackend and mtfLiqCode == 2 and (bullCHoCH or bullStructureBreak) and (ctxStructDir >= 0 or macroStructDir >= 0)
    playbook := "LONG MTF Sweep Reversal"
    guide := mtfLiqTf + " SSL sweep → LTF CHoCH → Buy FVG/CE"
    risk := "SL below MTF sweep | Target 15m BSL/PDH"
else if useMTFBackend and mtfLiqCode == -2 and (bearCHoCH or bearStructureBreak) and (ctxStructDir <= 0 or macroStructDir <= 0)
    playbook := "SHORT MTF Sweep Reversal"
    guide := mtfLiqTf + " BSL sweep → LTF CHoCH → Sell FVG/CE"
    risk := "SL above MTF sweep | Target 15m SSL/PDL"
else if useMTFBackend and ctxFvgTouch == 1 and mtfBullFvg and (bullCETap or priceInBullFVG)
    playbook := "LONG MTF FVG Retest"
    guide := mtfContextTf + " Bull FVG → LTF CE/FVG confirmation"
    risk := "SL below FVG | Target BSL/PDH"
else if useMTFBackend and ctxFvgTouch == 1 and mtfBearFvg and (bearCETap or priceInBearFVG)
    playbook := "SHORT MTF FVG Retest"
    guide := mtfContextTf + " Bear FVG → LTF CE/FVG confirmation"
    risk := "SL above FVG | Target SSL/PDL"
else if bullSweepSetup and bullCHoCH
    playbook := "LONG Sweep Reversal"
    guide := "SSL sweep → Bull CHoCH → Buy FVG/CE"
    risk := "SL below sweep | Target BSL/PDH"
else if bearSweepSetup and bearCHoCH
    playbook := "SHORT Sweep Reversal"
    guide := "BSL sweep → Bear CHoCH → Sell FVG/CE"
    risk := "SL above sweep | Target SSL/PDL"
else if bullIFVG or (priceInBullFVG and bullCETap and bestBullFvgQ >= 0.60)
    playbook := "LONG High-Q FVG"
    guide := "Bull FVG/IFVG → CE tap → Confirm"
    risk := "SL below FVG | Target BSL/PDH"
else if bearIFVG or (priceInBearFVG and bearCETap and bestBearFvgQ >= 0.60)
    playbook := "SHORT High-Q FVG"
    guide := "Bear FVG/IFVG → CE tap → Confirm"
    risk := "SL above FVG | Target SSL/PDL"
else if bullBreakoutNow and (htfDir >= 0 or trendFilterBull)
    playbook := "LONG Breakout Retest"
    guide := "Break high/OR → Retest → Long"
    risk := "SL below retest | Target BSL/PDH"
else if bearBreakoutNow and (htfDir <= 0 or trendFilterBear)
    playbook := "SHORT Breakout Retest"
    guide := "Break low/OR → Retest → Short"
    risk := "SL above retest | Target SSL/PDL"
else if trendFilterBull and htfDir == 1 and inDiscount
    playbook := "LONG Continuation"
    guide := "HTF bullish → Buy discount FVG/OB"
    risk := "SL below swing | Target weak high/BSL"
else if trendFilterBear and htfDir == -1 and inPremium
    playbook := "SHORT Continuation"
    guide := "HTF bearish → Sell premium FVG/OB"
    risk := "SL above swing | Target weak low/SSL"
else if bias == "Neutral"
    playbook := "RANGE Liquidity Play"
    guide := "Buy SSL sweep / Sell BSL sweep"
    risk := "No middle entries"
else if conflict != "None"
    playbook := "COUNTERTREND Caution"
    guide := "Trade smaller, wait for CHoCH + FVG"
    risk := "Fast TP only"
else
    playbook := bias == "Bullish" ? "WAIT For Long Trigger" : bias == "Bearish" ? "WAIT For Short Trigger" : "WAIT"
    guide := bias == "Bullish" ? "Need SSL sweep or breakout retest" : bias == "Bearish" ? "Need BSL sweep or breakdown retest" : "Need cleaner condition"
    risk := "No chase"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// v4.7 Risk Governance Layer
// Conservative behavior engine. Backend IT3 can support the workflow, but it no
// longer prints as a dashboard row or renames the playbook.
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

rangeAvg47 = ta.sma(high - low, 100)
rangeRatio47 = not na(rangeAvg47) and rangeAvg47 != 0.0 ? (high - low) / rangeAvg47 : 1.0
absVwapZ47 = math.abs(vwapZ43)
int tailScore47 = 0
tailScore47 += volExtreme462 ? 2 : (volHigh462 or volExpanded ? 1 : 0)
tailScore47 += rangeRatio47 > 2.20 ? 2 : rangeRatio47 > 1.50 ? 1 : 0
tailScore47 += absVwapZ47 > 2.25 ? 1 : 0
tailScore47 += rvol > 2.50 ? 1 : 0
tailScore47 += inBlackout ? 1 : 0
string tailRisk47 = not useGovernance47 ? "Off" : tailScore47 >= 4 ? "Extreme" : tailScore47 >= 2 ? "Elevated" : "Normal"
color tailRiskColor47 = tailRisk47 == "Extreme" ? color.red : tailRisk47 == "Elevated" ? color.orange : tailRisk47 == "Normal" ? color.lime : color.gray

string autoDrawdownMode47 = dailyLossCount47 >= maxDailyLosses47 or losingStreak47 >= 3 ? "Lockout" : losingStreak47 == 2 or weeklyLossCount47 >= 5 ? "Recovery" : dailyLossCount47 >= 1 or losingStreak47 == 1 ? "Caution" : "Normal"
string drawdownMode47 = not useGovernance47 ? "Off" : drawdownInputMode47 == "Auto" ? autoDrawdownMode47 : drawdownInputMode47
color drawdownColor47 = drawdownMode47 == "Lockout" ? color.red : drawdownMode47 == "Recovery" ? color.orange : drawdownMode47 == "Caution" ? color.yellow : drawdownMode47 == "Normal" ? color.lime : color.gray

bool researchBlocked47 = researchStatus47 == "Rejected" or researchStatus47 == "Untrained"
bool researchCaution47 = researchStatus47 == "Forward Test" or researchStatus47 == "Watchlist"
color researchColor47 = researchStatus47 == "Approved" ? color.lime : researchBlocked47 ? color.red : color.orange

bool usdPair47 = isForex and str.contains(syminfo.tickerid, "USD")
bool indexGroup47 = str.contains(syminfo.tickerid, "US100") or str.contains(syminfo.tickerid, "NAS") or str.contains(syminfo.tickerid, "SPX") or str.contains(syminfo.tickerid, "USA500") or str.contains(syminfo.tickerid, "US30") or str.contains(syminfo.tickerid, "GER40")
bool cryptoGroup47 = isCrypto or str.contains(syminfo.tickerid, "BTC") or str.contains(syminfo.tickerid, "ETH")
string autoCorrRisk47 = usdPair47 ? "Medium" : indexGroup47 or cryptoGroup47 ? "Medium" : "Low"
string corrRisk47 = correlationOverride47 == "Auto" ? autoCorrRisk47 : correlationOverride47
color corrRiskColor47 = corrRisk47 == "High" ? color.red : corrRisk47 == "Medium" ? color.orange : color.lime
string corrGuide47 = corrRisk47 == "High" ? "Avoid duplicate exposure" : corrRisk47 == "Medium" ? "Check related positions" : "No major overlap"

float kellyWin47 = kellyWinRate47 / 100.0
float kellyRaw47 = kellyRR47 > 0.0 ? kellyWin47 - ((1.0 - kellyWin47) / kellyRR47) : 0.0
float fractionalKellyPct47 = math.max(0.0, kellyRaw47 * 0.25 * 100.0)

string riskMode47 = "OBSERVE"
float riskPctBase47 = 0.0
string riskReason47 = "Need cleaner conditions"

if not useGovernance47
    riskMode47 := "OFF"
    riskPctBase47 := 0.0
    riskReason47 := "Governance disabled"
else if marketBad or quality == "No Trade" or actionState461 == "NO TRADE"
    riskMode47 := "NO TRADE"
    riskPctBase47 := 0.0
    riskReason47 := marketBad ? "Bad market filter" : "No valid setup"
else if quality == "Trap Risk" or actionState461 == "TRAP RISK"
    riskMode47 := "NO TRADE"
    riskPctBase47 := 0.0
    riskReason47 := "Trap risk active"
else if drawdownMode47 == "Lockout"
    riskMode47 := "NO TRADE"
    riskPctBase47 := 0.0
    riskReason47 := "Drawdown lockout"
else if tailRisk47 == "Extreme"
    riskMode47 := "OBSERVE"
    riskPctBase47 := 0.0
    riskReason47 := "Extreme tail risk"
else if researchBlocked47
    riskMode47 := "OBSERVE"
    riskPctBase47 := 0.0
    riskReason47 := "Research not approved"
else if drawdownMode47 == "Recovery" or tailRisk47 == "Elevated" or conflict != "None" or corrRisk47 == "High"
    riskMode47 := "REDUCED"
    riskPctBase47 := reducedRiskPct47
    riskReason47 := drawdownMode47 == "Recovery" ? "Recovery mode" : tailRisk47 == "Elevated" ? "Elevated tail risk" : corrRisk47 == "High" ? "Correlation exposure" : "Context conflict"
else if actionState461 == "READY" and quality == "A+" and researchStatus47 == "Approved" and drawdownMode47 == "Normal" and tailRisk47 == "Normal"
    riskMode47 := "PREMIUM"
    riskPctBase47 := premiumRiskPct47
    riskReason47 := "A+ approved conditions"
else if actionState461 == "READY" and (quality == "A+" or quality == "A")
    riskMode47 := researchCaution47 or drawdownMode47 == "Caution" ? "REDUCED" : "NORMAL"
    riskPctBase47 := researchCaution47 or drawdownMode47 == "Caution" ? reducedRiskPct47 : normalRiskPct47
    riskReason47 := researchCaution47 ? "Forward-test caution" : drawdownMode47 == "Caution" ? "Drawdown caution" : "Checklist aligned"
else if quality == "B" or actionState461 == "WAIT RETEST"
    riskMode47 := "REDUCED"
    riskPctBase47 := reducedRiskPct47
    riskReason47 := "B setup / wait retest"
else
    riskMode47 := "OBSERVE"
    riskPctBase47 := 0.0
    riskReason47 := "Need confirmation"

float riskPctCapped47 = math.min(riskPctBase47, maxRiskCapPct47)
float suggestedRiskPct47 = useKellyReference47 ? math.min(riskPctCapped47, fractionalKellyPct47) : riskPctCapped47
color riskModeColor47 = riskMode47 == "PREMIUM" ? color.yellow : riskMode47 == "NORMAL" ? color.lime : riskMode47 == "REDUCED" ? color.orange : riskMode47 == "OBSERVE" ? color.aqua : riskMode47 == "NO TRADE" ? color.red : color.gray
string riskDisplay47 = riskMode47 + " | " + f_pct(suggestedRiskPct47)
string tailDisplay47 = tailRisk47 + " | Range " + str.tostring(rangeRatio47, "#.##") + "x"
string drawdownDisplay47 = drawdownMode47 + " | L" + str.tostring(dailyLossCount47) + "/S" + str.tostring(losingStreak47)

fvgState = priceInBullFVG ? "Inside Bull FVG " + f_prob(bestBullFvgQ) : priceInBearFVG ? "Inside Bear FVG " + f_prob(bestBearFvgQ) : "No Touch"
// liqState is computed by v4.6 staged liquidity backend above.
structureState = structureEvent
emaState = trendFilterBull ? "Bull Trend" : trendFilterBear ? "Bear Trend" : close > ema50 ? "Bull Lean" : close < ema50 ? "Bear Lean" : "Flat"
dwmState = not showDWM ? "Off" : close > prevDayHigh ? "Above PDH" : close < prevDayLow ? "Below PDL" : close > dayOpen ? "Above D Open" : close < dayOpen ? "Below D Open" : "At D Open"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Scanner
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

f_scan(sym, tf) =>
    request.security(sym, tf, [close, close[1], ta.ema(close, 50), ta.ema(close, 200), ta.rsi(close, 14)], barmerge.gaps_off, barmerge.lookahead_off)

[s1C, s1P, s1E50, s1E200, s1R] = f_scan(scan1, scannerTf)
[s2C, s2P, s2E50, s2E200, s2R] = f_scan(scan2, scannerTf)
[s3C, s3P, s3E50, s3E200, s3R] = f_scan(scan3, scannerTf)

t1 = f_trend_text(s1C, s1E50, s1E200)
t2 = f_trend_text(s2C, s2E50, s2E200)
t3 = f_trend_text(s3C, s3E50, s3E200)

chgPct(x, p) =>
    p == 0 ? 0.0 : (x - p) / p * 100.0

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Chart Tint
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

chartTint = not dashUseChartTint ? na : bias == "Bullish" ? color.new(color.lime, 92) : bias == "Bearish" ? color.new(color.red, 92) : color.new(color.gray, 95)
bgcolor(chartTint, title="Bias Background Tint")

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Dashboard Styling
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

panelBg = dashTheme == "Glass" ? color.new(color.rgb(18, 20, 26), dashOpacity) : dashTheme == "Carbon" ? color.new(color.rgb(12, 12, 12), dashOpacity) : dashTheme == "Midnight" ? color.new(color.rgb(9, 14, 25), dashOpacity) : color.new(color.rgb(10, 10, 10), dashOpacity)
rowOdd = dashTheme == "Glass" ? color.new(color.rgb(28, 30, 38), dashOpacity + 10) : dashTheme == "Carbon" ? color.new(color.rgb(18, 18, 18), dashOpacity + 10) : dashTheme == "Midnight" ? color.new(color.rgb(16, 22, 38), dashOpacity + 10) : color.new(color.rgb(16, 16, 16), dashOpacity + 10)
rowEven = dashTheme == "Glass" ? color.new(color.rgb(22, 24, 30), dashOpacity + 10) : dashTheme == "Carbon" ? color.new(color.rgb(14, 14, 14), dashOpacity + 10) : dashTheme == "Midnight" ? color.new(color.rgb(12, 18, 32), dashOpacity + 10) : color.new(color.rgb(12, 12, 12), dashOpacity + 10)
frameCol = color.new(color.white, dashBorderOpacity)

biasBg = bias == "Bullish" ? color.new(color.lime, 70) : bias == "Bearish" ? color.new(color.red, 70) : color.new(color.gray, 80)
qualityColor = quality == "A+" ? color.yellow : quality == "A" ? color.lime : quality == "B" ? color.aqua : quality == "C" ? color.orange : quality == "Trap Risk" ? color.red : quality == "No Trade" ? color.red : color.gray
signalBg = longConfluence ? color.new(color.lime, 65) : shortConfluence ? color.new(color.red, 65) : color.new(color.gray, 85)
txtSize = f_size(dashTextSize)

f_row_bg(r) =>
    r % 2 == 0 ? rowEven : rowOdd

var table dash = table.new(f_pos(dashPosition), 3, 40, bgcolor=panelBg, border_color=frameCol, border_width=1, frame_color=frameCol, frame_width=1)

if barstate.isfirst
    table.merge_cells(dash, 0, 0, 2, 0)

if showDashboard and barstate.islast
    table.cell(dash, 0, 0, "Rell SMI v4.7 Premium", text_color=color.yellow, bgcolor=color.new(color.black, 0), text_size=txtSize)

    int row = 1

    table.cell(dash, 0, row, "Bias", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, bias, text_color=color.white, bgcolor=biasBg, text_size=txtSize)
    table.cell(dash, 2, row, f_score_text(bullScore, bearScore, maxScore), text_color=color.white, bgcolor=biasBg, text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "Quality", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, quality, text_color=qualityColor, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, "Auto Thr " + str.tostring(effConfluenceAuto), text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    if showWorkflow461
        table.cell(dash, 0, row, "Action / Checklist", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, actionState461 + " | " + wfText461, text_color=actionColor461, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, actionNote461, text_color=actionColor461, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    if showGovernanceRows47
        table.cell(dash, 0, row, "Risk Mode", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, riskDisplay47, text_color=riskModeColor47, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, riskReason47, text_color=riskModeColor47, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "Tail / Drawdown", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, tailDisplay47, text_color=tailRiskColor47, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, drawdownDisplay47, text_color=drawdownColor47, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "Research / Correlation", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, researchStatus47, text_color=researchColor47, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, corrRisk47 + " | " + corrGuide47, text_color=corrRiskColor47, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    table.cell(dash, 0, row, "Playbook", text_color=color.white, bgcolor=signalBg, text_size=txtSize)
    table.cell(dash, 1, row, playbook, text_color=color.white, bgcolor=signalBg, text_size=txtSize)
    table.cell(dash, 2, row, target, text_color=color.white, bgcolor=signalBg, text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "Guide", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, guide, text_color=color.yellow, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, risk, text_color=color.orange, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "HTF / Structure", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, htfBiasText, text_color=htfDir == 1 ? color.lime : htfDir == -1 ? color.red : color.gray, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, structureState, text_color=str.contains(structureState, "Bull") ? color.lime : str.contains(structureState, "Bear") ? color.red : color.gray, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "MTF Backend", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, mtfContextText, text_color=mtfContextColor462, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, mtfDetailText, text_color=mtfDetailColor462, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "Liquidity / FVG", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, liqState, text_color=liqStateColor46, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, fvgState, text_color=priceInBullFVG ? color.lime : priceInBearFVG ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "Supply-Demand / Breaker", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, sdState46, text_color=sdColor46, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, breakerState46, text_color=breakerColor46, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    table.cell(dash, 0, row, "S/R / Volume Break", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, srState46, text_color=srColor46, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, volBreakState46, text_color=volBreakColor46, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    if showBTCycle46 and isBTC46
        table.cell(dash, 0, row, "BTC Cycle", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, btcCycleState46, text_color=btcCycleColor46, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "200W " + str.tostring(nz(btcDist200W) * 100.0, "#") + "% | DD " + str.tostring(nz(btcAthDD) * 100.0, "#") + "%", text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    table.cell(dash, 0, row, "Zone / Volume", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 1, row, pdState, text_color=inDiscount ? color.lime : inPremium ? color.red : color.gray, bgcolor=f_row_bg(row), text_size=txtSize)
    table.cell(dash, 2, row, "RVOL " + str.tostring(rvol, "#.##") + " " + rvolState, text_color=rvol >= effRvolMin ? color.lime : color.red, bgcolor=f_row_bg(row), text_size=txtSize)
    row += 1

    if dashMode == "Full"
        table.cell(dash, 0, row, "Brain / Quality", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, quality + " | " + qualityDetail43, text_color=qualityColor, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, brainState43, text_color=brainBull43 > brainBear43 ? color.lime : brainBear43 > brainBull43 ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        if showSentiment43
            table.cell(dash, 0, row, "Sentiment", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
            table.cell(dash, 1, row, sentBar43, text_color=color.white, bgcolor=sentColor43, text_size=txtSize, tooltip=sentText43)
            table.cell(dash, 2, row, sentText43, text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
            row += 1

        table.cell(dash, 0, row, "Momentum / VWAP", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, momState43, text_color=momBull43 ? color.lime : momBear43 ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, vwapState43, text_color=vwapBull43 ? color.lime : vwapBear43 ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "Trend / Delta", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, trendStrong43 ? "ADX " + str.tostring(adx43, "#") + " Strong" : "ADX " + str.tostring(adx43, "#") + " Weak", text_color=trendBull43 ? color.lime : trendBear43 ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, deltaState43, text_color=deltaBull43 ? color.lime : deltaBear43 ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    if dashMode == "Balanced" or dashMode == "Full"
        table.cell(dash, 0, row, "Session / DWM", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        sessTxt = isCrypto ? "24/7" : (effShowKillzones ? currentSession : "Off")
        table.cell(dash, 1, row, sessTxt, text_color=(killzoneActive or isCrypto) ? color.yellow : color.gray, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, dwmState, text_color=str.contains(dwmState, "Above") ? color.lime : str.contains(dwmState, "Below") ? color.red : color.gray, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "Volatility", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        volTxt = isForex ? str.tostring(atrPips, "#.0") + " pips" : f_pct((atr / close) * 100.0)
        table.cell(dash, 1, row, volRegime, text_color=volRegimeColor462, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "ATR " + volTxt + " | R " + str.tostring(volAtrRatio462, "#.##"), text_color=volRegimeColor462, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "OB / VP", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        obState = obRetestBull ? "Bull OB Retest" : obRetestBear ? "Bear OB Retest" : obBrokenBull ? "Bull OB Broken" : obBrokenBear ? "Bear OB Broken" : "None"
        table.cell(dash, 1, row, obState, text_color=obRetestBull ? color.lime : obRetestBear ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, useVP ? vpState : "VP Off", text_color=str.contains(vpState, "Above") ? color.lime : str.contains(vpState, "Below") ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    if dashMode == "Full"
        table.cell(dash, 0, row, "Market", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, profile, text_color=color.yellow, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, marketMode == "Auto" ? "Auto" : "Manual", text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "OR / Conflict", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, orState, text_color=orBreakUp ? color.lime : orBreakDown ? color.red : color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, conflict, text_color=conflict == "None" ? color.gray : color.orange, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, "Strong/Weak", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, strongWeakState, text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "Active FVG " + str.tostring(array.size(fvgZones)), text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    if scannerMode == "Compact"
        table.cell(dash, 0, row, "Scanner", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        compact = "1:" + t1 + " | 2:" + t2 + " | 3:" + t3
        table.cell(dash, 1, row, compact, text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, scannerTf, text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

    if scannerMode == "Full"
        table.cell(dash, 0, row, "Scanner TF", text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, scannerTf, text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "Trend / RSI", text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        s1p = chgPct(s1C, s1P)
        s2p = chgPct(s2C, s2P)
        s3p = chgPct(s3C, s3P)

        table.cell(dash, 0, row, scan1, text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, t1 + " " + f_pct(s1p), text_color=f_trend_color(t1), bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "RSI " + str.tostring(s1R, "#"), text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, scan2, text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, t2 + " " + f_pct(s2p), text_color=f_trend_color(t2), bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "RSI " + str.tostring(s2R, "#"), text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

        table.cell(dash, 0, row, scan3, text_color=color.white, bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 1, row, t3 + " " + f_pct(s3p), text_color=f_trend_color(t3), bgcolor=f_row_bg(row), text_size=txtSize)
        table.cell(dash, 2, row, "RSI " + str.tostring(s3R, "#"), text_color=color.silver, bgcolor=f_row_bg(row), text_size=txtSize)
        row += 1

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Alerts
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// Core FVG alerts
alertcondition(alertFVG and bullFVG, "Bullish FVG", "Rell SMI v4.7: Bullish FVG detected")
alertcondition(alertFVG and bearFVG, "Bearish FVG", "Rell SMI v4.7: Bearish FVG detected")
alertcondition(alertFVG and bullIFVG, "Bullish IFVG", "Rell SMI v4.7: Bullish IFVG active")
alertcondition(alertFVG and bearIFVG, "Bearish IFVG", "Rell SMI v4.7: Bearish IFVG active")
alertcondition(alertFVG and bullFvgMitigatedNow, "Bullish FVG Filled", "Rell SMI v4.7: Bullish FVG filled/deleted")
alertcondition(alertFVG and bearFvgMitigatedNow, "Bearish FVG Filled", "Rell SMI v4.7: Bearish FVG filled/deleted")

// Structure alerts
alertcondition(alertStructure and bullBOS, "Bullish BOS", "Rell SMI v4.7: Bullish BOS")
alertcondition(alertStructure and bearBOS, "Bearish BOS", "Rell SMI v4.7: Bearish BOS")
alertcondition(alertStructure and bullCHoCH, "Bullish CHoCH", "Rell SMI v4.7: Bullish CHoCH")
alertcondition(alertStructure and bearCHoCH, "Bearish CHoCH", "Rell SMI v4.7: Bearish CHoCH")

// Liquidity alerts
alertcondition(alertLiquidity and bullLiquiditySweep, "Sell-Side Liquidity Sweep", "Rell SMI v4.7: SSL swept and reclaimed")
alertcondition(alertLiquidity and bearLiquiditySweep, "Buy-Side Liquidity Sweep", "Rell SMI v4.7: BSL swept and rejected")
alertcondition(cryptoSweepBull, "Crypto SSL Sweep", "Rell SMI v4.7: Crypto sell-side liquidity sweep detected")
alertcondition(cryptoSweepBear, "Crypto BSL Sweep", "Rell SMI v4.7: Crypto buy-side liquidity sweep detected")

// Confluence alerts
alertcondition(alertConfluence and longConfluence and not longConfluence[1], "Bullish Confluence", "Rell SMI v4.7: Bullish confluence formed")
alertcondition(alertConfluence and shortConfluence and not shortConfluence[1], "Bearish Confluence", "Rell SMI v4.7: Bearish confluence formed")

// v4.6 Quality alerts
alertcondition(alertConfluence and quality == "A+" and quality[1] != "A+", "A+ Setup", "Rell SMI v4.7: A+ setup detected")
alertcondition(alertConfluence and quality == "A" and quality[1] != "A", "A Setup", "Rell SMI v4.7: A setup detected")
alertcondition(alertConfluence and quality == "B" and quality[1] != "B", "B Setup / Wait Retest", "Rell SMI v4.7: B setup detected, wait for retest")
alertcondition(alertConfluence and quality == "Trap Risk" and quality[1] != "Trap Risk", "Trap Risk", "Rell SMI v4.7: Trap Risk detected")
alertcondition(alertConfluence and quality == "No Trade" and quality[1] != "No Trade", "No Trade", "Rell SMI v4.7: No Trade state detected")

// v4.6 Sentiment alerts
sentBullDom45 = sentBullPct43 >= 65 and sentBullPct43[1] < 65
sentBearDom45 = sentBullPct43 <= 35 and sentBullPct43[1] > 35
sentNeutral45 = sentBullPct43 > 45 and sentBullPct43 < 55 and (sentBullPct43[1] <= 45 or sentBullPct43[1] >= 55)

alertcondition(alertConfluence and sentBullDom45, "Bull Sentiment Dominance", "Rell SMI v4.7: Bull sentiment dominance detected")
alertcondition(alertConfluence and sentBearDom45, "Bear Sentiment Dominance", "Rell SMI v4.7: Bear sentiment dominance detected")
alertcondition(alertConfluence and sentNeutral45, "Sentiment Neutralized", "Rell SMI v4.7: Sentiment returned to neutral")

// v4.6 Brain state alerts
momBullShift45 = momBull43 and not momBull43[1]
momBearShift45 = momBear43 and not momBear43[1]
vwapHighExt45 = vwapOverHigh43 and not vwapOverHigh43[1]
vwapLowExt45 = vwapOverLow43 and not vwapOverLow43[1]

alertcondition(alertConfluence and momBullShift45, "Bull Momentum Shift", "Rell SMI v4.7: Bull momentum shift detected")
alertcondition(alertConfluence and momBearShift45, "Bear Momentum Shift", "Rell SMI v4.7: Bear momentum shift detected")
alertcondition(alertConfluence and vwapHighExt45, "VWAP High Extension", "Rell SMI v4.7: Price overextended above VWAP")
alertcondition(alertConfluence and vwapLowExt45, "VWAP Low Extension", "Rell SMI v4.7: Price overextended below VWAP")

// v4.6 Premium / IT3 forward-test alerts
alertcondition(enableIT3Alerts46 and it3ApprovedLongSignal46 and not it3ApprovedLongSignal46[1], "IT3 Approved Long Forward Test", "Rell SMI v4.7: IT3 approved long forward-test signal")
alertcondition(usePremium46 and demandReaction46 and not demandReaction46[1], "Demand Reaction", "Rell SMI v4.7: Demand reaction detected")
alertcondition(usePremium46 and supplyReaction46 and not supplyReaction46[1], "Supply Reaction", "Rell SMI v4.7: Supply reaction detected")
alertcondition(usePremium46 and bullBreakerRetest46 and not bullBreakerRetest46[1], "Bull Breaker Retest", "Rell SMI v4.7: Bull breaker retest detected")
alertcondition(usePremium46 and bearBreakerRetest46 and not bearBreakerRetest46[1], "Bear Breaker Retest", "Rell SMI v4.7: Bear breaker retest detected")
alertcondition(usePremium46 and volBreakEvent46 and not volBreakEvent46[1], "Volume Confirmed Break", "Rell SMI v4.7: Volume-confirmed support/resistance break")

// v4.7 Governance alerts
alertcondition(useGovernance47 and riskMode47 == "PREMIUM" and riskMode47[1] != "PREMIUM", "Premium Risk Mode", "Rell SMI v4.7: Risk mode became PREMIUM")
alertcondition(useGovernance47 and riskMode47 == "NO TRADE" and riskMode47[1] != "NO TRADE", "No Trade Risk Mode", "Rell SMI v4.7: Risk governance moved to NO TRADE")
alertcondition(useGovernance47 and tailRisk47 == "Extreme" and tailRisk47[1] != "Extreme", "Tail Risk Extreme", "Rell SMI v4.7: Tail risk became EXTREME")

// Master alert for people who do not want TradingView screaming 40 times like a possessed smoke alarm
majorSignal45 = (quality == "A+" and quality[1] != "A+") or (quality == "Trap Risk" and quality[1] != "Trap Risk") or sentBullDom45 or sentBearDom45 or bullLiquiditySweep or bearLiquiditySweep or bullCHoCH or bearCHoCH or it3ApprovedLongSignal46
alertcondition(alertConfluence and majorSignal45, "Rell SMI Major Signal", "Rell SMI v4.7: Major market brain signal detected")
alertcondition(alertConfluence and actionState461 == "READY" and actionState461[1] != actionState461, "Rell SMI Action State", "Rell SMI v4.7: Action state became READY")
```