Supertrend Rsi Strategy
Implementation of strategy - Supertrend + RSI. Buy when price is above Supertrend and RSI is below 30 (oversold). Sell when price is below Supertrend and RSI is above 70 (overbought).
Testing indicates an average annual return of about 43%. It performs best in the stocks market.
The Supertrend indicator shows the current trend, and RSI spots when price is stretched. Orders follow the Supertrend direction once RSI reaches an extreme.
A good choice for traders relying on trailing stops. The built-in stop from Supertrend works with the ATR setting to cap losses.
Details
- Entry Criteria:
- Long:
Close > Supertrend && RSI < RsiOversold - Short:
Close < Supertrend && RSI > RsiOverbought
- Long:
- Long/Short: Both
- Exit Criteria:
- Supertrend flip in opposite direction
- Stops: Uses Supertrend as trailing stop
- Default Values:
SupertrendPeriod= 10SupertrendMultiplier= 3.0mRsiPeriod= 14RsiOversold= 30mRsiOverbought= 70mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Mean reversion
- Direction: Both
- Indicators: Supertrend, RSI
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining manual Supertrend with RSI.
/// Buys when price above Supertrend and RSI oversold.
/// Sells when price below Supertrend and RSI overbought.
/// </summary>
public class SupertrendRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private readonly List<decimal> _closes = new();
private decimal _prevSupertrend;
private bool _prevUpTrend;
private bool _stInitialized;
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// ATR period for Supertrend.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Supertrend multiplier.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize strategy.
/// </summary>
public SupertrendRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_atrPeriod = Param(nameof(AtrPeriod), 10)
.SetRange(5, 30)
.SetDisplay("ATR Period", "ATR period for Supertrend", "Supertrend");
_multiplier = Param(nameof(Multiplier), 3.0m)
.SetDisplay("Multiplier", "ATR multiplier for Supertrend", "Supertrend");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetRange(7, 21)
.SetDisplay("RSI Period", "Period for RSI", "RSI");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI oversold level", "RSI");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_closes.Clear();
_prevSupertrend = 0;
_prevUpTrend = true;
_stInitialized = false;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
_highs.Add(high);
_lows.Add(low);
_closes.Add(close);
var period = AtrPeriod;
if (_closes.Count < period + 1)
{
if (_cooldown > 0) _cooldown--;
return;
}
// Manual ATR calculation
decimal sumTr = 0;
var count = _highs.Count;
for (int i = count - period; i < count; i++)
{
var h = _highs[i];
var l = _lows[i];
var prevC = _closes[i - 1];
var tr = Math.Max(h - l, Math.Max(Math.Abs(h - prevC), Math.Abs(l - prevC)));
sumTr += tr;
}
var atr = sumTr / period;
// Manual Supertrend
var midPrice = (high + low) / 2m;
var upperBand = midPrice + Multiplier * atr;
var lowerBand = midPrice - Multiplier * atr;
bool upTrend;
decimal supertrend;
if (!_stInitialized)
{
upTrend = close > midPrice;
supertrend = upTrend ? lowerBand : upperBand;
_stInitialized = true;
}
else
{
if (_prevUpTrend)
{
// In uptrend: lower band can only increase
if (lowerBand < _prevSupertrend)
lowerBand = _prevSupertrend;
upTrend = close >= lowerBand;
supertrend = upTrend ? lowerBand : upperBand;
}
else
{
// In downtrend: upper band can only decrease
if (upperBand > _prevSupertrend)
upperBand = _prevSupertrend;
upTrend = close > upperBand;
supertrend = upTrend ? lowerBand : upperBand;
}
}
_prevSupertrend = supertrend;
_prevUpTrend = upTrend;
// Trim lists
if (_highs.Count > period * 3)
{
var trim = _highs.Count - period * 2;
_highs.RemoveRange(0, trim);
_lows.RemoveRange(0, trim);
_closes.RemoveRange(0, trim);
}
if (_cooldown > 0)
{
_cooldown--;
return;
}
if (Position != 0)
return;
// Buy: uptrend + RSI below midpoint (momentum not exhausted)
if (upTrend && rsiValue < 50m)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Sell: downtrend + RSI above midpoint
else if (!upTrend && rsiValue > 50m)
{
SellMarket();
_cooldown = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class supertrend_rsi_strategy(Strategy):
"""
Strategy combining manual Supertrend with RSI.
"""
def __init__(self):
super(supertrend_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_period = self.Param("AtrPeriod", 10) \
.SetRange(5, 30) \
.SetDisplay("ATR Period", "ATR period for Supertrend", "Supertrend")
self._multiplier = self.Param("Multiplier", 3.0) \
.SetDisplay("Multiplier", "ATR multiplier for Supertrend", "Supertrend")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetRange(7, 21) \
.SetDisplay("RSI Period", "Period for RSI", "RSI")
self._rsi_oversold = self.Param("RsiOversold", 30.0) \
.SetDisplay("RSI Oversold", "RSI oversold level", "RSI")
self._rsi_overbought = self.Param("RsiOverbought", 70.0) \
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 100) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._highs = []
self._lows = []
self._closes = []
self._prev_supertrend = 0.0
self._prev_up_trend = True
self._st_initialized = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(supertrend_rsi_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._closes = []
self._prev_supertrend = 0.0
self._prev_up_trend = True
self._st_initialized = False
self._cooldown = 0
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.ProcessCandle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
rsi_area = self.CreateChartArea()
if rsi_area is not None:
self.DrawIndicator(rsi_area, rsi)
def ProcessCandle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
rv = float(rsi_value)
self._highs.append(high)
self._lows.append(low)
self._closes.append(close)
period = self._atr_period.Value
if len(self._closes) < period + 1:
if self._cooldown > 0:
self._cooldown -= 1
return
# Manual ATR
sum_tr = 0.0
count = len(self._highs)
for i in range(count - period, count):
h = self._highs[i]
l = self._lows[i]
prev_c = self._closes[i - 1]
tr = max(h - l, max(abs(h - prev_c), abs(l - prev_c)))
sum_tr += tr
atr = sum_tr / period
# Manual Supertrend
mult = float(self._multiplier.Value)
mid_price = (high + low) / 2.0
upper_band = mid_price + mult * atr
lower_band = mid_price - mult * atr
if not self._st_initialized:
up_trend = close > mid_price
supertrend = lower_band if up_trend else upper_band
self._st_initialized = True
else:
if self._prev_up_trend:
if lower_band < self._prev_supertrend:
lower_band = self._prev_supertrend
up_trend = close >= lower_band
supertrend = lower_band if up_trend else upper_band
else:
if upper_band > self._prev_supertrend:
upper_band = self._prev_supertrend
up_trend = close > upper_band
supertrend = lower_band if up_trend else upper_band
self._prev_supertrend = supertrend
self._prev_up_trend = up_trend
# Trim lists
if len(self._highs) > period * 3:
trim = len(self._highs) - period * 2
self._highs = self._highs[trim:]
self._lows = self._lows[trim:]
self._closes = self._closes[trim:]
if self._cooldown > 0:
self._cooldown -= 1
return
if self.Position != 0:
return
cd = self._cooldown_bars.Value
# Buy: uptrend + RSI below midpoint
if up_trend and rv < 50:
self.BuyMarket()
self._cooldown = cd
# Sell: downtrend + RSI above midpoint
elif not up_trend and rv > 50:
self.SellMarket()
self._cooldown = cd
def OnReseted(self):
super(supertrend_rsi_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._closes = []
self._prev_supertrend = 0.0
self._prev_up_trend = True
self._st_initialized = False
self._cooldown = 0
def CreateClone(self):
return supertrend_rsi_strategy()