Three Exponential Moving Averages and Stochastic Relative Strength Index Trading Strategy

Author: ChaoZhang, Date: 2024-01-30 16:52:48



This is a trend following strategy that combines triple exponential moving average (EMA) and Stochastic Relative Strength Index (Stoch RSI) to generate trading signals. It goes long when the fast EMA crosses above the medium EMA and the medium EMA crosses above the slow EMA. It goes short when the reverse happens. The strategy also uses Stoch RSI as an auxiliary indicator.


  1. Use 8, 14, 50 days EMAs. Going long when 8 day EMA > 14 day EMA > 50 day EMA. Going short when it’s the opposite.

  2. Use Stochastic RSI as auxiliary indicator. Calculate 14 days RSI first, then calculate Stochastics on RSI, finally calculate 3 days SMA as K line and 3 days SMA on K line as D line. K crossing over D gives long signal.

  3. Enter long trades when close > 8 day EMA on long signal. Enter short trades when close < 8 day EMA on short signal.

  4. Stop loss sets at 1 ATR distance below/above entry price. Take profit sets at 4 ATR distance above/below entry price.


  1. EMA as base indicator can track trends effectively. Triple EMA captures both short and long term trends by combing multi periods.

  2. Adding Stoch RSI can filter false signals and increase entry accuracy.

  3. ATR based stop loss and take profit can dynamically track market volatility, avoiding improper placement.

  4. This strategy has well tuned parameters and performs great during trending periods. Drawdown is smaller and profit is consistent for long term trades.


  1. Combination of multiple indicators increases whipsaw risk. Conflicting signals between EMA and Stoch RSI may cause entering at bad levels. Prices trend itself needs monitoring in such cases.

  2. Conservative stop loss and take profit settings could be violated by huge market swings, causing premature exits missing further trends. Adjusting ATR parameters or increasing SL/TP multiples may help.

  3. Triple EMA setup has certain lag when fast and medium lines reversing. Prices trend itself needs monitoring to decide entries.

  4. This strategy favors trending market. Sideway markets would not perform well. Adjusting MA periods or adding other auxiliary indicators may help.


  1. Add indicators like MACD for better entries. Testing different periods combination of MAs.

  2. Optimizing long/short testing parameters on ATR. Such as adjusting stop loss from 1 ATR to 1.5 ATR, take profit from 4 ATR to 3 ATR for better results.

  3. Removing Stoch RSI and keeping just MAs for filtering noises and more stable profits.

  4. Adding more criteria judging the trend, like trading volumes, to operate under significant levels.


This strategy combines triple EMA and Stoch RSI to determine trends. Strict entry signals reduce unnecessary trades. Dynamic SL and TP based on ATR makes parameters adaptive. Backtests show great results during trending periods with smaller drawdowns and consistent profits. Further optimizations could lead to even better results.

start: 2023-12-01 00:00:00
end: 2023-12-31 23:59:59
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]

//              3ESRA
//              v0.2a

// Coded by Vaida Bogdan

// 3ESRA consists of a 3 EMA cross + a close above (for longs) the quickest EMA
// or below (for shorts). Note that I've deactivated the RSI Cross Over/Under
// (you can modify the code and activate it). The strategy also uses a stop loss
// that's at 1 ATR distance from the entry price and a take profit that's at
// 4 times the ATR distance from the entry price.

// Feedback:
// Tested BTCUSDT Daily
// 1. Stoch-RSI makes you miss opportunities.
// 2. Changing RR to 4:1 times ATR works better.

strategy(title="3 EMA + Stochastic RSI + ATR", shorttitle="3ESRA", overlay=true, pyramiding=1,
     process_orders_on_close=true, calc_on_every_tick=true,
     initial_capital=1000, currency = currency.USD, default_qty_value=10, 
     commission_type=strategy.commission.percent, commission_value=0.1, slippage=2)

startDate = input(title="Start Date", type=input.integer,
     defval=1, minval=1, maxval=31, group="Backtesting range")
startMonth = input(title="Start Month", type=input.integer,
     defval=1, minval=1, maxval=12, group="Backtesting range")
startYear = input(title="Start Year", type=input.integer,
     defval=1900, minval=1800, maxval=2100, group="Backtesting range")
endDate = input(title="End Date", type=input.integer,
     defval=1, minval=1, maxval=31, group="Backtesting range")
