Guppy Multiple Moving Average: read the two EMA groups
The Guppy Multiple Moving Average (GMMA) plots 12 EMAs in two groups. The short group uses 3, 5, 8, 10, 12 and 15 bars; the long group uses 30, 35, 40, 45, 50 and 60. Compare the width inside each group and the gap between the groups to describe trend structure.
Daryl Guppy describes trader and investor behavior as an inference. All 12 lines here use the same price source. They cannot establish actual trader identities, holdings or institutional buying.
Keep the periods fixed before interpreting the ribbon
Short group, blue lines
EMA 3, 5, 8, 10, 12, 15
Faster responses to changes in the shared source.
Long group, red lines
EMA 30, 35, 40, 45, 50, 60
Slower responses using smaller weights on the latest source value.
Each EMA is calculated independently from the source: EMA = α × source + (1 − α) × previous EMA, where α = 2 / (length + 1). A GMMA is not an average of the 12 lines. Use the same symbol, session, timeframe, source and available history when comparing implementations.
The fixed periods match the TTR library's GMMA defaults. See EMA calculation and initialization for the single-line mechanics.
Measure group width separately from the gap
Let Smin and Smax be the minimum and maximum of all six short EMAs, and Lmin and Lmax the corresponding long-group bounds. Length order can change, so EMA 3 and EMA 15 are not always the actual short-group boundaries.
- Within-group width
- Short = Smax − Smin
Long = Lmax − Lmin - Directional boundary gap
- Upward = Smin − Lmax
Downward = Lmin − Smax
A positive upward gap places every short EMA above every long EMA. A positive downward gap places every short EMA below every long EMA. If both gaps are negative, the group ranges overlap. A zero gap means the boundaries touch.
| Snapshot | Short range | Long range | Short width | Long width | Upward gap |
|---|---|---|---|---|---|
| A: separated | 103.8 to 105.0 | 102.0 to 103.0 | 1.2 | 1.0 | +0.8 |
| B: short group narrows | 104.5 to 105.0 | 102.0 to 103.0 | 0.5 | 1.0 | +1.5 |
| C: overlapping | 102.9 to 103.4 | 102.7 to 103.2 | 0.5 | 0.5 | −0.3 |
For A, possible short values in length order are 105, 104.8, 104.5, 104.2, 104 and 103.8; long values are 103, 102.8, 102.6, 102.4, 102.2 and 102. At a close of 105, the upward gap is 100 × (103.8 − 103) / 105 = 0.762%. The short width is 1.143% and long width is 0.952%.
B is a counterexample to treating a narrow short ribbon as a reversal: its short width is smaller than A's, yet it remains entirely above the long group. In C, the upward gap is −0.3 and the downward gap is 102.7 − 103.4 = −0.7. Neither direction is fully separated, even though both ribbons are narrow.
These boundary measurements are this page's explicit implementation choices. They are not a universal GMMA trading rule. Percentage values divide by absolute chart close; a zero close leaves the percentage undefined. Near-zero prices make percentage filters unstable.
Read the sequence, including the failure cases
- Inspect several confirmed bars of the long group. Record slope and width separately; a wide group can slope down as well as up.
- Compare the short group's position with the long group. A short-group contraction followed by renewed upward separation can fit an existing rising trend. The contraction itself does not establish continuation.
- Check for repeated overlap. When the ribbons interweave and change order repeatedly, a crossover rule can alternate directions without a sustained price move.
- Treat expansion after compression as a new observation. It supplies no probability that the move will persist. Abrupt gaps can produce large EMA separation after much of the price change has already happened.
Guppy's original guidance emphasizes relationships within and between groups, and warns against treating GMMA as a moving-average crossover signal. The visual framework does not specify a complete entry, exit or risk model.
Pine Script v6: fixed groups and measurable separation
Paste this indicator into TradingView's Pine Editor, save it and add it to the chart. It draws all 12 lines and fills the true minimum-to-maximum envelope of each group. Open the Data Window to inspect both widths, both directional gaps and the separation state.
//@version=6
indicator("Guppy Multiple Moving Average: group measurements", overlay=true)
source = input.source(close, "Source")
minGapPct = input.float(0.5, "Minimum group gap (%)", minval=0.0, step=0.1)
// Fixed short-term group.
s3 = ta.ema(source, 3)
s5 = ta.ema(source, 5)
s8 = ta.ema(source, 8)
s10 = ta.ema(source, 10)
s12 = ta.ema(source, 12)
s15 = ta.ema(source, 15)
// Fixed long-term group.
l30 = ta.ema(source, 30)
l35 = ta.ema(source, 35)
l40 = ta.ema(source, 40)
l45 = ta.ema(source, 45)
l50 = ta.ema(source, 50)
l60 = ta.ema(source, 60)
plot(s3, "Short EMA 3", color=color.blue)
plot(s5, "Short EMA 5", color=color.blue)
plot(s8, "Short EMA 8", color=color.blue)
plot(s10, "Short EMA 10", color=color.blue)
plot(s12, "Short EMA 12", color=color.blue)
plot(s15, "Short EMA 15", color=color.blue)
plot(l30, "Long EMA 30", color=color.red)
plot(l35, "Long EMA 35", color=color.red)
plot(l40, "Long EMA 40", color=color.red)
plot(l45, "Long EMA 45", color=color.red)
plot(l50, "Long EMA 50", color=color.red)
plot(l60, "Long EMA 60", color=color.red)
shortMin = math.min(s3, s5, s8, s10, s12, s15)
shortMax = math.max(s3, s5, s8, s10, s12, s15)
longMin = math.min(l30, l35, l40, l45, l50, l60)
longMax = math.max(l30, l35, l40, l45, l50, l60)
// Bounds include every EMA even when their length order becomes tangled.
psMin = plot(shortMin, "Short minimum", display=display.none)
psMax = plot(shortMax, "Short maximum", display=display.none)
plMin = plot(longMin, "Long minimum", display=display.none)
plMax = plot(longMax, "Long maximum", display=display.none)
fill(psMin, psMax, color=color.new(color.blue, 90), title="Short group envelope")
fill(plMin, plMax, color=color.new(color.red, 90), title="Long group envelope")
// Normalize by absolute chart close; zero close has no defined percentage.
denominator = math.abs(close)
shortWidthPct = denominator > 0 ? 100 * (shortMax - shortMin) / denominator : na
longWidthPct = denominator > 0 ? 100 * (longMax - longMin) / denominator : na
upGapPct = denominator > 0 ? 100 * (shortMin - longMax) / denominator : na
downGapPct = denominator > 0 ? 100 * (longMin - shortMax) / denominator : na
// Example state filter, not a complete trading system.
above = barstate.isconfirmed and not na(upGapPct) and upGapPct > minGapPct
below = barstate.isconfirmed and not na(downGapPct) and downGapPct > minGapPct
plot(shortWidthPct, "Short group width (%)", display=display.data_window)
plot(longWidthPct, "Long group width (%)", display=display.data_window)
plot(upGapPct, "Upward boundary gap (%)", display=display.data_window)
plot(downGapPct, "Downward boundary gap (%)", display=display.data_window)
plot(above ? 1 : below ? -1 : 0, "Confirmed separation state", display=display.data_window)The example state is +1 when the confirmed upward gap exceeds the chosen threshold, −1 when the confirmed downward gap exceeds it, and 0 otherwise. The default 0.5% is only a demonstration. State 0 also includes an unconfirmed bar or undefined measurements; it is not a definitive label for a ranging market.
The EMA lines still update while the bar is open. Confirmed-bar filtering changes the timing of the state calculation, not the behavior of the plotted prices. This example has no alerts, trades, higher-timeframe requests or performance results.
Turn a ribbon description into an auditable coding request
Pine Script AI Coding Agent can help modify the example into explicit conditions. Supply the exact group periods, normalization, bar-close rule and intended output. Request a syntax check, then compare the generated values with the chart before using them in a strategy.
Keep the 12 fixed EMA periods. Define upward separation as the minimum short EMA minus the maximum long EMA, divided by absolute close. Add an alert only when a confirmed bar first enters a gap above 0.5%. Handle zero close, and show the two group widths in the Data Window.
This is a request to generate and review code, not a built-in GMMA profitability filter. Testing trading rules also requires exits, execution timing, position size, commissions and slippage.
Adapt the example with Pine Script AI Coding AgentGMMA questions
Sources and limits
- Daryl Guppy: GMMA information, for the two-group interpretation and crossover warning.
- TTR source: GMMA, for the fixed period groups and independent EMA calculations.
- TradingView: Exponential Moving Average, for the EMA recurrence and source settings.
- TradingView: Repainting, for open-bar changes and historical-data limitations.
Sources checked October 17, 2026. The numerical snapshots are illustrative calculations, not market observations. This page provides education, not investment advice or a promise of returns.
Continue with the EMA reference or browse TradingView indicators.