RSI Threshold Strategy
Converts the MetaTrader Exp_RSI expert into StockSharp. The strategy opens and closes positions when the Relative Strength Index (RSI) crosses predefined overbought and oversold levels.
Details
- Entry Criteria:
- Long: RSI crosses above
RSI Low Level. - Short: RSI crosses below
RSI High Level.
- Long: RSI crosses above
- Long/Short: Both directions.
- Exit Criteria:
- Reverse signal or stop parameters.
- Stops: Take Profit & Stop Loss in absolute price units.
- Default Values:
RSI Period= 14RSI High Level= 60RSI Low Level= 40Stop Loss= 1000Take Profit= 2000
- Filters:
- Category: Oscillator
- Direction: Both
- Indicators: Single
- Stops: Yes
- Complexity: Beginner
- Timeframe: H4
- 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>
/// Simple RSI threshold strategy converted from MQL/16855.
/// Buys or sells when RSI crosses predefined levels.
/// </summary>
public class RsiThresholdStrategy : Strategy
{
public enum TrendModes
{
/// <summary>
/// Trade in the direction of RSI crossings.
/// Buy when RSI crosses above the low level, sell when it crosses below the high level.
/// </summary>
Direct,
/// <summary>
/// Trade against the direction of RSI crossings.
/// Sell when RSI crosses above the low level, buy when it crosses below the high level.
/// </summary>
Reverse
}
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<TrendModes> _trend;
private readonly StrategyParam<bool> _buyOpen;
private readonly StrategyParam<bool> _sellOpen;
private readonly StrategyParam<bool> _buyClose;
private readonly StrategyParam<bool> _sellClose;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevRsi;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
public TrendModes Trend { get => _trend.Value; set => _trend.Value = value; }
public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RsiThresholdStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicator")
.SetOptimize(10, 30, 2);
_highLevel = Param(nameof(HighLevel), 70m)
.SetDisplay("RSI High Level", "Overbought level", "Signal")
.SetOptimize(50m, 80m, 5m);
_lowLevel = Param(nameof(LowLevel), 30m)
.SetDisplay("RSI Low Level", "Oversold level", "Signal")
.SetOptimize(20m, 50m, 5m);
_trend = Param(nameof(Trend), TrendModes.Direct)
.SetDisplay("Trend Mode", "Trading direction relative to RSI crossings", "General");
_buyOpen = Param(nameof(BuyOpen), true)
.SetDisplay("Enable Buy Entry", "Allow opening long positions", "General");
_sellOpen = Param(nameof(SellOpen), true)
.SetDisplay("Enable Sell Entry", "Allow opening short positions", "General");
_buyClose = Param(nameof(BuyClose), true)
.SetDisplay("Enable Buy Exit", "Allow closing long positions", "General");
_sellClose = Param(nameof(SellClose), true)
.SetDisplay("Enable Sell Exit", "Allow closing short positions", "General");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk Management");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit in price units", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished) // only process completed candles
return;
if (_prevRsi is null)
{
// store first RSI value to detect crossings
_prevRsi = rsi;
return;
}
if (Trend == TrendModes.Direct)
{
if (_prevRsi <= LowLevel && rsi > LowLevel)
// RSI crossed above oversold level
{
if (SellClose && Position < 0) // close short positions
BuyMarket();
if (BuyOpen && Position <= 0) // open long
BuyMarket();
}
if (_prevRsi >= HighLevel && rsi < HighLevel)
// RSI crossed below overbought level
{
if (BuyClose && Position > 0) // close long positions
SellMarket();
if (SellOpen && Position >= 0) // open short
SellMarket();
}
}
else
{
if (_prevRsi <= LowLevel && rsi > LowLevel)
// RSI crossed above oversold level
{
if (BuyClose && Position > 0) // close long positions
SellMarket();
if (SellOpen && Position >= 0) // open short
SellMarket();
}
if (_prevRsi >= HighLevel && rsi < HighLevel)
// RSI crossed below overbought level
{
if (SellClose && Position < 0) // close short positions
BuyMarket();
if (BuyOpen && Position <= 0) // open long
BuyMarket();
}
}
_prevRsi = rsi; // remember last RSI value
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_threshold_strategy(Strategy):
DIRECT = 0
REVERSE = 1
def __init__(self):
super(rsi_threshold_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14)
self._high_level = self.Param("HighLevel", 70.0)
self._low_level = self.Param("LowLevel", 30.0)
self._trend = self.Param("Trend", 0)
self._buy_open = self.Param("BuyOpen", True)
self._sell_open = self.Param("SellOpen", True)
self._buy_close = self.Param("BuyClose", True)
self._sell_close = self.Param("SellClose", True)
self._stop_loss = self.Param("StopLoss", 1000.0)
self._take_profit = self.Param("TakeProfit", 2000.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_rsi = None
@property
def RsiPeriod(self): return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, v): self._rsi_period.Value = v
@property
def HighLevel(self): return self._high_level.Value
@HighLevel.setter
def HighLevel(self, v): self._high_level.Value = v
@property
def LowLevel(self): return self._low_level.Value
@LowLevel.setter
def LowLevel(self, v): self._low_level.Value = v
@property
def Trend(self): return self._trend.Value
@Trend.setter
def Trend(self, v): self._trend.Value = v
@property
def BuyOpen(self): return self._buy_open.Value
@BuyOpen.setter
def BuyOpen(self, v): self._buy_open.Value = v
@property
def SellOpen(self): return self._sell_open.Value
@SellOpen.setter
def SellOpen(self, v): self._sell_open.Value = v
@property
def BuyClose(self): return self._buy_close.Value
@BuyClose.setter
def BuyClose(self, v): self._buy_close.Value = v
@property
def SellClose(self): return self._sell_close.Value
@SellClose.setter
def SellClose(self, v): self._sell_close.Value = v
@property
def StopLoss(self): return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, v): self._stop_loss.Value = v
@property
def TakeProfit(self): return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, v): self._take_profit.Value = v
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
def OnStarted2(self, time):
super(rsi_threshold_strategy, self).OnStarted2(time)
self._prev_rsi = None
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(rsi, self.ProcessCandle).Start()
self.StartProtection(
Unit(float(self.TakeProfit), UnitTypes.Absolute),
Unit(float(self.StopLoss), UnitTypes.Absolute))
def ProcessCandle(self, candle, rsi):
if candle.State != CandleStates.Finished:
return
r = float(rsi)
if self._prev_rsi is None:
self._prev_rsi = r
return
if self.Trend == self.DIRECT:
if self._prev_rsi <= float(self.LowLevel) and r > float(self.LowLevel):
if self.SellClose and self.Position < 0:
self.BuyMarket()
if self.BuyOpen and self.Position <= 0:
self.BuyMarket()
if self._prev_rsi >= float(self.HighLevel) and r < float(self.HighLevel):
if self.BuyClose and self.Position > 0:
self.SellMarket()
if self.SellOpen and self.Position >= 0:
self.SellMarket()
else:
if self._prev_rsi <= float(self.LowLevel) and r > float(self.LowLevel):
if self.BuyClose and self.Position > 0:
self.SellMarket()
if self.SellOpen and self.Position >= 0:
self.SellMarket()
if self._prev_rsi >= float(self.HighLevel) and r < float(self.HighLevel):
if self.SellClose and self.Position < 0:
self.BuyMarket()
if self.BuyOpen and self.Position <= 0:
self.BuyMarket()
self._prev_rsi = r
def OnReseted(self):
super(rsi_threshold_strategy, self).OnReseted()
self._prev_rsi = None
def CreateClone(self):
return rsi_threshold_strategy()