endMonth = input(title="End Month", type=input.integer,
     defval=1, minval=1, maxval=12, group="Backtesting range")
endYear = input(title="End Year", type=input.integer,
     defval=2040, minval=1800, maxval=2100, group="Backtesting range")

// Date range filtering
inDateRange = (time >= timestamp(syminfo.timezone, startYear, startMonth, startDate, 0, 0)) and
     (time < timestamp(syminfo.timezone, endYear, endMonth, endDate, 23, 59))
fast = input(8, minval=8, title="Fast EMA", group="EMAs")
medium = input(14, minval=8, title="Medium EMA", group="EMAs")
slow = input(50, minval=8, title="Slow EMA", group="EMAs")
src = input(close, title="Source")

smoothK = input(3, "K", minval=1, group="Stoch-RSI", inline="K&D")
smoothD = input(3, "D", minval=1, group="Stoch-RSI", inline="K&D")
lengthRSI = input(14, "RSI Length", minval=1, group="Stoch-RSI", inline="length")
lengthStoch = input(14, "Stochastic Length", minval=1, group="Stoch-RSI", inline="length")
rsiSrc = input(close, title="RSI Source", group="Stoch-RSI")

length = input(title="Length", defval=14, minval=1, group="ATR")
smoothing = input(title="Smoothing", defval="RMA", options=["RMA", "SMA", "EMA", "WMA"], group="ATR")

// EMAs
fastema = ema(src, fast)
mediumema = ema(src, medium)
slowema = ema(src, slow)

// S-RSI
rsi1 = rsi(rsiSrc, lengthRSI)
k = sma(stoch(rsi1, rsi1, rsi1, lengthStoch), smoothK)
d = sma(k, smoothD)
sRsiCrossOver = k[1] < d[1] and k > d
sRsiCrossUnder = k[1] > d[1] and k < d

// ATR
ma_function(source, length) =>
	if smoothing == "RMA"
		rma(source, length)
		if smoothing == "SMA"
			sma(source, length)
			if smoothing == "EMA"
				ema(source, length)
				wma(source, length)
atr = ma_function(tr(true), length)

// Trading Logic
longCond1 = (fastema > mediumema) and (mediumema > slowema)
longCond2 = true
// longCond2 = sRsiCrossOver
longCond3 = close > fastema
longCond4 = strategy.position_size <= 0
longCond = longCond1 and longCond2 and longCond3 and longCond4 and inDateRange

shortCond1 = (fastema < mediumema) and (mediumema < slowema)
shortCond2 = true 
// shortCond2 = sRsiCrossUnder
shortCond3 = close < fastema
shortCond4 = strategy.position_size >= 0
shortCond = shortCond1 and shortCond2 and shortCond3 and shortCond4 and inDateRange

var takeProfit = float(na), var stopLoss = float(na)
if longCond and strategy.position_size <= 0
    takeProfit := close + 4*atr
    stopLoss := close - 1*atr
    // takeProfit := close + 2*atr
    // stopLoss := close - 3*atr

else if shortCond and strategy.position_size >= 0
    takeProfit := close - 4*atr
    stopLoss := close + 1*atr
    // takeProfit := close - 2*atr
    // stopLoss := close + 3*atr
// Strategy calls
strategy.entry("3ESRA", strategy.long, comment="Long", when=longCond and strategy.position_size <= 0)
strategy.entry("3ESRA", strategy.short, comment="Short", when=shortCond and strategy.position_size >= 0)
strategy.exit(id="TP-SL", from_entry="3ESRA", limit=takeProfit, stop=stopLoss)
if (not inDateRange)
// Plot EMAs
plot(fastema, color=color.purple, linewidth=2, title="Fast EMA")
plot(mediumema, color=color.teal, linewidth=2, title="Medium EMA")
plot(slowema, color=color.yellow, linewidth=2, title="Slow EMA")
// Plot S-RSI
// plotshape((strategy.position_size > 0) ? na : sRsiCrossOver, title="StochRSI Cross Over", style=shape.triangleup, location=location.belowbar, color=color.teal, text="SRSI", size=size.small)
// Plot trade
bgcolor(strategy.position_size > 0 ?, 75) : strategy.position_size < 0 ?,75) : color(na))
// Plot Strategy
plot((strategy.position_size != 0) ? takeProfit : na, style=plot.style_linebr,, title="TP")
plot((strategy.position_size != 0) ? stopLoss : na, style=plot.style_linebr,, title="SL")