Files

7.0 KiB
Raw Permalink Blame History

MADH: Ehlers Moving Average Difference with Hann

By computing the percentage difference between short and long Hann-windowed FIR averages, Ehlers creates a zero-crossing trend oscillator analogous to MACD but with superior spectral properties.

Property Value
Category Oscillator
Inputs Source (close)
Parameters shortLength (default 8), dominantCycle (default 27)
Outputs Single series (Madh)
Output range Unbounded (typically ±5%), zero-centered
Zero mean Yes
Warmup LongLength bars
PineScript madh.pine
  • MADH (Moving Average Difference with Hann) computes the percentage difference between a short and long Hann-windowed FIR moving average, producing a zero-crossing trend oscillator similar in concept to MACD but using FIR filters with no spectral leakage.
  • Similar: MACD, APO, DECO | Complementary: RSIH for momentum confirmation | Trading note: Zero crossings signal trend changes; peaks/valleys indicate overbought/oversold.
  • No external validation libraries implement MADH. Validated through self-consistency and behavioral testing.

MADH applies two separate Hann FIR filters to the close price — a short window and a long window derived from the dominant cycle estimate — then expresses their difference as a percentage: 100 × (Filt1/Filt2 - 1). The Hann window eliminates spectral leakage, making MADH more responsive than EMA-based MACD while avoiding Gibbs ringing artifacts.

Historical Context

The MADH indicator was published by John F. Ehlers in the November 2021 issue of Technical Analysis of Stocks & Commodities magazine under the title "The MAD Indicator, Enhanced." It is an enhancement of the basic MAD indicator from October 2021, replacing simple moving averages with Hann-windowed FIR filters. Ehlers demonstrated that the Hann window provides inherent smoothing without the spectral leakage of rectangular or exponential windows, resulting in cleaner trend signals with fewer whipsaws.

Architecture & Physics

MADH operates as a dual-FIR comparator:

Parameter Derivation

The long window length is derived from the short length and dominant cycle estimate:

L_{\text{long}} = \text{IntPortion}\left(L_{\text{short}} + \frac{D}{2}\right)

where L_{\text{short}} is the short length (default 8) and D is the dominant cycle (default 27).

Hann Window Coefficients

Two sets of coefficients are precomputed in the constructor:

w(k) = 1 - \cos\left(\frac{2\pi k}{N + 1}\right) \quad \text{for } k = 1, 2, \ldots, N

Note: Ehlers uses (N + 1) in the denominator, not the standard symmetric Hann formula (N - 1).

Dual FIR Filters

\text{Filt1} = \frac{\sum_{k=1}^{L_{\text{short}}} w_s(k) \cdot \text{Close}_{t-k+1}}{\sum_{k=1}^{L_{\text{short}}} w_s(k)} \text{Filt2} = \frac{\sum_{k=1}^{L_{\text{long}}} w_l(k) \cdot \text{Close}_{t-k+1}}{\sum_{k=1}^{L_{\text{long}}} w_l(k)}

Percentage Difference

\text{MADH}_t = 100 \times \left(\frac{\text{Filt1}}{\text{Filt2}} - 1\right)

When \text{Filt2} = 0 (degenerate case), MADH returns 0.

Implemented with FMA for coefficient multiplication:

filt1 = Math.FusedMultiplyAdd(w, _closeBuf[available - k], filt1);

Performance Profile

MADH is an O(LongLength) FIR filter — each bar requires scanning both windows.

Operation Count (Streaming Mode, Scalar)

Operation Count Cost (cycles) Subtotal
Short Hann Scan
FMA (w × close + acc) Ls 4 4Ls
ADD (coef sum) Ls 1 Ls
Long Hann Scan
FMA (w × close + acc) Ll 4 4Ll
ADD (coef sum) Ll 1 Ll
Normalization
DIV (filt1/coef1, filt2/coef2) 2 15 30
DIV (filt1/filt2) 1 15 15
MUL (× 100) 1 3 3
SUB (- 1) 1 1 1
Total ~5(Ls + Ll) + 49 cycles

For defaults Ls=8, Ll=21: ~194 cycles per bar.

Dominant cost: FMA loops (4(Ls + Ll) cycles, ~60%)

Batch Mode (SIMD Analysis)

MADH is not SIMD-parallelizable across bars because each bar's window overlaps with adjacent bars. However, the inner coefficient × price accumulation loops could benefit from SIMD vectorization within a single bar.

Quality Metrics

Metric Score Notes
Accuracy 9/10 Hann window provides excellent spectral properties
Timeliness 9/10 FIR filters with minimal group delay
Overshoot 7/10 Unbounded output; can overshoot during sharp moves
Smoothness 8/10 Hann window provides inherent anti-aliasing

Validation

MADH is not implemented in mainstream libraries. Validation relies on behavioral testing.

Library Status Notes
TA-Lib N/A Not implemented
Skender N/A Not implemented
Tulip N/A Not implemented
Ooples N/A Not implemented
Behavioral Validated: constant→zero, symmetry, mode consistency

Behavioral Test Summary

  • Constant Input → Zero: Constant close → Filt1 = Filt2 → ratio = 1 → MADH = 0
  • Trending Input → Non-Zero: Ascending close → short MA leads long MA → positive MADH
  • Direction Symmetry: MADH(ascending) > 0 and MADH(descending) < 0
  • Mode Consistency: Streaming, batch, span, and event-driven modes produce identical results
  • Bar Correction: Snapshot/Restore via RingBuffer produces exact rollback

Common Pitfalls

  1. Warmup Period: MADH requires LongLength bars to fill the close buffer. Use IsHot to detect readiness. With defaults (8, 27), LongLength = 21.

  2. Hann Window Denominator: Ehlers uses (N + 1) in the Hann formula, NOT the standard symmetric (N - 1). Using the wrong denominator will produce incorrect coefficients.

  3. Unbounded Output: Unlike RSIH (bounded [-1, +1]), MADH is unbounded. During sharp trends, values can exceed ±5%. Do not use fixed overbought/oversold levels.

  4. LongLength Derivation: Uses integer division: LongLength = ShortLength + DominantCycle / 2. For odd DominantCycle values, the result is truncated (e.g., DominantCycle=27 → 27/2=13 → LongLength=21).

  5. Division Safety: When Filt2 ≈ 0 (near-zero average price), the ratio is undefined. The implementation returns 0.0 using an epsilon floor of 1e-10.

  6. FIR Complexity: MADH is O(LongLength) per bar, not O(1) like IIR indicators. For very large dominant cycle values, this may impact performance.

  7. Bar Correction: Like all QuanTAlib indicators, MADH supports bar correction via the isNew parameter. The RingBuffer Snapshot()/Restore() mechanism handles this atomically.