Rsi Supertrend Strategy
Strategy based on RSI and Supertrend indicators. Enters long when RSI is oversold (< 30) and price is above Supertrend Enters short when RSI is overbought (> 70) and price is below Supertrend
Testing indicates an average annual return of about 112%. It performs best in the forex market.
The RSI oscillator defines momentum extremes while Supertrend points to the prevailing direction. Trades occur when RSI aligns with the Supertrend color.
Works for traders who appreciate a trailing-stop style exit. ATR settings further safeguard the position.
Details
- Entry Criteria:
- Long:
RSI < 30 && Close > Supertrend - Short:
RSI > 70 && Close < Supertrend
- Long:
- Long/Short: Both
- Exit Criteria: Supertrend change
- Stops: Trailing with Supertrend
- Default Values:
RsiPeriod= 14SupertrendPeriod= 10SupertrendMultiplier= 3.0mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Mean reversion
- Direction: Both
- Indicators: RSI, Supertrend
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on RSI and Supertrend indicators.
/// Enters long when RSI is oversold (< 30) and price is above Supertrend
/// Enters short when RSI is overbought (> 70) and price is below Supertrend
/// </summary>
public class RsiSupertrendStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _supertrendPeriod;
private readonly StrategyParam<decimal> _supertrendMultiplier;
private readonly StrategyParam<DataType> _candleType;
// Custom Supertrend indicator
private AverageTrueRange _atr;
private decimal _upValue;
private decimal _downValue;
private decimal _currentTrend;
private decimal _prevUpValue;
private decimal _prevDownValue;
private decimal _prevClose;
private bool _isFirstValue = true;
/// <summary>
/// RSI period
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Supertrend ATR period
/// </summary>
public int SupertrendPeriod
{
get => _supertrendPeriod.Value;
set => _supertrendPeriod.Value = value;
}
/// <summary>
/// Supertrend ATR multiplier
/// </summary>
public decimal SupertrendMultiplier
{
get => _supertrendMultiplier.Value;
set => _supertrendMultiplier.Value = value;
}
/// <summary>
/// Candle type for strategy calculation
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor
/// </summary>
public RsiSupertrendStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period for RSI indicator", "Indicators")
.SetOptimize(10, 20, 2);
_supertrendPeriod = Param(nameof(SupertrendPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Supertrend Period", "ATR period for Supertrend", "Indicators")
.SetOptimize(7, 14, 1);
_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 3.0m)
.SetGreaterThanZero()
.SetDisplay("Supertrend Multiplier", "ATR multiplier for Supertrend", "Indicators")
.SetOptimize(2.0m, 4.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
// Reset state variables
_atr = null;
_isFirstValue = true;
_currentTrend = 1;
_downValue = 0;
_prevUpValue = 0;
_prevDownValue = 0;
_prevClose = 0;
_upValue = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create RSI indicator
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
// Create ATR indicator for Supertrend calculation
_atr = new AverageTrueRange { Length = SupertrendPeriod };
// Enable using Supertrend as a dynamic stop-loss
// We'll implement our own stop management based on Supertrend
// Subscribe to candles and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, _atr, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
// Separate area for RSI
var rsiArea = CreateChartArea();
if (rsiArea != null)
{
DrawIndicator(rsiArea, rsi);
}
// Note: We'll manually draw Supertrend lines in ProcessCandle method
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal atrValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Calculate Supertrend
var closePrice = candle.ClosePrice;
var highPrice = candle.HighPrice;
var lowPrice = candle.LowPrice;
// Basic bands calculation
var basicUpperBand = (highPrice + lowPrice) / 2 + SupertrendMultiplier * atrValue;
var basicLowerBand = (highPrice + lowPrice) / 2 - SupertrendMultiplier * atrValue;
if (_isFirstValue)
{
// Initialize values for the first candle
_upValue = basicUpperBand;
_downValue = basicLowerBand;
_prevUpValue = _upValue;
_prevDownValue = _downValue;
_prevClose = closePrice;
_isFirstValue = false;
return;
}
// Calculate final upper and lower bands
_upValue = basicUpperBand;
if (_upValue < _prevUpValue || _prevClose > _prevUpValue)
_upValue = _prevUpValue;
_downValue = basicLowerBand;
if (_downValue > _prevDownValue || _prevClose < _prevDownValue)
_downValue = _prevDownValue;
// Determine trend direction
var prevTrend = _currentTrend;
if (_prevClose <= _prevUpValue)
_currentTrend = -1; // Downtrend
if (_prevClose >= _prevDownValue)
_currentTrend = 1; // Uptrend
// Store values for next iteration
_prevUpValue = _upValue;
_prevDownValue = _downValue;
_prevClose = closePrice;
// Get Supertrend value based on current trend
var supertrendValue = _currentTrend == 1 ? _downValue : _upValue;
// Trading logic
var isTrendChange = prevTrend != _currentTrend;
// Long condition: RSI oversold and price above Supertrend
if (rsiValue < 30 && _currentTrend == 1 && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
// Note: We're using Supertrend as our stop-loss level,
// so we don't need to set a separate stop-loss order
}
// Short condition: RSI overbought and price below Supertrend
else if (rsiValue > 70 && _currentTrend == -1 && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
}
// Exit conditions - based on Supertrend direction change
else if (isTrendChange)
{
if (_currentTrend == -1 && Position > 0)
{
// Trend changed to down - exit long
SellMarket(Position);
}
else if (_currentTrend == 1 && Position < 0)
{
// Trend changed to up - exit short
BuyMarket(Math.Abs(Position));
}
}
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class rsi_supertrend_strategy(Strategy):
"""
Strategy based on RSI and Supertrend indicators.
Enters long when RSI is oversold (< 30) and price is above Supertrend
Enters short when RSI is overbought (> 70) and price is below Supertrend
"""
def __init__(self):
super(rsi_supertrend_strategy, self).__init__()
# Initialize strategy parameters
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "Period for RSI indicator", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(10, 20, 2)
self._supertrend_period = self.Param("SupertrendPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("Supertrend Period", "ATR period for Supertrend", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(7, 14, 1)
self._supertrend_multiplier = self.Param("SupertrendMultiplier", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("Supertrend Multiplier", "ATR multiplier for Supertrend", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(2.0, 4.0, 0.5)
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
# Custom Supertrend indicator
self._atr = None
self._up_value = 0.0
self._down_value = 0.0
self._current_trend = 0.0
self._prev_up_value = 0.0
self._prev_down_value = 0.0
self._prev_close = 0.0
self._is_first_value = True
@property
def rsi_period(self):
"""RSI period"""
return self._rsi_period.Value
@rsi_period.setter
def rsi_period(self, value):
self._rsi_period.Value = value
@property
def supertrend_period(self):
"""Supertrend ATR period"""
return self._supertrend_period.Value
@supertrend_period.setter
def supertrend_period(self, value):
self._supertrend_period.Value = value
@property
def supertrend_multiplier(self):
"""Supertrend ATR multiplier"""
return self._supertrend_multiplier.Value
@supertrend_multiplier.setter
def supertrend_multiplier(self, value):
self._supertrend_multiplier.Value = value
@property
def candle_type(self):
"""Candle type for strategy calculation"""
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
"""
Resets internal state when strategy is reset.
"""
super(rsi_supertrend_strategy, self).OnReseted()
self._atr = None
self._is_first_value = True
self._current_trend = 1
self._down_value = 0
self._prev_up_value = 0
self._prev_down_value = 0
self._prev_close = 0
self._up_value = 0
def OnStarted2(self, time):
"""
Called when the strategy starts. Sets up indicators, subscriptions, and charting.
:param time: The time when the strategy started.
"""
super(rsi_supertrend_strategy, self).OnStarted2(time)
# Create RSI indicator
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
# Create ATR indicator for Supertrend calculation
self._atr = AverageTrueRange()
self._atr.Length = self.supertrend_period
# Enable using Supertrend as a dynamic stop-loss
# We'll implement our own stop management based on Supertrend
# Subscribe to candles and bind indicators
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._atr, self.ProcessCandle).Start()
# Setup chart visualization if available
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
# Separate area for RSI
rsi_area = self.CreateChartArea()
if rsi_area is not None:
self.DrawIndicator(rsi_area, rsi)
# Note: We'll manually draw Supertrend lines in ProcessCandle method
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, rsi_value, atr_value):
"""
Process candle and execute trading logic based on RSI and Supertrend.
:param candle: The processed candle message.
:param rsi_value: The current RSI value.
:param atr_value: The current ATR value.
"""
# Skip unfinished candles
if candle.State != CandleStates.Finished:
return
# Check if strategy is ready to trade
# Calculate Supertrend
close_price = float(candle.ClosePrice)
high_price = float(candle.HighPrice)
low_price = float(candle.LowPrice)
# Basic bands calculation
basic_upper_band = (high_price + low_price) / 2 + self.supertrend_multiplier * atr_value
basic_lower_band = (high_price + low_price) / 2 - self.supertrend_multiplier * atr_value
if self._is_first_value:
# Initialize values for the first candle
self._up_value = basic_upper_band
self._down_value = basic_lower_band
self._prev_up_value = self._up_value
self._prev_down_value = self._down_value
self._prev_close = close_price
self._is_first_value = False
return
# Calculate final upper and lower bands
self._up_value = basic_upper_band
if self._up_value < self._prev_up_value or self._prev_close > self._prev_up_value:
self._up_value = self._prev_up_value
self._down_value = basic_lower_band
if self._down_value > self._prev_down_value or self._prev_close < self._prev_down_value:
self._down_value = self._prev_down_value
# Determine trend direction
prev_trend = self._current_trend
if self._prev_close <= self._prev_up_value:
self._current_trend = -1 # Downtrend
if self._prev_close >= self._prev_down_value:
self._current_trend = 1 # Uptrend
# Store values for next iteration
self._prev_up_value = self._up_value
self._prev_down_value = self._down_value
self._prev_close = close_price
# Get Supertrend value based on current trend
supertrend_value = self._down_value if self._current_trend == 1 else self._up_value
# Trading logic
is_trend_change = prev_trend != self._current_trend
# Long condition: RSI oversold and price above Supertrend
if rsi_value < 30 and self._current_trend == 1 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
# Note: We're using Supertrend as our stop-loss level,
# so we don't need to set a separate stop-loss order
# Short condition: RSI overbought and price below Supertrend
elif rsi_value > 70 and self._current_trend == -1 and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
# Exit conditions - based on Supertrend direction change
elif is_trend_change:
if self._current_trend == -1 and self.Position > 0:
# Trend changed to down - exit long
self.SellMarket(self.Position)
elif self._current_trend == 1 and self.Position < 0:
# Trend changed to up - exit short
self.BuyMarket(Math.Abs(self.Position))
def CreateClone(self):
"""
!! REQUIRED!! Creates a new instance of the strategy.
"""
return rsi_supertrend_strategy()