RSI Stochastic MA Strategy
This strategy combines a simple moving average (SMA) trend filter with RSI and Stochastic oscillators. The moving average defines the market bias. When price is above the SMA the strategy looks for long entries; when below the SMA it seeks short entries. RSI and Stochastic levels identify oversold or overbought conditions to time entries.
Positions are closed when the oscillators leave their extreme zones. This keeps trades aligned with the prevailing trend while avoiding extended moves against the indicators.
Parameters
RsiPeriod– RSI calculation period.RsiUpperLevel– RSI overbought threshold.RsiLowerLevel– RSI oversold threshold.MaPeriod– period of the trend moving average.StochKPeriod– %K period for the Stochastic oscillator.StochDPeriod– %D smoothing period for the Stochastic oscillator.StochUpperLevel– Stochastic overbought level.StochLowerLevel– Stochastic oversold level.Volume– order volume.CandleType– candle data type used for calculations.
Indicators
- Simple Moving Average
- Relative Strength Index
- Stochastic Oscillator
Trading rules
- Buy when price is above the SMA, RSI is below
RsiLowerLevel, and both Stochastic lines are belowStochLowerLevel. - Sell when price is below the SMA, RSI is above
RsiUpperLevel, and both Stochastic lines are aboveStochUpperLevel. - Exit long when RSI or Stochastic rises above their upper levels.
- Exit short when RSI or Stochastic falls below their lower levels.
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>
/// Combined RSI, Stochastic, and Moving Average strategy.
/// The MA defines the trend. Entries on RSI+Stochastic oversold/overbought in trend direction.
/// </summary>
public class RsiStochasticMaStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiUpperLevel;
private readonly StrategyParam<decimal> _rsiLowerLevel;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _stochUpperLevel;
private readonly StrategyParam<decimal> _stochLowerLevel;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private StochasticOscillator _stochastic;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiUpperLevel { get => _rsiUpperLevel.Value; set => _rsiUpperLevel.Value = value; }
public decimal RsiLowerLevel { get => _rsiLowerLevel.Value; set => _rsiLowerLevel.Value = value; }
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public decimal StochUpperLevel { get => _stochUpperLevel.Value; set => _stochUpperLevel.Value = value; }
public decimal StochLowerLevel { get => _stochLowerLevel.Value; set => _stochLowerLevel.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RsiStochasticMaStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 3)
.SetDisplay("RSI Period", "RSI calculation period", "RSI");
_rsiUpperLevel = Param(nameof(RsiUpperLevel), 65m)
.SetDisplay("RSI Upper Level", "RSI overbought level", "RSI");
_rsiLowerLevel = Param(nameof(RsiLowerLevel), 35m)
.SetDisplay("RSI Lower Level", "RSI oversold level", "RSI");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetDisplay("MA Period", "Moving average period", "Trend");
_stochUpperLevel = Param(nameof(StochUpperLevel), 60m)
.SetDisplay("Stochastic Upper", "Stochastic overbought level", "Stochastic");
_stochLowerLevel = Param(nameof(StochLowerLevel), 40m)
.SetDisplay("Stochastic Lower", "Stochastic oversold level", "Stochastic");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stochastic = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ma = new ExponentialMovingAverage { Length = MaPeriod };
_stochastic = new StochasticOscillator();
Indicators.Add(_stochastic);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, rsi, (candle, maValue, rsiValue) =>
{
if (candle.State != CandleStates.Finished)
return;
var stochResult = _stochastic.Process(candle);
if (!_stochastic.IsFormed)
return;
var stochVal = (StochasticOscillatorValue)stochResult;
if (stochVal.K is not decimal k || stochVal.D is not decimal d)
return;
var price = candle.ClosePrice;
var isUpTrend = price > maValue;
var isDownTrend = price < maValue;
if (isUpTrend && rsiValue < RsiLowerLevel && k < StochLowerLevel && Position == 0)
{
BuyMarket();
}
else if (isDownTrend && rsiValue > RsiUpperLevel && k > StochUpperLevel && Position == 0)
{
SellMarket();
}
})
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
}
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, ExponentialMovingAverage, StochasticOscillator, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class rsi_stochastic_ma_strategy(Strategy):
def __init__(self):
super(rsi_stochastic_ma_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 3) \
.SetDisplay("RSI Period", "RSI calculation period", "RSI")
self._rsi_upper_level = self.Param("RsiUpperLevel", 65.0) \
.SetDisplay("RSI Upper Level", "RSI overbought level", "RSI")
self._rsi_lower_level = self.Param("RsiLowerLevel", 35.0) \
.SetDisplay("RSI Lower Level", "RSI oversold level", "RSI")
self._ma_period = self.Param("MaPeriod", 20) \
.SetDisplay("MA Period", "Moving average period", "Trend")
self._stoch_upper_level = self.Param("StochUpperLevel", 60.0) \
.SetDisplay("Stochastic Upper", "Stochastic overbought level", "Stochastic")
self._stoch_lower_level = self.Param("StochLowerLevel", 40.0) \
.SetDisplay("Stochastic Lower", "Stochastic oversold level", "Stochastic")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._stochastic = None
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def rsi_upper_level(self):
return self._rsi_upper_level.Value
@property
def rsi_lower_level(self):
return self._rsi_lower_level.Value
@property
def ma_period(self):
return self._ma_period.Value
@property
def stoch_upper_level(self):
return self._stoch_upper_level.Value
@property
def stoch_lower_level(self):
return self._stoch_lower_level.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(rsi_stochastic_ma_strategy, self).OnReseted()
self._stochastic = None
def OnStarted2(self, time):
super(rsi_stochastic_ma_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
ma = ExponentialMovingAverage()
ma.Length = self.ma_period
self._stochastic = StochasticOscillator()
self.Indicators.Add(self._stochastic)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, rsi, self.on_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def on_candle(self, candle, ma_value, rsi_value):
if candle.State != CandleStates.Finished:
return
cv = CandleIndicatorValue(self._stochastic, candle)
stoch_result = self._stochastic.Process(cv)
if not self._stochastic.IsFormed:
return
k = stoch_result.K
d = stoch_result.D
if k is None or d is None:
return
k = float(k)
ma_value = float(ma_value)
rsi_value = float(rsi_value)
price = float(candle.ClosePrice)
is_up_trend = price > ma_value
is_down_trend = price < ma_value
if is_up_trend and rsi_value < float(self.rsi_lower_level) and k < float(self.stoch_lower_level) and self.Position == 0:
self.BuyMarket()
elif is_down_trend and rsi_value > float(self.rsi_upper_level) and k > float(self.stoch_upper_level) and self.Position == 0:
self.SellMarket()
def CreateClone(self):
return rsi_stochastic_ma_strategy()