TradingView indicator guides

WSA SMA M: Exact Stage Rules and the Zero-Threshold Gap

WSA SMA M compares close with an SMA and compares that SMA’s one-bar change with a threshold. Its four labels are code conditions, not observations of institutional accumulation or distribution. At the supplied threshold of zero, Stage 1 is unreachable, and some valid bars match none of the four conditions.

Inspect the Pine Script

Inspect the implementation before relying on its name

Pineify names this configuration WSA SMA M and associates it with Weinstein Stage Analysis. The implementation below is a specific two-variable classifier; the name does not establish equivalence with a complete historical trading method. It reads only close, computes an SMA, and returns four Boolean fields.

Let C be current close, A = SMA(close, N), and s = A − previous A. The fixed call uses N = 150 and threshold t = 0. The source contains no volume analysis, relative-strength comparison, base-duration test, breakout test or memory of a preceding stage. It can move directly between conditions and does not enforce a four-stage cycle.

The SMA uses the active chart timeframe. On a daily chart it averages 150 daily closes; on a weekly chart it averages 150 weekly closes. There is no weekly data request. Even 150 daily closes and 30 weekly closes are different samples: weekly closes omit the other daily observations, so their averages are not generally equal.

Read the four Boolean rules literally

Label in this scriptConditionBoundary
Stage 1, light greenC < A and abs(s) < tImpossible when t = 0.
Stage 2, greenC > A and s > tA current state, not necessarily a fresh crossing.
Stage 3, yellowC > A and s < tAt positive t, can include a rising SMA.
Stage 4, redC < A and s < −tStrictly below the negative threshold.

With t = 0, abs(s) < 0 cannot be true, including when s equals zero. This means the light-green Stage 1 branch never activates in the supplied copy. A rule requiring twenty preceding Stage 1 bars cannot be satisfied with this default. The code is retained here for inspection; this limitation has not been silently repaired or redefined.

When close equals the SMA, all four conditions are false. At t = 0, a flat SMA also yields no stage; price below a rising SMA is another unclassified combination. The script passes na to its barcolor calls in those cases, leaving the chart’s ordinary candle coloring rather than inventing a neutral stage.

For a positive t, equality still matters. Above the SMA with s = t is neither Stage 2 nor Stage 3. Below the SMA with s = −t is neither Stage 1 nor Stage 4. Below the SMA with s ≥ t is also unclassified. Thus these predicates do not form a complete four-way partition of valid inputs.

Use small counterexamples to verify stage meaning

With N = 3, previous closes 90, 100 and 110 average 100. After a new close of 90, the window is 100, 110 and 90, still averaging 100. Price is below a flat SMA, yet Stage 1 remains false when t = 0. This is a complete-price example of the unreachable default branch.

For another window, previous closes 90, 100 and 110 again average 100, but the new close is 108. The new average is (100 + 110 + 108) / 3 = 106, giving slope 6. At t = 0, the bar is Stage 2 because 108 exceeds 106 and the SMA rises. The latest close nevertheless fell from 110 to 108. A Stage 2 label does not mean the latest price change was positive.

Consider an assumed state C = 101, A = 100 and s = 0.2. At t = 0 it is Stage 2; at t = 0.5 it is Stage 3 even though the SMA still rises. Increasing the threshold does not simply hold an existing bullish state longer. There is no retained-state variable. These examples test conditions; they establish no market win rate.

Slope units change how thresholds behave

With complete, equally spaced observations and fixed N, the SMA change is (current close − close leaving the window) / N. It is measured in chart price units per bar, not percent, angle or ATR. The SMA guide explains the rolling-window calculation; TradingView documents the underlying average.

Scaling every price by ten scales A and s by ten. If t stays 0.5, the preceding assumed slope 0.2 becomes 2 and the classification changes from Stage 3 to Stage 2, despite the identical proportionate price path. To preserve this price-unit rule under positive scaling, the threshold must scale too. A numerical threshold cannot be transferred indiscriminately between currencies, share prices or split-adjusted histories.

The helper does not restrict negative thresholds. Those values alter the meaning further: Stage 1 remains impossible, and a negative t can let Stage 4 include a positive slope because −t is positive. Treat any proposed normalized or nonnegative-threshold variant as an explicitly different rule, with its own equality and missing-data policies.

Current colors are not confirmed orders

The pasted code contains no input functions. Its two helper arguments are fixed at 150 and 0; changing them requires editing the call or adding inputs. The separate Visual Editor configuration has SMA Period and Minimum Slope fields, which do not turn into controls in this fixed standalone copy.

There is no bar-close gate. Current close changes the SMA and its slope during an open bar, so a color can change before close. Unused time-range helpers in the listing do not create a session filter. There are no alerts, entries, exits or backtest results. An SMA seed also needs history; comparing two successive valid averages needs another observation beyond the first complete window.

Before adapting this into a regime filter, specify whether every valid bar must receive a category, how equalities work, whether a flat band is symmetric, and whether state persists through unclassified bars. A rising-average condition alone does not identify who is buying, establish a base or demonstrate that a decline has ended. Additional indicators cannot establish those conclusions without a separately tested rule.

