EMA Crossover + ATR Strategy for TradingView:
Working PineScript Code + Backtest
This strategy enters long on EMA(9) crossing above EMA(21) and uses a 2x ATR(14) trailing stop for dynamic risk management. The EMA crossover captures the beginning of swing moves, while the ATR trailing stop adapts to the asset's volatility — tightening in calm markets and widening in volatile ones. This combination is particularly effective for swing trading individual stocks like AAPL where trends develop over days to weeks. The strategy exits on either the ATR trailing stop or a bearish EMA crossover, whichever comes first.
What is Exponential Moving Average Crossover with ATR Trailing Stop?
The EMA 9/21 crossover is one of the most popular swing trading signals. The fast EMA(9) responds quickly to recent price changes, while the slower EMA(21) represents the intermediate trend. When the fast EMA crosses above the slow EMA, it signals a shift to bullish momentum. The Average True Range (ATR) is a volatility measure developed by J. Welles Wilder that calculates the average range of price bars over a specified period. Using ATR as a trailing stop dynamically adjusts the exit distance based on current market volatility.
| Parameter | Value |
|---|---|
| fast E M A | 9 |
| slow E M A | 21 |
| atr Period | 14 |
EMA 9/21 Crossover Swing Trade with ATR Trailing Stop: The Setup
Entry Rules
- ▶EMA(9) crosses above EMA(21) (bullish crossover)
- ▶Price must be above both EMAs at the time of crossover
- ▶Enter long on the next bar open
- ▶Position size: 100% of equity per trade
Exit Rules
- ■ATR(14) trailing stop at 2x ATR below the highest close since entry
- ■Exit if EMA(9) crosses below EMA(21) (bearish crossover)
- ■Whichever exit triggers first closes the position
Working PineScript Code
The script calculates EMA(9) and EMA(21) using `ta.ema()` and ATR(14) using `ta.atr()`. A bullish crossover is detected with `ta.crossover(fastEMA, slowEMA)`, with an additional filter requiring price to be above both EMAs at the crossover moment. The ATR trailing stop uses persistent variables (`var`): `trailHigh` tracks the highest close since entry and `atrStop` is set at 2x ATR below that high. The trailing stop moves up but never down. Exits trigger on either the ATR stop being breached or a bearish EMA crossover. The chart shows both EMAs and the active trailing stop level.
//@version=5
strategy("EMA Crossover Swing", 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)
atrLen = input.int(14, "ATR Length", minval=1)
atrMult = input.float(2.0, "ATR Trailing Multiplier", minval=0.5, step=0.1)
// === CALCULATIONS ===
fastEMA = ta.ema(close, fastLen)
slowEMA = ta.ema(close, slowLen)
atrValue = ta.atr(atrLen)
// === CROSSOVER DETECTION ===
bullCross = ta.crossover(fastEMA, slowEMA)
bearCross = ta.crossunder(fastEMA, slowEMA)
// === ATR TRAILING STOP ===
var float trailHigh = na
var float atrStop = na
if strategy.position_size > 0
trailHigh := math.max(nz(trailHigh, close), close)
atrStop := trailHigh - atrMult * atrValue
else
trailHigh := na
atrStop := na
// === ENTRY CONDITIONS ===
longCondition = bullCross and close > fastEMA and close > slowEMA
// === EXIT CONDITIONS ===
atrExit = strategy.position_size > 0 and close < atrStop
emaExit = bearCross
// === STRATEGY EXECUTION ===
if longCondition
strategy.entry("EMA Swing", strategy.long)
if atrExit or emaExit
strategy.close("EMA Swing")
// === PLOTTING ===
plot(fastEMA, "Fast EMA (9)", color=color.blue, linewidth=2)
plot(slowEMA, "Slow EMA (21)", color=color.orange, linewidth=2)
plot(strategy.position_size > 0 ? atrStop : na, "ATR Trailing Stop", color=color.red, linewidth=1, style=plot.style_linebr)
bgcolor(bullCross ? color.new(color.green, 85) : bearCross ? color.new(color.red, 85) : na, title="Crossover Signal")
plotshape(longCondition, title="Long Entry", location=location.belowbar, style=shape.triangleup, color=color.green, size=size.small)Backtest Results: AAPL (1D, 2019-2025)
Total Return
+58.2%
Annualized
+8.0%
Win Rate
52%
Max Drawdown
-16.3%
Sharpe Ratio
0.95
Total Trades
42
Profit Factor
1.55
Past performance does not guarantee future results. Backtest results may not reflect actual trading conditions including slippage, commissions, and liquidity constraints.
Optimization Tips
Add a volume confirmation on the crossover bar. Require the crossover bar's volume to be at least 1.2x the 20-period average. Strong crossovers with volume support are more likely to produce sustained moves.
Test different ATR multipliers for different market conditions. During low-volatility environments (VIX < 15), tighten the ATR multiplier to 1.5x. During high-volatility periods, widen to 2.5-3x. This can be automated using a VIX-based adaptive multiplier.
Consider adding a MACD histogram filter. Only take EMA crossover entries when the MACD histogram is positive and increasing. This adds a momentum acceleration filter to the trend signal.
Experiment with EMA length pairs. While 9/21 is the classic swing pair, 8/21 or 10/30 may perform better on specific assets. Run a parameter sweep but avoid over-optimization — look for stable regions where nearby parameters produce similar results.
Implement a re-entry mechanism. After an ATR stop exit, if the EMAs remain in bullish alignment (fast > slow), re-enter on the next pullback to the slow EMA. This prevents missing the continuation of strong trends.
Common Mistakes with EMA Crossover + ATR on TradingView
Trading EMA crossovers in a sideways market. When price is range-bound, EMAs intertwine and produce multiple false crossovers (whipsaws). Add a trend filter like ADX > 20 to only take crossover signals when a real trend is present.
Using the same EMA lengths for all assets. AAPL's price behavior differs from high-volatility stocks or indices. The 9/21 pair works well for large-cap stocks with moderate volatility, but may need adjustment for small-caps or commodities.
Setting the ATR trailing stop on the entry bar and never updating it. The trailing stop should recalculate the ATR value on every bar, not just at entry. If ATR was low at entry but increases, the stop should widen to accommodate the new volatility.
Ignoring earnings dates for AAPL. Earnings announcements cause large gap moves that can blow through any trailing stop. Consider closing positions 1-2 days before earnings or widening stops to 4x ATR around earnings.
Build This Strategy in 60 Seconds with SpendDock
Instead of copying and debugging this code, describe your strategy in plain English. SpendDock generates the PineScript code, backtests it against real market data, and lets you iterate until it is right.
Try SpendDock Free