Stochastic RSI SuperTrend Strategy
This system blends the fast oscillations of the Stochastic RSI with a trend filter and a simplified SuperTrend model. The oscillator highlights short term momentum extremes, while the moving average and ATR bands define the dominant trend. Trades are opened only when the %K line crosses %D inside the relevant zone and the broader trend is aligned, reducing whipsaws during sideways conditions.
The default configuration focuses on long trades but can optionally enable short entries. The strategy is designed for intraday to swing time frames where Stochastic RSI signals appear frequently and the ATR based bands provide a volatility‑adaptive bias. Exits occur on opposite crossovers, allowing the market to run until momentum fades.
Details
- Entry Criteria:
- Long: close above trend MA, %K < 20, %K crosses above %D, SuperTrend shows uptrend.
- Short: close below trend MA, %K > 80, %K crosses below %D, SuperTrend shows downtrend.
- Long/Short: Long by default, optional short.
- Exit Criteria:
- Long: %K > 80 and crosses below %D.
- Short: %K < 20 and crosses above %D.
- Stops: None by default; can be added externally.
- Default Values:
- RSI period = 14, Stochastic length = 14.
- MA type = EMA, MA length = 100.
- ATR period = 10, ATR factor = 3.0.
- Filters:
- Category: Momentum + Trend
- Direction: Primarily long
- Indicators: RSI, ATR, Moving Average
- Stops: No
- 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>
/// Stochastic RSI + Supertrend Strategy.
/// Uses RSI levels with SuperTrend direction and EMA trend filter.
/// Buys when RSI is oversold, SuperTrend is bullish, and price above EMA.
/// Sells when RSI is overbought, SuperTrend is bearish, and price below EMA.
/// </summary>
public class StochRsiSupertrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _supertrendLength;
private readonly StrategyParam<decimal> _supertrendMultiplier;
private readonly StrategyParam<int> _cooldownBars;
private RelativeStrengthIndex _rsi;
private ExponentialMovingAverage _ema;
private SuperTrend _supertrend;
private decimal _prevRsi;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int SupertrendLength
{
get => _supertrendLength.Value;
set => _supertrendLength.Value = value;
}
public decimal SupertrendMultiplier
{
get => _supertrendMultiplier.Value;
set => _supertrendMultiplier.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public StochRsiSupertrendStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_emaLength = Param(nameof(EmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Trend EMA period", "Moving Average");
_supertrendLength = Param(nameof(SupertrendLength), 11)
.SetGreaterThanZero()
.SetDisplay("SuperTrend Length", "SuperTrend ATR period", "SuperTrend");
_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 2.0m)
.SetDisplay("SuperTrend Multiplier", "SuperTrend ATR multiplier", "SuperTrend");
_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();
_rsi = null;
_ema = null;
_supertrend = null;
_prevRsi = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_ema = new ExponentialMovingAverage { Length = EmaLength };
_supertrend = new SuperTrend { Length = SupertrendLength, Multiplier = SupertrendMultiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_rsi, _ema, _supertrend, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawIndicator(area, _supertrend);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue emaValue, IIndicatorValue stValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed || !_ema.IsFormed || !_supertrend.IsFormed)
return;
if (rsiValue.IsEmpty || emaValue.IsEmpty || stValue.IsEmpty)
return;
var rsiVal = rsiValue.ToDecimal();
var emaVal = emaValue.ToDecimal();
var stTyped = (SuperTrendIndicatorValue)stValue;
var isUpTrend = stTyped.IsUpTrend;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevRsi = rsiVal;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevRsi = rsiVal;
return;
}
if (_prevRsi == 0)
{
_prevRsi = rsiVal;
return;
}
// RSI crossovers
var rsiCrossUpOversold = rsiVal > 40 && _prevRsi <= 40;
var rsiCrossDownOverbought = rsiVal < 60 && _prevRsi >= 60;
// Buy: RSI crosses above oversold + SuperTrend bullish + price above EMA
if (rsiCrossUpOversold && isUpTrend && candle.ClosePrice > emaVal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: RSI crosses below overbought + SuperTrend bearish + price below EMA
else if (rsiCrossDownOverbought && !isUpTrend && candle.ClosePrice < emaVal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: SuperTrend turns bearish
else if (Position > 0 && !isUpTrend)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: SuperTrend turns bullish
else if (Position < 0 && isUpTrend)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevRsi = rsiVal;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage, SuperTrend, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class stoch_rsi_supertrend_strategy(Strategy):
"""Stochastic RSI + Supertrend Strategy."""
def __init__(self):
super(stoch_rsi_supertrend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend EMA period", "Moving Average")
self._supertrend_length = self.Param("SupertrendLength", 11) \
.SetDisplay("SuperTrend Length", "SuperTrend ATR period", "SuperTrend")
self._supertrend_multiplier = self.Param("SupertrendMultiplier", 2.0) \
.SetDisplay("SuperTrend Multiplier", "SuperTrend ATR multiplier", "SuperTrend")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._rsi = None
self._ema = None
self._supertrend = None
self._prev_rsi = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stoch_rsi_supertrend_strategy, self).OnReseted()
self._rsi = None
self._ema = None
self._supertrend = None
self._prev_rsi = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(stoch_rsi_supertrend_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
self._supertrend = SuperTrend()
self._supertrend.Length = int(self._supertrend_length.Value)
self._supertrend.Multiplier = float(self._supertrend_multiplier.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._rsi, self._ema, self._supertrend, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawIndicator(area, self._supertrend)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, ema_value, st_value):
if candle.State != CandleStates.Finished:
return
if not self._rsi.IsFormed or not self._ema.IsFormed or not self._supertrend.IsFormed:
return
if rsi_value.IsEmpty or ema_value.IsEmpty or st_value.IsEmpty:
return
rsi_val = float(IndicatorHelper.ToDecimal(rsi_value))
ema_val = float(IndicatorHelper.ToDecimal(ema_value))
is_up_trend = st_value.IsUpTrend
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rsi_val
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_rsi = rsi_val
return
if self._prev_rsi == 0.0:
self._prev_rsi = rsi_val
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
rsi_cross_up_oversold = rsi_val > 40 and self._prev_rsi <= 40
rsi_cross_down_overbought = rsi_val < 60 and self._prev_rsi >= 60
if rsi_cross_up_oversold and is_up_trend and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif rsi_cross_down_overbought and not is_up_trend and close < ema_val 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 not is_up_trend:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and is_up_trend:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_rsi = rsi_val
def CreateClone(self):
return stoch_rsi_supertrend_strategy()