Indicator · Pine Script v6 · On the chart

Regime Detector (MA Zones Overlay) [WavesUnchained]

by casoon

This overlay classifies the chart into bull, bear, or neutral regimes from moving-average zone position and an adaptive engine driven by the efficiency ratio and ATR entry and exit bands.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code2,232
Settings40inputs
LicenceMIT
Last updateyesterday27 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

2,232 lines of Pine Script v6 from indicators/trend_strength/regime_detector/regime_detector.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

regime_detector.pine
//@version=6
// ============================================================================
// Regime Detector (MA Zones Overlay) [WavesUnchained]
// Version: 1.1.7
// Author: WavesUnchained
// Build: 2026-10-12
// ============================================================================
// MA-zone based trend regime overlay with adaptive sensitivity and HTF gate.
// v1.1 adds a second dimension (volatility), regime maturity / transition
// warnings, a plain-language playbook, and an optional RS-vs-benchmark modifier.
//
// Features:
//   ✓ Adaptive core engine — sensitivity adjusts to recent volatility
//   ✓ Regime detection — Bull, Bear, Neutral via MA zone positioning
//   ✓ Volatility dimension — Quiet / Normal / Volatile → combined regime
//     (e.g. "Quiet Bull", "Volatile Range", "Compression")
//   ✓ Regime maturity + transition warning — Fresh / Mature / Aging-weakening
//   ✓ Plain-language playbook — what the current regime means for action
//   ✓ Optional RS vs. benchmark (SPY/QQQ) modifier for equities
//   ✓ Modern visuals — theme selector, gradient bands, candle tint, soft heatmap
//   ✓ Multi-timeframe gate — higher-timeframe regime filters chart signals
//   ✓ Advanced signal filters for quality and frequency management
//   ✓ Zone visualization (up to 500 lines, 100 boxes)
//
// ============================================================================
indicator("Regime Detector (MA Zones Overlay) [WavesUnchained]", "RDP", overlay=true, max_labels_count=200, max_lines_count=500, max_boxes_count=100)

// ============================================================================
// Regime Detector Pro
// ============================================================================
// MODULES:
// - inputs.pine
// - core.pine
// - visualization.pine
// ============================================================================

// NOTE: Modules are combined by the build system.

// ============================================================================
// INLINE MODULES (Combined at build time)
// ============================================================================

// ============================================================================
// MODULE: engine_local
// ============================================================================

// ============================================================================
// LOCAL ENGINE (Lite) for Regime Detector
// Minimal engine: Adaptive -> Regime -> MTF Gate -> Filters -> Signals -> Perf
// ============================================================================

// --- Helpers
f_clamp01(x) => math.max(0.0, math.min(1.0, x))

// --- JMA (CPM/Everget-style)
calculateJMA(src, length, phase, power) =>
    ph = math.max(-100.0, math.min(100.0, phase))
    pr = (ph + 100.0) / 200.0
    len = math.max(1.0, length)
    beta = 0.45 * (len - 1.0) / (0.45 * (len - 1.0) + 2.0)
    alpha = math.pow(beta, power)
    var float e0 = na
    var float e1 = na
    var float e2 = na
    var float jma = na
    e0 := (1.0 - alpha) * src + alpha * nz(e0[1], src)
    e1 := (src - e0) * (1.0 - beta) + beta * nz(e1[1], 0.0)
    e2 := (e0 + pr * e1 - nz(jma[1], src)) * math.pow(1.0 - alpha, 2.0) + math.pow(alpha, 2.0) * nz(e2[1], 0.0)
    jma := e2 + nz(jma[1], src)
    jma

// --- Adaptive Engine

type AdaptiveConfig
    bool enableAdaptiveEngine
    int erLength
    float alphaMin
    float alphaMax
    float gamma
    int atrLength
    float source

type AdaptiveState
    float line
    float atr
    float efficiencyRatio
    float currentAlpha
    float normalizedSlope

newAdaptiveConfig() =>
    AdaptiveConfig.new(true, 20, 0.03, 0.30, 1.25, 14, close)

newAdaptiveState() =>
    AdaptiveState.new(na, na, 0.0, 0.0, 0.0)

processAdaptiveEngine(AdaptiveState state, AdaptiveConfig config, int atrLen) =>
    src = config.source
    erLen = math.max(2, config.erLength)
    direction = math.abs(src - src[erLen])
    volatility = ta.sma(math.abs(src - src[1]), erLen) * erLen
    er = volatility == 0 ? 0.0 : direction / volatility

    alpha = config.alphaMin + (config.alphaMax - config.alphaMin) * math.pow(er, config.gamma)
    var float lastLine = na
    prevLine = na(lastLine) ? src : lastLine
    line = prevLine + alpha * (src - prevLine)

    atr = ta.atr(atrLen)
    slope = atr > 0 ? (line - prevLine) / atr : 0.0

    state.line := line
    state.atr := atr
    state.efficiencyRatio := er
    state.currentAlpha := alpha
    state.normalizedSlope := slope
    lastLine := line
    state

// --- Regime Detection

type RegimeConfig
    bool enableAdvancedRegime
    string regimeMode
    float erThreshold
    float erThresholdExit
    float sensitivity
    float minConfidence
    float minConfidenceExit
    bool enableMLEnhancement
    int mlLookback
    float mlConfidenceThreshold

type RegimeState
    int currentRegime
    float confidence
    float strength

newRegimeConfig() =>
    RegimeConfig.new(true, "Trend + Momentum", 0.2, 0.15, 0.6, 0.65, 0.55, false, 50, 0.7)

newRegimeState() =>
    RegimeState.new(0, 0.0, 0.0)

processRegimeDetection(RegimeState state, RegimeConfig config, AdaptiveState adaptiveState) =>
    slope = adaptiveState.normalizedSlope
    strength = f_clamp01(math.abs(slope))

    erEnterOk = adaptiveState.efficiencyRatio >= config.erThreshold
    erExitOk = adaptiveState.efficiencyRatio >= config.erThresholdExit

    baseConfEnter = erEnterOk ? f_clamp01((adaptiveState.efficiencyRatio - config.erThreshold) / math.max(1e-6, 1.0 - config.erThreshold)) : 0.0
    baseConfExit = erExitOk ? f_clamp01((adaptiveState.efficiencyRatio - config.erThresholdExit) / math.max(1e-6, 1.0 - config.erThresholdExit)) : 0.0
    // Confidence is the normalized ER itself. Scaling it by sensitivity capped it at the
    // sensitivity value (0.60 at default) - below Min Confidence, so no regime could ever form.
    // Sensitivity acts on the momentum gate below.
    confEnter = baseConfEnter
    confExit = baseConfExit

    trendEnterOk = erEnterOk
    trendExitOk = erExitOk
    momEnterOk = strength >= config.sensitivity
    momExitOk = strength >= (config.sensitivity * 0.8)

    modeEnterOk = not config.enableAdvancedRegime ? true : config.regimeMode == "Trend Only" ? trendEnterOk : config.regimeMode == "Momentum Only" ? momEnterOk : config.regimeMode == "Trend + Momentum" ? (trendEnterOk and momEnterOk) : (trendEnterOk and momEnterOk)

    modeExitOk = not config.enableAdvancedRegime ? true : config.regimeMode == "Trend Only" ? trendExitOk : config.regimeMode == "Momentum Only" ? momExitOk : config.regimeMode == "Trend + Momentum" ? (trendExitOk and momExitOk) : (trendExitOk and momExitOk)

    int prevRegime = state.currentRegime
    int regime = prevRegime

    if regime == 0
        if erEnterOk and modeEnterOk and confEnter >= config.minConfidence
            regime := slope > 0 ? 1 : slope < 0 ? -1 : 0
    else if regime == 1
        if erExitOk and modeExitOk and confExit >= config.minConfidenceExit and slope > 0
            regime := 1
        else if erEnterOk and modeEnterOk and confEnter >= config.minConfidence and slope < 0
            regime := -1
        else
            regime := 0
    else
        if erExitOk and modeExitOk and confExit >= config.minConfidenceExit and slope < 0
            regime := -1
        else if erEnterOk and modeEnterOk and confEnter >= config.minConfidence and slope > 0
            regime := 1
        else
            regime := 0

    conf = regime == 0 ? confEnter : (regime == prevRegime ? confExit : confEnter)

    state.currentRegime := regime
    state.confidence := conf
    state.strength := strength
    state

// --- MTF Gate

type MultiTimeframeConfig
    bool enableHTFGate
    bool enableDivergenceDetection
    string[] timeframes
    map<string, float> weights
    float minAgreement

type MultiTimeframeState
    bool enabled
    float agreementRatio
    int overallBias
    float divergenceStrength
    float minAgreement

newMultiTimeframeConfig() =>
    MultiTimeframeConfig.new(true, true, array.new_string(), map.new<string, float>(), 0.6)

newMultiTimeframeState() =>
    MultiTimeframeState.new(false, 1.0, 0, 0.0, 0.6)

processMultiTimeframeAnalysis(MultiTimeframeState state, MultiTimeframeConfig config, int baseRegime) =>
    state.enabled := config.enableHTFGate
    state.minAgreement := config.minAgreement

    if not config.enableHTFGate
        state.agreementRatio := 1.0
        state.overallBias := baseRegime
        state.divergenceStrength := 0.0
        state
    else
        totalW = 0.0
        agreeW = 0.0
        biasSum = 0.0

        for i = 0 to array.size(config.timeframes) - 1
            tf = array.get(config.timeframes, i)
            w = map.contains(config.weights, tf) ? map.get(config.weights, tf) : 1.0
            dir = request.security(syminfo.tickerid, tf, close > ta.ema(close, 50) ? 1 : close < ta.ema(close, 50) ? -1 : 0, barmerge.gaps_off, barmerge.lookahead_off)

            totalW += w
            biasSum += dir * w
            if baseRegime != 0 and dir == baseRegime
                agreeW += w

        agreement = totalW > 0 ? (baseRegime == 0 ? math.abs(biasSum) / totalW : agreeW / totalW) : 0.0
        overallBias = biasSum > 0 ? 1 : biasSum < 0 ? -1 : 0
        divergence = config.enableDivergenceDetection ? (1.0 - agreement) : 0.0

        state.agreementRatio := agreement
        state.overallBias := overallBias
        state.divergenceStrength := divergence
        state

// --- Filters

type FilterConfig
    bool enableAdvancedFilters
    float minATRPercent
    string volatilityMode
    bool enableVolumeFilter
    int volumeLength
    float volumeQuantile
    bool enableMarketCondition
    int marketConditionPeriod
    bool enableConfluence
    float confluenceThreshold

type FilterState
    float signalQuality
    int marketCondition

newFilterConfig() =>
    FilterConfig.new(true, 0.0007, "Adaptive", false, 50, 0.3, true, 100, true, 0.6)

newFilterState() =>
    FilterState.new(0.0, 0)

processFilterSystem(FilterState state, FilterConfig config, AdaptiveState adaptiveState, RegimeState regimeState) =>
    if not config.enableAdvancedFilters
        state.signalQuality := 1.0
        state.marketCondition := 0
        state
    else
        atrPct = close > 0 ? (adaptiveState.atr / close) : 0.0
        atrPctAvg = ta.sma(atrPct, config.marketConditionPeriod)
        atrPctThresh = config.volatilityMode == "Static" ? config.minATRPercent : config.volatilityMode == "Adaptive" ? math.max(config.minATRPercent, atrPctAvg * 0.7) : math.max(config.minATRPercent, atrPctAvg * (regimeState.currentRegime != 0 ? 0.6 : 0.85))
        volOk = atrPct >= atrPctThresh

        // Real trade volume only (feed delivers volume, volumetype not "tick");
        // volume carries no confirmation signal. volGate degrades to pass-through rather than a permanent
        // block/pass built on tick-count noise.
        volumeIsReal = (ta.cum(nz(volume)) > 0 and syminfo.volumetype != "tick") and not na(volume)
        volAvg = ta.sma(volume, config.volumeLength)
        volGate = not config.enableVolumeFilter or not volumeIsReal or (volAvg > 0 and volume >= volAvg * config.volumeQuantile)

        trendScore = ta.sma(math.abs(adaptiveState.normalizedSlope), config.marketConditionPeriod)
        erAvg = ta.sma(adaptiveState.efficiencyRatio, config.marketConditionPeriod)
        marketCond = 0
        if config.enableMarketCondition
            marketCond := trendScore > 0.35 ? 1 : erAvg < 0.15 ? -1 : 0

        quality = 0.0
        if config.enableVolumeFilter and not volumeIsReal
            // volGate's 0.3 weight has no real signal without trade volume —
            // dropped from the sum and redistributed proportionally across
            // volOk (0.5) and trendScore (0.2) so the composite ceiling stays
            // 1.0 instead of silently sinking on tick-volume symbols.
            // 0.5/0.7 ≈ 0.7143, 0.2/0.7 ≈ 0.2857
            quality += volOk ? (0.5 / 0.7) : 0.0
            quality += trendScore > 0.2 ? (0.2 / 0.7) : 0.0
        else
            quality += volOk ? 0.5 : 0.0
            quality += volGate ? 0.3 : 0.0
            quality += trendScore > 0.2 ? 0.2 : 0.0

        if config.enableConfluence and quality < config.confluenceThreshold
            quality := quality * 0.5

        state.signalQuality := f_clamp01(quality)
        state.marketCondition := marketCond
        state

// --- Signal Engine

type SignalConfig
    int confirmBars
    bool enableSmartConfirm
    bool enableAdvancedExits
    string exitMode
    bool enableTrailingStop
    float trailingSensitivity
    bool enableRiskManagement
    int maxConsecutiveLosses
    int cooldownPeriod
    float exitMultiplier
    float tierAConf
    float tierAAgree
    float tierAQuality
    float tierBConf
    float tierBAgree
    float tierBQuality

type SignalState
    bool longEntry
    bool shortEntry
    bool exitLong
    bool exitShort
    bool lowQualityLong
    bool lowQualityShort
    int position
    int lastSignalBar

newSignalConfig() =>
    SignalConfig.new(2, true, true, "Hybrid", true, 0.7, true, 3, 5, 1.2, 0.8, 0.65, 0.7, 0.7, 0.55, 0.6)

newSignalState() =>
    SignalState.new(false, false, false, false, false, false, 0, na)

processSignalGeneration(SignalState state, SignalConfig config, AdaptiveState adaptiveState, RegimeState regimeState, MultiTimeframeState mtfState, FilterState filterState) =>
    bull = regimeState.currentRegime == 1
    bear = regimeState.currentRegime == -1

    barsOkBull = config.confirmBars <= 1 ? bull : ta.sma(bull ? 1 : 0, config.confirmBars) == 1.0
    barsOkBear = config.confirmBars <= 1 ? bear : ta.sma(bear ? 1 : 0, config.confirmBars) == 1.0

    canFire = na(state.lastSignalBar) or (bar_index - state.lastSignalBar >= config.cooldownPeriod)
    htfGateOk = not mtfState.enabled or (mtfState.agreementRatio >= mtfState.minAgreement)
    filterOk = filterState.signalQuality >= 0.5 and htfGateOk
    htfAgree = mtfState.enabled ? mtfState.agreementRatio : 1.0
    qualA = (regimeState.confidence >= config.tierAConf) and (htfAgree >= config.tierAAgree) and (filterState.signalQuality >= config.tierAQuality)
    qualB = not qualA and (regimeState.confidence >= config.tierBConf) and (htfAgree >= config.tierBAgree) and (filterState.signalQuality >= config.tierBQuality)
    allowEntry = qualA or qualB

    longCandidate = canFire and filterOk and barsOkBull and state.position != 1
    shortCandidate = canFire and filterOk and barsOkBear and state.position != -1

    longEntry = longCandidate and allowEntry
    shortEntry = shortCandidate and allowEntry
    lowQualityLong = longCandidate and not allowEntry
    lowQualityShort = shortCandidate and not allowEntry

    upperExit = adaptiveState.line + config.exitMultiplier * adaptiveState.atr
    lowerExit = adaptiveState.line - config.exitMultiplier * adaptiveState.atr

    exitByBandLong = state.position == 1 and close < lowerExit
    exitByBandShort = state.position == -1 and close > upperExit

    exitByRegimeLong = state.position == 1 and not bull
    exitByRegimeShort = state.position == -1 and not bear

    exitLong = config.enableAdvancedExits ? (config.exitMode == "Conservative" ? exitByBandLong : config.exitMode == "Aggressive" ? exitByRegimeLong : (exitByBandLong or exitByRegimeLong)) : exitByRegimeLong

    exitShort = config.enableAdvancedExits ? (config.exitMode == "Conservative" ? exitByBandShort : config.exitMode == "Aggressive" ? exitByRegimeShort : (exitByBandShort or exitByRegimeShort)) : exitByRegimeShort

    if longEntry
        state.position := 1
        state.lastSignalBar := bar_index
    else if shortEntry
        state.position := -1
        state.lastSignalBar := bar_index
    else if exitLong or exitShort
        state.position := 0

    state.longEntry := longEntry
    state.shortEntry := shortEntry
    state.exitLong := exitLong
    state.exitShort := exitShort
    state.lowQualityLong := lowQualityLong
    state.lowQualityShort := lowQualityShort
    state

