Trend Resumption Filter Osccillator

cando13579

Well-known member
VIP
Plus
Multi-Factor Trend Resumption Filter is a non-repainting lower study designed primarily for 5-minute ES charts. It evaluates the prior completed bar using normalized trend displacement, momentum, VWAP position, volume pressure, range expansion, and confirmed higher-timeframe context.

Rather than chasing an initial move, it identifies qualified pullbacks within established bullish or bearish conditions, then flags potential trend resumption when momentum and propagation speed re-accelerate. Signals and alerts appear on the next bar using completed data only, with no future-bar references or historical backfilling.

This is a market-context and setup filter—not a complete trading system. Use defined entry, exit, and risk rules, and validate settings independently for each ticker and timeframe.
  • candidates: ES,
  • Code:
    # ============================================================
    #  ES Propagation Osccilator V3 — NON-REPAINTING
    # Designed primarily for: ES 5-minute chart
    #
    # NON-REPAINTING DESIGN:
    #   - All indicator inputs use the PREVIOUS completed primary bar [1]
    #   - Session VWAP uses the PREVIOUS completed bar's VWAP
    #   - Higher-timeframe data uses the PREVIOUS closed HTF bar
    #   - No future offsets or forward references are used
    #   - Signals and alerts occur on the first update of the next bar,
    #     using only completed data from the prior bar
    #
    # Trade-off: Signals appear 1 bar later than a current-bar version,
    # but do not backfill or disappear once printed.
    # ============================================================
    
    declare lower;
    
    # ============================================================
    # GENERAL SETTINGS
    # ============================================================
    
    input setupMode = {default Balanced, Conservative, Aggressive};
    input sourceType = {default HLC3, Close, OHLC4};
    
    input showDashboard = yes;
    input showSignals = yes;
    input showWaveBackground = yes;
    input paintPriceBars = no;
    
    # ============================================================
    # MARKET STATE ENGINE
    # ============================================================
    
    input meanLength = 34;
    input momentumLength = 8;
    input normalizationLength = 50;
    input volumeLength = 20;
    input atrLength = 14;
    
    input useVWAP = yes;
    input useVolume = yes;
    input useRangeExpansion = yes;
    
    input weightMean = 1.10;
    input weightVWAP = 1.00;
    input weightMomentum = 1.20;
    input weightVolume = 0.65;
    input weightRange = 0.55;
    
    # ============================================================
    # HIGHER-TIMEFRAME CONTEXT
    # ============================================================
    
    input useHTF = yes;
    input htfAggregation = AggregationPeriod.FIFTEEN_MIN;
    input htfFastLength = 21;
    input htfSlowLength = 50;
    input weightHTF = 0.75;
    
    # ============================================================
    # NONLINEAR DIFFUSION ENGINE
    # ============================================================
    
    input diffusionM = 1.35;
    input diffusionP = 2.20;
    
    input diffusionGain = 0.18;
    input reactionGain = 0.16;
    input anchorGain = 0.22;
    
    # ============================================================
    # PROPAGATION SPEED ENGINE
    # ============================================================
    
    input speedLength = 4;
    input criticalLength = 40;
    input criticalMultiplierInput = 1.25;
    input speedSmoothLength = 3;
    
    input bullStateInput = 0.62;
    input bearStateInput = 0.38;
    
    # ============================================================
    # RETRACEMENT / RESUMPTION ENGINE
    # ============================================================
    
    input retraceWindow = 12;
    input minimumPullbackBars = 1;
    
    input requireVWAPAlignment = yes;
    input requireHTFAlignment = yes;
    
    input signalCooldown = 6;
    
    # ============================================================
    # EXHAUSTION ENGINE
    # ============================================================
    
    input showExhaustion = yes;
    input exhaustionState = 0.72;
    input exhaustionBars = 3;
    
    # ============================================================
    # DISPLAY SETTINGS
    # ============================================================
    
    input showRawState = yes;
    input showThresholdClouds = yes;
    input showSpeedLabel = yes;
    input showStateLabels = yes;
    input showSetupLabel = yes;
    input showActionLabel = yes;
    
    # ============================================================
    # HELPER SCRIPTS
    # ============================================================
    
    script Clamp {
        input value = 0.0;
        input minimum = 0.0;
        input maximum = 1.0;
    
        plot result =
            Max(minimum, Min(maximum, value));
    }
    
    script SafeZScore {
        input value = 0.0;
        input length = 50;
    
        def average = Average(value, length);
        def deviation = StDev(value, length);
    
        plot result =
            if deviation > 0
            then (value - average) / deviation
            else 0;
    }
    
    script Logistic {
        input value = 0.0;
    
        def limitedValue =
            Max(-10.0, Min(10.0, value));
    
        plot result =
            1.0 / (1.0 + Exp(-limitedValue));
    }
    
    script SignedPower {
        input value = 0.0;
        input exponent = 1.0;
    
        plot result =
            if value > 0
            then Power(value, exponent)
            else if value < 0
            then -Power(AbsValue(value), exponent)
            else 0;
    }
    
    # ============================================================
    # SOURCE — PREVIOUS CONFIRMED BAR
    # ============================================================
    
    def srcRaw =
        if sourceType == sourceType.Close
        then close
        else if sourceType == sourceType.OHLC4
        then (open + high + low + close) / 4
        else (high + low + close) / 3;
    
    # Gate to previous confirmed bar — eliminates intrabar repaint
    def src = srcRaw[1];
    
    # ============================================================
    # NON-REPAINT TIMING
    # ============================================================
    #
    # Every signal dependency below is based on the prior completed
    # primary bar or prior completed higher-timeframe bar. Do not use
    # a forward-looking bar reference as a confirmation proxy: it
    # causes backfilling onto the previous bar.
    
    # ============================================================
    # MODE ADJUSTMENTS
    # ============================================================
    
    def modeCriticalAdjustment =
        if setupMode == setupMode.Conservative
        then 1.20
        else if setupMode == setupMode.Aggressive
        then 0.82
        else 1.00;
    
    def modeStateAdjustment =
        if setupMode == setupMode.Conservative
        then 0.025
        else if setupMode == setupMode.Aggressive
        then -0.025
        else 0.0;
    
    def criticalMultiplier =
        criticalMultiplierInput *
        modeCriticalAdjustment;
    
    def bullStateThreshold =
        Max(
            0.51,
            Min(
                0.90,
                bullStateInput + modeStateAdjustment
            )
        );
    
    def bearStateThreshold =
        Max(
            0.10,
            Min(
                0.49,
                bearStateInput - modeStateAdjustment
            )
        );
    
    # ============================================================
    # BASE MARKET MEASUREMENTS — PREVIOUS BAR
    # ============================================================
    
    def trueRangeValue =
        TrueRange(high, close, low)[1];
    
    def atr =
        Average(trueRangeValue, atrLength);
    
    def safeATR =
        Max(atr, TickSize());
    
    def adaptiveMean =
        ExpAverage(src, meanLength);
    
    def meanDisplacement =
        (src - adaptiveMean) / safeATR;
    
    # ============================================================
    # STABLE SESSION VWAP — PREVIOUS BAR
    # ============================================================
    
    # reference VWAP() recalculates on every tick. Taking [1]
    # gives us the previous confirmed bar's VWAP, which is
    # stable and non-repainting.
    
    def sessionVWAP =
        if useVWAP
        then reference VWAP().VWAP[1]
        else Double.NaN;
    
    def vwapDisplacement =
        if useVWAP and !IsNaN(sessionVWAP)
        then (src - sessionVWAP) / safeATR
        else 0;
    
    def momentum =
        if !IsNaN(src[momentumLength])
        then (src - src[momentumLength]) / safeATR
        else 0;
    
    def barRange =
        high[1] - low[1];
    
    def averageRange =
        Average(high - low, atrLength)[1];
    
    def rangeExpansion =
        if averageRange > 0
        then barRange / averageRange - 1.0
        else 0;
    
    def averageVolume =
        Average(volume, volumeLength)[1];
    
    def relativeVolume =
        if averageVolume > 0
        then volume[1] / averageVolume
        else 1.0;
    
    def candleEfficiency =
        if barRange > 0
        then (close[1] - open[1]) / barRange
        else 0;
    
    def volumePressure =
        candleEfficiency *
        Max(relativeVolume - 1.0, 0);
    
    # ============================================================
    # HIGHER-TIMEFRAME BIAS — PREVIOUS CLOSED HTF BAR
    # ============================================================
    
    # close(period = ...) returns the forming HTF bar's close.
    # Taking [1] ensures we only use the PREVIOUS CLOSED HTF bar,
    # which is stable and non-repainting.
    
    def htfClose =
        close(period = htfAggregation)[1];
    
    def htfHigh =
        high(period = htfAggregation)[1];
    
    def htfLow =
        low(period = htfAggregation)[1];
    
    def htfFast =
        ExpAverage(htfClose, htfFastLength);
    
    def htfSlow =
        ExpAverage(htfClose, htfSlowLength);
    
    def htfTrueRange =
        TrueRange(htfHigh, htfClose, htfLow);
    
    def htfATR =
        Average(htfTrueRange, atrLength);
    
    def htfBiasRaw =
        if htfATR > 0
        then (htfFast - htfSlow) / htfATR
        else 0;
    
    def htfBias =
        Max(-2.0, Min(2.0, htfBiasRaw));
    
    def htfBull =
        htfFast > htfSlow;
    
    def htfBear =
        htfFast < htfSlow;
    
    # ============================================================
    # NORMALIZED COMPONENTS
    # ============================================================
    
    def meanComponent =
        Max(
            -3.0,
            Min(
                3.0,
                SafeZScore(
                    meanDisplacement,
                    normalizationLength
                )
            )
        );
    
    def vwapComponent =
        Max(
            -3.0,
            Min(
                3.0,
                SafeZScore(
                    vwapDisplacement,
                    normalizationLength
                )
            )
        );
    
    def momentumComponent =
        Max(
            -3.0,
            Min(
                3.0,
                SafeZScore(
                    momentum,
                    normalizationLength
                )
            )
        );
    
    def volumeComponent =
        if useVolume
        then
            Max(
                -3.0,
                Min(
                    3.0,
                    SafeZScore(
                        volumePressure,
                        normalizationLength
                    )
                )
            )
        else 0;
    
    def rangeDirection =
        if close[1] > open[1]
        then 1
        else if close[1] < open[1]
        then -1
        else 0;
    
    def rangeComponent =
        if useRangeExpansion
        then
            Max(
                -3.0,
                Min(
                    3.0,
                    SafeZScore(
                        rangeExpansion,
                        normalizationLength
                    )
                )
            ) * rangeDirection
        else 0;
    
    # ============================================================
    # ACTIVE WEIGHT
    # ============================================================
    
    def activeWeight =
        weightMean +
        weightMomentum +
        (if useVWAP then weightVWAP else 0) +
        (if useVolume then weightVolume else 0) +
        (if useRangeExpansion then weightRange else 0) +
        (if useHTF then weightHTF else 0);
    
    def weightedScore =
        weightMean * meanComponent +
        weightMomentum * momentumComponent +
        (if useVWAP
         then weightVWAP * vwapComponent
         else 0) +
        (if useVolume
         then weightVolume * volumeComponent
         else 0) +
        (if useRangeExpansion
         then weightRange * rangeComponent
         else 0) +
        (if useHTF
         then weightHTF * htfBias
         else 0);
    
    def normalizedScore =
        if activeWeight > 0
        then weightedScore / activeWeight
        else 0;
    
    # ============================================================
    # RAW BOUNDED MARKET STATE
    # ============================================================
    
    def rawState =
        Logistic(normalizedScore * 2.25);
    
    # ============================================================
    # REACTION IMPULSE
    # ============================================================
    
    def impulseDirection =
        Max(
            -2.0,
            Min(
                2.0,
                0.45 * momentumComponent +
                0.30 * volumeComponent +
                0.25 * rangeComponent
            )
        );
    
    def impulseStrength =
        Max(
            0.0,
            Min(
                2.0,
                AbsValue(momentumComponent) * 0.40 +
                AbsValue(volumeComponent) * 0.30 +
                AbsValue(rangeComponent) * 0.30
            )
        );
    
    def directionalImpulse =
        if impulseDirection > 0
        then impulseStrength
        else if impulseDirection < 0
        then -impulseStrength
        else 0;
    
    def reactionTerm =
        rawState *
        (1.0 - rawState) *
        directionalImpulse;
    
    # ============================================================
    # DOUBLY NONLINEAR DIFFUSION APPROXIMATION
    # ============================================================
    
    def safeRawState =
        Max(0.0001, Min(1.0, rawState));
    
    def densityNow =
        Power(safeRawState, diffusionM);
    
    def densityPrevious =
        Power(
            Max(
                0.0001,
                Min(
                    1.0,
                    if IsNaN(rawState[1])
                    then rawState
                    else rawState[1]
                )
            ),
            diffusionM
        );
    
    def densityTwoBarsAgo =
        Power(
            Max(
                0.0001,
                Min(
                    1.0,
                    if IsNaN(rawState[2])
                    then rawState
                    else rawState[2]
                )
            ),
            diffusionM
        );
    
    def densityCurvature =
        densityNow -
        2.0 * densityPrevious +
        densityTwoBarsAgo;
    
    def nonlinearDiffusion =
        SignedPower(
            densityCurvature,
            diffusionP - 1.0
        );
    
    # ============================================================
    # RECURSIVE WAVE STATE
    # ============================================================
    
    rec waveState =
        if BarNumber() == 1
        then 0.50
        else
            Max(
                0.0,
                Min(
                    1.0,
                    waveState[1] +
                    diffusionGain * nonlinearDiffusion +
                    reactionGain * reactionTerm +
                    anchorGain * (rawState - waveState[1])
                )
            );
    
    # ============================================================
    # PROPAGATION SPEED
    # ============================================================
    
    def rawWaveSpeed =
        if BarNumber() > speedLength
        then
            (waveState - waveState[speedLength]) /
            speedLength
        else 0;
    
    def waveSpeed =
        ExpAverage(rawWaveSpeed, speedSmoothLength);
    
    def stateChange =
        if BarNumber() > 1
        then waveState - waveState[1]
        else 0;
    
    def speedNoise =
        StDev(stateChange, criticalLength);
    
    def criticalSpeed =
        Max(
            speedNoise * criticalMultiplier,
            0.00001
        );
    
    def normalizedSpeed =
        waveSpeed / criticalSpeed;
    
    # ============================================================
    # WAVE CLASSIFICATION
    # ============================================================
    
    def bullState =
        waveState >= bullStateThreshold;
    
    def bearState =
        waveState <= bearStateThreshold;
    
    def equilibriumState =
        !bullState and !bearState;
    
    def bullWave =
        bullState and
        waveSpeed > criticalSpeed;
    
    def bearWave =
        bearState and
        waveSpeed < -criticalSpeed;
    
    def subcriticalBull =
        bullState and
        !bullWave;
    
    def subcriticalBear =
        bearState and
        !bearWave;
    
    def stalled =
        AbsValue(waveSpeed) <
        criticalSpeed * 0.35;
    
    # ============================================================
    # RECENT WAVE MEMORY
    # ============================================================
    
    rec barsSinceBullWave =
        if BarNumber() == 1
        then retraceWindow + 100
        else if bullWave
        then 0
        else
            Min(
                barsSinceBullWave[1] + 1,
                retraceWindow + 100
            );
    
    rec barsSinceBearWave =
        if BarNumber() == 1
        then retraceWindow + 100
        else if bearWave
        then 0
        else
            Min(
                barsSinceBearWave[1] + 1,
                retraceWindow + 100
            );
    
    def recentBullWave =
        barsSinceBullWave <= retraceWindow;
    
    def recentBearWave =
        barsSinceBearWave <= retraceWindow;
    
    # ============================================================
    # RETRACEMENT STATE
    # ============================================================
    
    def bullPullback =
        recentBullWave and
        bullState and
        waveSpeed <= criticalSpeed;
    
    def bearPullback =
        recentBearWave and
        bearState and
        waveSpeed >= -criticalSpeed;
    
    rec bullPullbackBars =
        if BarNumber() == 1
        then 0
        else if bullPullback
        then bullPullbackBars[1] + 1
        else 0;
    
    rec bearPullbackBars =
        if BarNumber() == 1
        then 0
        else if bearPullback
        then bearPullbackBars[1] + 1
        else 0;
    
    # ============================================================
    # ALIGNMENT FILTERS — PREVIOUS BAR
    # ============================================================
    
    def vwapLongOK =
        !requireVWAPAlignment or
        !useVWAP or
        close[1] > sessionVWAP;
    
    def vwapShortOK =
        !requireVWAPAlignment or
        !useVWAP or
        close[1] < sessionVWAP;
    
    def htfLongOK =
        !requireHTFAlignment or
        !useHTF or
        htfBull;
    
    def htfShortOK =
        !requireHTFAlignment or
        !useHTF or
        htfBear;
    
    # ============================================================
    # SPEED CROSSINGS
    # ============================================================
    
    def bullSpeedResume =
        waveSpeed > criticalSpeed and
        waveSpeed[1] <= criticalSpeed[1];
    
    def bearSpeedResume =
        waveSpeed < -criticalSpeed and
        waveSpeed[1] >= -criticalSpeed[1];
    
    # ============================================================
    # ARMED RETRACEMENT SETUPS
    # ============================================================
    
    rec bullResumeArmed =
        if BarNumber() == 1
        then no
        else if !recentBullWave or !bullState
        then no
        else if bullSpeedResume and bullResumeArmed[1]
        then no
        else if bullPullback and
                bullPullbackBars >= minimumPullbackBars
        then yes
        else bullResumeArmed[1];
    
    rec bearResumeArmed =
        if BarNumber() == 1
        then no
        else if !recentBearWave or !bearState
        then no
        else if bearSpeedResume and bearResumeArmed[1]
        then no
        else if bearPullback and
                bearPullbackBars >= minimumPullbackBars
        then yes
        else bearResumeArmed[1];
    
    # ============================================================
    # PRELIMINARY RESUME SIGNALS
    # ============================================================
    
    def preliminaryLongResume =
        bullSpeedResume and
        bullResumeArmed[1] and
        recentBullWave and
        bullState and
        vwapLongOK and
        htfLongOK;
    
    def preliminaryShortResume =
        bearSpeedResume and
        bearResumeArmed[1] and
        recentBearWave and
        bearState and
        vwapShortOK and
        htfShortOK;
    
    # ============================================================
    # SIGNAL COOLDOWN
    # ============================================================
    
    rec barsSinceLongSignal =
        if BarNumber() == 1
        then signalCooldown + 1
        else if preliminaryLongResume and
                barsSinceLongSignal[1] > signalCooldown
        then 0
        else barsSinceLongSignal[1] + 1;
    
    rec barsSinceShortSignal =
        if BarNumber() == 1
        then signalCooldown + 1
        else if preliminaryShortResume and
                barsSinceShortSignal[1] > signalCooldown
        then 0
        else barsSinceShortSignal[1] + 1;
    
    def longCooldownOK =
        barsSinceLongSignal[1] > signalCooldown;
    
    def shortCooldownOK =
        barsSinceShortSignal[1] > signalCooldown;
    
    # ============================================================
    # FINAL SIGNALS — PAST-ONLY, NO FORWARD OFFSET
    # ============================================================
    
    def longResumeSignal =
        preliminaryLongResume and
        longCooldownOK;
    
    def shortResumeSignal =
        preliminaryShortResume and
        shortCooldownOK;
    
    # ============================================================
    # EXHAUSTION CONDITIONS
    # ============================================================
    
    def speedFalling =
        Sum(
            waveSpeed < waveSpeed[1],
            exhaustionBars
        ) == exhaustionBars;
    
    def speedRising =
        Sum(
            waveSpeed > waveSpeed[1],
            exhaustionBars
        ) == exhaustionBars;
    
    def bullExhaustionCondition =
        waveState >= exhaustionState and
        waveSpeed > 0 and
        speedFalling and
        close[1] >= Highest(close, exhaustionBars)[1];
    
    def bearExhaustionCondition =
        waveState <= 1.0 - exhaustionState and
        waveSpeed < 0 and
        speedRising and
        close[1] <= Lowest(close, exhaustionBars)[1];
    
    def bullExhaustion =
        bullExhaustionCondition and
        !bullExhaustionCondition[1];
    
    def bearExhaustion =
        bearExhaustionCondition and
        !bearExhaustionCondition[1];
    
    # ============================================================
    # GLOBAL COLORS
    # ============================================================
    
    DefineGlobalColor(
        "BullWave",
        CreateColor(0, 220, 120)
    );
    
    DefineGlobalColor(
        "BearWave",
        CreateColor(235, 70, 70)
    );
    
    DefineGlobalColor(
        "BullRetrace",
        CreateColor(70, 165, 255)
    );
    
    DefineGlobalColor(
        "BearRetrace",
        CreateColor(255, 170, 50)
    );
    
    DefineGlobalColor(
        "BullStalled",
        CreateColor(70, 150, 110)
    );
    
    DefineGlobalColor(
        "BearStalled",
        CreateColor(160, 90, 90)
    );
    
    DefineGlobalColor(
        "Neutral",
        CreateColor(160, 160, 160)
    );
    
    # ============================================================
    # MAIN STATE PLOT
    # ============================================================
    
    plot Wave =
        waveState;
    
    Wave.SetLineWeight(3);
    
    Wave.AssignValueColor(
        if bullWave
        then GlobalColor("BullWave")
        else if bearWave
        then GlobalColor("BearWave")
        else if bullPullback
        then GlobalColor("BullRetrace")
        else if bearPullback
        then GlobalColor("BearRetrace")
        else if subcriticalBull
        then GlobalColor("BullStalled")
        else if subcriticalBear
        then GlobalColor("BearStalled")
        else GlobalColor("Neutral")
    );
    
    # ============================================================
    # RAW STATE
    # ============================================================
    
    plot RawMarketState =
        if showRawState
        then rawState
        else Double.NaN;
    
    RawMarketState.SetDefaultColor(Color.DARK_GRAY);
    RawMarketState.SetLineWeight(1);
    RawMarketState.HideBubble();
    
    # ============================================================
    # THRESHOLDS
    # ============================================================
    
    plot BullThreshold =
        bullStateThreshold;
    
    BullThreshold.SetDefaultColor(Color.DARK_GREEN);
    BullThreshold.SetStyle(Curve.SHORT_DASH);
    BullThreshold.HideBubble();
    
    plot BearThreshold =
        bearStateThreshold;
    
    BearThreshold.SetDefaultColor(Color.DARK_RED);
    BearThreshold.SetStyle(Curve.SHORT_DASH);
    BearThreshold.HideBubble();
    
    plot Equilibrium =
        0.50;
    
    Equilibrium.SetDefaultColor(Color.YELLOW);
    Equilibrium.SetStyle(Curve.SHORT_DASH);
    Equilibrium.HideBubble();
    
    plot UpperBoundary =
        1.0;
    
    UpperBoundary.SetDefaultColor(Color.BLACK);
    UpperBoundary.HideBubble();
    UpperBoundary.HideTitle();
    
    plot LowerBoundary =
        0.0;
    
    LowerBoundary.SetDefaultColor(Color.BLACK);
    LowerBoundary.HideBubble();
    LowerBoundary.HideTitle();
    
    # ============================================================
    # THRESHOLD CLOUDS
    # ============================================================
    
    AddCloud(
        if showThresholdClouds
        then UpperBoundary
        else Double.NaN,
        if showThresholdClouds
        then BullThreshold
        else Double.NaN,
        Color.DARK_GREEN,
        Color.DARK_GREEN
    );
    
    AddCloud(
        if showThresholdClouds
        then BearThreshold
        else Double.NaN,
        if showThresholdClouds
        then LowerBoundary
        else Double.NaN,
        Color.DARK_RED,
        Color.DARK_RED
    );
    
    # ============================================================
    # REGIME BACKGROUND
    # ============================================================
    
    AssignBackgroundColor(
        if !showWaveBackground
        then Color.CURRENT
        else if bullWave
        then CreateColor(0, 35, 20)
        else if bearWave
        then CreateColor(40, 10, 10)
        else if bullPullback
        then CreateColor(10, 25, 45)
        else if bearPullback
        then CreateColor(45, 25, 5)
        else Color.CURRENT
    );
    
    # ============================================================
    # SIGNAL MARKERS
    # ============================================================
    
    plot LongResume =
        if showSignals and longResumeSignal
        then 0.04
        else Double.NaN;
    
    LongResume.SetPaintingStrategy(
        PaintingStrategy.ARROW_UP
    );
    
    LongResume.SetDefaultColor(Color.GREEN);
    LongResume.SetLineWeight(4);
    LongResume.HideBubble();
    LongResume.HideTitle();
    
    plot ShortResume =
        if showSignals and shortResumeSignal
        then 0.96
        else Double.NaN;
    
    ShortResume.SetPaintingStrategy(
        PaintingStrategy.ARROW_DOWN
    );
    
    ShortResume.SetDefaultColor(Color.RED);
    ShortResume.SetLineWeight(4);
    ShortResume.HideBubble();
    ShortResume.HideTitle();
    
    # ============================================================
    # EXHAUSTION MARKERS
    # ============================================================
    
    plot BullExhaustionMarker =
        if showExhaustion and bullExhaustion
        then 0.96
        else Double.NaN;
    
    BullExhaustionMarker.SetPaintingStrategy(
        PaintingStrategy.POINTS
    );
    
    BullExhaustionMarker.SetDefaultColor(Color.ORANGE);
    BullExhaustionMarker.SetLineWeight(5);
    BullExhaustionMarker.HideBubble();
    BullExhaustionMarker.HideTitle();
    
    plot BearExhaustionMarker =
        if showExhaustion and bearExhaustion
        then 0.04
        else Double.NaN;
    
    BearExhaustionMarker.SetPaintingStrategy(
        PaintingStrategy.POINTS
    );
    
    BearExhaustionMarker.SetDefaultColor(Color.ORANGE);
    BearExhaustionMarker.SetLineWeight(5);
    BearExhaustionMarker.HideBubble();
    BearExhaustionMarker.HideTitle();
    
    # ============================================================
    # OPTIONAL PRICE-BAR COLORING
    # ============================================================
    
    AssignPriceColor(
        if !paintPriceBars
        then Color.CURRENT
        else if bullWave
        then GlobalColor("BullWave")
        else if bearWave
        then GlobalColor("BearWave")
        else if bullPullback
        then GlobalColor("BullRetrace")
        else if bearPullback
        then GlobalColor("BearRetrace")
        else Color.CURRENT
    );
    
    # ============================================================
    # DASHBOARD LABELS
    # ============================================================
    
    AddLabel(
        showDashboard,
        "KPP PROPAGATION | " +
        (if setupMode == setupMode.Conservative
         then "CONSERVATIVE"
         else if setupMode == setupMode.Aggressive
         then "AGGRESSIVE"
         else "BALANCED"),
        Color.WHITE
    );
    
    AddLabel(
        showDashboard and showStateLabels,
        if bullWave
        then "REGIME: BULL WAVE"
        else if bearWave
        then "REGIME: BEAR WAVE"
        else if bullPullback
        then "REGIME: BULL RETRACE"
        else if bearPullback
        then "REGIME: BEAR RETRACE"
        else if subcriticalBull
        then "REGIME: BULL STALLED"
        else if subcriticalBear
        then "REGIME: BEAR STALLED"
        else if stalled
        then "REGIME: NO PROPAGATION"
        else "REGIME: TRANSITION",
        if bullWave
        then GlobalColor("BullWave")
        else if bearWave
        then GlobalColor("BearWave")
        else if bullPullback
        then GlobalColor("BullRetrace")
        else if bearPullback
        then GlobalColor("BearRetrace")
        else if subcriticalBull
        then GlobalColor("BullStalled")
        else if subcriticalBear
        then GlobalColor("BearStalled")
        else GlobalColor("Neutral")
    );
    
    AddLabel(
        showDashboard and showStateLabels,
        "STATE: " +
        AsText(Round(waveState * 100, 1)) +
        "%",
        if bullState
        then GlobalColor("BullWave")
        else if bearState
        then GlobalColor("BearWave")
        else Color.GRAY
    );
    
    AddLabel(
        showDashboard and showSpeedLabel,
        "SPEED: " +
        AsText(Round(normalizedSpeed, 2)) +
        "x",
        if normalizedSpeed > 1
        then Color.GREEN
        else if normalizedSpeed < -1
        then Color.RED
        else Color.GRAY
    );
    
    AddLabel(
        showDashboard,
        "VWAP: " +
        (if !useVWAP
         then "OFF"
         else if close[1] > sessionVWAP
         then "ABOVE"
         else if close[1] < sessionVWAP
         then "BELOW"
         else "AT"),
        if !useVWAP
        then Color.GRAY
        else if close[1] > sessionVWAP
        then Color.GREEN
        else if close[1] < sessionVWAP
        then Color.RED
        else Color.GRAY
    );
    
    AddLabel(
        showDashboard,
        "HTF: " +
        (if !useHTF
         then "OFF"
         else if htfBull
         then "BULL"
         else if htfBear
         then "BEAR"
         else "FLAT"),
        if !useHTF
        then Color.GRAY
        else if htfBull
        then Color.GREEN
        else if htfBear
        then Color.RED
        else Color.GRAY
    );
    
    # ============================================================
    # SETUP STATUS
    # ============================================================
    
    AddLabel(
        showDashboard and showSetupLabel,
        if bullResumeArmed
        then
            "SETUP: LONG ARMED | " +
            AsText(bullPullbackBars) +
            " BARS"
        else if bearResumeArmed
        then
            "SETUP: SHORT ARMED | " +
            AsText(bearPullbackBars) +
            " BARS"
        else if bullPullback
        then
            "SETUP: LONG RETRACE | " +
            AsText(bullPullbackBars) +
            " BARS"
        else if bearPullback
        then
            "SETUP: SHORT RETRACE | " +
            AsText(bearPullbackBars) +
            " BARS"
        else
            "SETUP: NOT ARMED",
        if bullResumeArmed
        then Color.GREEN
        else if bearResumeArmed
        then Color.RED
        else if bullPullback
        then GlobalColor("BullRetrace")
        else if bearPullback
        then GlobalColor("BearRetrace")
        else Color.GRAY
    );
    
    # ============================================================
    # ACTION STATUS
    # ============================================================
    
    AddLabel(
        showDashboard and showActionLabel,
        if longResumeSignal
        then "ACTION: LONG RESUME"
        else if shortResumeSignal
        then "ACTION: SHORT RESUME"
        else if bullExhaustion
        then "ACTION: BULL EXHAUSTION"
        else if bearExhaustion
        then "ACTION: BEAR EXHAUSTION"
        else if bullResumeArmed or bearResumeArmed
        then "ACTION: WAIT FOR RESTART"
        else if bullPullback
        then "ACTION: BUILDING LONG SETUP"
        else if bearPullback
        then "ACTION: BUILDING SHORT SETUP"
        else if bullWave
        then "ACTION: HOLD LONG BIAS"
        else if bearWave
        then "ACTION: HOLD SHORT BIAS"
        else "ACTION: WAIT",
        if longResumeSignal
        then Color.GREEN
        else if shortResumeSignal
        then Color.RED
        else if bullExhaustion or bearExhaustion
        then Color.ORANGE
        else if bullResumeArmed
        then Color.GREEN
        else if bearResumeArmed
        then Color.RED
        else if bullWave
        then GlobalColor("BullWave")
        else if bearWave
        then GlobalColor("BearWave")
        else Color.GRAY
    );
    
    # ============================================================
    # CHART BUBBLES
    # ============================================================
    
    AddChartBubble(
        showSignals and longResumeSignal,
        0.04,
        "GO",
        Color.GREEN,
        no
    );
    
    AddChartBubble(
        showSignals and shortResumeSignal,
        0.96,
        "GO",
        Color.RED,
        yes
    );
    
    # ============================================================
    # ALERTS — fire once on the next bar using completed prior-bar data
    # ============================================================
    
    Alert(
        longResumeSignal,
        "KPP bullish propagation resumed after a qualified retracement",
        Alert.BAR,
        Sound.Ding
    );
    
    Alert(
        shortResumeSignal,
        "KPP bearish propagation resumed after a qualified retracement",
        Alert.BAR,
        Sound.Ding
    );
    
    Alert(
        bullResumeArmed and !bullResumeArmed[1],
        "KPP bullish retracement qualified and armed",
        Alert.BAR,
        Sound.Chimes
    );
    
    Alert(
        bearResumeArmed and !bearResumeArmed[1],
        "KPP bearish retracement qualified and armed",
        Alert.BAR,
        Sound.Chimes
    );
    
    Alert(
        bullExhaustion,
        "KPP bullish propagation exhaustion",
        Alert.BAR,
        Sound.Ring
    );
    
    Alert(
        bearExhaustion,
        "KPP bearish propagation exhaustion",
        Alert.BAR,
        Sound.Ring
    );
  • NQ, RTY, YM, and liquid index ETFs such as SPY/QQQ.
  • Test separately: individual stocks; earnings gaps and regular-hours settings can materially change behavior.
  • Avoid initially: thin stocks and individual options, where spread/volume noise can overwhelm the signal.
  • Treat forex cautiously: its volume/VWAP behavior is not comparable to centralized futures volume.
Keep the chart timeframe at or below the chosen HTF; for example, a 5-minute chart with a 15-minute HTF is valid, while a 30-minute chart needs an HTF of at least 30 minutes. ThinkScript requires secondary aggregation to be no smaller than the chart aggregation. Reference
 

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
2499 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top