mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 12:38:06 +00:00
Add new moving average implementations: LTMA, MCNMA, NLMA, NMA, NYQMA, RAIN, and TRAMA
- LTMA (Linear Trend Moving Average): Introduces a predictive moving average using dual cascaded EMAs for trend estimation. - MCNMA (McNicholl EMA): Implements a zero-lag TEMA using a cascaded EMA structure for enhanced responsiveness. - NLMA (Non-Lag Moving Average): Utilizes a damped cosine kernel to achieve reduced lag in moving averages. - NMA (Natural Moving Average): Adapts smoothing based on volatility profiles using a square-root kernel. - NYQMA (Nyquist Moving Average): Applies the Nyquist-Shannon theorem to prevent aliasing in cascaded moving averages. - RAIN (Rainbow Moving Average): Combines multiple SMA layers with weighted averages for multi-scale smoothing. - TRAMA (Trend Regularity Adaptive Moving Average): Adapts smoothing based on the frequency of new highs and lows in price data.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# POLYFIT: Polynomial Fitting
|
||||
|
||||
Polynomial Fitting computes a rolling polynomial regression of configurable degree over a lookback window, returning the fitted value at the current bar. Degree 1 produces a linear regression endpoint (identical to LSQR), degree 2 produces a quadratic fit that captures curvature, and degree 3 produces a cubic fit that captures inflection points. The implementation solves the normal equations $\mathbf{X}^T\mathbf{X}\mathbf{a} = \mathbf{X}^T\mathbf{y}$ via Gauss-Jordan elimination with partial pivoting, evaluating the resulting polynomial at $x = 1$ (the current bar position). With $O(Nd + d^3)$ complexity per bar where $N$ is the period and $d$ is the degree, POLYFIT provides a general-purpose curve-fitting tool that subsumes linear regression and extends it to arbitrary polynomial order.
|
||||
|
||||
## Historical Context
|
||||
|
||||
Polynomial regression traces to Adrien-Marie Legendre (1805) and Carl Friedrich Gauss (1809), who independently developed the method of least squares. The normal equations formulation provides the minimum-sum-of-squares solution in closed form, though numerical stability requires careful implementation. Gauss-Jordan elimination with partial pivoting (Jordan, 1873) is the standard approach for small systems like those arising in polynomial fitting with degrees 1-6.
|
||||
|
||||
In technical analysis, linear regression (degree 1) is well established via the Linear Regression Channel and LSQR indicators. Higher-degree fits are less common due to overfitting concerns, but degree 2 (quadratic) is useful for detecting acceleration/deceleration in trends, and degree 3 (cubic) can capture reversal patterns. The key insight is that higher degrees track price more closely but also amplify noise; the optimal degree depends on the signal-to-noise ratio and the lookback period.
|
||||
|
||||
The x-normalization step (mapping time indices to $[0, 1]$) is critical for numerical stability: without it, the Vandermonde matrix entries $x^d$ would span many orders of magnitude for typical lookback periods, causing catastrophic cancellation in the normal equations. With normalization, the matrix condition number remains manageable up to degree 6.
|
||||
|
||||
## Architecture and Physics
|
||||
|
||||
The implementation uses a circular buffer to maintain the last `period` values, with NaN substitution via last-valid-value tracking.
|
||||
|
||||
**Matrix assembly**: Constructs the $(d+1) \times (d+1)$ Gram matrix $\mathbf{G} = \mathbf{X}^T\mathbf{X}$ and right-hand side $\mathbf{r} = \mathbf{X}^T\mathbf{y}$ in a single pass over the data. The Vandermonde basis vectors are $[1, x, x^2, \ldots, x^d]$ where $x_i = i/(n-1)$ is the normalized time position. The matrix is symmetric so only the upper triangle needs explicit computation (mirrored to lower).
|
||||
|
||||
**Solver**: Gauss-Jordan elimination with partial pivoting transforms the augmented matrix $[\mathbf{G} | \mathbf{r}]$ into $[\mathbf{I} | \mathbf{a}]$. Partial pivoting selects the row with the largest absolute value in the current column to minimize round-off error. Singular or near-singular matrices (pivot $< 10^{-30}$) abort gracefully.
|
||||
|
||||
**Evaluation**: The polynomial $P(x) = a_0 + a_1 x + a_2 x^2 + \cdots + a_d x^d$ is evaluated at $x = 1.0$ (current bar, since time is normalized to $[0, 1]$). This gives the fitted value at the most recent observation.
|
||||
|
||||
**Degree clamping**: If `degree` exceeds `period - 1`, it is automatically reduced to prevent underdetermined systems.
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
The polynomial model:
|
||||
|
||||
$$P(x) = \sum_{j=0}^{d} a_j x^j = a_0 + a_1 x + a_2 x^2 + \cdots + a_d x^d$$
|
||||
|
||||
The **normal equations** for least-squares fitting:
|
||||
|
||||
$$\mathbf{X}^T\mathbf{X}\,\mathbf{a} = \mathbf{X}^T\mathbf{y}$$
|
||||
|
||||
where $\mathbf{X}$ is the $n \times (d+1)$ Vandermonde matrix:
|
||||
|
||||
$$X_{ij} = x_i^j, \quad x_i = \frac{i}{n-1} \in [0, 1]$$
|
||||
|
||||
The Gram matrix elements:
|
||||
|
||||
$$G_{jk} = \sum_{i=0}^{n-1} x_i^{j+k}$$
|
||||
|
||||
The right-hand side:
|
||||
|
||||
$$r_j = \sum_{i=0}^{n-1} x_i^j \cdot y_i$$
|
||||
|
||||
**Gauss-Jordan with partial pivoting** reduces $[\mathbf{G} | \mathbf{r}]$ to $[\mathbf{I} | \mathbf{a}]$:
|
||||
|
||||
1. For each column $c$: find the row $p$ in $[c, d]$ with maximum $|G_{pc}|$
|
||||
2. Swap rows $c$ and $p$
|
||||
3. Scale row $c$ so the pivot becomes 1
|
||||
4. Subtract multiples of row $c$ from all other rows
|
||||
|
||||
**Output**: $\hat{y}_{\text{current}} = P(1.0) = \sum_{j=0}^{d} a_j$
|
||||
|
||||
**Parameter constraints**: `period` $\ge 2$, `degree` $\ge 1$ (clamped to `period - 1`). Computational complexity: $O(nd + d^3)$.
|
||||
|
||||
```
|
||||
POLYFIT(source, period, degree):
|
||||
d = min(degree, period - 1)
|
||||
m = d + 1
|
||||
normalize x_i = i / (n-1) for i in [0, n-1]
|
||||
|
||||
// Build normal equations
|
||||
G = (m x m) matrix of zeros
|
||||
r = m-vector of zeros
|
||||
for each (x_i, y_i) in window:
|
||||
for j = 0 to d:
|
||||
r[j] += x_i^j * y_i
|
||||
for k = j to d:
|
||||
G[j][k] += x_i^(j+k)
|
||||
G[k][j] = G[j][k] // symmetric
|
||||
|
||||
// Gauss-Jordan with partial pivoting
|
||||
for col = 0 to d:
|
||||
pivot_row = argmax |G[row][col]| for row in [col, d]
|
||||
swap rows col and pivot_row in G and r
|
||||
scale row col by 1/G[col][col]
|
||||
eliminate col from all other rows
|
||||
|
||||
// Evaluate at x = 1.0 (current bar)
|
||||
return sum(r[j] for j = 0 to d)
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Legendre, A.M. "Nouvelles methodes pour la determination des orbites des cometes." 1805.
|
||||
- Gauss, C.F. "Theoria Motus Corporum Coelestium." 1809.
|
||||
- Golub, G. & Van Loan, C. "Matrix Computations." 4th edition, Johns Hopkins University Press, 2013.
|
||||
- Press, W.H. et al. "Numerical Recipes: The Art of Scientific Computing." 3rd edition, Cambridge University Press, 2007. Chapter 15 (Modeling of Data).
|
||||
- Draper, N. & Smith, H. "Applied Regression Analysis." 3rd edition, Wiley, 1998.
|
||||
@@ -0,0 +1,84 @@
|
||||
# TRIM: Trimmed Mean Moving Average
|
||||
|
||||
The Trimmed Mean Moving Average computes a rolling average after discarding a configurable percentage of the most extreme values from each tail of the sorted lookback window. By removing the lowest and highest `trimPct%` of observations, TRIM eliminates the influence of outliers while retaining more information than a pure median. At `trimPct = 0` it degenerates to the SMA; at `trimPct = 50` it becomes the median. The default 10% trim provides a robust central tendency estimator that resists spike contamination with minimal loss of responsiveness, requiring $O(N \log N)$ for the sort plus $O(N)$ for the summation per bar.
|
||||
|
||||
## Historical Context
|
||||
|
||||
The trimmed mean was introduced by W.J. Dixon (1960) as part of a systematic study of robust estimators for location. John Tukey (1960, 1977) championed its use as part of his program of exploratory data analysis, arguing that no single estimator dominates all contamination models, and the trimmed mean provides a practical compromise between efficiency under normality (where the SMA is optimal) and resistance to outliers (where the median excels).
|
||||
|
||||
In financial applications, the trimmed mean addresses a pervasive problem: price series contain erroneous ticks, flash crashes, and gap events that can corrupt moving average calculations. A single outlier in a 20-bar SMA shifts the average by $1/20 = 5\%$ of the outlier magnitude. The trimmed mean bounds this influence: with a 10% trim on 20 bars, the 2 lowest and 2 highest values are discarded, and the remaining 16 values contribute equally. If an outlier falls in a discarded tail, it has zero effect.
|
||||
|
||||
The trimmed mean also appears in economic statistics: the Federal Reserve Bank of Cleveland publishes a 16% trimmed-mean CPI as an alternative inflation measure that filters out volatile food and energy prices.
|
||||
|
||||
## 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** arranges the values in ascending order using Pine's built-in `array.sort()`. This is $O(N \log N)$ and dominates the per-bar cost.
|
||||
|
||||
**Step 3: Trimmed average** computes the arithmetic mean of the middle `keepCount` values:
|
||||
|
||||
$$\text{trimCount} = \left\lfloor \frac{\text{period} \times \text{trimPct}}{100} \right\rfloor$$
|
||||
|
||||
$$\text{keepCount} = \text{period} - 2 \times \text{trimCount}$$
|
||||
|
||||
$$\text{TRIM} = \frac{1}{\text{keepCount}} \sum_{i=\text{trimCount}}^{\text{trimCount} + \text{keepCount} - 1} x_{(i)}$$
|
||||
|
||||
where $x_{(i)}$ denotes the $i$-th order statistic.
|
||||
|
||||
**Edge case**: If `keepCount` would fall below 1 (extreme trim percentage with small period), the implementation clamps it to 1 and adjusts `trimCount` accordingly, effectively returning the median.
|
||||
|
||||
**Comparison with WINS**: TRIM discards extreme values entirely, reducing the effective sample size. WINS (Winsorized mean) replaces extremes with boundary values, preserving the full sample size. TRIM has a higher breakdown point for the same percentage, but WINS is more efficient when outliers are moderate rather than extreme.
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
The **$\alpha$-trimmed mean** for a sample of size $n$:
|
||||
|
||||
$$\bar{x}_\alpha = \frac{1}{n - 2k} \sum_{i=k+1}^{n-k} x_{(i)}$$
|
||||
|
||||
where $k = \lfloor \alpha \cdot n \rfloor$ and $\alpha = \text{trimPct}/100$.
|
||||
|
||||
**Influence function**: The trimmed mean has a bounded influence function that equals zero outside the trimmed range:
|
||||
|
||||
$$\text{IF}(x; \bar{x}_\alpha) = \begin{cases} 0 & \text{if } x < x_{(\alpha)} \text{ or } x > x_{(1-\alpha)} \\ \frac{x - \bar{x}_\alpha}{1 - 2\alpha} & \text{otherwise} \end{cases}$$
|
||||
|
||||
**Breakdown point**: $\alpha$ (the trim fraction). With 10% trim, up to 10% of the data can be arbitrarily corrupted without affecting the estimator.
|
||||
|
||||
**Asymptotic efficiency** relative to SMA under normality:
|
||||
|
||||
| Trim % | Efficiency |
|
||||
|--------|-----------|
|
||||
| 0% | 100% (SMA) |
|
||||
| 5% | ~98% |
|
||||
| 10% | ~95% |
|
||||
| 25% | ~85% |
|
||||
| 50% | ~64% (median) |
|
||||
|
||||
**Parameter constraints**: `period` $\ge 3$, `trimPct` $\in [0, 49]$.
|
||||
|
||||
```
|
||||
TRIM(source, period, trimPct):
|
||||
trimCount = floor(period * trimPct / 100)
|
||||
keepCount = period - 2 * trimCount
|
||||
if keepCount < 1: keepCount = 1
|
||||
|
||||
// Collect and sort
|
||||
vals = [source[0], source[1], ..., source[period-1]]
|
||||
sort(vals, ascending)
|
||||
|
||||
// Average middle portion
|
||||
sum = 0
|
||||
for i = trimCount to trimCount + keepCount - 1:
|
||||
sum += vals[i]
|
||||
return sum / keepCount
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Dixon, W.J. "Simplified Estimation from Censored Normal Samples." Annals of Mathematical Statistics, 1960.
|
||||
- Tukey, J.W. "Exploratory Data Analysis." Addison-Wesley, 1977.
|
||||
- Huber, P.J. & Ronchetti, E. "Robust Statistics." 2nd edition, Wiley, 2009.
|
||||
- Wilcox, R.R. "Fundamentals of Modern Statistical Methods." 2nd edition, Springer, 2010.
|
||||
- Bryan, M. & Cecchetti, S. "Measuring Core Inflation." In Monetary Policy, NBER, 1994.
|
||||
@@ -0,0 +1,83 @@
|
||||
# WAVG: Weighted Average
|
||||
|
||||
The Weighted Average computes a rolling linearly-weighted mean where the most recent observation receives weight $N$ and the oldest receives weight 1, making it mathematically identical to the Weighted Moving Average (WMA) but categorized as a statistical measure. The implementation uses a circular buffer with an $O(1)$ incremental update scheme: rather than recomputing the full weighted sum each bar, it maintains running sums and adjusts them through add/subtract operations as values enter and exit the window. This makes WAVG one of the most efficient weighted estimators available, with constant per-bar cost regardless of the lookback period.
|
||||
|
||||
## Historical Context
|
||||
|
||||
The linearly-weighted average is one of the oldest weighted estimators, predating formal statistical theory. The concept of assigning decreasing importance to older observations appears in early actuarial work (17th-18th centuries) and was formalized in weather forecasting by the mid-19th century. In technical analysis, the Weighted Moving Average became popular through the work of Martin Pring and other chartists who sought a middle ground between the SMA (equal weights, excessive lag) and the EMA (exponential weights, infinite memory).
|
||||
|
||||
The linear weighting scheme assigns weight $w_i = i + 1$ to the $i$-th sample from oldest ($i = 0$) to newest ($i = N-1$). This produces a centroid (center of mass) that is biased toward recent data: the effective lag is $N/3$ bars compared to $(N-1)/2$ for the SMA. The triangular weight distribution means the most recent value contributes $2/(N+1)$ times the total weight, versus $1/N$ for the SMA.
|
||||
|
||||
The $O(1)$ update trick used in this implementation is well known in DSP: the weighted sum $W = \sum i \cdot x_i$ can be maintained incrementally by tracking the unweighted sum $S = \sum x_i$ and noting that when all indices shift by 1, $W_{\text{new}} = W_{\text{old}} - S_{\text{old}} + N \cdot x_{\text{new}}$.
|
||||
|
||||
## Architecture and Physics
|
||||
|
||||
The implementation uses a circular buffer of size `period` with three state variables:
|
||||
|
||||
- `weightedSum`: The current linearly-weighted sum $\sum_{i=1}^{n} i \cdot x_{(i)}$ where $(i)$ is position from oldest.
|
||||
- `runningSum`: The unweighted sum $\sum x_i$ of all values in the buffer.
|
||||
- `count`: The current fill level (increases during warmup, equals `period` at steady state).
|
||||
|
||||
**Per-bar update** ($O(1)$ operations):
|
||||
|
||||
1. **Remove departing value**: If the buffer position being overwritten contains a valid value, subtract it from `runningSum`.
|
||||
2. **Shift weights down**: Subtract `runningSum` from `weightedSum`. This decrements every existing value's weight by 1 (equivalent to aging all observations).
|
||||
3. **Add new value**: Add `srcVal` to `runningSum` and add `count * srcVal` to `weightedSum` (new value gets the highest weight).
|
||||
4. **Store and advance**: Write to the circular buffer and advance the head pointer.
|
||||
|
||||
**Normalization**: The denominator is $n(n+1)/2$ where $n$ is the current count. This handles the warmup period naturally: when only $k < N$ values have been received, the result uses $k$-based weights.
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
The linearly-weighted average with window size $n$:
|
||||
|
||||
$$\text{WAVG} = \frac{\sum_{i=0}^{n-1} (i + 1) \cdot x_{n-1-i}}{\sum_{i=0}^{n-1} (i + 1)} = \frac{\sum_{i=1}^{n} i \cdot x_i}{\frac{n(n+1)}{2}}$$
|
||||
|
||||
where $x_n$ is the most recent value (weight $n$) and $x_1$ is the oldest (weight 1).
|
||||
|
||||
**Effective lag** (centroid offset from current bar):
|
||||
|
||||
$$\text{lag} = \frac{\sum_{i=0}^{n-1} i \cdot (n - i)}{\sum_{i=0}^{n-1}(n-i)} = \frac{n-1}{3}$$
|
||||
|
||||
**O(1) incremental update** on arrival of new value $x_{\text{new}}$ and departure of $x_{\text{old}}$:
|
||||
|
||||
$$S_{\text{new}} = S_{\text{old}} - x_{\text{old}} + x_{\text{new}}$$
|
||||
|
||||
$$W_{\text{new}} = W_{\text{old}} - S_{\text{old}} + n \cdot x_{\text{new}}$$
|
||||
|
||||
$$\text{WAVG} = \frac{W_{\text{new}}}{n(n+1)/2}$$
|
||||
|
||||
**Weight distribution**: Weight of position $i$ from newest is $\frac{n - i}{n(n+1)/2}$. Most recent: $\frac{2}{n+1}$. Oldest: $\frac{2}{n(n+1)}$.
|
||||
|
||||
**Parameter constraints**: `period` $> 0$.
|
||||
|
||||
```
|
||||
WAVG(source, period):
|
||||
// State variables (persistent)
|
||||
var buffer[period], head = 0, weightedSum = 0, runningSum = 0, count = 0
|
||||
|
||||
srcVal = nz(source)
|
||||
oldest = buffer[head]
|
||||
|
||||
if oldest is valid:
|
||||
runningSum -= oldest
|
||||
else:
|
||||
count += 1
|
||||
|
||||
weightedSum -= runningSum // shift all weights down by 1
|
||||
runningSum += srcVal
|
||||
weightedSum += count * srcVal // new value gets highest weight
|
||||
|
||||
buffer[head] = srcVal
|
||||
head = (head + 1) % period
|
||||
|
||||
denom = count * (count + 1) / 2
|
||||
return denom > 0 ? weightedSum / denom : srcVal
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Pring, M.J. "Technical Analysis Explained." 5th edition, McGraw-Hill, 2014.
|
||||
- Murphy, J.J. "Technical Analysis of the Financial Markets." New York Institute of Finance, 1999.
|
||||
- Oppenheim, A.V. & Schafer, R.W. "Discrete-Time Signal Processing." 3rd edition, Pearson, 2010.
|
||||
- Haykin, S. "Adaptive Filter Theory." 5th edition, Pearson, 2013.
|
||||
@@ -0,0 +1,89 @@
|
||||
# WINS: Winsorized Mean Moving Average
|
||||
|
||||
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)
|
||||
```
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user