// --- Performance

type PerformanceConfig
    bool enableAutoTuning
    int tuningPeriod
    float adaptationRate
    bool enablePerformanceTracking
    int trackingPeriod
    bool enableMemoryOptimization
    int maxHistoryBars

type PerformanceMetrics
    int sampleCount
    float winRate
    float profitFactor
    int wins
    int losses
    float grossProfit
    float grossLoss
    float lastEntryPrice
    int lastPosition

newPerformanceConfig() =>
    PerformanceConfig.new(true, 200, 0.02, true, 500, true, 2000)

newPerformanceMetrics() =>
    PerformanceMetrics.new(0, 0.5, 1.0, 0, 0, 0.0, 0.0, na, 0)

updatePerformanceMetrics(PerformanceMetrics metrics, PerformanceConfig config, SignalState signalState) =>
    if not config.enablePerformanceTracking
        metrics
    else
        if signalState.position != metrics.lastPosition
            if metrics.lastPosition == 0 and signalState.position != 0
                metrics.lastEntryPrice := close
            else if metrics.lastPosition != 0 and signalState.position == 0 and not na(metrics.lastEntryPrice)
                pnl = (close - metrics.lastEntryPrice) * (metrics.lastPosition == 1 ? 1 : -1)
                if pnl > 0
                    metrics.wins += 1
                    metrics.grossProfit += pnl
                else
                    metrics.losses += 1
                    metrics.grossLoss += math.abs(pnl)

                metrics.sampleCount := metrics.wins + metrics.losses
                // 1.0 * forces float division — int/int truncates in Pine
                metrics.winRate := metrics.sampleCount > 0 ? (1.0 * metrics.wins / metrics.sampleCount) : 0.0
                metrics.profitFactor := metrics.grossLoss > 0 ? (metrics.grossProfit / metrics.grossLoss) : (metrics.grossProfit > 0 ? 99.0 : 0.0)

        metrics.lastPosition := signalState.position
        metrics


// ============================================================================
// MODULE: inputs
// ============================================================================

// === INPUT GROUPS ===
var string CORE_GROUP = "Core Adaptive Engine"
var string REGIME_GROUP = "Regime Detection"
var string MTF_GROUP = "Multi-Timeframe Gate"
var string FILTER_GROUP = "Advanced Filters"
var string SIGNAL_GROUP = "Signal Management"
var string VISUAL_GROUP = "Visualization"
var string PERFORMANCE_GROUP = "Performance & Auto-Tune"
var string MAZONE_GROUP = "MA Overlay Zones"
var string WIP_GROUP = "Experimental (WIP)"
var string LOG_GROUP = "Logging"

// === CORE ADAPTIVE ENGINE SETTINGS ===
profilePreset = input.string("Smart", "Profile Preset", ["Smart", "Futures", "Spot", "ETF", "Crypto", "Custom"], group=CORE_GROUP, tooltip="Auto-optimized parameters for different market types")

dataSource = input.source(close, "Data Source", group=CORE_GROUP)
enableAdaptiveEngine = input.bool(true, "Enable Adaptive Engine", group=CORE_GROUP, tooltip="Use the local adaptive engine for calculations")

// KAMA-Hybrid Parameters
erLength = input.int(20, "Efficiency Ratio Length", minval=5, maxval=100, group=CORE_GROUP, tooltip="Period for trend efficiency calculation")
alphaMin = input.float(0.03, "Alpha Minimum", minval=0.001, maxval=0.5, step=0.001, group=CORE_GROUP, tooltip="Minimum smoothing factor (slow reaction)")
alphaMax = input.float(0.30, "Alpha Maximum", minval=0.01, maxval=1.0, step=0.01, group=CORE_GROUP, tooltip="Maximum smoothing factor (fast reaction)")
gamma = input.float(1.25, "Gamma (Reaction Hardness)", minval=0.5, maxval=3.0, step=0.05, group=CORE_GROUP, tooltip="Controls non-linearity of alpha adaptation")

// ATR Bands Configuration
atrLength = input.int(14, "ATR Length", minval=2, maxval=50, group=CORE_GROUP)
entryMultiplier = input.float(1.6, "Entry Band Multiplier", minval=0.5, maxval=5.0, step=0.1, group=CORE_GROUP, tooltip="ATR factor for entry threshold")
exitMultiplier = input.float(1.2, "Exit Band Multiplier", minval=0.3, maxval=5.0, step=0.1, group=CORE_GROUP, tooltip="ATR factor for exit threshold (< Entry = Hysteresis)")

// === REGIME DETECTION SETTINGS ===
enableAdvancedRegime = input.bool(true, "Enable Advanced Regime Detection", group=REGIME_GROUP, tooltip="Use enhanced regime classification")
regimeMode = input.string("Trend + Momentum", "Regime Detection Mode", ["Trend Only", "Momentum Only", "Trend + Momentum", "Full Analysis"], group=REGIME_GROUP)

erThreshold = input.float(0.2, "ER Threshold (Trend)", minval=0.0, maxval=1.0, step=0.01, group=REGIME_GROUP, tooltip="Minimum ER value for trend regime")
erThresholdExit = input.float(0.15, "ER Exit Threshold", minval=0.0, maxval=1.0, step=0.01, group=REGIME_GROUP, tooltip="Lower ER threshold to exit regime (hysteresis)")
regimeSensitivity = input.float(0.6, "Regime Sensitivity", minval=0.1, maxval=1.0, step=0.05, group=REGIME_GROUP, tooltip="Minimum normalized slope strength the momentum gate requires (Momentum Only / Trend + Momentum / Full Analysis modes; exit uses 80% of it). Higher = stricter, fewer regimes.")
regimeConfidenceMin = input.float(0.65, "Min Regime Confidence", minval=0.0, maxval=1.0, step=0.05, group=REGIME_GROUP, tooltip="Minimum confidence for regime classification")
regimeConfidenceExit = input.float(0.55, "Min Regime Confidence (Exit)", minval=0.0, maxval=1.0, step=0.05, group=REGIME_GROUP, tooltip="Lower confidence threshold to exit regime (hysteresis)")

// === REGIME CONTEXT (v1.1) — second dimension, maturity, playbook, RS ===
var string CONTEXT_GROUP = "Regime Context (v1.1)"
enableVolaDim  = input.bool(true,  "Volatility Dimension", group=CONTEXT_GROUP, tooltip="Adds a second axis to the regime: Quiet / Normal / Volatile, from ATR percentile + Bollinger-width percentile. Turns 'Bull' into 'Quiet Bull' vs. 'Volatile Bull' etc.")
volaLookback   = input.int(100, "Volatility Rank Lookback", minval=20, maxval=500, group=CONTEXT_GROUP, tooltip="Window for the ATR / BB-width percentile rank that defines the volatility state.")
volaQuietMax   = input.int(33,  "Quiet below (percentile)", minval=5, maxval=49, group=CONTEXT_GROUP)
volaVolMin     = input.int(66,  "Volatile above (percentile)", minval=51, maxval=95, group=CONTEXT_GROUP)
showPlaybook   = input.bool(true,  "Playbook Line", group=CONTEXT_GROUP, tooltip="Plain-language action hint for the current combined regime in the info panel.")
showContextLabels = input.bool(true, "Context Labels on Chart", group=CONTEXT_GROUP, tooltip="Marks regime context events directly on the chart: COMPRESS (volatility contracted → breakout setup), EXPAND (volatility spiked → climax/whipsaw risk), AGING (trend regime weakening).")
enableRSContext = input.bool(false, "RS vs. Benchmark (equities)", group=CONTEXT_GROUP, tooltip="Adds relative strength vs. a benchmark (SPY/QQQ) as a regime modifier — for stocks like TSLA/MSFT where relative strength matters more than absolute trend.")
rsBenchSym     = input.symbol("AMEX:SPY", "  Benchmark", group=CONTEXT_GROUP)
rsMaLen        = input.int(50, "  RS MA Length", minval=5, maxval=200, group=CONTEXT_GROUP)

// Machine Learning Enhancement
enableMLRegime = input.bool(true, "ML-Enhanced Classification (WIP)", group=WIP_GROUP, tooltip="WIP: not yet used in engine")
mlLookback = input.int(50, "ML Pattern Lookback (WIP)", minval=20, maxval=200, group=WIP_GROUP, tooltip="WIP: not yet used in engine")
mlConfidence = input.float(0.7, "ML Confidence Threshold (WIP)", minval=0.5, maxval=0.95, step=0.05, group=WIP_GROUP, tooltip="WIP: not yet used in engine")

// === MULTI-TIMEFRAME GATE SETTINGS ===
enableMTFGate = input.bool(true, "Enable MTF Gate", group=MTF_GROUP, tooltip="Higher timeframe must confirm trend (auto-stacked by chart TF)")

// HTF Configuration
htfWeights = input.string("Balanced", "HTF Weight Distribution", ["Primary Focus", "Balanced", "Long-term Focus"], group=MTF_GROUP)
htfMinAgreement = input.float(0.6, "Min HTF Agreement", minval=0.0, maxval=1.0, step=0.05, group=MTF_GROUP, tooltip="Minimum weighted agreement ratio")
enableHTFDivergence = input.bool(true, "HTF Divergence Detection", group=MTF_GROUP, tooltip="Detect divergence between timeframes")

// === ADVANCED FILTERS ===
enableAdvancedFilters = input.bool(true, "Enable Advanced Filter System", group=FILTER_GROUP)

// Volatility Filter
minATRPercent = input.float(0.0007, "Min ATR/Price", minval=0.0, step=0.0001, group=FILTER_GROUP, tooltip="Minimum volatility filter (0.07% = 0.0007)")
volatilityMode = input.string("Adaptive", "Volatility Filter Mode", ["Static", "Adaptive", "Dynamic"], group=FILTER_GROUP)

// Volume Filter
useVolumeFilter = input.bool(false, "Volume Filter", group=FILTER_GROUP)
volumeLength = input.int(50, "Volume MA Length", minval=5, maxval=200, group=FILTER_GROUP)
volumeQuantile = input.float(0.3, "Volume Quantile", minval=0.0, maxval=1.0, step=0.05, group=FILTER_GROUP, tooltip="Minimum volume as factor of MA")

// Market Condition Filter
enableMarketCondition = input.bool(true, "Market Condition Filter", group=FILTER_GROUP)
marketConditionPeriod = input.int(100, "Market Analysis Period", minval=50, maxval=500, group=FILTER_GROUP)

// Confluence Filter
enableConfluenceFilter = input.bool(true, "Confluence Filter", group=FILTER_GROUP, tooltip="Use confluence scoring for signal validation")
confluenceThreshold = input.float(0.6, "Confluence Threshold", minval=0.0, maxval=1.0, step=0.05, group=FILTER_GROUP)

// === SIGNAL MANAGEMENT ===
// Trade-engine isolation: default OFF keeps RDP a pure regime classifier/visual.
showTradeEngine = input.bool(false, "Show Trade Engine (entries/exits/TP/SL + performance)", group=SIGNAL_GROUP, tooltip="Off (default): RDP is a pure regime classifier — only regime bands, heatmap, context labels and warnings render. On: enables RDP entry/exit markers, tier/quality labels, MA-zone entry/SL engine, trade alerts and the performance statistics rows.")
// Whipsaw Protection
confirmBars = input.int(2, "Signal Confirmation Bars", minval=1, maxval=10, group=SIGNAL_GROUP, tooltip="Bars required for signal confirmation")
enableSmartConfirm = input.bool(true, "Smart Confirmation (WIP)", group=WIP_GROUP, tooltip="WIP: not yet used in engine")

// Position Management
enableAdvancedExits = input.bool(true, "Advanced Exit Logic", group=SIGNAL_GROUP)
exitMode = input.string("Hybrid", "Exit Strategy", ["Conservative", "Balanced", "Hybrid", "Aggressive"], group=SIGNAL_GROUP)
enableTrailingStop = input.bool(true, "Trailing Stop Logic (WIP)", group=WIP_GROUP, tooltip="WIP: not yet used in engine")
trailingSensitivity = input.float(0.7, "Trailing Sensitivity (WIP)", minval=0.1, maxval=1.0, step=0.05, group=WIP_GROUP, tooltip="WIP: not yet used in engine")

// Risk Management
enableRiskManagement = input.bool(true, "Risk Management (WIP)", group=WIP_GROUP, tooltip="WIP: not yet used in engine")
maxConsecutiveLosses = input.int(3, "Max Consecutive Losses (WIP)", minval=1, maxval=10, group=WIP_GROUP, tooltip="WIP: not yet used in engine")
cooldownPeriod = input.int(5, "Cooldown Period (bars)", minval=0, maxval=50, group=SIGNAL_GROUP)

// Quality Tiers
tierAConf = input.float(0.80, "Tier A: Min Confidence", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)
tierAAgree = input.float(0.65, "Tier A: Min HTF Agreement", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)
tierAQuality = input.float(0.70, "Tier A: Min Signal Quality", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)

tierBConf = input.float(0.70, "Tier B: Min Confidence", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)
tierBAgree = input.float(0.55, "Tier B: Min HTF Agreement", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)
tierBQuality = input.float(0.60, "Tier B: Min Signal Quality", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)

// Warnings
showWarnings = input.bool(true, "Show Warnings (events only)", group=SIGNAL_GROUP)
warningDivergenceThreshold = input.float(0.65, "Warning: MTF Divergence", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)
warningRegimeStrengthMin = input.float(0.30, "Warning: Low Regime Strength", minval=0.0, maxval=1.0, step=0.05, group=SIGNAL_GROUP)
warningCooldownBars = input.int(15, "Warning Cooldown (bars)", minval=0, maxval=200, group=SIGNAL_GROUP)

// === VISUALIZATION ===
visualTheme = input.string("Modern", "Visual Theme", options=["Modern", "Soft", "Classic"], group=VISUAL_GROUP, tooltip="Modern = teal/coral with soft gradients. Soft = muted pastels. Classic = the original saturated green/red.")
regimeCandleTint = input.bool(true, "Regime Candle Tint", group=VISUAL_GROUP, tooltip="Tints candles by regime direction and volatility intensity for an at-a-glance read. Suppressed automatically when the SSA candle coloring is active.")
smoothBands = input.bool(true, "Gradient Bands", group=VISUAL_GROUP, tooltip="Layered, soft gradient fill for the ATR bands instead of a single flat fill.")
showBands = input.bool(true, "Show ATR Bands", group=VISUAL_GROUP)
showRegimeHeatmap = input.bool(true, "Regime Heatmap", group=VISUAL_GROUP)
showInfoPanel = input.bool(false, "Info Panel", group=VISUAL_GROUP)
infoPanelPosition = input.string("Bottom Left", "Panel Position", ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group=VISUAL_GROUP, tooltip="Defaults to Bottom Left so the regime context panel sits clear of the SSA status table (Top Right) and the broker's symbol/price overlay (Top Left).")
compactMode = input.bool(false, "Compact Mode", group=VISUAL_GROUP)

