mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 14:28:05 +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,71 @@
|
||||
# MEDF: Moving Median Filter
|
||||
|
||||
> "The median is the only filter that can remove a spike without flinching. SMA smears it, EMA decays it over time, but the median simply ignores it. For impulse noise in financial data — bad ticks, flash crashes, fat-finger errors — the median is the correct tool."
|
||||
|
||||
MEDF outputs the median of the most recent $N$ values in a sliding window, providing a nonlinear filter that is robust to impulse noise and outliers while preserving edges and steps better than any linear filter. Unlike SMA or EMA, which spread the effect of a single outlier across the entire window (SMA) or decay it exponentially (EMA), the median completely rejects outliers that do not constitute a majority of the window. This makes MEDF the filter of choice for cleaning price data contaminated with bad ticks or anomalous prints.
|
||||
|
||||
## Historical Context
|
||||
|
||||
The running median was introduced by John Tukey in *Exploratory Data Analysis* (1977) as a fundamental tool for resistant smoothing. Tukey recognized that the arithmetic mean (and by extension, linear filters like SMA and EMA) is highly sensitive to outliers: a single extreme value can shift the mean arbitrarily far from the "typical" value. The median, being the 50th percentile, requires more than $N/2$ values to be corrupted before it fails.
|
||||
|
||||
In signal processing, median filters gained prominence in image processing (Huang, Yang, and Tang, 1979), where they excel at removing "salt and pepper" noise while preserving sharp edges. The same property applies to financial time series: price levels often exhibit step-like behavior (e.g., after a gap or news event), and the median preserves these steps while linear filters blur them.
|
||||
|
||||
The computational cost of a naive median filter is $O(N \log N)$ per bar (sort the window, extract the middle). More efficient algorithms exist: the rolling median via two heaps achieves $O(\log N)$ per bar, and Huang's histogram method achieves $O(1)$ amortized for integer-valued data. The Pine implementation uses a sort-based approach.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### 1. Circular Buffer
|
||||
|
||||
A ring buffer of size $N$ stores the most recent $N$ values.
|
||||
|
||||
### 2. Window Extraction and Sort
|
||||
|
||||
Each bar, the buffer contents are copied to a temporary array and sorted. This is $O(N \log N)$ via array sort.
|
||||
|
||||
### 3. Median Extraction
|
||||
|
||||
For odd $N$: the middle element is the median. For even $N$: the average of the two middle elements.
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
The median of a set $\{x_1, x_2, \ldots, x_N\}$ is:
|
||||
|
||||
$$
|
||||
\text{median}(X) = \begin{cases} X_{[(N+1)/2]} & N \text{ odd} \\ \frac{X_{[N/2]} + X_{[N/2+1]}}{2} & N \text{ even} \end{cases}
|
||||
$$
|
||||
|
||||
where $X_{[k]}$ denotes the $k$-th order statistic (sorted value).
|
||||
|
||||
**Key properties:**
|
||||
|
||||
| Property | Median | SMA | EMA |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Outlier rejection | Complete (if $< N/2$ outliers) | None | Partial (decays) |
|
||||
| Edge preservation | Yes | Blurs edges | Blurs edges |
|
||||
| Linearity | Nonlinear | Linear | Linear |
|
||||
| Frequency response | No closed form | Sinc | Exponential decay |
|
||||
| Idempotent | No | No | No |
|
||||
|
||||
**Breakdown point:** The median has a 50% breakdown point, meaning up to $\lfloor N/2 \rfloor$ values can be arbitrarily corrupted without affecting the output (assuming the remaining values are within the signal range). This is the highest possible breakdown point for any estimator.
|
||||
|
||||
**Default parameters:** `period = 5`, `minPeriod = 1`.
|
||||
|
||||
**Pseudo-code (streaming):**
|
||||
|
||||
```
|
||||
buffer[head] = src
|
||||
head = (head + 1) % period
|
||||
count = min(count + 1, period)
|
||||
|
||||
sorted = sort(buffer[0..count-1])
|
||||
if count is odd:
|
||||
return sorted[count / 2]
|
||||
else:
|
||||
return (sorted[count/2 - 1] + sorted[count/2]) / 2
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Tukey, J.W. (1977). *Exploratory Data Analysis*. Addison-Wesley. Chapter 7: Resistant Smoothing.
|
||||
- Huang, T.S., Yang, G.J., & Tang, G.Y. (1979). "A Fast Two-Dimensional Median Filtering Algorithm." *IEEE Trans. Acoust., Speech, Signal Process.*, 27(1), 13-18.
|
||||
- Yin, L. et al. (1996). "Weighted Median Filters: A Tutorial." *IEEE Trans. Circuits and Systems II*, 43(3), 157-192.
|
||||
@@ -0,0 +1,114 @@
|
||||
# MODF: Modular Filter
|
||||
|
||||
> "alexgrover designed a filter with two paths — one tracks uptrends, one tracks downtrends — and a state machine that picks between them. Add a beta knob for aggression and an optional feedback loop, and you get one of the most versatile adaptive filters on TradingView."
|
||||
|
||||
MODF is a dual-path adaptive filter that maintains separate upper and lower EMA bands with conditional state selection. The upper band snaps up to price when price exceeds it (tracking rallies), while the lower band snaps down when price drops below it (tracking selloffs). An oscillator state variable determines which band is active, and a beta parameter controls the blend between filter mode (smooth tracking) and trailing-stop mode (step-like following). An optional feedback loop blends the filter's output back into its input for additional smoothing. Developed by alexgrover (CPO at LuxAlgo).
|
||||
|
||||
## Historical Context
|
||||
|
||||
MODF was published by alexgrover on TradingView as a novel approach to adaptive filtering that combines elements of trailing stops, envelope filters, and state machines. The "modular" name refers to the composable design: the beta parameter morphs the filter continuously between two behaviors (smooth average at $\beta = 1$ and trailing stop at $\beta = 0$), and the feedback option adds a third dimension of control.
|
||||
|
||||
The dual-band architecture is reminiscent of Keltner channels and Donchian channels, where upper and lower bands track extremes. MODF's innovation is the conditional snap-to-price behavior: the upper band only updates via EMA when price is below it, but jumps instantly to price when price exceeds it. This creates a band that ratchets upward during trends and smoothly decays during pullbacks, the opposite of a trailing stop but with the same structural mechanism.
|
||||
|
||||
The state machine ($os = 1$ when price touches the upper band, $os = 0$ when it touches the lower band) provides regime detection without any lookback or explicit trend measurement. The filter naturally enters "bullish" (upper band active) or "bearish" (lower band active) mode based purely on which extreme price has most recently visited.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### 1. Dual EMA Bands
|
||||
|
||||
- **Upper band ($b$):** EMA of input, but snaps up to input when input exceeds EMA.
|
||||
- **Lower band ($c$):** EMA of input, but snaps down to input when input falls below EMA.
|
||||
|
||||
### 2. Oscillator State
|
||||
|
||||
Binary state $os$: 1 if price last touched the upper band, 0 if it last touched the lower band.
|
||||
|
||||
### 3. Beta-Weighted Combination
|
||||
|
||||
$$
|
||||
\text{upper\_mix} = \beta \cdot b + (1 - \beta) \cdot c
|
||||
$$
|
||||
|
||||
$$
|
||||
\text{lower\_mix} = \beta \cdot c + (1 - \beta) \cdot b
|
||||
$$
|
||||
|
||||
### 4. State-Selected Output
|
||||
|
||||
$$
|
||||
\text{MODF} = os \cdot \text{upper\_mix} + (1 - os) \cdot \text{lower\_mix}
|
||||
$$
|
||||
|
||||
### 5. Optional Feedback
|
||||
|
||||
When enabled, the input becomes a blend of source and previous output:
|
||||
|
||||
$$
|
||||
a = w \cdot \text{source} + (1 - w) \cdot \text{MODF}_{t-1}
|
||||
$$
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
With $\alpha = 2/(N+1)$:
|
||||
|
||||
**Band updates:**
|
||||
|
||||
$$
|
||||
b_t = \begin{cases} a_t & \text{if } a_t > \alpha \cdot a_t + (1-\alpha) \cdot b_{t-1} \\ \alpha \cdot a_t + (1-\alpha) \cdot b_{t-1} & \text{otherwise} \end{cases}
|
||||
$$
|
||||
|
||||
$$
|
||||
c_t = \begin{cases} a_t & \text{if } a_t < \alpha \cdot a_t + (1-\alpha) \cdot c_{t-1} \\ \alpha \cdot a_t + (1-\alpha) \cdot c_{t-1} & \text{otherwise} \end{cases}
|
||||
$$
|
||||
|
||||
**State transition:**
|
||||
|
||||
$$
|
||||
os_t = \begin{cases} 1 & \text{if } a_t = b_t \\ 0 & \text{if } a_t = c_t \\ os_{t-1} & \text{otherwise} \end{cases}
|
||||
$$
|
||||
|
||||
**Output:**
|
||||
|
||||
$$
|
||||
\text{MODF}_t = os_t \cdot [\beta b_t + (1-\beta) c_t] + (1-os_t) \cdot [\beta c_t + (1-\beta) b_t]
|
||||
$$
|
||||
|
||||
**Beta interpretation:**
|
||||
|
||||
| $\beta$ | Behavior |
|
||||
| :---: | :--- |
|
||||
| 1.0 | Pure filter: tracks active band smoothly |
|
||||
| 0.5 | Balanced: midpoint of both bands |
|
||||
| 0.0 | Pure trailing stop: follows inactive band |
|
||||
|
||||
**Default parameters:** `period = 14`, `beta = 0.8`, `feedback = false`, `fbWeight = 0.5`.
|
||||
|
||||
**Pseudo-code (streaming):**
|
||||
|
||||
```
|
||||
alpha = 2/(period+1)
|
||||
|
||||
// Optional feedback blend
|
||||
a = feedback ? fbWeight*src + (1-fbWeight)*ts : src
|
||||
|
||||
// Upper band (snaps up)
|
||||
ema_b = alpha*a + (1-alpha)*b
|
||||
b = (a > ema_b) ? a : ema_b
|
||||
|
||||
// Lower band (snaps down)
|
||||
ema_c = alpha*a + (1-alpha)*c
|
||||
c = (a < ema_c) ? a : ema_c
|
||||
|
||||
// State machine
|
||||
os = (a == b) ? 1 : (a == c) ? 0 : os
|
||||
|
||||
// Beta-weighted output
|
||||
upper = beta*b + (1-beta)*c
|
||||
lower = beta*c + (1-beta)*b
|
||||
ts = os*upper + (1-os)*lower
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- alexgrover (LuxAlgo). "Modular Filter" indicator. Published on TradingView.
|
||||
- Ehlers, J.F. (2001). *Rocket Science for Traders*. Wiley. Chapter 6: Adaptive Filters (general framework).
|
||||
@@ -0,0 +1,85 @@
|
||||
# NW: Nadaraya-Watson Kernel Regression
|
||||
|
||||
> "Nadaraya and Watson independently discovered the same thing in 1964: weight each observation by how close it is, normalize, and average. Fifty years later, it became one of the most popular nonparametric smoothers on TradingView. The math did not change; only our ability to compute it in real time."
|
||||
|
||||
NW computes the Nadaraya-Watson kernel regression estimator with a Gaussian kernel, producing a nonparametric smooth of the price series. For each bar, every observation in the lookback window is weighted by a Gaussian function of its temporal distance, with the bandwidth parameter $h$ controlling the effective smoothing radius. Small $h$ tracks price tightly (low bias, high variance); large $h$ smooths heavily (high bias, low variance). This implementation is non-repainting (backward-looking only).
|
||||
|
||||
## Historical Context
|
||||
|
||||
Elizbar Nadaraya (1964) and Geoffrey Watson (1964) independently published the same kernel regression estimator, now universally known as the Nadaraya-Watson estimator. It is the foundational method of nonparametric regression: given paired observations $(x_i, y_i)$, estimate $\hat{y}(x) = \sum w_i y_i / \sum w_i$ where $w_i = K((x - x_i)/h)$ and $K$ is a kernel function.
|
||||
|
||||
In the time-series context, the $x$-values are bar indices and the kernel reduces to a temporal weighting function. The Gaussian kernel $K(u) = e^{-u^2/2}$ produces smooth, infinitely differentiable output and has the theoretical property of minimizing the asymptotic mean integrated squared error (MISE) under certain regularity conditions.
|
||||
|
||||
The NW estimator became popular on TradingView around 2022, when several prominent indicator authors (including LuxAlgo) published implementations. Most TradingView versions use centered or forward-looking kernels that repaint as new bars arrive. This implementation is strictly backward-looking (endpoint mode), meaning the estimate at bar $t$ uses only bars $t, t-1, \ldots, t-N+1$. The nonrepainting property is essential for backtesting validity.
|
||||
|
||||
The bandwidth $h$ plays the role that "period" plays in traditional MAs, but with different semantics: $h$ controls the width of the Gaussian bell, and bars beyond $\sim 3h$ contribute negligible weight regardless of the lookback window size.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### 1. Gaussian Kernel Weights
|
||||
|
||||
For each bar $i$ in the lookback window (where $i = 0$ is newest):
|
||||
|
||||
$$
|
||||
w_i = \exp\!\left(-\frac{i^2}{2h^2}\right)
|
||||
$$
|
||||
|
||||
### 2. Normalized Weighted Average
|
||||
|
||||
$$
|
||||
\text{NW}_t = \frac{\sum_{i=0}^{N-1} w_i \cdot x_{t-i}}{\sum_{i=0}^{N-1} w_i}
|
||||
$$
|
||||
|
||||
### 3. Bandwidth-Period Relationship
|
||||
|
||||
Observations beyond $3h$ bars of lag contribute $< 1.1\%$ of peak weight. Setting $N \geq 4h$ captures $> 99.97\%$ of the kernel mass.
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
The Nadaraya-Watson estimator for time series:
|
||||
|
||||
$$
|
||||
\hat{m}(t) = \frac{\sum_{i=0}^{N-1} K_h(i) \cdot x_{t-i}}{\sum_{i=0}^{N-1} K_h(i)}
|
||||
$$
|
||||
|
||||
with Gaussian kernel:
|
||||
|
||||
$$
|
||||
K_h(u) = \frac{1}{\sqrt{2\pi}h}\exp\!\left(-\frac{u^2}{2h^2}\right)
|
||||
$$
|
||||
|
||||
(The normalizing constant $1/(\sqrt{2\pi}h)$ cancels in the ratio and is omitted in practice.)
|
||||
|
||||
**Bias-variance trade-off:**
|
||||
|
||||
| $h$ (relative to $N$) | Bias | Variance | Behavior |
|
||||
| :---: | :---: | :---: | :--- |
|
||||
| $h \ll N$ | Low | High | Tracks noise, overfits |
|
||||
| $h \approx N/4$ | Balanced | Balanced | Good default |
|
||||
| $h \gg N$ | High | Low | Over-smooths, flat |
|
||||
|
||||
**Effective number of observations:** The kernel entropy $N_{\text{eff}} = (\sum w_i)^2 / \sum w_i^2 \approx \sqrt{2\pi} \cdot h$ for the Gaussian.
|
||||
|
||||
**Group delay:** Approximately $h \cdot \sqrt{\pi/2} \approx 1.25h$ for the Gaussian kernel.
|
||||
|
||||
**Default parameters:** `period = 64`, `bandwidth = 8.0`, `minPeriod = 1`.
|
||||
|
||||
**Pseudo-code (streaming):**
|
||||
|
||||
```
|
||||
h2x2 = 2 * bandwidth²
|
||||
num = 0; den = 0
|
||||
|
||||
for i = 0 to min(bar_count, period) - 1:
|
||||
w = exp(-i² / h2x2)
|
||||
num += w * source[i]
|
||||
den += w
|
||||
|
||||
return den > 0 ? num/den : source
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Nadaraya, E.A. (1964). "On Estimating Regression." *Theory of Probability and Its Applications*, 9(1), 141-142.
|
||||
- Watson, G.S. (1964). "Smooth Regression Analysis." *Sankhyā: The Indian Journal of Statistics*, Series A, 26(4), 359-372.
|
||||
- Wand, M.P. & Jones, M.C. (1995). *Kernel Smoothing*. Chapman & Hall/CRC. Chapter 2: The Density Estimator.
|
||||
@@ -0,0 +1,103 @@
|
||||
# REFLEX: Ehlers Reflex Indicator
|
||||
|
||||
> "John Ehlers measured how much a filtered price deviates from its own linear extrapolation. The result is a zero-lag oscillator that catches reversals before they happen, because the deviation is largest precisely when the trend is bending."
|
||||
|
||||
REFLEX is a zero-lag oscillator that measures the reversal tendency of price by comparing a Super-Smoother-filtered price against a linear extrapolation from $N$ bars ago. The filter computes the slope of the filtered series over the lookback window, projects a straight line, and sums the deviations of the actual filtered values from this projected line. The sum is normalized by an exponential RMS estimate to produce values in roughly $\pm \sigma$ scale. Values above 0 indicate uptrend, below 0 indicate downtrend; crossovers signal potential reversals.
|
||||
|
||||
## Historical Context
|
||||
|
||||
John F. Ehlers published REFLEX in "Reflex: A New Zero-Lag Indicator" (*Technical Analysis of Stocks & Commodities*, February 2020). Ehlers' motivation was to create a cycle-based oscillator that responds to trend reversals with zero lag, unlike traditional oscillators (RSI, stochastic) that inherently lag price due to their smoothing components.
|
||||
|
||||
The core idea is that linear extrapolation of a smoothed series will overshoot (undershoot) when the trend is decelerating (accelerating). By measuring the sum of these overshoots, REFLEX detects curvature changes — exactly the inflection points where trends reverse. This is mathematically similar to measuring the second derivative (acceleration), but the linear-extrapolation approach is more numerically stable and naturally adapts to the trend's own slope.
|
||||
|
||||
The 2-pole Super Smoother pre-filter (at half the specified period) removes high-frequency noise before the reflex computation, preventing false signals from bar-to-bar price noise. The exponential RMS normalization ensures the output has consistent scale regardless of the instrument's volatility.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### 1. Super Smoother Pre-Filter
|
||||
|
||||
A 2-pole IIR low-pass filter with cutoff at half the specified period:
|
||||
|
||||
$$
|
||||
\text{Filt} = c_1 \cdot \frac{x_t + x_{t-1}}{2} + c_2 \cdot \text{Filt}_{t-1} + c_3 \cdot \text{Filt}_{t-2}
|
||||
$$
|
||||
|
||||
where $a_1 = e^{-\sqrt{2}\pi / (N/2)}$, $c_2 = 2a_1\cos(\sqrt{2}\pi/(N/2))$, $c_3 = -a_1^2$, $c_1 = 1-c_2-c_3$.
|
||||
|
||||
### 2. Linear Extrapolation Slope
|
||||
|
||||
$$
|
||||
\text{slope} = \frac{\text{Filt}_{t-N} - \text{Filt}_t}{N}
|
||||
$$
|
||||
|
||||
### 3. Deviation Summation
|
||||
|
||||
$$
|
||||
\text{Sum} = \frac{1}{N}\sum_{i=1}^{N}\left[(\text{Filt}_t + i \cdot \text{slope}) - \text{Filt}_{t-i}\right]
|
||||
$$
|
||||
|
||||
### 4. Exponential RMS Normalization
|
||||
|
||||
$$
|
||||
\text{MS} = 0.04 \cdot \text{Sum}^2 + 0.96 \cdot \text{MS}_{t-1}
|
||||
$$
|
||||
|
||||
$$
|
||||
\text{REFLEX} = \frac{\text{Sum}}{\sqrt{\text{MS}}}
|
||||
$$
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
**Super Smoother coefficients (half-period cutoff):**
|
||||
|
||||
$$
|
||||
a_1 = e^{-\sqrt{2}\pi / (N/2)}, \quad c_2 = 2a_1\cos\!\left(\frac{\sqrt{2}\pi}{N/2}\right), \quad c_3 = -a_1^2, \quad c_1 = 1-c_2-c_3
|
||||
$$
|
||||
|
||||
**Deviation from linear trend:**
|
||||
|
||||
$$
|
||||
D_i = (\text{Filt}_t + i \cdot \text{slope}) - \text{Filt}_{t-i}, \quad i = 1, \ldots, N
|
||||
$$
|
||||
|
||||
**Mean deviation:**
|
||||
|
||||
$$
|
||||
\text{Sum} = \frac{1}{N}\sum_{i=1}^{N} D_i
|
||||
$$
|
||||
|
||||
**Interpretation:**
|
||||
|
||||
- $\text{Sum} > 0$: filtered price is above its linear extrapolation (upward curvature, potential uptrend)
|
||||
- $\text{Sum} < 0$: filtered price is below its linear extrapolation (downward curvature, potential downtrend)
|
||||
- Zero crossings signal inflection points (trend reversals)
|
||||
|
||||
**Default parameters:** `period = 20`, `minPeriod = 2`. Output is an oscillator (not overlay).
|
||||
|
||||
**Pseudo-code (streaming):**
|
||||
|
||||
```
|
||||
// Super Smoother (2-pole IIR)
|
||||
filt = c1*(price + price[1])/2 + c2*filt[1] + c3*filt[2]
|
||||
|
||||
// Store in circular buffer
|
||||
buf[head] = filt
|
||||
|
||||
// Slope from N-bar-ago to current
|
||||
slope = (filt_lag_N - filt) / N
|
||||
|
||||
// Sum deviations from linear extrapolation
|
||||
sum = 0
|
||||
for i = 1 to N:
|
||||
sum += (filt + i*slope) - filt[i]
|
||||
sum /= N
|
||||
|
||||
// Normalize by exponential RMS
|
||||
ms = 0.04 * sum² + 0.96 * ms[1]
|
||||
return ms > 0 ? sum / sqrt(ms) : 0
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Ehlers, J.F. (2020). "Reflex: A New Zero-Lag Indicator." *Technical Analysis of Stocks & Commodities*, February 2020.
|
||||
- Ehlers, J.F. (2013). *Cycle Analytics for Traders*. Wiley. Chapter 3: Super Smoothers.
|
||||
@@ -0,0 +1,92 @@
|
||||
# RMED: Ehlers Recursive Median Filter
|
||||
|
||||
> "John Ehlers combined two tools that rarely meet: the median (nonlinear, spike-resistant) and the EMA (smooth, recursive). The median kills the spikes, the EMA smooths the survivors. Together they produce a filter that is both resistant and smooth."
|
||||
|
||||
RMED applies exponential smoothing to a 5-bar running median, creating a nonlinear IIR filter that rejects impulsive spike noise while providing smooth recursive tracking. The median component eliminates outliers that would corrupt any linear filter, while the EMA provides the recursive continuity that a pure median lacks. The EMA constant $\alpha$ is derived from Ehlers' cycle-period formula, connecting the smoothing rate to the dominant cycle length of the data.
|
||||
|
||||
## Historical Context
|
||||
|
||||
John F. Ehlers published "Recursive Median Filters" in *Technical Analysis of Stocks & Commodities* (March 2018). The article addressed a fundamental limitation of linear filters: no matter how sophisticated an EMA, DEMA, or Butterworth design is, a single bad tick or flash-crash spike will corrupt the output for its entire impulse response duration.
|
||||
|
||||
Median filters solve this problem completely for impulse noise, but traditional median filters are non-recursive (pure FIR), which means they have no "memory" between bars — each output depends only on the current window, creating a choppy, step-like output. Ehlers' innovation was to follow the median with an exponential average, combining the spike rejection of the median with the smooth continuity of the EMA.
|
||||
|
||||
The 5-bar median window is a design choice: it can reject up to 2 simultaneous bad ticks in a row (the breakdown point is $\lfloor 5/2 \rfloor = 2$), while having minimal lag (centered at bar 2 of 5). Wider median windows would reject more spikes but add more lag.
|
||||
|
||||
The EMA constant $\alpha = (\cos\theta + \sin\theta - 1)/\cos\theta$ where $\theta = 2\pi/P$ is Ehlers' standard cycle-period-to-smoothing mapping, which produces a critically damped response at the specified period.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### 1. Five-Bar Median
|
||||
|
||||
A circular buffer of 5 values is sorted each bar; the middle element is extracted as the median.
|
||||
|
||||
### 2. Ehlers EMA Constant
|
||||
|
||||
$$
|
||||
\alpha = \frac{\cos(2\pi/P) + \sin(2\pi/P) - 1}{\cos(2\pi/P)}
|
||||
$$
|
||||
|
||||
Clamped to $[0, 1]$ for numerical safety.
|
||||
|
||||
### 3. Recursive Smoothing
|
||||
|
||||
$$
|
||||
\text{RMED}_t = \alpha \cdot \text{Median5}_t + (1 - \alpha) \cdot \text{RMED}_{t-1}
|
||||
$$
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
**Five-bar median:**
|
||||
|
||||
$$
|
||||
\text{Med5}_t = \text{median}(x_t, x_{t-1}, x_{t-2}, x_{t-3}, x_{t-4})
|
||||
$$
|
||||
|
||||
**Ehlers smoothing constant (from cycle period $P$):**
|
||||
|
||||
$$
|
||||
\theta = \frac{2\pi}{P}, \quad \alpha = \frac{\cos\theta + \sin\theta - 1}{\cos\theta}
|
||||
$$
|
||||
|
||||
For common periods:
|
||||
|
||||
| $P$ | $\alpha$ | Equivalent EMA period |
|
||||
| :---: | :---: | :---: |
|
||||
| 5 | 0.72 | ~3.6 |
|
||||
| 10 | 0.38 | ~4.3 |
|
||||
| 20 | 0.20 | ~9.0 |
|
||||
| 40 | 0.10 | ~19 |
|
||||
|
||||
**Recursive filter:**
|
||||
|
||||
$$
|
||||
\text{RMED}_t = \alpha \cdot \text{Med5}_t + (1-\alpha) \cdot \text{RMED}_{t-1}
|
||||
$$
|
||||
|
||||
**Spike rejection:** A single outlier in the 5-bar window is always rejected by the median (it cannot be the middle value). Two consecutive outliers are also rejected. Three or more consecutive outliers breach the breakdown point.
|
||||
|
||||
**Default parameters:** `period = 12`, `minPeriod = 1`.
|
||||
|
||||
**Pseudo-code (streaming):**
|
||||
|
||||
```
|
||||
// Ehlers alpha from cycle period
|
||||
angle = 2π / period
|
||||
alpha = (cos(angle) + sin(angle) - 1) / cos(angle)
|
||||
alpha = clamp(alpha, 0, 1)
|
||||
|
||||
// 5-bar median
|
||||
buf[head] = price
|
||||
head = (head + 1) % 5
|
||||
sorted = sort(buf)
|
||||
med5 = sorted[2]
|
||||
|
||||
// Recursive EMA of median
|
||||
rm = alpha * med5 + (1-alpha) * rm
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Ehlers, J.F. (2018). "Recursive Median Filters." *Technical Analysis of Stocks & Commodities*, March 2018.
|
||||
- Tukey, J.W. (1977). *Exploratory Data Analysis*. Addison-Wesley. Chapter 7: Resistant Smoothing.
|
||||
- Ehlers, J.F. (2001). *Rocket Science for Traders*. Wiley. Chapter 3: Smoothing Constants from Cycle Period.
|
||||
@@ -0,0 +1,92 @@
|
||||
# SAK: Swiss Army Knife Indicator
|
||||
|
||||
> "John Ehlers unified nine filter types into one second-order IIR framework. Change the coefficients and you get EMA, SMA, Gaussian, Butterworth, smoother, high-pass, 2-pole high-pass, band-pass, or band-stop. One formula to implement them all."
|
||||
|
||||
SAK is a unified second-order IIR filter framework where five coefficient sets ($c_0$, $b_0$, $b_1$, $b_2$, $a_1$, $a_2$) determine the filter type. The general form $\text{Filt} = c_0(b_0 x + b_1 x_{t-1} + b_2 x_{t-2}) + a_1 \text{Filt}_{t-1} + a_2 \text{Filt}_{t-2}$ can instantiate nine different filters by selecting the appropriate coefficient derivation. Published by John Ehlers in "Swiss Army Knife Indicator" (*Technical Analysis of Stocks & Commodities*, January 2006).
|
||||
|
||||
## Historical Context
|
||||
|
||||
John F. Ehlers published the Swiss Army Knife indicator in TASC (January 2006), motivated by the observation that most common technical analysis filters (EMA, SMA, Gaussian, Butterworth, high-pass, band-pass) share the same second-order difference equation structure. Only the coefficients differ. By parameterizing the coefficient derivation, a single implementation can serve as any of nine filter types.
|
||||
|
||||
This unification has both practical and theoretical value. Practically, it reduces code duplication: one function with a mode selector replaces nine separate implementations. Theoretically, it reveals the deep connection between seemingly different filters: they are all members of the same family of second-order IIR filters, differing only in their pole and zero placements in the z-plane.
|
||||
|
||||
Ehlers derives the coefficients from the cycle period $P$ using trigonometric formulas that place poles/zeros at specific frequencies, ensuring each filter type has its cutoff or center frequency aligned with the user-specified period.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### 1. Unified Second-Order IIR
|
||||
|
||||
$$
|
||||
\text{Filt}_t = c_0(b_0 x_t + b_1 x_{t-1} + b_2 x_{t-2}) + a_1 \text{Filt}_{t-1} + a_2 \text{Filt}_{t-2}
|
||||
$$
|
||||
|
||||
### 2. Coefficient Derivation by Mode
|
||||
|
||||
Three smoothing parameters are computed from the period:
|
||||
- **EMA/HP/SMA/Smooth modes:** $\alpha = (\cos\theta + \sin\theta - 1)/\cos\theta$, $\theta = 2\pi/P$
|
||||
- **Gauss/Butter/2PHP modes:** $\beta = 2.415(1 - \cos\theta)$, $\alpha = -\beta + \sqrt{\beta^2 + 2\beta}$
|
||||
- **BP/BS modes:** $\gamma = 1/\cos(2\pi\delta/P)$, $\beta = \cos(2\pi/P)$, $\alpha = \gamma - \sqrt{\gamma^2 - 1}$
|
||||
|
||||
### 3. Nine Filter Types
|
||||
|
||||
| Mode | Type | Overlay? |
|
||||
| :--- | :--- | :---: |
|
||||
| EMA | Low-pass (1-pole) | Yes |
|
||||
| SMA | Low-pass (running sum) | Yes |
|
||||
| Gauss | Low-pass (2-pole Gaussian) | Yes |
|
||||
| Butter | Low-pass (2-pole Butterworth) | Yes |
|
||||
| Smooth | Low-pass (FIR-like) | Yes |
|
||||
| HP | High-pass (1-pole) | No |
|
||||
| 2PHP | High-pass (2-pole) | No |
|
||||
| BP | Band-pass | No |
|
||||
| BS | Band-stop (notch) | No |
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
**Unified transfer function (z-domain):**
|
||||
|
||||
$$
|
||||
H(z) = \frac{c_0(b_0 + b_1 z^{-1} + b_2 z^{-2})}{1 - a_1 z^{-1} - a_2 z^{-2}}
|
||||
$$
|
||||
|
||||
**Coefficient table:**
|
||||
|
||||
| Mode | $c_0$ | $b_0$ | $b_1$ | $b_2$ | $a_1$ | $a_2$ |
|
||||
| :--- | :--- | :---: | :---: | :---: | :--- | :--- |
|
||||
| EMA | 1 | $\alpha$ | 0 | 0 | $1-\alpha$ | 0 |
|
||||
| SMA | $1/n$ | 1 | 0 | 0 | 1 | 0 |
|
||||
| Gauss | $\alpha^2$ | 1 | 0 | 0 | $2(1-\alpha)$ | $-(1-\alpha)^2$ |
|
||||
| Butter | $\alpha^2/4$ | 1 | 2 | 1 | $2(1-\alpha)$ | $-(1-\alpha)^2$ |
|
||||
| Smooth | $\alpha^2/4$ | 1 | 2 | 1 | 0 | 0 |
|
||||
| HP | $1-\alpha/2$ | 1 | $-1$ | 0 | $1-\alpha$ | 0 |
|
||||
| 2PHP | $(1-\alpha/2)^2$ | 1 | $-2$ | 1 | $2(1-\alpha)$ | $-(1-\alpha)^2$ |
|
||||
| BP | $(1-\alpha)/2$ | 1 | 0 | $-1$ | $\beta(1+\alpha)$ | $-\alpha$ |
|
||||
| BS | $(1+\alpha)/2$ | 1 | $-2\beta$ | 1 | $\beta(1+\alpha)$ | $-\alpha$ |
|
||||
|
||||
**SMA special path:** Uses $\text{Filt} = \frac{1}{n}x_t + \text{Filt}_{t-1} - \frac{1}{n}x_{t-n}$ (running sum).
|
||||
|
||||
**Stability:** All modes produce stable filters for $P > 2$. The Gauss and Butter modes have conjugate poles inside the unit circle; BP/BS modes have poles on the real axis for the specified bandwidth.
|
||||
|
||||
**Default parameters:** `filterType = "BP"`, `period = 20`, `n = 10` (SMA only), `delta = 0.1` (BP/BS), `minPeriod = 2`.
|
||||
|
||||
**Pseudo-code (streaming):**
|
||||
|
||||
```
|
||||
// Compute alpha, beta, gamma from period and mode
|
||||
[alpha, beta, gamma] = derive_params(filterType, period, delta)
|
||||
|
||||
// Select coefficients by mode
|
||||
[c0, b0, b1, b2, a1, a2] = select_coeffs(filterType, alpha, beta, gamma, n)
|
||||
|
||||
// Apply unified 2nd-order IIR
|
||||
if filterType == "SMA":
|
||||
result = (1/n)*src + result[1] - (1/n)*src[n]
|
||||
else:
|
||||
result = c0*(b0*src + b1*src[1] + b2*src[2]) + a1*result[1] + a2*result[2]
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Ehlers, J.F. (2006). "Swiss Army Knife Indicator." *Technical Analysis of Stocks & Commodities*, January 2006.
|
||||
- Ehlers, J.F. (2001). *Rocket Science for Traders*. Wiley. Chapters 3-4: IIR and FIR filter design.
|
||||
- Ehlers, J.F. (2004). *Cybernetic Analysis for Stocks and Futures*. Wiley. Chapter 2: Filters.
|
||||
Reference in New Issue
Block a user