TradingView indicator calculations

Exponential Hull Moving Average: A Controlled HMA Comparison

An exponential Hull moving average replaces the three weighted moving averages in the Hull construction with exponential moving averages: EHMA = EMA(2 × EMA(source, half length) − EMA(source, full length), root length). This public variant differs from Alan Hull's WMA-based HMA. Equal base lengths do not imply equal response, overshoot or signal timing.

See the decision guide

Define the EHMA variant before comparing scripts

CQG publishes the EMA-based Hull construction used here. Alan Hull's original construction uses WMA. InSilico's public Hull Suite lists EHMA separately from HMA and THMA. Those references establish a public formula family; they do not establish one universal seed, rounding rule or alert convention across every script called EHMA.

  • The final EMA consumes the current corrected value, after updating the fast and slow EMAs on that bar. It is not an EMA applied to an already computed HMA.
  • The floor convention is explicit here; CQG's displayed formula alone does not specify all implementation details. At n = 15 this page uses h = 7 and r = 3. Rounding both to nearest integers would give h = 8 and r = 4, changing the output.
  • At n = 1, both derived lengths are clamped to 1 and both outputs equal the source. Zero or negative lengths are rejected. The script caps n at 500 to bound its WMA loops.
CalculationExplicit formula on this page
Lengthsh = max(1, floor(n/2)); r = max(1, floor(√n))
EMA recurrenceE(t) = α × x(t) + (1 − α) × E(t−1), α = 2/(length + 1)
EHMAEMA(2 × EMA(x, h) − EMA(x, n), r)
WMA Hull comparatorWMA(2 × WMA(x, h) − WMA(x, n), r)
WMA weightsFor length m, newest weight m, then m−1 down to 1; divide by m(m+1)/2

Seeds, missing observations and a common comparison window

All three EMA states begin at their first valid inputs. With a first price of 100, the fast EMA, slow EMA and corrected input are 100, so the final EMA also starts at 100. A missing source resets all three EMA states. The WMA implementation propagates missing observations instead of skipping them.

  • With uninterrupted data, EHMA produces a value immediately. HMA first has enough data after n + r − 1 observations: at zero-based bar n + r − 2. With n = 9 and r = 3, that is bar 10. This availability difference is not evidence that EHMA detects a trend earlier.
  • A single EMA seed difference decays by (1 − α) on each update. The EHMA cascade also receives decaying differences from its inner stages, so there is no fixed bar count that erases every startup effect.
  • The script requires at least max(user warm-up, n + r − 1) consecutive valid observations, then requires the current and previous two bars to be ready before scoring slope events. The default warm-up is 100 observations. With this fixture, slope-event scoring starts at k = 2, so the initial upward onset at k = 1 is excluded; the plotted response values remain available. This is a comparison policy, not a convergence theorem.
  • Use the same source, chart timeframe, session, adjustments and timestamps for both lines. Load more prior history and compare the same completed bars again if seed sensitivity matters. The synthetic example starts with 100 identical prices, so its states are exactly at the baseline before the step.

A step and reversal measured on the same bars

Use n = 9, h = 4 and r = 3. Feed 100 on chart bars 0 through 99, 110 on bars 100 through 111, then 100 from bar 112 onward. In the table, relative bar k = chart bar − 99: k = 0 is the last baseline bar, k = 1 is the upward step and k = 13 is the reversal. Both lines receive exactly the same observations. Values are calculated before rounding to four decimals; this is a synthetic arithmetic test, not market performance.

  • At k = 1, fast EMA = 0.4×110 + 0.6×100 = 104; slow EMA = 0.2×110 + 0.8×100 = 102. Corrected input = 2×104 − 102 = 106. The final EMA has α = 0.5, giving 0.5×106 + 0.5×100 = 103.
  • At k = 2, fast EMA = 106.4 and slow EMA = 103.6. Corrected input = 109.2, so EHMA = 0.5×109.2 + 0.5×103 = 106.1. The independent WMA calculation gives 107.1111.
  • On the upward plateau k = 1 through 12, HMA peaks at 112.6667 on k = 5; EHMA peaks at 111.4030 on k = 7. Overshoot above 110 is 2.6667 and 1.4030 price units, or 26.67% and 14.03% of the 10-unit step. These percentages are not investment returns.
Relative bar kInputEHMA(9)WMA Hull(9)
0100100.0000100.0000
1110103.0000103.0000
2110106.1000107.1111
3110108.4500110.7407
4110109.9770112.5926
5110110.8493112.6667
6110111.2688111.9630
7110111.4030111.1481
8110111.3724110.5556
12110110.7946110.0000
13100107.6591107.0000
15100101.992099.2593
1710099.441197.3333
1810098.965498.0370
2010098.779199.4444

Response speed and confirmed events answer different questions

Measure both filters over the same window rather than selecting a favorable timestamp for each. Define step response as the first completed bar at or above 109, which is 90% of the rise from 100 to 110. Define a downward slope onset as current slope < −ε and previous slope ≥ −ε, with ε = 0.000001 price units. The upward onset uses current slope > ε and previous slope ≤ ε.

  • Both first downward onsets happen before the input falls on k = 13. The cause is decay from overshoot while the input remains 110. A filter turning down is therefore insufficient evidence of a price reversal.
  • HMA settles to 110 by k = 11 and has another downward onset at k = 13 after a flat interval. EHMA is already declining at k = 13 and has no new downward onset there. Counting every falling bar, counting sign changes and counting new slope onsets are different signal rules.
  • Use barstate.isconfirmed and Once Per Bar Close for alerts. Confirmation removes the provisional intrabar event; it does not remove overshoot or predict subsequent prices. Lines can still move while the live bar is open.
  • No offset, future data or higher-timeframe request is used here. Results for this input do not establish a universal speed ordering, a best length or a trading edge. The two filters are related transformations of the same source, not independent evidence.