// Advanced Visualization
showHTFIndicators = input.bool(true, "HTF Indicators", group=VISUAL_GROUP)
showConfidenceMeters = input.bool(true, "Confidence Meters", group=VISUAL_GROUP)
enableDynamicColors = input.bool(true, "Dynamic Colors", group=VISUAL_GROUP)
showSignalStrength = input.bool(true, "Signal Strength", group=VISUAL_GROUP)
dotMode = input.string("On Adaptive Line", "Strength Dot Position", options=["On Adaptive Line","Above/Below Bar","Off"], group=VISUAL_GROUP)
dotOffsetAtr = input.float(0.15, "Dot Offset (ATR)", step=0.05, group=VISUAL_GROUP)
dotColorMode = input.string("By Tier", "Strength Dot Color", options=["By Tier","By Confidence"], group=VISUAL_GROUP)

// === MA OVERLAY ZONES (Regime-gated) ===
enableMAOverlayZones = input.bool(false, "Enable MA Overlay Zones", group=MAZONE_GROUP)
useRDPRegime = input.bool(true, "Use RDP Regime for Zones", group=MAZONE_GROUP)
maType = input.string("DEMA", "MA Type", options=["SMA","EMA","DEMA","TEMA","JMA"], group=MAZONE_GROUP)
len1 = input.int(21, "MA 1 Length", minval=1, group=MAZONE_GROUP)
len2 = input.int(55, "MA 2 Length", minval=1, group=MAZONE_GROUP)
len3 = input.int(89, "MA 3 Length", minval=1, group=MAZONE_GROUP)
atrLenZones = input.int(20, "ATR Length", minval=1, group=MAZONE_GROUP)
multZones = input.float(2.0, "ATR Stop Multiplier", minval=0.1, step=0.1, group=MAZONE_GROUP)
strengthModeZones = input.string("By MA Type", "Strength Normalization", options=["Off","By MA Type","Auto"], group=MAZONE_GROUP)
baseMinStrengthZones = input.float(0.6, "Base Min Strength (spread / ATR)", step=0.05, group=MAZONE_GROUP)
autoLookbackZones = input.int(200, "Auto Normalize Lookback", minval=50, group=MAZONE_GROUP)
minConfidenceForZones = input.float(0.6, "Min Regime Confidence", minval=0.0, maxval=1.0, step=0.05, group=MAZONE_GROUP)
minHTFAgreementForZones = input.float(0.5, "Min HTF Agreement", minval=0.0, maxval=1.0, step=0.05, group=MAZONE_GROUP)
showMAZones = input.bool(true, "Show MA Zones", group=MAZONE_GROUP)
showMAZoneLabels = input.bool(true, "Show MA Zone Labels", group=MAZONE_GROUP)
zoneRenderMode = input.string("Lines + Fill", "Zone Render Mode", options=["Fill only", "Lines + Fill"], group=MAZONE_GROUP)

// === PERFORMANCE & AUTO-TUNING ===
enableAutoTuning = false
tuningPeriod = 200
adaptationRate = 0.02
enablePerformanceTracking = false
trackingPeriod = 500
enableMemoryOptimization = true
maxHistoryBars = 2000

// Alert Settings
enableSmartAlerts = input.bool(true, "Smart Alerts", group=PERFORMANCE_GROUP)
alertConfidenceMin = input.float(0.7, "Alert Confidence Min", minval=0.5, maxval=1.0, step=0.05, group=PERFORMANCE_GROUP)

// === SSA: SMART SIGNALS ASSISTANT ===
var string SSA_GROUP = "SSA: Smart Signals"
var string SSA_VIS = "SSA: Visuals"
var string SSA_TM = "SSA: Trade Management"
var string SSA_ALERT = "SSA: Alert Builder"

ssaEnable = input.bool(false, "Enable SSA Overlay", group=SSA_GROUP)
ssaMode = input.string("Swing", "Mode", options=["Swing","Scalping"], group=SSA_GROUP)
ssaSignalMode = input.string("Trend", "Signal Mode", options=["Trend","Reversal"], group=SSA_GROUP)
ssaEntrySource = input.string("SSA Engine", "SSA Entry Source", options=["SSA Engine","RDP A/B (both)","RDP A only","RDP B only"], group=SSA_GROUP, tooltip="When set to RDP: SSA uses RDP entry events as triggers instead of its own engine.")
ssaUseRDPGate = input.bool(true, "Use RDP as Primary Gate", group=SSA_GROUP, tooltip="Uses RDP regime/conf/quality/HTF as main filter for SSA signals")

ssaMinConfSwing = input.float(0.70, "Min Confidence (Swing)", step=0.05, group=SSA_GROUP)
ssaMinConfScalp = input.float(0.60, "Min Confidence (Scalping)", step=0.05, group=SSA_GROUP)
ssaMinHTF = input.float(0.55, "Min HTF Agreement", step=0.05, group=SSA_GROUP)
ssaMinQuality = input.float(0.60, "Min Signal Quality", step=0.05, group=SSA_GROUP)

ssaStrongConf = input.float(0.80, "Strong: Min Confidence", step=0.05, group=SSA_GROUP)
ssaStrongHTF = input.float(0.65, "Strong: Min HTF Agreement", step=0.05, group=SSA_GROUP)
ssaStrongQuality = input.float(0.70, "Strong: Min Quality", step=0.05, group=SSA_GROUP)

ssaCooldownBars = input.int(5, "SSA Cooldown (bars)", minval=0, maxval=100, group=SSA_GROUP)

ssaSuperEnabled = input.bool(true, "Enable Super tier", group=SSA_GROUP)
ssaSuperNeedsTRC = input.bool(true, "Super requires TRC Trending", group=SSA_GROUP, tooltip="Uses filterState.marketCondition == 1 as TRC trending proxy")
ssaSuperNeedsFVT = input.bool(true, "Super requires FVT aligned", group=SSA_GROUP)
ssaSuperNeedsCloud = input.bool(true, "Super requires Cloud aligned", group=SSA_GROUP)
ssaRequireRdpTierBPlus = input.bool(true, "SSA entries require RDP Tier B+", group=SSA_GROUP)
ssaEntryTierGate = input.string("Tier B+", "SSA Entry Tier Gate", options=["Off","Tier B+","Tier A only"], group=SSA_GROUP)

ssaCandleColoring = input.string("Off", "Candle Coloring", options=["Off","Trend Colors","Momentum Gradient","Volume Gradient","Gray In Ranging"], group=SSA_VIS)
ssaShowSignals = input.bool(true, "Show SSA Signals", group=SSA_VIS)
ssaSignalDisplay = input.string("All", "SSA Signal Display", options=["Off","All","Strong+","Super only"], group=SSA_VIS)
ssaPreferSSAOverRDP = input.bool(false, "Prefer SSA visuals over RDP", group=SSA_VIS)
ssaAutoHideRdpWhenUsingRdpEntries = input.bool(true, "Auto-hide RDP visuals when SSA uses RDP entries", group=SSA_VIS)
ssaShowPending = input.bool(true, "Show pending marker", group=SSA_VIS)
ssaShowStrongPlus = input.bool(true, "Strong shows plus markers", group=SSA_VIS)
ssaShowFVT = input.bool(true, "Show Fair Value Trail (FVT)", group=SSA_VIS)
ssaShowTrendBias = input.bool(true, "Show Trend Bias Band", group=SSA_VIS)
ssaShowClouds = input.bool(true, "Show ATR Clouds", group=SSA_VIS)

ssaTradeMgmt = false
ssaSLSource = "RDP Exit Band"
ssaRR = 2.0
ssaExtendBars = 50
ssaExitMode = "Hybrid"
ssaExitOnRegimeFlip = true
ssaExitOnFVTFlip = true
ssaExitOnCloudFlip = false
ssaTimeStopBars = 0
ssaTrailEnabled = true
ssaTrailSource = "FVT"
ssaTrailBufferATR = 0.2
ssaRdpEntryPriceMode = "Close"
ssaRdpProMode = true
ssaRdpEntryPriceModeA = "Close"
ssaRdpEntryPriceModeB = "Entry Band (upper/lower)"
ssaRdpLimitStyle = true
ssaRdpLimitTimeoutBars = 20
ssaRdpLimitTouchMode = "Touch Entry Band"
ssaPendingDualSlots = true
ssaPendingFlipMode = "Flip"
ssaLimitActivation = "Touch"
ssaCloseConfirmBars = 1

var string SSA_TABLE = "SSA: Component Status Table"
ssaShowTable = input.bool(false, "Show Status Table", group=SSA_TABLE)
ssaTablePos = input.string("Top Right", "Position", options=["Top Right","Top Left","Bottom Right","Bottom Left"], group=SSA_TABLE)
ssaTableCompact = input.bool(false, "Compact", group=SSA_TABLE)

ssaEnableAlerts = input.bool(true, "Enable SSA Alert Builder", group=SSA_ALERT)
ssaStepCount = input.int(2, "Steps (1..7)", minval=1, maxval=7, group=SSA_ALERT)
ssaLogicDefault = input.string("AND", "Default Connector", options=["AND","OR"], group=SSA_ALERT)

ssaExternal1 = input.source(close, "External #1 (series)", group=SSA_ALERT)
ssaExternal2 = input.source(close, "External #2 (series)", group=SSA_ALERT)
ssaExternal3 = input.source(close, "External #3 (series)", group=SSA_ALERT)
ssaExternal4 = input.source(close, "External #4 (series)", group=SSA_ALERT)
ssaExternal5 = input.source(close, "External #5 (series)", group=SSA_ALERT)

ssaExtCmp = input.string("Crossover", "External comparator", options=["Crossover","Crossunder","Above 0","Below 0","Rising","Falling"], group=SSA_ALERT)

step1Cond = input.string("SSA Strong Long", "Step 1 Condition", options=[
 "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short",
 "RDP Bull Regime","RDP Bear Regime","RDP Ranging",
 "RDP Tier A","RDP Tier B+",
 "FVT Bull","FVT Bear",
 "Cloud Bull","Cloud Bear",
 "External #1","External #2","External #3","External #4","External #5"
], group=SSA_ALERT)
step2Cond = input.string("RDP Tier A", "Step 2 Condition", options=[
 "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short",
 "RDP Bull Regime","RDP Bear Regime","RDP Ranging",
 "RDP Tier A","RDP Tier B+",
 "FVT Bull","FVT Bear",
 "Cloud Bull","Cloud Bear",
 "External #1","External #2","External #3","External #4","External #5"
], group=SSA_ALERT)
step2Conn = input.string("AND", "Step 2 Connector", options=["AND","OR"], group=SSA_ALERT)

step3Cond = input.string("HTF OK", "Step 3 Condition", options=[
 "None",
 "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short",
 "RDP Bull Regime","RDP Bear Regime","RDP Ranging",
 "RDP Tier A","RDP Tier B+",
 "HTF OK","Quality OK","Confidence OK",
 "FVT Bull","FVT Bear",
 "Cloud Bull","Cloud Bear",
 "External #1","External #2","External #3","External #4","External #5"
], group=SSA_ALERT)
step3Conn = input.string("AND", "Step 3 Connector", options=["AND","OR"], group=SSA_ALERT)

step4Cond = input.string("None", "Step 4 Condition", options=["None", "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short", "RDP Bull Regime","RDP Bear Regime","RDP Ranging", "HTF OK","Quality OK","Confidence OK", "FVT Bull","FVT Bear","Cloud Bull","Cloud Bear", "External #1","External #2","External #3","External #4","External #5" ], group=SSA_ALERT)
step4Conn = input.string("AND", "Step 4 Connector", options=["AND","OR"], group=SSA_ALERT)

step5Cond = input.string("None", "Step 5 Condition", options=["None", "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short", "RDP Bull Regime","RDP Bear Regime","RDP Ranging", "HTF OK","Quality OK","Confidence OK", "FVT Bull","FVT Bear","Cloud Bull","Cloud Bear", "External #1","External #2","External #3","External #4","External #5" ], group=SSA_ALERT)
step5Conn = input.string("AND", "Step 5 Connector", options=["AND","OR"], group=SSA_ALERT)

step6Cond = input.string("None", "Step 6 Condition", options=["None", "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short", "RDP Bull Regime","RDP Bear Regime","RDP Ranging", "HTF OK","Quality OK","Confidence OK", "FVT Bull","FVT Bear","Cloud Bull","Cloud Bear", "External #1","External #2","External #3","External #4","External #5" ], group=SSA_ALERT)
step6Conn = input.string("AND", "Step 6 Connector", options=["AND","OR"], group=SSA_ALERT)

step7Cond = input.string("None", "Step 7 Condition", options=["None", "SSA Long","SSA Short","SSA Strong Long","SSA Strong Short", "RDP Bull Regime","RDP Bear Regime","RDP Ranging", "HTF OK","Quality OK","Confidence OK", "FVT Bull","FVT Bear","Cloud Bull","Cloud Bear", "External #1","External #2","External #3","External #4","External #5" ], group=SSA_ALERT)
step7Conn = input.string("AND", "Step 7 Connector", options=["AND","OR"], group=SSA_ALERT)

// === LOGGING ===
logLabelMetrics = input.bool(false, "Log label metrics (last 2000 / step 10)", group=LOG_GROUP)


// ============================================================================
// MODULE: core
// ============================================================================

// === CORE CONFIGURATION ===

// Create Adaptive Config
adaptiveConfig = newAdaptiveConfig()
adaptiveConfig.enableAdaptiveEngine := enableAdaptiveEngine
adaptiveConfig.erLength := erLength
adaptiveConfig.alphaMin := alphaMin
adaptiveConfig.alphaMax := alphaMax
adaptiveConfig.gamma := gamma
adaptiveConfig.atrLength := atrLength
adaptiveConfig.source := dataSource

// Apply profile presets
if profilePreset != "Custom"
    if profilePreset == "Smart"
        adaptiveConfig.alphaMin := 0.025
        adaptiveConfig.alphaMax := 0.35
        adaptiveConfig.gamma := 1.15
    else if profilePreset == "Futures"
        adaptiveConfig.alphaMin := 0.02
        adaptiveConfig.alphaMax := 0.28
        adaptiveConfig.gamma := 1.35
    else if profilePreset == "Spot"
        adaptiveConfig.alphaMin := 0.03
        adaptiveConfig.alphaMax := 0.30
        adaptiveConfig.gamma := 1.25
    else if profilePreset == "ETF"
        adaptiveConfig.alphaMin := 0.015
        adaptiveConfig.alphaMax := 0.25
        adaptiveConfig.gamma := 1.45
    else if profilePreset == "Crypto"
        adaptiveConfig.alphaMin := 0.035
        adaptiveConfig.alphaMax := 0.40
        adaptiveConfig.gamma := 1.05

// Create Regime Config
regimeConfig = newRegimeConfig()
regimeConfig.enableAdvancedRegime := enableAdvancedRegime
regimeConfig.regimeMode := regimeMode
regimeConfig.erThreshold := erThreshold
regimeConfig.erThresholdExit := erThresholdExit
regimeConfig.sensitivity := regimeSensitivity
regimeConfig.minConfidence := regimeConfidenceMin
regimeConfig.minConfidenceExit := regimeConfidenceExit
regimeConfig.enableMLEnhancement := enableMLRegime
regimeConfig.mlLookback := mlLookback
regimeConfig.mlConfidenceThreshold := mlConfidence

// Create MTF Config
chartSec = timeframe.in_seconds(timeframe.period)
sec15 = timeframe.in_seconds("15")
sec1h = timeframe.in_seconds("60")
sec4h = timeframe.in_seconds("240")
secD = timeframe.in_seconds("D")

string htfTimeframe1 = "60"
string htfTimeframe2 = "240"
string htfTimeframe3 = "D"

if chartSec <= sec15
    htfTimeframe1 := "60"
    htfTimeframe2 := "240"
    htfTimeframe3 := "D"
else if chartSec <= sec1h
    htfTimeframe1 := "240"
    htfTimeframe2 := "D"
    htfTimeframe3 := "W"
