8.1 KiB
TRENDFLEX: Ehlers Trendflex Indicator
The trend is your friend until it bends.
| Property | Value |
|---|---|
| Category | Oscillator |
| Inputs | Source (close) |
| Parameters | period |
| Outputs | Single series (Trendflex) |
| Output range | Varies (see docs) |
| Warmup | period bars |
| PineScript | trendflex.pine |
- The Trendflex indicator combines a 2-pole Butterworth low-pass pre-filter (Super Smoother) with an O(1) cumulative slope measurement and exponentia...
- Similar: Reflex, Deco | Complementary: ADX | Trading note: Ehlers' Trendflex; trend-mode companion to Reflex. Positive = uptrend, negative = downtrend.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Introduction
The Trendflex indicator combines a 2-pole Butterworth low-pass pre-filter (Super Smoother) with an O(1) cumulative slope measurement and exponential RMS normalization to produce a zero-centered oscillator that quantifies trend strength. Unlike conventional slope or momentum indicators that suffer from noise amplification or lag, Trendflex pre-smooths via the Super Smoother, computes the least-squares slope of the filtered signal over a lookback window in constant time, then normalizes by a running RMS estimate. The result: a bounded oscillator where values above zero indicate uptrend, below zero indicate downtrend, and magnitude reflects trend conviction.
Historical Context
John F. Ehlers introduced the Trendflex indicator in his 2013 work on cycle and trend measurement for traders. The indicator addresses a fundamental problem: how do you separate trend from cycle without introducing excessive lag or noise? Ehlers' insight was to cascade two well-understood DSP components: a Super Smoother (2-pole Butterworth) that removes high-frequency noise without the phase distortion of moving averages, followed by a slope estimator that measures the linear regression slope of the filtered signal.
The original Pine Script implementation uses an O(N) summation loop per bar. QuanTAlib's implementation replaces this with a RingBuffer-based running sum, reducing the per-bar cost to O(1) while producing bit-identical results. This is a pure algorithmic optimization with no mathematical approximation.
No other major library (TA-Lib, Skender, Tulip, Ooples) implements Trendflex. QuanTAlib's implementation serves as a reference.
Architecture and Physics
1. Super Smoother Pre-Filter (2-Pole Butterworth)
The Super Smoother acts as a low-pass filter with cutoff at the half-period:
a_1 = e^{-\sqrt{2}\pi / P_{half}}, \quad b_1 = 2 a_1 \cos\!\left(\frac{\sqrt{2}\pi}{P_{half}}\right)
c_2 = b_1, \quad c_3 = -a_1^2, \quad c_1 = 1 - c_2 - c_3
The filter update is:
\text{Filt}_n = c_1 \cdot \frac{x_n + x_{n-1}}{2} + c_2 \cdot \text{Filt}_{n-1} + c_3 \cdot \text{Filt}_{n-2}
where P_{half} = \text{period} \times 0.5.
2. O(1) Cumulative Slope via Running Sum
The slope over the lookback window is computed from the identity:
\text{Slope} = \frac{N \cdot \text{Filt}_n - \sum_{i=0}^{N-1} \text{Filt}_{n-i}}{\text{period}}
The summation \sum \text{Filt}_{n-i} is maintained as a running sum in a circular buffer (RingBuffer). Each bar adds the new filtered value and removes the oldest, keeping the operation O(1) regardless of period length.
3. Exponential RMS Normalization
To produce a unit-scale oscillator, the slope is divided by its own running RMS:
\text{MS}_n = 0.04 \cdot \text{Slope}_n^2 + 0.96 \cdot \text{MS}_{n-1}
\text{Trendflex}_n = \frac{\text{Slope}_n}{\sqrt{\text{MS}_n}}
The 0.04/0.96 exponential weighting corresponds to approximately a 25-bar half-life for the mean-square estimate, providing smooth normalization without requiring a lookback buffer.
Mathematical Foundation
Z-Domain Transfer Function
The Super Smoother transfer function:
H_{SSF}(z) = \frac{c_1 \cdot \frac{1 + z^{-1}}{2}}{1 - c_2 z^{-1} - c_3 z^{-2}}
The slope estimator computes a differenced cumulative sum, effectively applying a comb filter:
H_{slope}(z) = \frac{N - \sum_{k=0}^{N-1} z^{-k}}{\text{period}}
The RMS normalization is a nonlinear operation with no closed-form transfer function, but its exponential smoothing has characteristic time constant \tau = 1/0.04 = 25 bars.
FMA Usage
Both the Super Smoother and RMS normalization use Math.FusedMultiplyAdd for the a*b + c patterns:
filt = Math.FusedMultiplyAdd(c1, (input + src1) * 0.5,
Math.FusedMultiplyAdd(c2, filt, c3 * filt1));
ms = Math.FusedMultiplyAdd(RMS_ALPHA, slopeSum * slopeSum, RMS_DECAY * ms);
Performance Profile
Operation Count (Streaming Mode, Scalar)
| Operation | Count | Notes |
|---|---|---|
| FMA (SSF filter) | 2 | Nested FusedMultiplyAdd for IIR |
| Multiply (SSF input avg) | 1 | (input + src1) * 0.5 |
| RingBuffer Add | 1 | O(1) circular write + sum update |
| Multiply + Subtract (slope) | 2 | n * filt - sum then / period |
| FMA (RMS update) | 1 | 0.04 * slope^2 + 0.96 * ms |
| Sqrt | 1 | Math.Sqrt(ms) |
| Division (normalize) | 1 | slope / sqrt(ms) |
| Total hot path | ~9 ops | O(1) per bar |
Batch Mode
The batch path uses CalculateCore which inlines the same logic without RingBuffer snapshot/restore overhead. Since the SSF is inherently serial (IIR dependency), SIMD parallelization is not applicable. The FMA chain provides excellent instruction-level pipelining.
Quality Metrics
| Metric | Score | Notes |
|---|---|---|
| Trend Detection | 9/10 | Strong trend/no-trend discrimination |
| Noise Rejection | 8/10 | SSF pre-filter removes HF noise |
| Lag | 6/10 | SSF introduces some phase delay |
| Responsiveness | 7/10 | Good for trend changes |
| Computational Cost | 9/10 | O(1), ~9 ops per bar |
| Memory Efficiency | 8/10 | RingBuffer(period) + ~64 bytes state |
Validation
| Library | Status | Notes |
|---|---|---|
| TA-Lib | N/A | Not implemented |
| Skender | N/A | Not implemented |
| Tulip | N/A | Not implemented |
| Ooples | N/A | Not implemented |
| PineScript | Reference | trendflex.pine validated self-consistency |
Self-consistency validation: Streaming, Batch (TSeries), and Span Batch modes produce identical results to machine precision (< 10^{-10}).
Common Pitfalls
-
Not an overlay. Trendflex is an oscillator centered around zero. Plot in a separate window, not overlaid on price.
-
Period interpretation. The
periodparameter controls both the SSF cutoff (via half-period) and the slope lookback window. Larger periods produce smoother output but increase lag. Typical range: 10-40. -
RMS normalization startup. The exponential mean-square estimate needs approximately 25 bars (1/0.04) to stabilize. During warmup, the normalization may produce values with higher variance.
IsHotfires atcount >= period. -
Constant input produces zero. By design, constant input produces zero slope and zero output. This is correct behavior, not a bug.
-
Sensitivity to period < 3. Very small periods cause the SSF coefficients to become extreme, potentially producing oscillatory artifacts. Use period >= 3 for stable results.
-
Bar correction cost. The RingBuffer snapshot/restore mechanism for
isNew=falseis O(period) due to the buffer copy. For very large periods (>1000), this may be noticeable in tight correction loops. -
Not bounded to [-1, 1]. Despite RMS normalization, Trendflex output is not strictly bounded. Strong trend initiations can produce values > 1 or < -1 before the RMS estimate catches up. Treat as a relative measure, not a percentage.
References
- Ehlers, J. F. (2013). "Trendflex and Reflex." Cycle Analytics for Traders. Wiley.
- Ehlers, J. F. (2004). Cybernetic Analysis for Stocks and Futures. Wiley.
- Ehlers, J. F. (2001). Rocket Science for Traders. Wiley.