Measurement on the shared inputEHMAWMA Hull
First completed bar at or above 109k = 4k = 3
First value above the 110 plateauk = 5k = 3
First downward slope onsetk = 8k = 6
First upward slope onset after the drop at k = 13k = 21k = 18

Reproduce the comparison in Pine Script v6

Enable Synthetic step and reversal with the default length 9, warm-up 100 and tolerance 0.000001. Inspect the Data Window at relative bars k shown above. Both confirmed event fields are available in test mode, but alerts are disabled there. With test mode off, the selected chart source drives both filters. The script is an independent implementation of the stated formulas and policies.

Pineify syntax check passed for this final source. The numerical fixture independently checks the formulas; syntax success alone does not establish chart parity or profitability.pine
//@version=6
indicator("EHMA and WMA Hull: controlled comparison", overlay=true, max_bars_back=1000)
n = input.int(9, "Base length", minval=1, maxval=500)
source = input.source(close, "Source")
warmup = input.int(100, "Minimum consecutive observations", minval=3)
epsilon = input.float(0.000001, "Slope tolerance in price units", minval=0)
isTest = input.bool(false, "Synthetic step and reversal")
halfLength = math.max(1, int(math.floor(n / 2.0)))
rootLength = math.max(1, int(math.floor(math.sqrt(n))))

emaSeeded(float x, int length) =>
    float a = 2.0 / (length + 1.0)
    float value = na
    value := na(x) ? na : na(value[1]) ? x : a * x + (1.0 - a) * value[1]
    value

wmaStrict(float x, int length) =>
    float total = 0.0
    for i = 0 to length - 1
        total += (length - i) * x[i]
    total / (length * (length + 1.0) / 2.0)

float sample = bar_index < 100 ? 100.0 : bar_index < 112 ? 110.0 : 100.0
float x = isTest ? sample : source
fast = emaSeeded(x, halfLength)
slow = emaSeeded(x, n)
ehma = emaSeeded(2.0 * fast - slow, rootLength)
fastW = wmaStrict(x, halfLength)
slowW = wmaStrict(x, n)
hma = wmaStrict(2.0 * fastW - slowW, rootLength)
var int consecutive = 0
consecutive := na(x) ? 0 : consecutive + 1
int required = math.max(warmup, n + rootLength - 1)
bool ready = consecutive >= required and not na(hma)
bool comparable = ready and ready[1] and ready[2]
float de = ehma - ehma[1]
float dh = hma - hma[1]
bool eUp = comparable and barstate.isconfirmed and de > epsilon and de[1] <= epsilon
bool eDown = comparable and barstate.isconfirmed and de < -epsilon and de[1] >= -epsilon
bool hUp = comparable and barstate.isconfirmed and dh > epsilon and dh[1] <= epsilon
bool hDown = comparable and barstate.isconfirmed and dh < -epsilon and dh[1] >= -epsilon
plot(ehma, "EHMA", color=color.blue, linewidth=2, style=plot.style_linebr)
plot(hma, "WMA Hull", color=color.orange, linewidth=2, style=plot.style_linebr)
plot(x, "Input", display=display.data_window)
plot(isTest ? bar_index - 99 : na, "Relative test bar k", display=display.data_window)
plot(ready ? 1 : 0, "Warm-up ready", display=display.data_window)
plot(eUp ? 1 : eDown ? -1 : 0, "EHMA confirmed slope event", display=display.data_window)
plot(hUp ? 1 : hDown ? -1 : 0, "HMA confirmed slope event", display=display.data_window)
alertcondition(not isTest and eUp, "EHMA upward slope onset", "EHMA slope became positive on a confirmed chart bar.")
alertcondition(not isTest and eDown, "EHMA downward slope onset", "EHMA slope became negative on a confirmed chart bar.")
alertcondition(not isTest and hUp, "HMA upward slope onset", "WMA Hull slope became positive on a confirmed chart bar.")
alertcondition(not isTest and hDown, "HMA downward slope onset", "WMA Hull slope became negative on a confirmed chart bar.")

Turn the verified calculation into an editable indicator

Pine Script AI Coding Agent can draft and revise a script from this public specification. Supply the EMA coefficient, floor lengths, first-value seed, missing-data reset and common warm-up policy. Include the synthetic rows as expected outputs, then request syntax diagnostics and inspect the resulting source.

  • Check completed-bar values and event timestamps in TradingView before adding trading rules. If you compare another EHMA script, record its author, public formula and implementation choices instead of assuming name equivalence.
  • An indicator event is not an order fill. A strategy also needs position sizing, entries, exits, costs and execution timing, evaluated on separate market data. Do not infer returns from a step-response table.
Where Pineify fits

Pine Script AI Coding Agent

Draft the specified EHMA and HMA comparison, inspect its Pine source, and request syntax diagnostics before checking chart values.

Build an EHMA comparison

Frequently asked questions

Educational indicator calculations, not investment advice. Synthetic outputs are formula checks, not backtest or live trading results. A smoother line or confirmed event does not guarantee profitable trades.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.