Files

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

  1. Not an overlay. Trendflex is an oscillator centered around zero. Plot in a separate window, not overlaid on price.

  2. Period interpretation. The period parameter 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.

  3. 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. IsHot fires at count >= period.

  4. Constant input produces zero. By design, constant input produces zero slope and zero output. This is correct behavior, not a bug.

  5. Sensitivity to period < 3. Very small periods cause the SSF coefficients to become extreme, potentially producing oscillatory artifacts. Use period >= 3 for stable results.

  6. Bar correction cost. The RingBuffer snapshot/restore mechanism for isNew=false is O(period) due to the buffer copy. For very large periods (>1000), this may be noticeable in tight correction loops.

  7. 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.