Use TradingView’s realtime calculation guidance when deciding when to observe the condition. A closed-bar signal still requires an execution model, costs and risk rules before it becomes a strategy test.

Pine Script v6: implementation and limits

The calculation is retained exactly, including the unreachable Stage 1 branch at the supplied zero threshold. The code’s field names describe predicates rather than verified market regimes. The preview is not proof that all four branches can occur with this fixed call.

Pine Script v6
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify

//======================================================================//
//                    ____  _            _  __                          //
//                   |  _ (_)_ __   ___(_)/ _|_   _                    //
//                   | |_) | | '_  / _  | |_| | | |                   //
//                   |  __/| | | | |  __/ |  _| |_| |                   //
//                   |_|   |_|_| |_|___|_|_|  __, |                   //
//                                             |___/                    //
//======================================================================//

//@version=6
indicator(title="WSA SMA M", overlay=true, max_labels_count=500)

//#region Custom Code
//#endregion

//#region Common Dependence

p_comm_time_range_to_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_unix_time = na
    int end_unix_time = na
    int start_time_hour = na
    int start_time_minute = na
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    start_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
    end_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
    [start_unix_time, end_unix_time]

p_comm_time_range_to_start_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_time_hour = na
    int start_time_minute = na
    if str.length(time_range) == 11
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
    else if str.length(time_range) == 9
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)

p_comm_time_range_to_end_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)

p_comm_timeframe_to_seconds(simple string tf) =>
    float seconds = 0
    tf_lower = str.lower(tf)
    value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
    if str.endswith(tf_lower, 's')
        seconds := value
    else if str.endswith(tf_lower, 'd')
        seconds := value * 86400
    else if str.endswith(tf_lower, 'w')
        seconds := value * 604800
    else if str.endswith(tf_lower, 'm')
        seconds := value * 2592000
    else
        seconds := str.tonumber(tf_lower) * 60
    seconds

p_custom_sources() =>
    [open, high, low, close, volume]

//#endregion

//#region Ta Dependence

p_ta_wsa_sma_m(simple int sma_length, simple float slope_threshold) =>
    sma = ta.sma(close, sma_length)
    sma_slope = sma - sma[1]
    stage_1 = close < sma and math.abs(sma_slope) < slope_threshold
    stage_2 = close > sma and sma_slope > slope_threshold
    stage_3 = close > sma and sma_slope < slope_threshold
    stage_4 = close < sma and sma_slope < -slope_threshold
    [sma, stage_1, stage_2, stage_3, stage_4]

//#endregion

//#region Constants

string P_GP_1      =      ""

//#endregion

//#region Inputs
//#endregion

//#region Price Data
//#endregion

//#region Indicators

[p_ind_1_sma, p_ind_1_stage_1, p_ind_1_stage_2, p_ind_1_stage_3, p_ind_1_stage_4]      =      p_ta_wsa_sma_m(150, 0)

//#endregion

//#region Conditions
//#endregion

//#region Indicator Plots

plot(p_ind_1_sma, title="WSA SMA M - SMA", color=color.rgb(120, 123, 134, 0), linewidth=2)
barcolor(p_ind_1_stage_1 ? color.rgb(176, 243, 200, 0) : na, title="WSA SMA M - Stage 1: Accumulation")
barcolor(p_ind_1_stage_2 ? color.rgb(76, 175, 80, 0) : na, title="WSA SMA M - Stage 2: Bullish")
barcolor(p_ind_1_stage_3 ? color.rgb(253, 216, 53, 0) : na, title="WSA SMA M - Stage 3: Distribution")
barcolor(p_ind_1_stage_4 ? color.rgb(242, 54, 69, 0) : na, title="WSA SMA M - Stage 4: Bearish")

//#endregion

//#region Custom Plots
//#endregion

//#region Alert
//#endregion
Existing indicator chart preview
Existing visual preview. Verify exact values against the source; the image is not a performance result.

Specify a checkable adaptation in Pineify

Give the Pine Script AI Coding Agent an explicit classification table covering price above, equal to and below the SMA, and slope below, inside and above a stated flat band. Ask it to distinguish a proposed correction from this retained helper, expose the compared values, and test equality cases. Validate the final source and inspect chart values before adding trading rules; this page does not claim the shared product helper has been corrected.

Open Pine Script AI Coding Agent

Educational indicator analysis. No measured trading performance is established here. Syntax validation does not prove formula equivalence, execution assumptions or profitability.

Implementation questions

Its predicate requires abs(SMA slope) to be less than zero. That is impossible. The fixed default therefore cannot produce the light-green Stage 1 color.

No. Equal close and SMA, threshold equalities and some price/slope combinations match none. This script leaves ordinary candle coloring in those cases.

Only with a zero threshold does Stage 3 require a negative slope. With a positive threshold, a smaller positive slope can also satisfy Stage 3.

No. It is the one-bar SMA difference in price units. Rescaling prices without rescaling the threshold can change the classification.

No. They average different observations, even when their approximate time spans are similar. The script uses the active chart bars and makes no weekly request.

No. It plots an SMA and four Boolean color conditions. It contains no prior-stage memory, volume or relative-strength checks, alerts, orders or tested trading outcomes.