else if chartSec <= sec4h
    htfTimeframe1 := "D"
    htfTimeframe2 := "W"
    htfTimeframe3 := "M"
else if chartSec <= secD
    htfTimeframe1 := "W"
    htfTimeframe2 := "M"
    htfTimeframe3 := "3M"
else
    htfTimeframe1 := "M"
    htfTimeframe2 := "3M"
    htfTimeframe3 := "6M"

mtfConfig = newMultiTimeframeConfig()
mtfConfig.enableHTFGate := enableMTFGate
mtfConfig.enableDivergenceDetection := enableHTFDivergence
array.clear(mtfConfig.timeframes)
array.push(mtfConfig.timeframes, htfTimeframe1)
array.push(mtfConfig.timeframes, htfTimeframe2)
array.push(mtfConfig.timeframes, htfTimeframe3)

// Set HTF weights
htf1Weight = htfWeights == "Primary Focus" ? 0.6 : htfWeights == "Long-term Focus" ? 0.2 : 0.4
htf2Weight = htfWeights == "Primary Focus" ? 0.3 : htfWeights == "Long-term Focus" ? 0.3 : 0.35
htf3Weight = htfWeights == "Primary Focus" ? 0.1 : htfWeights == "Long-term Focus" ? 0.5 : 0.25

map.put(mtfConfig.weights, htfTimeframe1, htf1Weight)
map.put(mtfConfig.weights, htfTimeframe2, htf2Weight)
map.put(mtfConfig.weights, htfTimeframe3, htf3Weight)
mtfConfig.minAgreement := htfMinAgreement

// Create Filter Config
filterConfig = newFilterConfig()
filterConfig.enableAdvancedFilters := enableAdvancedFilters
filterConfig.minATRPercent := minATRPercent
filterConfig.volatilityMode := volatilityMode
filterConfig.enableVolumeFilter := useVolumeFilter
filterConfig.volumeLength := volumeLength
filterConfig.volumeQuantile := volumeQuantile
filterConfig.enableMarketCondition := enableMarketCondition
filterConfig.marketConditionPeriod := marketConditionPeriod
filterConfig.enableConfluence := enableConfluenceFilter
filterConfig.confluenceThreshold := confluenceThreshold

// Create Signal Config
signalConfig = newSignalConfig()
signalConfig.confirmBars := confirmBars
signalConfig.enableSmartConfirm := enableSmartConfirm
signalConfig.enableAdvancedExits := enableAdvancedExits
signalConfig.exitMode := exitMode
signalConfig.enableTrailingStop := enableTrailingStop
signalConfig.trailingSensitivity := trailingSensitivity
signalConfig.enableRiskManagement := enableRiskManagement
signalConfig.maxConsecutiveLosses := maxConsecutiveLosses
signalConfig.cooldownPeriod := cooldownPeriod
signalConfig.exitMultiplier := exitMultiplier
signalConfig.tierAConf := tierAConf
signalConfig.tierAAgree := tierAAgree
signalConfig.tierAQuality := tierAQuality
signalConfig.tierBConf := tierBConf
signalConfig.tierBAgree := tierBAgree
signalConfig.tierBQuality := tierBQuality

// Create Performance Config
performanceConfig = newPerformanceConfig()
performanceConfig.enableAutoTuning := enableAutoTuning
performanceConfig.tuningPeriod := tuningPeriod
performanceConfig.adaptationRate := adaptationRate
performanceConfig.enablePerformanceTracking := enablePerformanceTracking
performanceConfig.trackingPeriod := trackingPeriod
performanceConfig.enableMemoryOptimization := enableMemoryOptimization
performanceConfig.maxHistoryBars := maxHistoryBars

// === STATE INITIALIZATION ===
var adaptiveState = newAdaptiveState()
var regimeState = newRegimeState()
var mtfState = newMultiTimeframeState()
var filterState = newFilterState()
var signalState = newSignalState()
var performanceMetrics = newPerformanceMetrics()

// === HELPER FUNCTIONS ===

// Get theme colors — [bull, bear, flat] per selected visual theme
getThemeColors() =>
    if visualTheme == "Soft"
        [color.rgb(77, 182, 172), color.rgb(229, 115, 115), color.rgb(144, 164, 174)]   // muted teal / coral / blue-gray
    else if visualTheme == "Classic"
        [color.rgb(34, 171, 108), color.rgb(220, 73, 73), color.rgb(120, 120, 120)]      // original saturated
    else
        [color.rgb(0, 191, 165), color.rgb(255, 82, 82), color.rgb(120, 144, 156)]       // Modern: teal / coral-red / slate

// Calculate adaptive parameters based on market conditions
calculateAdaptiveParams() =>
    if enableAutoTuning and performanceMetrics.sampleCount > tuningPeriod
        // Auto-tune based on performance
        if performanceMetrics.winRate < 0.4
            // Increase selectivity
            adaptiveConfig.gamma := math.min(3.0, adaptiveConfig.gamma * 1.05)
            regimeConfig.erThreshold := math.min(0.8, regimeConfig.erThreshold * 1.02)
        else if performanceMetrics.winRate > 0.7
            // Increase sensitivity
            adaptiveConfig.gamma := math.max(0.5, adaptiveConfig.gamma * 0.98)
            regimeConfig.erThreshold := math.max(0.1, regimeConfig.erThreshold * 0.99)

// === MAIN PROCESSING ===

// 1. Update adaptive parameters
calculateAdaptiveParams()

// 2. Process Adaptive Engine
adaptiveState := processAdaptiveEngine(adaptiveState, adaptiveConfig, atrLength)

// 3. Process Regime Detection
regimeState := processRegimeDetection(regimeState, regimeConfig, adaptiveState)

// 4. Process Multi-Timeframe Analysis
if enableMTFGate
    mtfState := processMultiTimeframeAnalysis(mtfState, mtfConfig, regimeState.currentRegime)

// 5. Process Filter System
filterState := processFilterSystem(filterState, filterConfig, adaptiveState, regimeState)

// 6. Process Signal Generation
signalState := processSignalGeneration(signalState, signalConfig,
    adaptiveState, regimeState, mtfState, filterState)

// 7. Update Performance Metrics
if enablePerformanceTracking
    performanceMetrics := updatePerformanceMetrics(performanceMetrics, performanceConfig, signalState)


// ============================================================================
// MODULE: visualization
// ============================================================================

// === CALCULATIONS FOR VISUALIZATION ===

// Extract key values from engine states
adaptiveLine = adaptiveState.line
upperEntry = adaptiveLine + entryMultiplier * adaptiveState.atr
lowerEntry = adaptiveLine - entryMultiplier * adaptiveState.atr
upperExit = adaptiveLine + exitMultiplier * adaptiveState.atr
lowerExit = adaptiveLine - exitMultiplier * adaptiveState.atr

// Regime information
currentRegime = regimeState.currentRegime // 1=Bull, -1=Bear, 0=Flat
regimeConfidence = regimeState.confidence
regimeStrength = regimeState.strength

// MTF information
htfAgreement = enableMTFGate ? mtfState.agreementRatio : 1.0
htfBias = enableMTFGate ? mtfState.overallBias : currentRegime

// Filter information — plain series copy: filterState is mutated in place, so
// history-referencing the object ([off]) would always return the current values
signalQualitySeries = filterState.signalQuality

// Signal information
longSignal = signalState.longEntry
shortSignal = signalState.shortEntry
exitLongSignal = signalState.exitLong
exitShortSignal = signalState.exitShort
lowQualityLong = signalState.lowQualityLong
lowQualityShort = signalState.lowQualityShort
currentPosition = signalState.position

// ============================================================================
// REGIME CONTEXT LAYER (v1.1) — additive, does not touch the direction engine
// ============================================================================

// --- Dimension 2: Volatility state (Quiet / Normal / Volatile) -------------
// ATR percentile + Bollinger-width percentile, averaged. Computed every bar
// (ta.* must be unconditional) and only consumed when enabled.
float ctxAtr     = adaptiveState.atr
float atrRank    = ta.percentrank(ctxAtr, volaLookback)
float bbBasisCtx = ta.sma(close, 20)
float bbDevCtx   = ta.stdev(close, 20)
float bbWidthCtx = bbBasisCtx != 0 ? (bbDevCtx * 4.0) / bbBasisCtx : 0.0
float bbwRank    = ta.percentrank(bbWidthCtx, volaLookback)
float volaScore  = (nz(atrRank, 50.0) + nz(bbwRank, 50.0)) / 2.0
// volaState: -1 Quiet, 0 Normal, 1 Volatile
int volaState = volaScore < volaQuietMax ? -1 : volaScore > volaVolMin ? 1 : 0
string volaTxt = volaState == -1 ? "Quiet" : volaState == 1 ? "Volatile" : "Normal"

// --- Regime maturity & transition warning ----------------------------------
bool regimeChgd   = currentRegime != currentRegime[1]
int  barsInRegime = nz(ta.barssince(regimeChgd), bar_index)

// Peak strength reached within the current regime (for fade detection)
var float regimePeakStr = 0.0
if regimeChgd
    regimePeakStr := regimeStrength
else
    regimePeakStr := math.max(regimePeakStr, regimeStrength)
float fadeRatio = regimePeakStr > 0 ? regimeStrength / regimePeakStr : 1.0

float ctxSlope = adaptiveState.normalizedSlope
float ctxEr    = adaptiveState.efficiencyRatio

// Weakening: trend losing efficiency, fading below peak, or slope turning against it
bool slopeAgainst = (currentRegime == 1 and ctxSlope < 0) or (currentRegime == -1 and ctxSlope > 0)
bool regimeWeakening = currentRegime != 0 and (ctxEr < erThresholdExit or fadeRatio < 0.5 or slopeAgainst)

// Context state-change events (used for both chart labels and alerts)
bool volaCompression     = enableVolaDim and volaState == -1 and volaState[1] != -1
bool volaExpansion       = enableVolaDim and volaState == 1 and volaState[1] != 1
bool regimeWeakeningEvent = regimeWeakening and not regimeWeakening[1]

string maturityTxt = "—"
if currentRegime != 0
    if regimeWeakening
        maturityTxt := "Aging / weakening"
    else if barsInRegime < 10
        maturityTxt := "Fresh (" + str.tostring(barsInRegime) + ")"
    else if fadeRatio > 0.8
        maturityTxt := "Mature / strong"
    else
        maturityTxt := "Mature"

// --- RS vs. benchmark (equities modifier) ----------------------------------
// request.security unconditional (avoids inconsistent-series pitfall); used only when enabled.
float rsBenchClose = request.security(rsBenchSym, timeframe.period, close, lookahead=barmerge.lookahead_off)
float ctxRs   = rsBenchClose != 0 and not na(rsBenchClose) ? close / rsBenchClose : na
float ctxRsMa = ta.sma(ctxRs, rsMaLen)
float ctxMrs  = not na(ctxRsMa) and ctxRsMa != 0 ? (ctxRs / ctxRsMa - 1.0) * 100.0 : na
int   rsBias  = not na(ctxMrs) ? (ctxMrs > 0 ? 1 : -1) : 0   // 1 leading, -1 lagging

// --- Playbook --------------------------------------------------------------
string playbook = "—"
if currentRegime == 0
    if enableVolaDim and volaState == -1
        playbook := "Compression — await breakout"
    else if enableVolaDim and volaState == 1
        playbook := "Choppy range — fade edges / stay out"
    else
        playbook := "Range — trade the boundaries"
else
    if regimeWeakening
        playbook := "Trend aging — tighten stops / bank profit"
    else if enableVolaDim and volaState == 1
        playbook := (currentRegime == 1 ? "Strong but stretched — ride, trail tight" : "Sharp down — ride, trail tight")
    else if enableVolaDim and volaState == -1
        playbook := (currentRegime == 1 ? "Healthy uptrend — buy pullbacks" : "Healthy downtrend — sell rallies")
    else
        playbook := (currentRegime == 1 ? "Uptrend — follow long" : "Downtrend — follow short")

// RS modifier appended to the playbook for equities
if enableRSContext and currentRegime != 0 and not na(ctxMrs)
    playbook := playbook + (rsBias == 1 ? "  ·  RS leading ✓" : "  ·  RS lagging ✗")

// === VISUALIZATION ===

// Theme colors
[colorBull, colorBear, colorFlat] = getThemeColors()

// Adaptive Line — regime-colored, slightly heavier for presence
lineColor = enableDynamicColors ? (currentRegime == 1 ? colorBull : currentRegime == -1 ? colorBear : colorFlat) : color.new(color.black, 0)
pLine = plot(adaptiveLine, "Adaptive Line", lineColor, linewidth=3)

// ATR Bands — soft vertical gradient filling the full channel from the centre
// line out to the entry edge (vivid at the line, fading at the edge). Filling
// against the centre line lets us drop the two exit plots and stay within the
// 64-output budget while looking cleaner. Exit levels remain for signal logic.
bandBullCol = colorBull
bandBearCol = colorBear

pUpperEntry = plot(showBands ? upperEntry : na, "Upper Entry",
 color=color.new(bandBullCol, 45), linewidth=1)
pLowerEntry = plot(showBands ? lowerEntry : na, "Lower Entry",
 color=color.new(bandBearCol, 45), linewidth=1)

uOuter = not showBands ? na : smoothBands ? color.new(bandBullCol, 94) : color.new(bandBullCol, 86)
uInner = not showBands ? na : color.new(bandBullCol, 80)
fill(pUpperEntry, pLine, upperEntry, adaptiveLine, uOuter, uInner, title="ATR Upper Fill")

lInner = not showBands ? na : color.new(bandBearCol, 80)
lOuter = not showBands ? na : smoothBands ? color.new(bandBearCol, 94) : color.new(bandBearCol, 86)
fill(pLine, pLowerEntry, adaptiveLine, lowerEntry, lInner, lOuter, title="ATR Lower Fill")

// Regime Heatmap — softer, with a touch more presence in volatile regimes
heatBase  = 93.0 - (regimeStrength * regimeConfidence * 16.0)
volaBoost = enableVolaDim and volaState == 1 ? 6.0 : 0.0
heatAlpha = int(math.max(76, math.min(95, heatBase - volaBoost)))
bgColor = currentRegime == 1 ? color.new(colorBull, heatAlpha) : currentRegime == -1 ? color.new(colorBear, heatAlpha) : na
bgcolor(showRegimeHeatmap ? bgColor : na, title="Regime Heatmap")

// Regime candle tint colour (applied by the shared barcolor() call below, so it
// does not add a second barcolor to the plot budget). Active only when the SSA
// candle coloring is Off.
color regimeCandleCol = na
if regimeCandleTint and ssaCandleColoring == "Off"
    tintBase = currentRegime == 1 ? colorBull : currentRegime == -1 ? colorBear : colorFlat
    tintTransp = enableVolaDim ? (volaState == 1 ? 20 : volaState == -1 ? 58 : 38) : 38
    regimeCandleCol := color.new(tintBase, tintTransp)

// --- Label anchoring (shared)
float atrLbl = nz(adaptiveState.atr, ta.atr(14))
float refY = math.max(high, math.max(upperEntry, upperExit))
float pad1 = atrLbl * 0.15
float pad2 = atrLbl * 0.15
float yHTF = refY + pad1 + pad2

// HTF Indicators (pinned to last change)
htfColor = htfAgreement >= 0.6 ? color.green : htfAgreement <= 0.4 ? color.red : color.orange
var label htfStatusLbl = na
var string htfLastTxt = ""
var color htfLastCol = na
var int htfLastBar = na
var float htfLastY = na

if showHTFIndicators and enableMTFGate
    string txt = "HTF " + str.tostring(htfAgreement, "#.00")
    string tip = "HTF agreement ratio (0..1) between higher timeframes and current regime"
    int xLbl = bar_index

    if txt != htfLastTxt
        htfLastTxt := txt
        htfLastCol := color.new(htfColor, 0)
        htfLastBar := xLbl
        htfLastY := yHTF

    if na(htfStatusLbl)
        htfStatusLbl := label.new( htfLastBar, htfLastY, htfLastTxt, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=color.white, color=htfLastCol, size=size.tiny, tooltip=tip, force_overlay=true )
    else
        label.set_xy(htfStatusLbl, htfLastBar, htfLastY)
        label.set_text(htfStatusLbl, htfLastTxt)
        label.set_color(htfStatusLbl, htfLastCol)
        label.set_textcolor(htfStatusLbl, color.white)
        label.set_tooltip(htfStatusLbl, tip)
