SuperTrend + EMA Rebound Strategy
The system trades in the direction of SuperTrend and looks for pullbacks to an exponential moving average. A position is opened either when the SuperTrend line flips direction or when price rebounds from the EMA while remaining in the prevailing SuperTrend bias. This combination attempts to capture the first leg of a new move and subsequent retracements within an established trend.
A percentage based take profit can be enabled via the built‑in protection module by setting the take profit type to "%". The defaults favor long trades but short entries can also be activated. Because the strategy relies on direction changes, it is most effective in trending markets where SuperTrend reacts quickly to momentum shifts.
Details
- Entry Criteria:
- SuperTrend flips to uptrend, or price rebounds above EMA during uptrend.
- SuperTrend flips to downtrend, or price rebounds below EMA during downtrend.
- Long/Short: Long enabled by default, short optional.
- Exit Criteria:
- Opposite SuperTrend flip.
- Optional take profit handled by protection module.
- Stops: Percentage take profit via protection; no stop loss included.
- Default Values:
- ATR period = 10, ATR factor = 3.0.
- EMA length = 20, TP = 1.5%.
- Filters:
- Category: Trend following
- Direction: Both (long default)
- Indicators: SuperTrend, EMA
- Stops: Optional TP
- Complexity: Moderate
- Timeframe: Short/medium
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Supertrend + EMA Rebound Strategy.
/// Trades SuperTrend direction changes and EMA rebounds.
/// Buys when SuperTrend turns bullish or price rebounds from EMA in uptrend.
/// Sells when SuperTrend turns bearish or price rebounds from EMA in downtrend.
/// </summary>
public class SupertrendEmaReboundStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrFactor;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _cooldownBars;
private SuperTrend _supertrend;
private ExponentialMovingAverage _ema;
private bool _prevIsUpTrend;
private bool _prevIsUpTrendSet;
private decimal _prevClose;
private decimal _prevEma;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public decimal AtrFactor
{
get => _atrFactor.Value;
set => _atrFactor.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public SupertrendEmaReboundStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_atrPeriod = Param(nameof(AtrPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for Supertrend", "Supertrend");
_atrFactor = Param(nameof(AtrFactor), 3.0m)
.SetDisplay("ATR Factor", "ATR factor for Supertrend", "Supertrend");
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Moving Average");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_supertrend = null;
_ema = null;
_prevIsUpTrend = false;
_prevIsUpTrendSet = false;
_prevClose = 0;
_prevEma = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_supertrend = new SuperTrend { Length = AtrPeriod, Multiplier = AtrFactor };
_ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_supertrend, _ema, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _supertrend);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue stValue, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_supertrend.IsFormed || !_ema.IsFormed)
return;
if (stValue.IsEmpty || emaValue.IsEmpty)
return;
var stTyped = (SuperTrendIndicatorValue)stValue;
var isUpTrend = stTyped.IsUpTrend;
var emaVal = emaValue.ToDecimal();
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevIsUpTrend = isUpTrend;
_prevIsUpTrendSet = true;
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevIsUpTrend = isUpTrend;
_prevIsUpTrendSet = true;
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
if (!_prevIsUpTrendSet || _prevClose == 0)
{
_prevIsUpTrend = isUpTrend;
_prevIsUpTrendSet = true;
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
// SuperTrend direction change
var trendTurnedUp = isUpTrend && !_prevIsUpTrend;
var trendTurnedDown = !isUpTrend && _prevIsUpTrend;
// EMA rebound: price was below EMA and now crosses above
var emaReboundUp = isUpTrend && _prevClose < _prevEma && candle.ClosePrice > emaVal;
// EMA rebound: price was above EMA and now crosses below
var emaReboundDown = !isUpTrend && _prevClose > _prevEma && candle.ClosePrice < emaVal;
// Buy: SuperTrend turns bullish or EMA rebound up in uptrend
if ((trendTurnedUp || emaReboundUp) && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: SuperTrend turns bearish or EMA rebound down in downtrend
else if ((trendTurnedDown || emaReboundDown) && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long on SuperTrend bearish flip
else if (Position > 0 && trendTurnedDown)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short on SuperTrend bullish flip
else if (Position < 0 && trendTurnedUp)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevIsUpTrend = isUpTrend;
_prevIsUpTrendSet = true;
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SuperTrend, ExponentialMovingAverage, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class supertrend_ema_rebound_strategy(Strategy):
"""Supertrend + EMA Rebound Strategy."""
def __init__(self):
super(supertrend_ema_rebound_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_period = self.Param("AtrPeriod", 10) \
.SetDisplay("ATR Period", "ATR period for Supertrend", "Supertrend")
self._atr_factor = self.Param("AtrFactor", 3.0) \
.SetDisplay("ATR Factor", "ATR factor for Supertrend", "Supertrend")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period", "Moving Average")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._supertrend = None
self._ema = None
self._prev_is_up_trend = False
self._prev_is_up_trend_set = False
self._prev_close = 0.0
self._prev_ema = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(supertrend_ema_rebound_strategy, self).OnReseted()
self._supertrend = None
self._ema = None
self._prev_is_up_trend = False
self._prev_is_up_trend_set = False
self._prev_close = 0.0
self._prev_ema = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(supertrend_ema_rebound_strategy, self).OnStarted2(time)
self._supertrend = SuperTrend()
self._supertrend.Length = int(self._atr_period.Value)
self._supertrend.Multiplier = float(self._atr_factor.Value)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._supertrend, self._ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._supertrend)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, st_value, ema_value):
if candle.State != CandleStates.Finished:
return
if not self._supertrend.IsFormed or not self._ema.IsFormed:
return
if st_value.IsEmpty or ema_value.IsEmpty:
return
is_up_trend = st_value.IsUpTrend
ema_val = float(IndicatorHelper.ToDecimal(ema_value))
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_is_up_trend = is_up_trend
self._prev_is_up_trend_set = True
self._prev_close = float(candle.ClosePrice)
self._prev_ema = ema_val
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_is_up_trend = is_up_trend
self._prev_is_up_trend_set = True
self._prev_close = float(candle.ClosePrice)
self._prev_ema = ema_val
return
if not self._prev_is_up_trend_set or self._prev_close == 0.0:
self._prev_is_up_trend = is_up_trend
self._prev_is_up_trend_set = True
self._prev_close = float(candle.ClosePrice)
self._prev_ema = ema_val
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
trend_turned_up = is_up_trend and not self._prev_is_up_trend
trend_turned_down = not is_up_trend and self._prev_is_up_trend
ema_rebound_up = is_up_trend and self._prev_close < self._prev_ema and close > ema_val
ema_rebound_down = not is_up_trend and self._prev_close > self._prev_ema and close < ema_val
if (trend_turned_up or ema_rebound_up) and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif (trend_turned_down or ema_rebound_down) and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and trend_turned_down:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and trend_turned_up:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_is_up_trend = is_up_trend
self._prev_is_up_trend_set = True
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return supertrend_ema_rebound_strategy()