Files
QuanTAlib/lib/statistics/wins/Wins.md
T

7.7 KiB

WINS: Winsorized Mean Moving Average

Winsorization clamps outliers to the nearest percentile fence, preserving sample size while taming extreme values.

Property Value
Category Statistic
Inputs Source (close)
Parameters period, winPct (default 10.0)
Outputs Single series (Wins)
Output range Varies (see docs)
Warmup period bars
PineScript wins.pine
  • The Winsorized Mean Moving Average computes a rolling average after replacing (not discarding) the most extreme values in each tail with the bounda...
  • Similar: Trim, Percentile | Trading note: Winsorized mean; replaces extreme values instead of removing them. Preserves sample size.
  • Validated against TA-Lib, Skender, and Tulip reference implementations where available.

The Winsorized Mean Moving Average computes a rolling average after replacing (not discarding) the most extreme values in each tail with the boundary values at the trim point. Unlike the trimmed mean (TRIM) which removes outliers entirely, Winsorization preserves the full sample size by clamping extreme values to the nearest non-extreme observation. At winPct = 0 it degenerates to the SMA; at winPct = 50 all values equal the median pair. The default 10% Winsorization provides a robust central tendency estimator that dampens outlier impact while maintaining the statistical efficiency advantages of the full sample size.

Historical Context

Winsorization is named after Charles P. Winsor, a biostatistician at Harvard, though the technique was popularized by John Tukey (1962) who credited Winsor with the idea. The concept arises naturally from the question: "what if instead of throwing away extreme values, we replace them with the most extreme non-discarded value?" This produces an estimator that is more efficient than the trimmed mean under light contamination models while retaining comparable robustness.

The distinction between trimming and Winsorizing is subtle but consequential. Consider a 20-bar window with 10% processing: TRIM discards the 2 lowest and 2 highest values, averaging the remaining 16. WINS replaces the 2 lowest with the 3rd-lowest value and the 2 highest with the 3rd-highest, averaging all 20. Both have the same breakdown point (10%), but WINS has higher asymptotic efficiency because it uses all n observations in the average.

In financial applications, Winsorization is standard practice in factor modeling: Fama-French factor returns are typically Winsorized at 1% or 5% to prevent a handful of extreme observations from dominating cross-sectional regressions. The Winsorized mean is also used in the construction of robust risk measures like the Winsorized variance and the Winsorized covariance matrix.

Architecture and Physics

The computation has three steps per bar:

Step 1: Collection gathers the most recent period values into an array, substituting 0 for NaN via nz().

Step 2: Sort and clamp arranges values in ascending order, then replaces the lowest winCount values with the value at index winCount (the lower boundary) and the highest winCount values with the value at index period - 1 - winCount (the upper boundary):

\text{winCount} = \left\lfloor \frac{\text{period} \times \text{winPct}}{100} \right\rfloor

The clamping preserves the boundary values themselves; only values beyond them are replaced.

Step 3: Average computes the arithmetic mean of all period values (including the replaced ones). Since replaced values equal the boundary values, this is equivalent to:

\text{WINS} = \frac{\text{winCount} \cdot x_{(k+1)} + \sum_{i=k+1}^{n-k} x_{(i)} + \text{winCount} \cdot x_{(n-k)}}{n}

where k = \text{winCount} and x_{(i)} is the $i$-th order statistic.

Edge case: If winCount would reach or exceed period / 2, it is clamped to (period - 1) / 2, producing the median pair (two middle values) replicated across all positions.

Mathematical Foundation

The Winsorized mean for a sample of size n with k replacements per tail:

\bar{x}_W = \frac{1}{n}\left[k \cdot x_{(k+1)} + \sum_{i=k+1}^{n-k} x_{(i)} + k \cdot x_{(n-k)}\right]

where x_{(i)} is the $i$-th order statistic and k = \lfloor \alpha n \rfloor with \alpha = \text{winPct}/100.

Winsorized variance (used for inference on the Winsorized mean):

s_W^2 = \frac{1}{n-1} \sum_{i=1}^{n} (w_i - \bar{x}_W)^2

where w_i are the Winsorized values.

Influence function: Bounded like TRIM, but the boundary behavior differs:

\text{IF}(x; \bar{x}_W) = \begin{cases} x_{(\alpha)} - \bar{x}_W & \text{if } x \le x_{(\alpha)} \\ x - \bar{x}_W & \text{if } x_{(\alpha)} < x < x_{(1-\alpha)} \\ x_{(1-\alpha)} - \bar{x}_W & \text{if } x \ge x_{(1-\alpha)} \end{cases}

Breakdown point: \alpha (the Winsorization fraction).

Asymptotic efficiency relative to SMA under normality (higher than TRIM at same percentage):

Win % WINS Efficiency TRIM Efficiency
0% 100% 100%
10% ~97% ~95%
25% ~90% ~85%

Parameter constraints: period \ge 3, winPct \in [0, 49].

WINS(source, period, winPct):
    winCount = floor(period * winPct / 100)
    if winCount >= period/2: winCount = (period-1)/2

    // Collect and sort
    vals = [source[0], source[1], ..., source[period-1]]
    sort(vals, ascending)

    // Replace tails with boundary values
    lowerBound = vals[winCount]
    upperBound = vals[period - 1 - winCount]
    for i = 0 to winCount-1:
        vals[i] = lowerBound
        vals[period - 1 - i] = upperBound

    // Average all values (full sample size)
    return mean(vals)

Performance Profile

Operation Count (Streaming Mode)

Winsorized mean collects the window, sorts it, replaces the tails with boundary values, then computes the mean of all N values.

Operation Count Cost (cycles) Subtotal
Ring buffer collect N 1 cy ~N cy
Array sort (introsort) N log N 2 cy ~2N log N cy
Tail clamping (2k assignments) 2k 1 cy ~2k cy
Sum all N values N 2 cy ~2N cy
Divide for mean 1 4 cy ~4 cy
NaN guard + state update 1 2 cy ~2 cy
Total (N=20, k=2) O(N log N) ~230 cy

O(N log N) per update due to sort. Slightly higher total cost than TRIM because the sum includes all N values (not N-2k), but both are dominated by the sort.

Batch Mode (SIMD Analysis)

Operation Vectorizable? Notes
Window collection Yes Gather from ring buffer with SIMD copy
Sort No Comparison sort is sequential
Clamping tail values Yes Vector conditional-select possible post-sort
Full-window sum Yes Vector sum over N values

Sort blocks SIMD on the main path. The sum phase can use Vector for modest gains. Outer loop (across M bars) has no cross-bar dependency — suitable for parallel batch.

Resources

  • Tukey, J.W. "The Future of Data Analysis." Annals of Mathematical Statistics, 1962.
  • Huber, P.J. & Ronchetti, E. "Robust Statistics." 2nd edition, Wiley, 2009.
  • Wilcox, R.R. "Introduction to Robust Estimation and Hypothesis Testing." 4th edition, Academic Press, 2017.
  • Fama, E.F. & French, K.R. "Common Risk Factors in the Returns on Stocks and Bonds." Journal of Financial Economics, 1993.
  • Dixon, W.J. & Tukey, J.W. "Approximate Behavior of the Distribution of Winsorized t." Technometrics, 1968.