RSI EA Strategy
This strategy emulates a classic RSI expert advisor. It trades when the Relative Strength Index crosses predefined levels and manages risk with stop loss, take profit and optional trailing stop.
Strategy Logic
- Calculate RSI using the configurable
RsiPeriod. - Long entry when RSI rises above
BuyLeveland no long position exists. - Short entry when RSI falls below
SellLeveland no short position exists. - When
CloseBySignalis enabled, an opposite cross closes the existing position. - Positions can be protected with
StopLoss,TakeProfitandTrailingStopmeasured in price units. - Works on candle data defined by
CandleType.
Parameters
OpenBuy– enable long entries.OpenSell– enable short entries.CloseBySignal– close by opposite RSI signal.StopLoss– loss in price units.TakeProfit– profit in price units.TrailingStop– trailing distance in price units.RsiPeriod– RSI calculation length.BuyLevel– RSI threshold for long signals.SellLevel– RSI threshold for short signals.CandleType– candle timeframe or type to subscribe.
The default trade volume is controlled by the strategy Volume property.
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>
/// RSI based expert advisor strategy replicating classic oversold/overbought rules.
/// Opens trades on level cross with stop loss and take profit.
/// </summary>
public class RsiEaStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _buyLevel;
private readonly StrategyParam<decimal> _sellLevel;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
private bool _hasPrevRsi;
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal BuyLevel { get => _buyLevel.Value; set => _buyLevel.Value = value; }
public decimal SellLevel { get => _sellLevel.Value; set => _sellLevel.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RsiEaStrategy()
{
_stopLoss = Param(nameof(StopLoss), 500m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 1000m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI length", "Indicator");
_buyLevel = Param(nameof(BuyLevel), 30m)
.SetDisplay("Buy Level", "RSI oversold threshold", "Indicator");
_sellLevel = Param(nameof(SellLevel), 70m)
.SetDisplay("Sell Level", "RSI overbought threshold", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = default;
_hasPrevRsi = default;
}
/// <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(
new Unit(StopLoss, UnitTypes.Absolute),
new Unit(TakeProfit, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevRsi = rsi;
_hasPrevRsi = true;
return;
}
if (!_hasPrevRsi)
{
_prevRsi = rsi;
_hasPrevRsi = true;
return;
}
// RSI crosses above buy level (oversold recovery) - buy
var buyCross = rsi > BuyLevel && _prevRsi <= BuyLevel;
// RSI crosses below sell level (overbought drop) - sell
var sellCross = rsi < SellLevel && _prevRsi >= SellLevel;
if (buyCross && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (sellCross && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevRsi = rsi;
}
}
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_ea_strategy(Strategy):
def __init__(self):
super(rsi_ea_strategy, self).__init__()
self._stop_loss = self.Param("StopLoss", 500.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 1000.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI length", "Indicator")
self._buy_level = self.Param("BuyLevel", 30.0) \
.SetDisplay("Buy Level", "RSI oversold threshold", "Indicator")
self._sell_level = self.Param("SellLevel", 70.0) \
.SetDisplay("Sell Level", "RSI overbought threshold", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_rsi = 0.0
self._has_prev_rsi = False
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def BuyLevel(self):
return self._buy_level.Value
@BuyLevel.setter
def BuyLevel(self, value):
self._buy_level.Value = value
@property
def SellLevel(self):
return self._sell_level.Value
@SellLevel.setter
def SellLevel(self, value):
self._sell_level.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(rsi_ea_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(rsi, self.ProcessCandle) \
.Start()
self.StartProtection(
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute),
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute)
)
def ProcessCandle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi = float(rsi_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rsi
self._has_prev_rsi = True
return
if not self._has_prev_rsi:
self._prev_rsi = rsi
self._has_prev_rsi = True
return
buy_cross = rsi > float(self.BuyLevel) and self._prev_rsi <= float(self.BuyLevel)
sell_cross = rsi < float(self.SellLevel) and self._prev_rsi >= float(self.SellLevel)
if buy_cross and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_cross and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_rsi = rsi
def OnReseted(self):
super(rsi_ea_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._has_prev_rsi = False
def CreateClone(self):
return rsi_ea_strategy()