// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 // https://mozilla.org/MPL/2.0/ // © QuanTAlib //@version=6 indicator("WINS: Winsorized Mean Moving Average", shorttitle="WINS", overlay=true) // @function Calculates the Winsorized Mean Moving Average. // Sorts the lookback window, then replaces (not discards) the lowest and // highest winPct% of values with the boundary values at the trim point. // This robust estimator reduces outlier influence while retaining the // full sample size (unlike trimmed mean which discards). // winPct=0 → SMA, winPct=50 → all values equal the median pair. // @param src Series to smooth. // @param period Window length. Must be >= 3. // @param winPct Percentage of values to winsorize from each tail (0-49). Default 10. // @returns The winsorized mean value. wins(series float src, simple int period, simple int winPct) => // Number of values to winsorize from each end int winCount = math.max(int(period * winPct / 100.0), 0) if winCount >= period / 2 winCount := (period - 1) / 2 // Collect values into array and sort float[] vals = array.new_float(period) for i = 0 to period - 1 array.set(vals, i, nz(src[i])) array.sort(vals, order.ascending) // Replace tail values with boundary values float lowerBound = array.get(vals, winCount) float upperBound = array.get(vals, period - 1 - winCount) for i = 0 to winCount - 1 array.set(vals, i, lowerBound) array.set(vals, period - 1 - i, upperBound) // Average all values (including replaced ones) float sum = 0.0 for i = 0 to period - 1 sum += array.get(vals, i) sum / period // ── Inputs ────────────────────────────────────────────── p = input.int(20, "Period", minval=3) w = input.int(10, "Winsorize %", minval=0, maxval=49, tooltip="Percentage winsorized from each tail. 0=SMA") // ── Calculation ───────────────────────────────────────── result = wins(close, p, w) // ── Plot ──────────────────────────────────────────────── plot(result, "WINS", color=color.yellow, linewidth=2)