EMA Crossover Strategy

Classic trend-following strategy using fast and slow EMA crossovers with an optional 200 EMA trend filter.

The EMA Crossover is popular because it feels like it should work — faster average crosses slower average, momentum confirmed. But a raw EMA crossover is close to useless in sideways markets, where it whipsaws constantly. This version adds a 200 EMA trend filter that quietly eliminates a significant portion of trades. The trades it keeps are the only ones that matter.

The Exponential Moving Average (EMA) crossover is one of the oldest and most widely-used trend-following techniques in technical analysis. Its roots trace back to the broader development of moving average theory, which has been a cornerstone of market analysis since the early 20th century.

Unlike the Simple Moving Average (SMA), the EMA gives more weight to recent prices, making it more responsive to new information. This responsiveness is what makes EMA crossovers particularly popular among active traders who need timely signals.

The strategy works by comparing two EMAs of different lengths — a fast EMA (typically 9 periods) and a slow EMA (typically 21 periods). When the fast EMA crosses above the slow EMA, it suggests bullish momentum is building. When it crosses below, bearish momentum is taking over.

The optional 200 EMA trend filter adds an extra layer of confirmation. By only taking long trades when price is above the 200 EMA (and short trades when below), you align your entries with the dominant trend — significantly reducing false signals in choppy markets.

//@version=6
strategy("EMA Crossover Strategy", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)

// Inputs
fastLen = input.int(9, "Fast EMA Length", minval=1)
slowLen = input.int(21, "Slow EMA Length", minval=1)
useTrendFilter = input.bool(true, "Use 200 EMA Trend Filter")

// Calculations
fastEMA = ta.ema(close, fastLen)
slowEMA = ta.ema(close, slowLen)
trendEMA = ta.ema(close, 200)

The crossover detection uses Pine Script's built-in ta.crossover() and ta.crossunder() functions, which return true on the exact bar where the crossing occurs. Combined with the trend filter, this creates a robust system that balances signal frequency with quality.

// Conditions
longCondition = ta.crossover(fastEMA, slowEMA)
shortCondition = ta.crossunder(fastEMA, slowEMA)

// Apply trend filter
if useTrendFilter
    longCondition := longCondition and close > trendEMA
    shortCondition := shortCondition and close < trendEMA

// Execute trades
if (longCondition)
    strategy.entry("Long", strategy.long)
if (shortCondition)
    strategy.entry("Short", strategy.short)

// Plotting
plot(fastEMA, "Fast EMA", color=color.green, linewidth=2)
plot(slowEMA, "Slow EMA", color=color.red, linewidth=2)
plot(useTrendFilter ? trendEMA : na, "200 EMA", color=color.gray, linewidth=1)
EMA Crossover Strategy on SPX500USD 1-minute chart showing crossover signals and equity curve
EMA Crossover Strategy applied to OANDA:SPX500USD on a 1-minute timeframe with Strategy Report overlay.