Simple Moving Average (SMA) Crossover Strategy: A Beginner's Guide to Automated Trading
Trading with moving averages is one of the fundamental strategies in technical analysis. In this guide, we'll explore how to implement and automate a Simple Moving Average (SMA) crossover strategy using TradingView's platform.
Understanding Simple Moving Averages
The Simple Moving Average (SMA) is a technical indicator that shows the average price of an asset over a specific period. For example, a 10-day moving average is calculated by adding the closing prices of the last 10 days and dividing by 10. This helps smooth out price data to reveal the overall market trend.
The SMA Crossover Strategy
The strategy utilizes two moving averages:
- A short-term moving average (faster)
- A long-term moving average (slower)
The basic rules are straightforward:
- Sell when the short moving average crosses below the long moving average
- Buy when the short moving average crosses above the long moving average
Pros and Cons
Advantages
- Simple and clear signals
- Adaptable to different timeframes
- Easy to automate
Disadvantages
- Can provide late signals as crosses occur after trends have started
- May generate false signals in sideways markets
Building a Simple Moving Average Crossover Strategy with Pine Script
Strategy Overview
The strategy is based on the following key components:
- Short and Long Simple Moving Averages (SMA) crossover for entry signals
- ATR-based dynamic stop losses
- Configurable risk-to-reward ratios
- Optional RSI filter for trade confirmation
- Option to use previous candle prices for stop loss calculation
Step 1: Setting Up the Inputs
Let's start by looking at the strategy's configurable parameters:
shortSmaLength = input.int(defval = 20, title = 'Short SMA length')
longSmaLength = input.int(defval = 200, title = 'Long SMA length')
atrMultipleForStopLoss = input.float(defval = 2.0, title = 'ATR multiplier SL')
riskToRewardRatio = input.float(defval = 1.0, title = 'Risk To Reward')
These inputs allow traders to:
- Adjust the short and long SMA periods (default 20 and 200)
- Modify the ATR multiplier for stop loss calculation
- Set their desired risk-to-reward ratio
- Enable/disable RSI filtering and strategy execution
Step 2: Calculating Core Indicators
The strategy calculates three main technical indicators:
shortSma = ta.sma(close, shortSmaLength)
longSma = ta.sma(close, longSmaLength)
atr = ta.atr(14)
The ATR (Average True Range) with a 14-period setting helps determine dynamic stop loss levels based on market volatility.
Step 3: Generating Trading Signals
Trade signals are generated when the shorter SMA crosses the longer SMA:
longSignal = ta.crossover(shortSma, longSma)
shortSignal = ta.crossunder(shortSma, longSma)
- Long entry: Short SMA crosses above Long SMA
- Short entry: Short SMA crosses below Long SMA
Step 4: Implementing RSI Filter (Optional)
The strategy includes an optional RSI filter to avoid trading in overbought/oversold conditions:
rsiValue = ta.rsi(close, rsiLength)
isNeutral = rsiValue > rsiOversoldLevel and rsiValue < rsiOverboughtLevel
if filterWithRsi
longSignal := longSignal and isNeutral
shortSignal := shortSignal and isNeutral
When enabled, trades are only taken when the RSI is in a neutral zone (between oversold and overbought levels).
Step 5: Stop Loss and Take Profit Calculation
The strategy offers two methods for stop loss calculation:
- ATR-based:
StoplossValue = atr * atrMultipleForStopLoss
LongStoploss = close - StoplossValue
ShortStopLoss = close + StoplossValue
2. Previous candle based:
if usePreviousCandleAsSl
LongStoploss := math.min(open, open[1], open[2])
ShortStopLoss := math.max(open, open[1], open[2])
Take profit levels are calculated based on the risk-to-reward ratio:
TakeProfitValue = StoplossValue * riskToRewardRatio
LongTakeProfit = close + TakeProfitValue
ShortTakeProfit = close - TakeProfitValue
Step 6: Trade Execution
The strategy enters trades only when there’s no existing position:
inTrade = strategy.opentrades > 0
if longCondition and showStrategyResult
strategy.entry('long', strategy.long)
strategy.exit('Long_Exit', from_entry = 'long', qty_percent = 100, stop = LongStoploss, limit = LongTakeProfit)
Visualization
The strategy includes visual elements to help traders identify signals:
- Green triangles for long entry signals
- Red triangles for short entry signals
- Blue line for short SMA
- Red line for long SMA
Considerations and Customization
- Timeframe Selection: The strategy can be applied to any timeframe, but longer timeframes typically generate fewer, more reliable signals.
- SMA Periods: The default values (20 and 200) represent common settings, but these can be adjusted based on your trading style:
- Shorter periods: More signals, higher sensitivity
- Longer periods: Fewer signals, lower sensitivity
- Risk Management: The ATR multiplier and risk-to-reward ratio should be adjusted based on:
- Your risk tolerance
- The instrument’s volatility
- Your overall trading strategy
- RSI Filter: Consider enabling the RSI filter in trending markets to avoid false signals in overbought/oversold conditions.
Testing Result
Our backtest results revealed interesting patterns across different timeframes:
1. Timeframe Performance:
- 4-hour and 1-hour timeframes showed consistent 60% win rates
- Weekly timeframe showed impressive 71% success rate (though with fewer trades)
- Lower timeframes (5-15 minutes) showed decreased reliability
2. Moving Average Combinations:
- Different SMA combinations (5/20, 10/50, 50/200) showed varying results
- The classic 20/200 combination proved relatively reliable
Market Applications
The strategy was tested across various markets:
- Forex (EUR/USD): ~60% success rate
- Stocks (Tesla): ~59% success rate
- Crypto (Bitcoin): ~55% success rate
- US30 Index: ~51% success rate
Risk Management Considerations
For proper implementation, consider:
- Using ATR for stop-loss placement
- Maintaining a 1:1 risk-to-reward ratio
- Only taking one trade at a time
Conclusion
The SMA crossover strategy, while simple, can be effective when properly implemented with risk management rules. Higher timeframes tend to produce more reliable signals, though this may mean fewer trading opportunities. As with any trading strategy, it's essential to thoroughly test and adapt it to your specific trading style and risk tolerance.
Remember that past performance doesn't guarantee future results, and it's crucial to account for trading costs like spreads, commissions, and swap fees when evaluating strategy performance.
FULL SCRIPT
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tradecraftsimplified
//@version=6
strategy('SMA CrossOver strategy', overlay = true)
// ===================== Inputs ==============
shortSmaLength = input.int(defval = 20, title = 'Short SMA length')
longSmaLength = input.int(defval = 200, title = 'Long SMA length')
atrMultipleForStopLoss = input.float(defval = 2.0, title = 'ATR multiplier SL')
riskToRewardRatio = input.float(defval = 1.0, title = 'Risk To Reward')
showStrategyResult = input.bool(false, title = 'Load Strategy Result')
usePreviousCandleAsSl = input.bool(false, 'Use Previous Candle as SL')
filterWithRsi = input.bool(false, 'Filter with RSI')
// RSI Parameters
rsiLength = input.int(14, minval = 1, title = 'RSI Length')
rsiOverboughtLevel = input.int(70, title = 'Overbought Level')
rsiOversoldLevel = input.int(30, title = 'Oversold Level')
// ===================================
shortSma = ta.sma(close, shortSmaLength)
longSma = ta.sma(close, longSmaLength)
atr = ta.atr(14)
longSignal = ta.crossover(shortSma, longSma)
shortSignal = ta.crossunder(shortSma, longSma)
// Calculate RSI
rsiValue = ta.rsi(close, rsiLength)
isNeutral = rsiValue > rsiOversoldLevel and rsiValue < rsiOverboughtLevel
if filterWithRsi
longSignal := longSignal and isNeutral
shortSignal := shortSignal and isNeutral
StoplossValue = atr * atrMultipleForStopLoss
LongStoploss = close - StoplossValue
ShortStopLoss = close + StoplossValue
if usePreviousCandleAsSl
LongStoploss := math.min(open, open[1], open[2])
ShortStopLoss := math.max(open, open[1], open[2])
TakeProfitValue = StoplossValue * riskToRewardRatio
LongTakeProfit = close + TakeProfitValue
ShortTakeProfit = close - TakeProfitValue
// ===================== Plots==============
plotshape(longSignal, style = shape.triangleup, location = location.belowbar, color = color.green, size = size.small)
plotshape(shortSignal, style = shape.triangledown, location = location.abovebar, color = color.red, size = size.small)
plot(shortSma, title = 'Short SMA', color = color.blue, linewidth = 2)
plot(longSma, title = 'Short SMA', color = color.red, linewidth = 2)
// ===================================
// ===================== Strategy ==============
inTrade = strategy.opentrades > 0
longCondition = longSignal and not inTrade
if longCondition and showStrategyResult
strategy.entry('long', strategy.long)
strategy.exit('Long_Exit', from_entry = 'long', qty_percent = 100, stop = LongStoploss, limit = LongTakeProfit)
shortCondition = shortSignal and not inTrade
if shortCondition and showStrategyResult
strategy.entry('short', strategy.short)
strategy.exit('Short_Exit', from_entry = 'short', qty_percent = 100, stop = ShortStopLoss, limit = ShortTakeProfit)
// ===================================