else
    if not na(htfStatusLbl)
        label.delete(htfStatusLbl)
        htfStatusLbl := na

// Signal Markers
qualA = (regimeConfidence >= tierAConf) and (htfAgreement >= tierAAgree) and (filterState.signalQuality >= tierAQuality)
qualB = not qualA and (regimeConfidence >= tierBConf) and (htfAgreement >= tierBAgree) and (filterState.signalQuality >= tierBQuality)
qualC = not qualA and not qualB

longEvent = longSignal and not longSignal[1]
shortEvent = shortSignal and not shortSignal[1]

longA = longEvent and qualA
shortA = shortEvent and qualA
longB = longEvent and qualB
shortB = shortEvent and qualB
longC = lowQualityLong and not lowQualityLong[1]
shortC = lowQualityShort and not lowQualityShort[1]

// --- RDP Entry exports for SSA
rdpLongAEvent = longA
rdpShortAEvent = shortA
rdpLongBEvent = longB
rdpShortBEvent = shortB
rdpLongAnyAB = longA or longB
rdpShortAnyAB = shortA or shortB

ssaRdpLongTierA = rdpLongAEvent
ssaRdpShortTierA = rdpShortAEvent
ssaRdpLongTierB = rdpLongBEvent
ssaRdpShortTierB = rdpShortBEvent

isRdpSource = ssaEntrySource != "SSA Engine"
showRdpEntryVisuals = showTradeEngine and not (ssaEnable and ssaPreferSSAOverRDP) and not (ssaEnable and ssaAutoHideRdpWhenUsingRdpEntries and isRdpSource)

rdpLongEv = showRdpEntryVisuals and (longA or longB)
rdpShortEv = showRdpEntryVisuals and (shortA or shortB)
rdpLongTxt = longA ? "A" : "B"
rdpShortTxt = shortA ? "A" : "B"
rdpLongCol = longA ? colorBull : color.new(colorBull, 35)
rdpShortCol = shortA ? colorBear : color.new(colorBear, 35)

plotshape(rdpLongEv, "RDP Long Entry", shape.triangleup, location.belowbar,
 rdpLongCol, size=size.normal)
plotshape(rdpShortEv, "RDP Short Entry", shape.triangledown, location.abovebar,
 rdpShortCol, size=size.normal)

if showRdpEntryVisuals and longC
    label.new(bar_index, low, "LOWQ",
        style=label.style_label_up,
        textcolor=color.white,
        color=color.new(color.orange, 0),
        size=size.tiny,
        tooltip="Low quality: below Tier B thresholds (confidence/HTF/quality)")
if showRdpEntryVisuals and shortC
    label.new(bar_index, high, "LOWQ",
        style=label.style_label_down,
        textcolor=color.white,
        color=color.new(color.orange, 0),
        size=size.tiny,
        tooltip="Low quality: below Tier B thresholds (confidence/HTF/quality)")

// Warnings (events only, throttled)
warnMTF = enableHTFDivergence and enableMTFGate and (mtfState.divergenceStrength >= warningDivergenceThreshold)
warnChop = (filterState.marketCondition == -1) or (regimeStrength < warningRegimeStrengthMin)
volBase = close > 0 ? (adaptiveState.atr / close) : 0.0
volThresh = volatilityMode == "Static" ? minATRPercent : volatilityMode == "Adaptive" ? (minATRPercent * (1.0 + (1.0 - adaptiveState.efficiencyRatio) * 0.5)) : ta.sma(volBase, marketConditionPeriod)
warnLowVol = volBase < volThresh

var int lastWarnMTF = na
var int lastWarnChop = na
var int lastWarnLowVol = na

warnMTFEvent = showWarnings and warnMTF and not warnMTF[1] and (na(lastWarnMTF) or (bar_index - lastWarnMTF >= warningCooldownBars))
warnChopEvent = showWarnings and warnChop and not warnChop[1] and (na(lastWarnChop) or (bar_index - lastWarnChop >= warningCooldownBars))
warnLowVolEvent = showWarnings and warnLowVol and not warnLowVol[1] and (na(lastWarnLowVol) or (bar_index - lastWarnLowVol >= warningCooldownBars))

if warnMTFEvent
    lastWarnMTF := bar_index
if warnChopEvent
    lastWarnChop := bar_index
if warnLowVolEvent
    lastWarnLowVol := bar_index

// Status label (pinned to last change)
var label statusLbl = na
var string statusLastTxt = ""
var color statusLastCol = na
var int statusLastBar = na
var float statusLastY = na

string statusTxt = (showTradeEngine and (longEvent or shortEvent)) ? (qualA ? "A" : qualB ? "B" : "LOWQ") : (warnMTF ? "MTF" : warnChop ? "RNG" : warnLowVol ? "VOL" : "")

color statusCol = (showTradeEngine and (longEvent or shortEvent)) ? (qualA ? color.new(color.green, 0) : qualB ? color.new(color.yellow, 0) : color.new(color.red, 0)) : (warnMTF ? color.new(color.orange, 0) : warnChop ? color.new(color.gray, 0) : warnLowVol ? color.new(color.yellow, 0) : na)

string statusTip = "Regime: " + (currentRegime == 1 ? "BULL" : currentRegime == -1 ? "BEAR" : "FLAT") + "\n" + "Confidence: " + str.tostring(regimeConfidence, "#.00") + "\n" + "Quality: " + str.tostring(filterState.signalQuality, "#.00") + "\n" + "HTF: " + str.tostring(htfAgreement, "#.00")

if statusTxt != ""
    int xLbl = bar_index
    float statusBaseY = longEvent ? low : shortEvent ? high : high
    float statusYOffset = atrLbl * 0.15
    float statusEventY = longEvent ? (statusBaseY - statusYOffset) : (statusBaseY + statusYOffset)

    if statusTxt != statusLastTxt
        statusLastTxt := statusTxt
        statusLastCol := statusCol
        statusLastBar := xLbl
        statusLastY := statusEventY

    if na(statusLbl)
        statusLbl := label.new( statusLastBar, statusLastY, statusLastTxt, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=color.white, color=statusLastCol, size=size.tiny, tooltip=statusTip, force_overlay=true )
    else
        label.set_xy(statusLbl, statusLastBar, statusLastY)
        label.set_text(statusLbl, statusLastTxt)
        label.set_color(statusLbl, statusLastCol)
        label.set_textcolor(statusLbl, color.white)
        label.set_tooltip(statusLbl, statusTip)
else
    if not na(statusLbl)
        label.delete(statusLbl)
        statusLbl := na
        statusLastTxt := ""

// ─────────────────────────────────────────────────────────────────────────────
// Logging (Pine log): last 2000 bars, step 10
// ─────────────────────────────────────────────────────────────────────────────
var int lastLogBar = na
if logLabelMetrics and barstate.islast and (na(lastLogBar) or lastLogBar != bar_index)
    lastLogBar := bar_index
    for i = 0 to 200
        int off = i * 10
        if bar_index >= off
            int logBar = bar_index - off
            string tierTxt = qualA[off] ? "A" : qualB[off] ? "B" : "C"
            string warnTxt = warnMTF[off] ? "MTF" : warnChop[off] ? "RNG" : warnLowVol[off] ? "VOL" : "None"
            log.info(str.format("LBL,bar={0},time={1},regime={2},conf={3},strength={4},htf={5},quality={6},tier={7},warn={8},longEv={9},shortEv={10},pos={11},line={12},uEntry={13},lEntry={14}",
                str.tostring(logBar),
                str.tostring(time[off]),
                str.tostring(currentRegime[off]),
                str.tostring(regimeConfidence[off], "#.00"),
                str.tostring(regimeStrength[off], "#.00"),
                str.tostring(htfAgreement[off], "#.00"),
                str.tostring(signalQualitySeries[off], "#.00"),
                tierTxt,
                warnTxt,
                str.tostring(longEvent[off]),
                str.tostring(shortEvent[off]),
                str.tostring(currentPosition[off]),
                str.tostring(adaptiveLine[off], format.mintick),
                str.tostring(upperEntry[off], format.mintick),
                str.tostring(lowerEntry[off], format.mintick)
            ))

if warnMTFEvent
    label.new(bar_index, high, "MTF",
        style=label.style_label_left,
        textcolor=color.white,
        color=color.new(color.orange, 0),
        size=size.tiny,
        tooltip="MTF divergence: higher timeframes conflict with current regime")
if warnChopEvent
    label.new(bar_index, high, "CHOP",
        style=label.style_label_down,
        textcolor=color.white,
        color=color.new(color.gray, 0),
        size=size.tiny,
        tooltip="Chop warning: marketCondition == -1 or regimeStrength below threshold")
plotshape(warnLowVolEvent, "Warn Low Vol", shape.triangleup, location.abovebar,
 color=color.new(color.yellow, 0), size=size.tiny, text="VOL")

