The Moving Average Crossover strategy is a momentum strategy that uses the crossover signals of double moving averages to determine the trend direction and generate trading signals. It employs 2 simple moving averages and 1 exponential moving average, judging long and short based on their crossover, belonging to a medium-term trading strategy.
The strategy uses 3 moving averages:
The strategy judges the trend based on the relationship between EMA1, SMA1 and SMA2:
Entry signals:
Exit signals:
The strategy provides multiple parameter configurations, with customizable moving averages for entry and exit.
The advantages of this strategy:
The risks of this strategy:
Whipsaw risk can be mitigated by tuning MA periods; Parameter sensitivity can be solved by optimization; Lagging risk can be reduced by incorporating other leading indicators.
Potential optimizations:
The Moving Average Crossover strategy is straight-forward, judging trend and timing by crossing of fast and slow MAs. Its advantage is capturing momentum with flexible configurations, but risks like whipsaw and lagging exist. With optimizations like additional filters, it can become a very practical quantitative trading strategy.
/*backtest
start: 2023-09-26 00:00:00
end: 2023-10-26 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Decam9
//@version=5
strategy(title = "Moving Average Crossover", shorttitle = "MA Crossover Strategy", overlay=true,
initial_capital = 100000,default_qty_type = strategy.percent_of_equity, default_qty_value = 10)
//Moving Average Inputs
EMA1 = input.int(title="Fast EMA", group = "Moving Averages:",
inline = "EMAs", defval=5, minval = 1)
isDynamicEMA = input.bool(title = "Dynamic Exponential Moving Average?", defval = true,
inline = "EMAs", group = "Moving Averages:", tooltip = "Changes the source of the MA based on trend")
SMA1 = input.int(title = "Slow SMA", group = "Moving Averages:",
inline = "SMAs", defval = 10, minval = 1)
isDynamicSMA = input.bool(title = "Dynamic Simple Moving Average?", defval = false,
inline = "SMAs", group = "Moving Averages:", tooltip = "Changes the source of the MA based on trend")
SMA2 = input.int(title="Trend Determining SMA", group = "Moving Averages:",
inline = "MAs", defval=13, minval = 1)
//Moving Averages
Trend = ta.sma(close, SMA2)
Fast = ta.ema(isDynamicEMA ? (close > Trend ? low : high) : close, EMA1)
Slow = ta.sma(isDynamicSMA ? (close > Trend ? low : high) : close, SMA1)
//Allowed Entries
islong = input.bool(title = "Long", group = "Allowed Entries:",
inline = "Entries",defval = true)
isshort = input.bool(title = "Short", group = "Allowed Entries:",
inline = "Entries", defval= true)
//Entry Long Conditions
buycond = input.string(title="Buy when", group = "Entry Conditions:",
inline = "Conditions",defval="Fast-Slow Crossing",
options=["Fast-Slow Crossing", "Fast-Trend Crossing","Slow-Trend Crossing"])
intrendbuy = input.bool(title = "In trend", defval = true, group = "Entry Conditions:",
inline = "Conditions", tooltip = "In trend if price is above SMA 2")
//Entry Short Conditions
sellcond = input.string(title="Sell when", group = "Entry Conditions:",
inline = "Conditions2",defval="Fast-Slow Crossing",
options=["Fast-Slow Crossing", "Fast-Trend Crossing","Slow-Trend Crossing"])
intrendsell = input.bool(title = "In trend",defval = true, group = "Entry Conditions:",
inline = "Conditions2", tooltip = "In trend if price is below SMA 2?")
//Exit Long Conditions
closebuy = input.string(title="Close long when", group = "Exit Conditions:",
defval="Fast-Slow Crossing", options=["Fast-Slow Crossing", "Fast-Trend Crossing","Slow-Trend Crossing"])
//Exit Short Conditions
closeshort = input.string(title="Close short when", group = "Exit Conditions:",
defval="Fast-Slow Crossing", options=["Fast-Slow Crossing", "Fast-Trend Crossing","Slow-Trend Crossing"])
//Filters
filterlong =input.bool(title = "Long Entries", inline = 'linefilt', group = 'Apply Filters to',
defval = true)
filtershort =input.bool(title = "Short Entries", inline = 'linefilt', group = 'Apply Filters to',
defval = true)
filterend =input.bool(title = "Exits", inline = 'linefilt', group = 'Apply Filters to',
defval = true)
usevol =input.bool(title = "", inline = 'linefiltvol', group = 'Relative Volume Filter:',
defval = false)
rvol = input.int(title = "Volume >", inline = 'linefiltvol', group = 'Relative Volume Filter:',
defval = 1)
len_vol = input.int(title = "Avg. Volume Over Period", inline = 'linefiltvol', group = 'Relative Volume Filter:',
defval = 30, minval = 1,
tooltip="The current volume must be greater than N times the M-period average volume.")
useatr =input.bool(title = "", inline = 'linefiltatr', group = 'Volatility Filter:',
defval = false)
len_atr1 = input.int(title = "ATR", inline = 'linefiltatr', group = 'Volatility Filter:',
defval = 5, minval = 1)
len_atr2 = input.int(title = "> ATR", inline = 'linefiltatr', group = 'Volatility Filter:',
defval = 30, minval = 1,
tooltip="The N-period ATR must be greater than the M-period ATR.")
usersi =input.bool(title = "", inline = 'linersi', group = 'Overbought/Oversold Filter:',
defval = false)
rsitrhs1 = input.int(title = "", inline = 'linersi', group = 'Overbought/Oversold Filter:',
defval = 0, minval=0, maxval=100)
rsitrhs2 = input.int(title = "< RSI (14) <", inline = 'linersi', group = 'Overbought/Oversold Filter:',
defval = 100, minval=0, maxval=100,
tooltip="RSI(14) must be in the range between N and M.")
issl = input.bool(title = "SL", inline = 'linesl1', group = 'Stop Loss / Take Profit:',
defval = false)
slpercent = input.float(title = ", %", inline = 'linesl1', group = 'Stop Loss / Take Profit:',
defval = 10, minval=0.0)
istrailing = input.bool(title = "Trailing", inline = 'linesl1', group = 'Stop Loss / Take Profit:',
defval = false)
istp = input.bool(title = "TP", inline = 'linetp1', group = 'Stop Loss / Take Profit:',
defval = false)
tppercent = input.float(title = ", %", inline = 'linetp1', group = 'Stop Loss / Take Profit:',
defval = 20)
//Conditions for Crossing
fscrossup = ta.crossover(Fast,Slow)
fscrossdw = ta.crossunder(Fast,Slow)
ftcrossup = ta.crossover(Fast,Trend)
ftcrossdw = ta.crossunder(Fast,Trend)
stcrossup = ta.crossover(Slow,Trend)
stcrossdw = ta.crossunder(Slow,Trend)
//Defining in trend
uptrend = Fast >= Slow and Slow >= Trend
downtrend = Fast <= Slow and Slow <= Trend
justCrossed = ta.cross(Fast,Slow) or ta.cross(Slow,Trend)
//Entry Signals
crosslong = if intrendbuy
(buycond =="Fast-Slow Crossing" and uptrend ? fscrossup:(buycond =="Fast-Trend Crossing" and uptrend ? ftcrossup:(buycond == "Slow-Trend Crossing" and uptrend ? stcrossup : na)))
else
(buycond =="Fast-Slow Crossing"?fscrossup:(buycond=="Fast-Trend Crossing"?ftcrossup:stcrossup))
crossshort = if intrendsell
(sellcond =="Fast-Slow Crossing" and downtrend ? fscrossdw:(sellcond =="Fast-Trend Crossing" and downtrend ? ftcrossdw:(sellcond == "Slow-Trend Crossing" and downtrend ? stcrossdw : na)))
else
(sellcond =="Fast-Slow Crossing"?fscrossdw:(buycond=="Fast-Trend Crossing"?ftcrossdw:stcrossdw))
crossexitlong = (closebuy =="Fast-Slow Crossing"?fscrossdw:(closebuy=="Fast-Trend Crossing"?ftcrossdw:stcrossdw))
crossexitshort = (closeshort =="Fast-Slow Crossing"?fscrossup:(closeshort=="Fast-Trend Crossing"?ftcrossup:stcrossup))
// Filters
rsifilter = usersi?(ta.rsi(close,14) > rsitrhs1 and ta.rsi(close,14) < rsitrhs2):true
volatilityfilter = useatr?(ta.atr(len_atr1) > ta.atr(len_atr2)):true
volumefilter = usevol?(volume > rvol*ta.sma(volume,len_vol)):true
totalfilter = volatilityfilter and volumefilter and rsifilter
//Filtered signals
golong = crosslong and islong and (filterlong?totalfilter:true)
goshort = crossshort and isshort and (filtershort?totalfilter:true)
endlong = crossexitlong and (filterend?totalfilter:true)
endshort = crossexitshort and (filterend?totalfilter:true)
// Entry price and TP
startprice = ta.valuewhen(condition=golong or goshort, source=close, occurrence=0)
pm = golong?1:goshort?-1:1/math.sign(strategy.position_size)
takeprofit = startprice*(1+pm*tppercent*0.01)
// fixed stop loss
stoploss = startprice * (1-pm*slpercent*0.01)
// trailing stop loss
if istrailing and strategy.position_size>0
stoploss := math.max(close*(1 - slpercent*0.01),stoploss[1])
else if istrailing and strategy.position_size<0
stoploss := math.min(close*(1 + slpercent*0.01),stoploss[1])
if golong and islong
strategy.entry("long", strategy.long )
if goshort and isshort
strategy.entry("short", strategy.short)
if endlong
strategy.close("long")
if endshort
strategy.close("short")
// Exit via SL or TP
strategy.exit(id="sl/tp long", from_entry="long", stop=issl?stoploss:na,
limit=istp?takeprofit:na)
strategy.exit(id="sl/tp short",from_entry="short",stop=issl?stoploss:na,
limit=istp?takeprofit:na)