// v1.1 context event labels (label.new — does not count toward the plot budget)
if showContextLabels and volaCompression
    label.new(bar_index, low, "COMPRESS",
        style=label.style_label_up, textcolor=color.white,
        color=color.new(#2962ff, 0), size=size.tiny, force_overlay=true,
        tooltip="Volatility contracted to Quiet — energy building, breakout setup")
if showContextLabels and volaExpansion
    label.new(bar_index, high, "EXPAND",
        style=label.style_label_down, textcolor=color.white,
        color=color.new(#ff6f00, 0), size=size.tiny, force_overlay=true,
        tooltip="Volatility expanded to Volatile — climax / whipsaw risk")
if showContextLabels and regimeWeakeningEvent
    label.new(bar_index, currentRegime == 1 ? high : low,
        "AGING",
        style=currentRegime == 1 ? label.style_label_down : label.style_label_up,
        textcolor=color.white, color=color.new(#d50000, 0), size=size.tiny, force_overlay=true,
        tooltip="Trend regime is aging/weakening — tighten stops / bank profit")

// Signal Quality Label (right of bar, left-pointing)
strengthColor = dotColorMode == "By Tier" ? (qualA ? color.new(color.green, 0) : qualB ? color.new(color.yellow, 0) : color.new(color.orange, 0)) : (regimeConfidence >= 0.8 ? color.new(color.green, 0) : regimeConfidence >= 0.6 ? color.new(color.yellow, 0) : regimeConfidence >= 0.4 ? color.new(color.orange, 0) : color.new(color.red, 0))

sigEvent = (longSignal and not longSignal[1]) or (shortSignal and not shortSignal[1])

f_qualityLabel(_isLong, _tierTxt, _color) =>
    string tip = "Tier: " + _tierTxt + "\n" + "Confidence: " + str.tostring(regimeConfidence, "#.00") + "\n" + "Quality: " + str.tostring(filterState.signalQuality, "#.00") + "\n" + "HTF: " + str.tostring(htfAgreement, "#.00") + "\n" + "Regime: " + (currentRegime == 1 ? "BULL" : currentRegime == -1 ? "BEAR" : "FLAT")

    label.new(bar_index, _isLong ? low : high, _tierTxt,
        xloc=xloc.bar_index, yloc=yloc.price,
        style=label.style_label_left,
        textcolor=color.white,
        color=_color,
        size=size.tiny,
        tooltip=tip,
        force_overlay=true)

if showRdpEntryVisuals and showSignalStrength and sigEvent
    if longSignal
        f_qualityLabel(true, qualA ? "A" : qualB ? "B" : "LOWQ", strengthColor)
    if shortSignal
        f_qualityLabel(false, qualA ? "A" : qualB ? "B" : "LOWQ", strengthColor)

// === MA OVERLAY ZONES (RDP-GATED) ===
f_ma_zone(_src, _len) =>
    if maType == "SMA"
        ta.sma(_src, _len)
    else if maType == "EMA"
        ta.ema(_src, _len)
    else if maType == "DEMA"
        ema1 = ta.ema(_src, _len)
        2.0 * ema1 - ta.ema(ema1, _len)
    else if maType == "TEMA"
        ema1 = ta.ema(_src, _len)
        ema2 = ta.ema(ema1, _len)
        3.0 * ema1 - 3.0 * ema2 + ta.ema(ema2, _len)
    else if maType == "JMA"
        calculateJMA(_src, _len, 0.0, 2.0)
    else
        ta.ema(_src, _len)

f_strengthFactor_zone() =>
    if maType == "SMA"
        1.00
    else if maType == "EMA"
        1.00
    else if maType == "DEMA"
        1.05
    else if maType == "TEMA"
        0.80
    else if maType == "JMA"
        0.85
    else
        1.00

ma1z = f_ma_zone(close, len1)
ma2z = f_ma_zone(close, len2)
ma3z = f_ma_zone(close, len3)
amaz = (ma1z + ma2z + ma3z) / 3.0

atrz = ta.atr(atrLenZones)
slDist = atrz * multZones

highestMA = math.max(ma1z, math.max(ma2z, ma3z))
lowestMA = math.min(ma1z, math.min(ma2z, ma3z))

spreadATR = atrz > 0 ? (highestMA - lowestMA) / atrz : 0.0
minStrengthByType = baseMinStrengthZones * f_strengthFactor_zone()
amaStep = atrz > 0 ? ta.rma(math.abs(amaz - amaz[1]), atrLenZones) / atrz : 0.0
amaStepRef = ta.sma(amaStep, autoLookbackZones)
autoFactor = (amaStepRef > 0) ? (amaStep / amaStepRef) : 1.0
minStrengthAuto = baseMinStrengthZones * autoFactor

// if/else instead of multi-line ternary (CE10156 with series operands)
float minStrengthZones = minStrengthAuto
if strengthModeZones == "Off"
    minStrengthZones := baseMinStrengthZones
else if strengthModeZones == "By MA Type"
    minStrengthZones := minStrengthByType

strongTrendZones = spreadATR >= minStrengthZones
bullFromMAs = barstate.isconfirmed and close >= ma1z and close >= ma2z and close >= ma3z and strongTrendZones
bearFromMAs = barstate.isconfirmed and close <= ma1z and close <= ma2z and close <= ma3z and strongTrendZones

htfOk = (not enableMTFGate) or (htfAgreement >= minHTFAgreementForZones)
zoneBull = useRDPRegime ? (currentRegime == 1 and regimeConfidence >= minConfidenceForZones and htfOk) : bullFromMAs
zoneBear = useRDPRegime ? (currentRegime == -1 and regimeConfidence >= minConfidenceForZones and htfOk) : bearFromMAs
zoneNeutral = not zoneBull and not zoneBear

var float longEntry = na
var float longSL = na
var float shortEntry = na
var float shortSL = na

zoneBullFlip = zoneBull and not zoneBull[1]
zoneBearFlip = zoneBear and not zoneBear[1]
zoneExitFlip = zoneNeutral and not zoneNeutral[1]

if zoneBullFlip
    longEntry := highestMA
    longSL := highestMA - slDist
    shortEntry := na
    shortSL := na

if zoneBearFlip
    shortEntry := lowestMA
    shortSL := lowestMA + slDist
    longEntry := na
    longSL := na

if zoneExitFlip
    longEntry := na
    longSL := na
    shortEntry := na
    shortSL := na

showZ = showTradeEngine and enableMAOverlayZones and showMAZones
fillOnly = zoneRenderMode == "Fill only"

lineColSL = fillOnly ? color.new(color.gray, 100) : color.new(color.gray, 0)
lineColEntry = fillOnly ? color.new(color.white, 100) : color.new(color.white, 0)

pLongSL = plot(showZ and zoneBull ? longSL : na, "MA Zone Long SL",
 color=lineColSL, style=plot.style_linebr, linewidth=1)
pLongEntry = plot(showZ and zoneBull ? longEntry : na, "MA Zone Long Entry",
 color=lineColEntry, style=plot.style_linebr, linewidth=1)
pShortSL = plot(showZ and zoneBear ? shortSL : na, "MA Zone Short SL",
 color=lineColSL, style=plot.style_linebr, linewidth=1)
pShortEntry = plot(showZ and zoneBear ? shortEntry : na, "MA Zone Short Entry",
 color=lineColEntry, style=plot.style_linebr, linewidth=1)

fill(pLongSL, pLongEntry, title="MA Zone Long Fill",
 color=(showZ and zoneBull) ? color.new(color.red, 75) : na)
fill(pShortEntry, pShortSL, title="MA Zone Short Fill",
 color=(showZ and zoneBear) ? color.new(color.yellow, 75) : na)

if showTradeEngine and enableMAOverlayZones and showMAZoneLabels
    if zoneBullFlip
        label.new(bar_index, high, "ZONE BULL",
            style=label.style_label_up, textcolor=color.white,
            color=color.new(color.green, 0), size=size.small)
    if zoneBearFlip
        label.new(bar_index, low, "ZONE BEAR",
            style=label.style_label_down, textcolor=color.white,
            color=color.new(color.red, 0), size=size.small)

// === INFO PANEL ===
if showInfoPanel and barstate.islast
    panelPos = infoPanelPosition == "Top Left" ? position.top_left : infoPanelPosition == "Bottom Right" ? position.bottom_right : infoPanelPosition == "Bottom Left" ? position.bottom_left : position.top_right

    // Performance statistics + auto-tune rows belong to the trade engine — gate them.
    bool showPerf = showTradeEngine and enablePerformanceTracking
    bool showTune = showTradeEngine and enableAutoTuning

    int rows = 1  // header
    rows += 6     // Profile, Regime, Confidence, Tier, Warning, Position

    // v1.1 context rows (shown in both modes)
    if enableVolaDim
        rows += 1  // Volatility
    rows += 1      // Maturity
    if enableRSContext
        rows += 1  // RS
    if showPlaybook
        rows += 1  // Playbook

    if compactMode
        if enableMTFGate
            rows += 1
        if showPerf
            rows += 2  // Win Rate, Profit Factor — cells are written regardless of compact mode
        if showConfidenceMeters
            rows += 1
        rows += 1  // Market Condition
        if showTune
            rows += 1  // Auto-Tune — cell written regardless of compact mode
    else
        rows += 3  // ER, Alpha, Slope
        if enableMTFGate
            rows += 1
        if showPerf
            rows += 2
        if showConfidenceMeters
            rows += 1
        rows += 1  // Market Condition
        if showTune
            rows += 1

    var table infoTable = na
    var int lastPanelRows = na
    var string lastPanelPos = ""
    if na(infoTable) or rows != lastPanelRows or infoPanelPosition != lastPanelPos
        if not na(infoTable)
            table.delete(infoTable)
        infoTable := table.new(panelPos, 2, rows,
            bgcolor=color.new(color.white, 5),
            border_width=1, border_color=color.new(color.gray, 60),
            frame_width=1, frame_color=color.new(color.gray, 40))
        lastPanelRows := rows
        lastPanelPos := infoPanelPosition

    color rdpTc = color.new(color.gray, 20)
    color rdpHd = color.new(color.gray, 90)

    table.cell(infoTable, 0, 0, "RDP", text_color=rdpTc, text_size=size.normal,
        bgcolor=rdpHd)
    table.cell(infoTable, 1, 0, "v1.1.0", text_color=rdpTc, text_size=size.small,
        bgcolor=rdpHd)

    int r = 1
    table.cell(infoTable, 0, r, "Profile:", text_color=rdpTc, text_size=size.small)
    table.cell(infoTable, 1, r, profilePreset, text_color=rdpTc, text_size=size.small)
    r += 1

    table.cell(infoTable, 0, r, "Regime:", text_color=rdpTc, text_size=size.small)
    regimeText = currentRegime == 1 ? "BULL" : currentRegime == -1 ? "BEAR" : "FLAT"
    regimeColor = currentRegime == 1 ? colorBull : currentRegime == -1 ? colorBear : rdpTc
    table.cell(infoTable, 1, r, regimeText, text_color=regimeColor, text_size=size.small)
    r += 1

    // v1.1: Volatility dimension → combined regime
    if enableVolaDim
        volaColor = volaState == 1 ? #ff6f00 : volaState == -1 ? #2962ff : rdpTc
        table.cell(infoTable, 0, r, "Volatility:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, volaTxt + " (" + str.tostring(math.round(volaScore)) + ")", text_color=volaColor, text_size=size.small)
        r += 1

    // v1.1: Regime maturity
    matColor = maturityTxt == "Aging / weakening" ? #d50000 : maturityTxt == "Mature / strong" ? #00c853 : rdpTc
    table.cell(infoTable, 0, r, "Maturity:", text_color=rdpTc, text_size=size.small)
    table.cell(infoTable, 1, r, maturityTxt, text_color=matColor, text_size=size.small)
    r += 1

    // v1.1: RS vs benchmark (equities)
    if enableRSContext
        rsColor = na(ctxMrs) ? rdpTc : (ctxMrs > 0 ? #00c853 : #d50000)
        rsTxt = na(ctxMrs) ? "—" : (ctxMrs > 0 ? "Leading " : "Lagging ") + str.tostring(ctxMrs, "#.#") + "%"
        table.cell(infoTable, 0, r, "RS:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, rsTxt, text_color=rsColor, text_size=size.small)
        r += 1

    table.cell(infoTable, 0, r, "Confidence:", text_color=rdpTc, text_size=size.small)
    table.cell(infoTable, 1, r, str.tostring(regimeConfidence, "#.##"),
        text_color=rdpTc, text_size=size.small)
    r += 1

    table.cell(infoTable, 0, r, "Tier:", text_color=rdpTc, text_size=size.small)
    tierText = qualA ? "A" : qualB ? "B" : "C"
    tierColor = qualA ? #00c853 : qualB ? #ff6f00 : #d50000
    table.cell(infoTable, 1, r, tierText, text_color=tierColor, text_size=size.small)
    r += 1

    table.cell(infoTable, 0, r, "Warning:", text_color=rdpTc, text_size=size.small)
    warnText = warnMTF ? "MTF" : warnChop ? "CHOP" : warnLowVol ? "LOWVOL" : "None"
    table.cell(infoTable, 1, r, warnText, text_color=rdpTc, text_size=size.small)
    r += 1

    table.cell(infoTable, 0, r, "Position:", text_color=rdpTc, text_size=size.small)
    posText = currentPosition == 1 ? "LONG" : currentPosition == -1 ? "SHORT" : "FLAT"
    table.cell(infoTable, 1, r, posText, text_color=rdpTc, text_size=size.small)
    r += 1

    if not compactMode
        table.cell(infoTable, 0, r, "ER:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, str.tostring(adaptiveState.efficiencyRatio, "#.##"),
            text_color=rdpTc, text_size=size.small)
        r += 1

        table.cell(infoTable, 0, r, "Alpha:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, str.tostring(adaptiveState.currentAlpha, "#.###"),
            text_color=rdpTc, text_size=size.small)
        r += 1

        table.cell(infoTable, 0, r, "Slope:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, str.tostring(adaptiveState.normalizedSlope, "#.###"),
            text_color=rdpTc, text_size=size.small)
        r += 1

    if enableMTFGate
        table.cell(infoTable, 0, r, "HTF Agreement:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, str.tostring(htfAgreement, "#.##"),
            text_color=rdpTc, text_size=size.small)
        r += 1

    if showPerf
        winRateTxt = performanceMetrics.sampleCount > 10 ? str.tostring(performanceMetrics.winRate * 100, "#") + "%" : "—"
        pfTxt = performanceMetrics.sampleCount > 10 ? str.tostring(performanceMetrics.profitFactor, "#.##") : "—"

        table.cell(infoTable, 0, r, "Win Rate:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, winRateTxt, text_color=rdpTc, text_size=size.small)
        r += 1

        table.cell(infoTable, 0, r, "Profit Factor:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, pfTxt, text_color=rdpTc, text_size=size.small)
        r += 1

    if showConfidenceMeters
        table.cell(infoTable, 0, r, "Signal Quality:", text_color=rdpTc, text_size=size.small)
        qualityText = filterState.signalQuality >= 0.8 ? "Excellent" : filterState.signalQuality >= 0.6 ? "Good" : filterState.signalQuality >= 0.4 ? "Fair" : "Poor"
        table.cell(infoTable, 1, r, qualityText, text_color=rdpTc, text_size=size.small)
        r += 1

    table.cell(infoTable, 0, r, "Market Condition:", text_color=rdpTc, text_size=size.small)
    conditionText = filterState.marketCondition == 1 ? "Trending" : filterState.marketCondition == -1 ? "Choppy" : "Neutral"
    table.cell(infoTable, 1, r, conditionText, text_color=rdpTc, text_size=size.small)
    r += 1

    // v1.1: plain-language action hint for the current combined regime
    if showPlaybook
        pbColor = currentRegime == 1 ? #00c853 : currentRegime == -1 ? #d50000 : #ff6f00
        table.cell(infoTable, 0, r, "Action:", text_color=rdpTc, text_size=size.small)
        table.cell(infoTable, 1, r, playbook, text_color=pbColor, text_size=size.small)
        r += 1

    if showTune
        table.cell(infoTable, 0, r, "Auto-Tune:", text_color=rdpTc, text_size=size.small)
        tuneStatus = performanceMetrics.sampleCount >= tuningPeriod ? "ACTIVE" : "LEARNING"
        table.cell(infoTable, 1, r, tuneStatus, text_color=rdpTc, text_size=size.small)
        r += 1

// === ALERTS ===
// Two ways to consume the same events — set up EITHER, not both, or signals double-fire:
//   1. Named alerts below (alertcondition) — pick individual events in the "Create Alert" dialog.
//   2. The single combined "any alert() function call" rule further down — one alert that covers
//      Long/Short/Exit/Regime/HTF and carries richer context (Regime/Conf/HTF/Q) for webhooks.
// Smart alert conditions based on confidence
highConfidenceLong = showTradeEngine and enableSmartAlerts and (longSignal and regimeConfidence >= alertConfidenceMin)
highConfidenceShort = showTradeEngine and enableSmartAlerts and (shortSignal and regimeConfidence >= alertConfidenceMin)

bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed

alertcondition(alertOk and (highConfidenceLong), "RDP Long Entry",
 "RDP · LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (highConfidenceShort), "RDP Short Entry",
 "RDP · SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (showTradeEngine and enableSmartAlerts and exitLongSignal), "RDP Exit Long",
 "RDP · EXIT LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (showTradeEngine and enableSmartAlerts and exitShortSignal), "RDP Exit Short",
 "RDP · EXIT SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (enableSmartAlerts and (highConfidenceLong or highConfidenceShort)), "RDP Any High Confidence",
 "RDP · HIGH CONFIDENCE SIGNAL · {{ticker}} {{interval}}")

// Regime change alerts
regimeChanged = ta.change(currentRegime) != 0
alertcondition(alertOk and (enableSmartAlerts and regimeChanged), "RDP Regime Change",
 "RDP · REGIME CHANGE · {{ticker}} {{interval}}")

// HTF divergence alerts
htfDivergence = enableHTFDivergence and enableMTFGate and (mtfState.divergenceStrength >= 0.7)
alertcondition(alertOk and (enableSmartAlerts and htfDivergence), "RDP HTF Divergence",
 "RDP · HTF DIVERGENCE · {{ticker}} {{interval}}")

// v1.1 context alerts (events computed in the context layer) — gated by enableSmartAlerts for
// consistency with the other context alerts (regime change, HTF divergence)
alertcondition(alertOk and (enableSmartAlerts and regimeWeakeningEvent), "RDP Regime Weakening",
 "RDP · REGIME WEAKENING · {{ticker}} {{interval}}")
alertcondition(alertOk and (enableSmartAlerts and volaCompression), "RDP Volatility Compression",
 "RDP · VOLA COMPRESSION · {{ticker}} {{interval}}")
alertcondition(alertOk and (enableSmartAlerts and volaExpansion), "RDP Volatility Expansion",
 "RDP · VOLA EXPANSION · {{ticker}} {{interval}}")

// Combined webhook alert (option 2 above) — one "any alert() function call" rule covers all events
// below with machine-parseable key=value context. Forced to bar close, never repaints intrabar.
// Don't also enable the named alertcondition() alerts above, or each event fires twice.
if highConfidenceLong
    alert("RDP LONG | Regime=" + str.tostring(currentRegime) + " | Conf=" + str.tostring(regimeConfidence, "#.00") + " | HTF=" + str.tostring(htfAgreement, "#.00") + " | Q=" + str.tostring(filterState.signalQuality, "#.00"), alert.freq_once_per_bar_close)
if highConfidenceShort
    alert("RDP SHORT | Regime=" + str.tostring(currentRegime) + " | Conf=" + str.tostring(regimeConfidence, "#.00") + " | HTF=" + str.tostring(htfAgreement, "#.00") + " | Q=" + str.tostring(filterState.signalQuality, "#.00"), alert.freq_once_per_bar_close)
if showTradeEngine and enableSmartAlerts and exitLongSignal
    alert("RDP EXIT LONG | Regime=" + str.tostring(currentRegime) + " | Conf=" + str.tostring(regimeConfidence, "#.00"), alert.freq_once_per_bar_close)
if showTradeEngine and enableSmartAlerts and exitShortSignal
    alert("RDP EXIT SHORT | Regime=" + str.tostring(currentRegime) + " | Conf=" + str.tostring(regimeConfidence, "#.00"), alert.freq_once_per_bar_close)
if enableSmartAlerts and regimeChanged
    alert("RDP REGIME CHANGE | Regime=" + str.tostring(currentRegime) + " | Conf=" + str.tostring(regimeConfidence, "#.00"), alert.freq_once_per_bar_close)
if enableSmartAlerts and htfDivergence
    alert("RDP HTF DIVERGENCE | Strength=" + str.tostring(mtfState.divergenceStrength, "#.00"), alert.freq_once_per_bar_close)

// === SSA: SMART SIGNALS ASSISTANT ===
ssaReg = currentRegime
ssaConf = regimeConfidence
ssaLine = adaptiveLine
ssaHTF = htfAgreement
ssaQual = filterState.signalQuality

ssaBull = ssaReg == 1
ssaBear = ssaReg == -1
ssaFlat = ssaReg == 0

ssaMinConf = ssaMode == "Swing" ? ssaMinConfSwing : ssaMinConfScalp
cond_conf_ok = ssaConf >= ssaMinConf
cond_htf_ok = ssaHTF >= ssaMinHTF
cond_qual_ok = ssaQual >= ssaMinQuality

cond_strong = (ssaConf >= ssaStrongConf) and (ssaHTF >= ssaStrongHTF) and (ssaQual >= ssaStrongQuality)
cond_tierA = (ssaConf >= tierAConf) and (ssaHTF >= tierAAgree) and (ssaQual >= tierAQuality)
cond_tierB = (ssaConf >= tierBConf) and (ssaHTF >= tierBAgree) and (ssaQual >= tierBQuality)
cond_tierBplus = cond_tierA or cond_tierB

trcTrending = filterState.marketCondition == 1
trcOk = not ssaSuperNeedsTRC or trcTrending

var int ssaLastFire = na
ssaCanFire = na(ssaLastFire) or (bar_index - ssaLastFire >= ssaCooldownBars)

// Fair Value Trail (FVT)
fvtLen = input.int(14, "FVT ATR Len", group=SSA_GROUP, minval=2)
fvtMult = input.float(1.4, "FVT Mult", group=SSA_GROUP, step=0.1)
fvtAtr = ta.atr(fvtLen)
fvtUpper = ssaLine + fvtMult * fvtAtr
fvtLower = ssaLine - fvtMult * fvtAtr

var float fvt = na
if barstate.isfirst
    fvt := ssaLine
else
    if ssaBull
        fvt := math.max(nz(fvt[1], ssaLine), fvtLower)
    else if ssaBear
        fvt := math.min(nz(fvt[1], ssaLine), fvtUpper)
    else
        fvt := nz(fvt[1], ssaLine)

fvtBull = close >= fvt
fvtBear = close <= fvt

// Shared ATR(14) — computed unconditionally at global scope so that functions
// called conditionally (SL source, trail candidate) never skip the ta.* call
float ssaAtr14 = ta.atr(14)

// Trend Bias Band
biasLen = input.int(20, "Trend Bias Len", group=SSA_GROUP, minval=2)
biasMom = ta.ema(close - ssaLine, biasLen)
biasAbs = ta.ema(math.abs(biasMom), biasLen)
biasNorm = biasAbs > 0 ? biasMom / biasAbs : 0.0
biasBull = biasNorm > 0
biasBear = biasNorm < 0

biasBand = (ssaAtr14 * 0.25) * biasNorm
biasTop = ssaLine + biasBand
biasBot = ssaLine - biasBand

// ATR Clouds
cloudLen = input.int(20, "Cloud ATR Len", group=SSA_GROUP, minval=2)
cloudMult = input.float(2.0, "Cloud Mult", group=SSA_GROUP, step=0.1)
cloudAtr = ta.atr(cloudLen)
cloudU = ssaLine + cloudMult * cloudAtr
cloudL = ssaLine - cloudMult * cloudAtr
cloudBull = close > ssaLine and biasBull
cloudBear = close < ssaLine and biasBear

fvtAligned = (ssaBull and fvtBull) or (ssaBear and fvtBear)
cloudAligned = (ssaBull and cloudBull) or (ssaBear and cloudBear)
fvtOk = not ssaSuperNeedsFVT or fvtAligned
cloudOk = not ssaSuperNeedsCloud or cloudAligned
cond_super = ssaSuperEnabled and cond_strong and trcOk and fvtOk and cloudOk

// SSA Signal Engine
tierGateOk = ssaEntryTierGate == "Off" ? true : ssaEntryTierGate == "Tier A only" ? cond_tierA : cond_tierBplus
tierGateOk := ssaRequireRdpTierBPlus ? (tierGateOk and cond_tierBplus) : tierGateOk

ssaGateOk = (not ssaUseRDPGate) or ((cond_conf_ok and cond_htf_ok and cond_qual_ok and not ssaFlat) and tierGateOk)

longTrend = ssaEnable and ssaCanFire and ssaGateOk and ssaBull
shortTrend = ssaEnable and ssaCanFire and ssaGateOk and ssaBear

revDistMult = input.float(1.5, "Reversal Distance (ATR)", group=SSA_GROUP, step=0.1)
revDist = ssaAtr14 * revDistMult

longRev = ssaEnable and ssaCanFire and (not ssaFlat) and (close < fvt - revDist) and biasBull
shortRev = ssaEnable and ssaCanFire and (not ssaFlat) and (close > fvt + revDist) and biasBear

ssaLong = ssaSignalMode == "Trend" ? longTrend : longRev
ssaShort = ssaSignalMode == "Trend" ? shortTrend : shortRev

ssaStrongLong = ssaLong and cond_strong
ssaStrongShort = ssaShort and cond_strong
ssaSuperLong = ssaLong and cond_super
ssaSuperShort = ssaShort and cond_super

ssaLongEvent_engine = ssaLong and not ssaLong[1]
ssaShortEvent_engine = ssaShort and not ssaShort[1]

ssaLongEvent_rdp = ssaEntrySource == "RDP A only" ? rdpLongAEvent : ssaEntrySource == "RDP B only" ? rdpLongBEvent : rdpLongAnyAB

ssaShortEvent_rdp = ssaEntrySource == "RDP A only" ? rdpShortAEvent : ssaEntrySource == "RDP B only" ? rdpShortBEvent : rdpShortAnyAB

ssaLongEvent = (ssaEntrySource == "SSA Engine") ? ssaLongEvent_engine : ssaLongEvent_rdp
ssaShortEvent = (ssaEntrySource == "SSA Engine") ? ssaShortEvent_engine : ssaShortEvent_rdp

if ssaLongEvent or ssaShortEvent
    ssaLastFire := bar_index

// SSA Visuals
plot(ssaEnable and ssaShowFVT ? fvt : na, "FVT", color=color.new(color.white, 0), linewidth=2)

pBiasTop = plot(ssaEnable and ssaShowTrendBias ? biasTop : na, "SSA Bias Top", color=color.new(color.teal, 60))
pBiasBot = plot(ssaEnable and ssaShowTrendBias ? biasBot : na, "SSA Bias Bot", color=color.new(color.teal, 60))
fill(pBiasTop, pBiasBot, color=(ssaEnable and ssaShowTrendBias) ? color.new(color.teal, 88) : na, title="SSA Bias Fill")

pCloudU = plot(ssaEnable and ssaShowClouds ? cloudU : na, "SSA Cloud U", color=color.new(color.gray, 80))
pCloudL = plot(ssaEnable and ssaShowClouds ? cloudL : na, "SSA Cloud L", color=color.new(color.gray, 80))
fill(pCloudU, pCloudL, color=(ssaEnable and ssaShowClouds) ? color.new(color.gray, 90) : na, title="SSA Cloud Fill")

ssaTierIsStrong = isRdpSource ? cond_tierA : cond_strong
ssaTierIsSuper = isRdpSource ? (cond_tierA and cond_super) : cond_super

ssaStrongLongEvent = ssaLongEvent and ssaTierIsStrong
ssaStrongShortEvent = ssaShortEvent and ssaTierIsStrong
ssaSuperLongEvent = ssaLongEvent and ssaTierIsSuper
ssaSuperShortEvent = ssaShortEvent and ssaTierIsSuper
ssaNormalLongEvent = ssaLongEvent and not ssaTierIsStrong
ssaNormalShortEvent = ssaShortEvent and not ssaTierIsStrong
ssaStrongOnlyLongEvent = ssaStrongLongEvent and not ssaSuperLongEvent
ssaStrongOnlyShortEvent = ssaStrongShortEvent and not ssaSuperShortEvent

drawOff = ssaSignalDisplay == "Off"
drawAll = ssaSignalDisplay == "All"
drawStrong = ssaSignalDisplay == "Strong+"
drawSuper = ssaSignalDisplay == "Super only"

drawSignalsOk = ssaEnable and ssaShowSignals and not drawOff

drawNormalLong = ssaNormalLongEvent and drawAll
drawNormalShort = ssaNormalShortEvent and drawAll
drawStrongLong = ssaStrongOnlyLongEvent and (drawAll or drawStrong)
drawStrongShort = ssaStrongOnlyShortEvent and (drawAll or drawStrong)
drawSuperLong = ssaSuperLongEvent and (drawAll or drawStrong or drawSuper)
drawSuperShort = ssaSuperShortEvent and (drawAll or drawStrong or drawSuper)

ssaLongEvDraw = drawSignalsOk and (drawNormalLong or drawStrongLong or drawSuperLong)
ssaShortEvDraw = drawSignalsOk and (drawNormalShort or drawStrongShort or drawSuperShort)
ssaLongTxt = drawSuperLong ? "L++" : drawStrongLong ? "L+" : "L"
ssaShortTxt = drawSuperShort ? "S++" : drawStrongShort ? "S+" : "S"

plotshape(ssaLongEvDraw, "SSA Long",
    shape.triangleup, location.belowbar, color=color.new(color.lime, 0), size=size.small)
plotshape(ssaShortEvDraw, "SSA Short",
    shape.triangledown, location.abovebar, color=color.new(color.red, 0), size=size.small)

// Candle coloring
ssa_norm01(x) => math.max(0.0, math.min(1.0, x))
mom = ta.ema(close - close[1], 10)
momN = ssa_norm01(math.abs(mom) / (ssaAtr14 + 1e-6))
volN = ssa_norm01(volume / (ta.sma(volume, 50) + 1e-6))

cTrend = ssaBull ? color.new(color.lime, 0) : ssaBear ? color.new(color.red, 0) : color.new(color.gray, 0)
cRange = color.new(color.gray, 0)
cMom = ssaBull ? color.new(color.lime, int(80 - momN * 70)) : ssaBear ? color.new(color.red, int(80 - momN * 70)) : color.new(color.gray, 30)
cVol = ssaBull ? color.new(color.lime, int(85 - volN * 75)) : ssaBear ? color.new(color.red, int(85 - volN * 75)) : color.new(color.gray, 40)
ssaIsRanging = ssaFlat or (filterState.marketCondition == -1)

// Shared bar coloring: SSA candle coloring takes precedence; when it is Off the
// regime candle tint (regimeCandleCol) is used. Single barcolor() to respect the plot budget.
ssaBarCol = ssaEnable ? ssaCandleColoring == "Trend Colors" ? cTrend : ssaCandleColoring == "Momentum Gradient" ? cMom : ssaCandleColoring == "Volume Gradient" ? cVol : ssaCandleColoring == "Gray In Ranging" ? (ssaIsRanging ? cRange : cTrend) : na : na
barcolor( na(ssaBarCol) ? regimeCandleCol : ssaBarCol )

// Trade Management (TP/SL)
var int ssaPos = 0
var float ssaEntryPx = na
var float ssaSL = na
var float ssaTP = na
var int ssaEntryBar = na
var bool pLong_on = false
var bool pLong_tierA = false
var float pLong_px = na
var int pLong_bar = na

var bool pShort_on = false
var bool pShort_tierA = false
var float pShort_px = na
var int pShort_bar = na

plotshape(ssaEnable and ssaShowPending and pLong_on, "SSA Pending Long",
    shape.circle, location=location.belowbar, color=color.new(color.aqua, 0), size=size.tiny, text="PL")
plotshape(ssaEnable and ssaShowPending and pShort_on, "SSA Pending Short",
    shape.circle, location=location.abovebar, color=color.new(color.aqua, 0), size=size.tiny, text="PS")

var box ssaRiskBox = na
var box ssaRewardBox = na
var line ssaEntryLine = na
var line ssaSLLine = na
var line ssaTPLine = na

ssaSL_Long() => ssaSLSource == "RDP Exit Band" ? lowerExit : ssaSLSource == "RDP Entry Band" ? lowerEntry : ssaSLSource == "MA Zone SL (if enabled)" ? (enableMAOverlayZones and not na(longSL) ? longSL : lowerExit) : (ssaLine - ssaAtr14 * 1.2)

ssaSL_Short() => ssaSLSource == "RDP Exit Band" ? upperExit : ssaSLSource == "RDP Entry Band" ? upperEntry : ssaSLSource == "MA Zone SL (if enabled)" ? (enableMAOverlayZones and not na(shortSL) ? shortSL : upperExit) : (ssaLine + ssaAtr14 * 1.2)

f_priceByMode(bool _isLong, string _mode) => _mode == "Close" ? close : _mode == "High/Low" ? (_isLong ? high : low) : _mode == "Entry Band (upper/lower)" ? (_isLong ? upperEntry : lowerEntry) : _mode == "Exit Band (upper/lower)" ? (_isLong ? upperExit : lowerExit) : ssaLine

f_rdpEntryPriceTier(bool _isLong, bool _isTierA) => ssaRdpProMode ? f_priceByMode(_isLong, _isTierA ? ssaRdpEntryPriceModeA : ssaRdpEntryPriceModeB) : f_priceByMode(_isLong, ssaRdpEntryPriceMode)

f_pendingTriggerLevel(bool _isLong) =>
    ssaRdpLimitTouchMode == "Touch Entry Band" ? (_isLong ? upperEntry : lowerEntry) : ssaRdpLimitTouchMode == "Touch Exit Band" ? (_isLong ? upperExit : lowerExit) : ssaRdpLimitTouchMode == "Touch Adaptive Line" ? ssaLine : ssaRdpLimitTouchMode == "Touch FVT" ? fvt : (_isLong ? cloudL : cloudU)

f_touch(float _level) =>
    low <= _level and high >= _level

f_closeConfirm(bool _isLong, float _lvl) => ssaCloseConfirmBars <= 1 ? (_isLong ? close >= _lvl : close <= _lvl) : ta.sma((_isLong ? close >= _lvl : close <= _lvl) ? 1 : 0, ssaCloseConfirmBars) == 1

f_activate(bool _isLong, float _lvl) => ssaLimitActivation == "Touch" ? f_touch(_lvl) : f_closeConfirm(_isLong, _lvl)

f_rdpEntryPrice(bool _isLong) => ssaRdpEntryPriceMode == "Close" ? close : ssaRdpEntryPriceMode == "High/Low" ? (_isLong ? high : low) : ssaRdpEntryPriceMode == "Entry Band (upper/lower)" ? (_isLong ? upperEntry : lowerEntry) : ssaRdpEntryPriceMode == "Exit Band (upper/lower)" ? (_isLong ? upperExit : lowerExit) : ssaLine

f_trailCandidate(bool _isLong) =>
    float atrB = ssaAtr14 * ssaTrailBufferATR
    float base = ssaTrailSource == "FVT" ? fvt : ssaTrailSource == "Cloud" ? (_isLong ? cloudL : cloudU) : ssaLine
    _isLong ? (base - atrB) : (base + atrB)

f_drawTrade(bool _isLong, float _entry, float _sl, float _tp, line _entryLine, line _slLine, line _tpLine, box _riskBox, box _rewardBox, int _extendBars) =>
    int x1 = bar_index
    int x2 = bar_index + _extendBars

    if not na(_riskBox)
        box.delete(_riskBox)
    if not na(_rewardBox)
        box.delete(_rewardBox)
    if not na(_entryLine)
        line.delete(_entryLine)
    if not na(_slLine)
        line.delete(_slLine)
    if not na(_tpLine)
        line.delete(_tpLine)

    line newEntry = line.new(x1, _entry, x2, _entry, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.white, 0), width=1)
    line newSL = line.new(x1, _sl, x2, _sl, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.red, 0), width=1)
    line newTP = line.new(x1, _tp, x2, _tp, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.lime, 0), width=1)

    float topRisk = _isLong ? _entry : _sl
    float botRisk = _isLong ? _sl : _entry
    float topRew = _isLong ? _tp : _entry
    float botRew = _isLong ? _entry : _tp

    box newRisk = box.new(x1, topRisk, x2, botRisk, xloc=xloc.bar_index, border_color=color.new(color.red, 30), bgcolor=color.new(color.red, 88))
    box newReward = box.new(x1, topRew, x2, botRew, xloc=xloc.bar_index, border_color=color.new(color.lime, 30), bgcolor=color.new(color.lime, 88))

    [newEntry, newSL, newTP, newRisk, newReward]

f_updateLines(line _entryLine, line _slLine, line _tpLine, float _entry, float _sl, float _tp) =>
    if not na(_entryLine)
        line.set_y1(_entryLine, _entry)
        line.set_y2(_entryLine, _entry)
    if not na(_slLine)
        line.set_y1(_slLine, _sl)
        line.set_y2(_slLine, _sl)
    if not na(_tpLine)
        line.set_y1(_tpLine, _tp)
        line.set_y2(_tpLine, _tp)

f_updateBoxes(box _riskBox, box _rewardBox, float _entry, float _sl, float _tp, bool _isLong) =>
    if not na(_riskBox)
        float topRisk = _isLong ? _entry : _sl
        float botRisk = _isLong ? _sl : _entry
        box.set_top(_riskBox, topRisk)
        box.set_bottom(_riskBox, botRisk)
    if not na(_rewardBox)
        float topRew = _isLong ? _tp : _entry
        float botRew = _isLong ? _entry : _tp
        box.set_top(_rewardBox, topRew)
        box.set_bottom(_rewardBox, botRew)

f_setPending(int _dir, bool _tierA, float _px, bool _pLong_on, bool _pLong_tierA, float _pLong_px, int _pLong_bar, bool _pShort_on, bool _pShort_tierA, float _pShort_px, int _pShort_bar, bool _dual) =>
    bool nLong_on = _pLong_on
    bool nLong_tierA = _pLong_tierA
    float nLong_px = _pLong_px
    int nLong_bar = _pLong_bar
    bool nShort_on = _pShort_on
    bool nShort_tierA = _pShort_tierA
    float nShort_px = _pShort_px
    int nShort_bar = _pShort_bar

    if _dir == 1
        nLong_on := true
        nLong_tierA := _tierA
        nLong_px := _px
        nLong_bar := bar_index
        if not _dual
            nShort_on := false
            nShort_tierA := false
            nShort_px := na
            nShort_bar := na
    else
        nShort_on := true
        nShort_tierA := _tierA
        nShort_px := _px
        nShort_bar := bar_index
        if not _dual
            nLong_on := false
            nLong_tierA := false
            nLong_px := na
            nLong_bar := na

    [nLong_on, nLong_tierA, nLong_px, nLong_bar, nShort_on, nShort_tierA, nShort_px, nShort_bar]



pLong_timeout = pLong_on and not na(pLong_bar) and ((bar_index - pLong_bar) >= ssaRdpLimitTimeoutBars)
pShort_timeout = pShort_on and not na(pShort_bar) and ((bar_index - pShort_bar) >= ssaRdpLimitTimeoutBars)

// Trading engine and TP/SL levels removed (reines Visuals-Tool)
ssaExitWasLong = false
ssaExitWasShort = false

// External condition evaluation
ssa_ext_cond(src) => ssaExtCmp == "Crossover" ? ta.crossover(src, 0) : ssaExtCmp == "Crossunder" ? ta.crossunder(src, 0) : ssaExtCmp == "Above 0" ? src > 0 : ssaExtCmp == "Below 0" ? src < 0 : ssaExtCmp == "Rising" ? ta.rising(src, 3) : ta.falling(src, 3)

ext1 = ssa_ext_cond(ssaExternal1)
ext2 = ssa_ext_cond(ssaExternal2)
ext3 = ssa_ext_cond(ssaExternal3)
ext4 = ssa_ext_cond(ssaExternal4)
ext5 = ssa_ext_cond(ssaExternal5)

ssa_cond(id) => id == "None" ? true : id == "SSA Long" ? ssaLongEvent : id == "SSA Short" ? ssaShortEvent : id == "SSA Strong Long" ? (ssaLongEvent and cond_strong) : id == "SSA Strong Short" ? (ssaShortEvent and cond_strong) : id == "RDP Bull Regime" ? ssaBull : id == "RDP Bear Regime" ? ssaBear : id == "RDP Ranging" ? ssaFlat : id == "RDP Tier A" ? cond_tierA : id == "RDP Tier B+" ? cond_tierBplus : id == "HTF OK" ? cond_htf_ok : id == "Quality OK" ? cond_qual_ok : id == "Confidence OK" ? cond_conf_ok : id == "FVT Bull" ? fvtBull : id == "FVT Bear" ? fvtBear : id == "Cloud Bull" ? cloudBull : id == "Cloud Bear" ? cloudBear : id == "External #1" ? ext1 : id == "External #2" ? ext2 : id == "External #3" ? ext3 : id == "External #4" ? ext4 : id == "External #5" ? ext5 : false

ssa_apply_conn(a, b, conn) =>
    conn == "OR" ? (a or b) : (a and b)

step1 = ssa_cond(step1Cond)
step2 = ssa_cond(step2Cond)
step3 = ssa_cond(step3Cond)
step4 = ssa_cond(step4Cond)
step5 = ssa_cond(step5Cond)
step6 = ssa_cond(step6Cond)
step7 = ssa_cond(step7Cond)

expr = step1
if ssaStepCount >= 2
    expr := ssa_apply_conn(expr, step2, step2Conn)
if ssaStepCount >= 3
    expr := ssa_apply_conn(expr, step3, step3Conn)
if ssaStepCount >= 4
    expr := ssa_apply_conn(expr, step4, step4Conn)
if ssaStepCount >= 5
    expr := ssa_apply_conn(expr, step5, step5Conn)
if ssaStepCount >= 6
    expr := ssa_apply_conn(expr, step6, step6Conn)
if ssaStepCount >= 7
    expr := ssa_apply_conn(expr, step7, step7Conn)

ssaAlertFire = ssaEnable and ssaEnableAlerts and expr

alertcondition(alertOk and (ssaAlertFire), "SSA Custom Alert (Builder)", "RDP · SSA BUILDER · {{ticker}} {{interval}}")
alertcondition(alertOk and (ssaEnable and ssaLongEvent), "SSA Long", "RDP · SSA LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (ssaEnable and ssaShortEvent), "SSA Short", "RDP · SSA SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (ssaEnable and ssaStrongLongEvent), "SSA Strong Long", "RDP · SSA STRONG LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (ssaEnable and ssaStrongShortEvent), "SSA Strong Short", "RDP · SSA STRONG SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (ssaEnable and ssaSuperLongEvent), "SSA Super Long", "RDP · SSA SUPER LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (ssaEnable and ssaSuperShortEvent), "SSA Super Short", "RDP · SSA SUPER SHORT · {{ticker}} {{interval}}")

// Status Table
f_stateTxt(_on, _bull, _bear, _flat) =>
    not _on ? "OFF" : _flat ? "RANGE" : _bull ? "BULL" : _bear ? "BEAR" : "-"

f_boolTxt(b) => b ? "ON" : "OFF"

var table ssaTable = na
var int ssaTableRows = na
var string ssaTablePosLast = ""

var int dRegBar = na
var int dFVTBar = na
var int dCloudBar = na
var int dTRCBar = na
var int dPosBar = na

regState = ssaFlat ? 0 : ssaBull ? 1 : -1
fvtState = fvtBull ? 1 : fvtBear ? -1 : 0
cloudState = cloudBull ? 1 : cloudBear ? -1 : 0
trcState = trcTrending ? 1 : (filterState.marketCondition == -1 ? -1 : 0)
posState = ssaPos

if barstate.isfirst
    dRegBar := bar_index
    dFVTBar := bar_index
    dCloudBar := bar_index
    dTRCBar := bar_index
    dPosBar := bar_index
else
    if regState != regState[1]
        dRegBar := bar_index
    if fvtState != fvtState[1]
        dFVTBar := bar_index
    if cloudState != cloudState[1]
        dCloudBar := bar_index
    if trcState != trcState[1]
        dTRCBar := bar_index
    if posState != posState[1]
        dPosBar := bar_index

durReg = bar_index - nz(dRegBar, bar_index)
durFVT = bar_index - nz(dFVTBar, bar_index)
durCloud = bar_index - nz(dCloudBar, bar_index)
durTRC = bar_index - nz(dTRCBar, bar_index)
durPos = bar_index - nz(dPosBar, bar_index)

if ssaEnable and ssaShowTable and barstate.islast
    pos = ssaTablePos == "Top Left" ? position.top_left : ssaTablePos == "Bottom Right" ? position.bottom_right : ssaTablePos == "Bottom Left" ? position.bottom_left : position.top_right

    rows = ssaTableCompact ? 9 : 12
    if na(ssaTable) or ssaTableRows != rows or ssaTablePosLast != ssaTablePos
        if not na(ssaTable)
            table.delete(ssaTable)
        ssaTable := table.new(pos, 4, rows,
            bgcolor=color.new(color.white, 5),
            border_color=color.new(color.gray, 60), border_width=1,
            frame_color=color.new(color.gray, 40), frame_width=1)
        ssaTableRows := rows
        ssaTablePosLast := ssaTablePos

    color ssaTc = color.new(color.gray, 20)
    color ssaHd = color.new(color.gray, 90)

    table.cell(ssaTable, 0, 0, "SSA", text_color=ssaTc, bgcolor=ssaHd)
    table.cell(ssaTable, 1, 0, "State", text_color=ssaTc, bgcolor=ssaHd)
    table.cell(ssaTable, 2, 0, "Dur", text_color=ssaTc, bgcolor=ssaHd)
    table.cell(ssaTable, 3, 0, "Info", text_color=ssaTc, bgcolor=ssaHd)

    int r = 1
    table.cell(ssaTable, 0, r, "Regime", text_color=ssaTc)
    table.cell(ssaTable, 1, r, f_stateTxt(true, ssaBull, ssaBear, ssaFlat), text_color=ssaTc)
    table.cell(ssaTable, 2, r, str.tostring(durReg), text_color=ssaTc)
    table.cell(ssaTable, 3, r, "conf " + str.tostring(ssaConf, "#.00"), text_color=ssaTc)
    r += 1

    table.cell(ssaTable, 0, r, "HTF", text_color=ssaTc)
    table.cell(ssaTable, 1, r, f_boolTxt(cond_htf_ok), text_color=ssaTc)
    table.cell(ssaTable, 2, r, "", text_color=ssaTc)
    table.cell(ssaTable, 3, r, str.tostring(ssaHTF, "#.00"), text_color=ssaTc)
    r += 1

    table.cell(ssaTable, 0, r, "Quality", text_color=ssaTc)
    table.cell(ssaTable, 1, r, f_boolTxt(cond_qual_ok), text_color=ssaTc)
    table.cell(ssaTable, 2, r, "", text_color=ssaTc)
    table.cell(ssaTable, 3, r, str.tostring(ssaQual, "#.00"), text_color=ssaTc)
    r += 1

    table.cell(ssaTable, 0, r, "TRC", text_color=ssaTc)
    table.cell(ssaTable, 1, r, trcTrending ? "TREND" : (filterState.marketCondition == -1 ? "CHOP" : "NEUT"), text_color=ssaTc)
    table.cell(ssaTable, 2, r, str.tostring(durTRC), text_color=ssaTc)
    table.cell(ssaTable, 3, r, "mc " + str.tostring(filterState.marketCondition), text_color=ssaTc)
    r += 1

    table.cell(ssaTable, 0, r, "FVT", text_color=ssaTc)
    table.cell(ssaTable, 1, r, f_stateTxt(ssaShowFVT, fvtBull, fvtBear, fvtState == 0), text_color=ssaTc)
    table.cell(ssaTable, 2, r, str.tostring(durFVT), text_color=ssaTc)
    table.cell(ssaTable, 3, r, "d " + str.tostring((close - fvt), format.mintick), text_color=ssaTc)
    r += 1

    table.cell(ssaTable, 0, r, "Cloud", text_color=ssaTc)
    table.cell(ssaTable, 1, r, f_stateTxt(ssaShowClouds, cloudBull, cloudBear, cloudState == 0), text_color=ssaTc)
    table.cell(ssaTable, 2, r, str.tostring(durCloud), text_color=ssaTc)
    table.cell(ssaTable, 3, r, "", text_color=ssaTc)
    r += 1

    table.cell(ssaTable, 0, r, "Tier", text_color=ssaTc)
    table.cell(ssaTable, 1, r, cond_super ? "SUPER" : cond_strong ? "STRONG" : "NORMAL", text_color=ssaTc)
    table.cell(ssaTable, 2, r, "", text_color=ssaTc)
    table.cell(ssaTable, 3, r, "", text_color=ssaTc)
    r += 1

    posTxt = ssaPos == 1 ? "LONG" : ssaPos == -1 ? "SHORT" : "FLAT"
    posInfo = ssaPos != 0 ? ("SL " + str.tostring(ssaSL, format.mintick) + " | TP " + str.tostring(ssaTP, format.mintick)) : ""
    table.cell(ssaTable, 0, r, "Trade", text_color=ssaTc)
    table.cell(ssaTable, 1, r, posTxt, text_color=ssaTc)
    table.cell(ssaTable, 2, r, str.tostring(durPos), text_color=ssaTc)
    table.cell(ssaTable, 3, r, posInfo, text_color=ssaTc)
    r += 1

    for rr = r to rows - 1
        table.cell(ssaTable, 0, rr, "")
        table.cell(ssaTable, 1, rr, "")
        table.cell(ssaTable, 2, rr, "")
        table.cell(ssaTable, 3, rr, "")


// === EXPORTS FOR OTHER INDICATORS ===
regimeDetectorRegime = currentRegime
regimeDetectorConfidence = regimeConfidence
regimeDetectorLine = adaptiveLine
regimeDetectorHTFAgreement = htfAgreement
regimeDetectorSignalQuality = filterState.signalQuality
regimeDetectorPosition = currentPosition

Overview

A volatility percentile rank adds quiet, normal, or volatile context, plus maturity and transition warnings. Optional settings enable a higher-timeframe gate, a work-in-progress machine-learning confidence filter, and a relative-strength comparison with a benchmark such as SPY. It draws bands, zone lines and boxes, candle tint, labels, and a playbook on the price chart. It does not place trades.

Details

Published by

casoon on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Trend, Moving averages, Volatility & bands, AI / machine learning

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Profile PresetstringSmartprofilePreset
Data SourcesourceclosedataSource
Enable Adaptive EnginebooltrueenableAdaptiveEngine
Efficiency Ratio Lengthint20erLength
Alpha Minimumfloat0.03alphaMin
Alpha Maximumfloat0.30alphaMax
Gamma (Reaction Hardness)float1.25gamma
ATR Lengthint14atrLength
Entry Band Multiplierfloat1.6entryMultiplier
Exit Band Multiplierfloat1.2exitMultiplier
Enable Advanced Regime DetectionbooltrueenableAdvancedRegime
Regime Detection ModestringTrend + MomentumregimeMode
Show all 40 settings
SettingTypeDefaultVariable
ER Threshold (Trend)float0.2erThreshold
ER Exit Thresholdfloat0.15erThresholdExit
Regime Sensitivityfloat0.6regimeSensitivity
Min Regime Confidencefloat0.65regimeConfidenceMin
Min Regime Confidence (Exit)float0.55regimeConfidenceExit
Volatility DimensionbooltrueenableVolaDim
Volatility Rank Lookbackint100volaLookback
Quiet below (percentile)int33volaQuietMax
Volatile above (percentile)int66volaVolMin
Playbook LinebooltrueshowPlaybook
Context Labels on ChartbooltrueshowContextLabels
RS vs. Benchmark (equities)boolfalseenableRSContext
BenchmarksymbolAMEX:SPYrsBenchSym
RS MA Lengthint50rsMaLen
ML-Enhanced Classification (WIP)booltrueenableMLRegime
ML Pattern Lookback (WIP)int50mlLookback
ML Confidence Threshold (WIP)float0.7mlConfidence
Enable MTF GatebooltrueenableMTFGate
HTF Weight DistributionstringBalancedhtfWeights
Min HTF Agreementfloat0.6htfMinAgreement
HTF Divergence DetectionbooltrueenableHTFDivergence
Enable Advanced Filter SystembooltrueenableAdvancedFilters
Min ATR/Pricefloat0.0007minATRPercent
Volatility Filter ModestringAdaptivevolatilityMode
Volume FilterboolfalseuseVolumeFilter
Volume MA Lengthint50volumeLength
Volume Quantilefloat0.3volumeQuantile
Market Condition FilterbooltrueenableMarketCondition
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

9 stars

Volume SuperTrend AI (Expo) combines SuperTrend trend filtering with a k-nearest neighbors machine learning classification algorithm. It computes a SuperTrend using customizable moving average types, including simple, exponential, weighted, smoothed, or volume

Indicator v5 TrendAI / machine learning
Updated 3 years ago · None stated GitHub repository of Volume SuperTrend AI (Expo)
318 stars

This indicator plots four customizable moving averages directly on the main price chart using default lengths of 3, 8, 21, and 50 periods. It fills the space between adjacent averages in green or maroon depending on trend alignment. Additionally, it computes B

Indicator v5 Moving averagesTrend
Updated 2 months ago · MIT GitHub repository of Multiple Moving Averages

NVDA Trend Ribbon

by longsurf-ai
277 stars

This indicator plots three exponential moving averages and ATR volatility envelopes on the price chart. It compares the fast and slow EMAs to shade a trend ribbon and marks confirmed upward and downward crossovers with triangle labels. Settings change the thre

Indicator v6 TrendMoving averages
Updated yesterday · MIT GitHub repository of NVDA Trend Ribbon

Bollinger Bands Force

by harryguiacorn
219 stars

This indicator modifies standard Bollinger Bands to measure directional trend force based on line alignment. It calculates a simple moving average basis along with upper and lower volatility bands. When all three lines move upward or downward simultaneously, i

Indicator v5 Volatility & bandsTrend
Updated 1 year ago · None stated GitHub repository of Bollinger Bands Force

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers