Port of the MetaTrader expert advisor "Macd diver rsi mt4" to the StockSharp high-level API.
Trades a single symbol using RSI filters combined with MACD divergence recognition to time reversals.
Only one market position can be open at a time; the strategy waits for the flat state before issuing a new signal.
Signal Logic
Every finished candle from the selected timeframe feeds four indicators bound to the strategy:
Two independent RelativeStrengthIndex instances (for oversold and overbought filters) sampled one bar back.
Two MovingAverageConvergenceDivergence indicators with configurable fast/slow EMA and signal lengths.
Bullish setup
The previous bar RSI must be below the configurable oversold threshold.
The most recent MACD values must form a local dip below a dynamic threshold (equivalent to 3 pips in the current instrument).
Historical data is scanned to locate an earlier MACD dip and the associated price swing low. Divergence is confirmed when
the MACD trough rises while price makes a lower low (regular divergence) or the MACD trough falls while price makes a higher
low (hidden divergence), matching the original MQL logic.
When confirmed and the strategy has no open position, a market buy is sent with direction-specific volume and risk settings.
Bearish setup mirrors the bullish rules with the RSI overbought filter and MACD peaks. Divergence is validated by
comparing previous swing highs against the current one.
Immediately after an entry the strategy converts the configured stop-loss and take-profit distances from pips to price units
(respecting the original point-format rules) and applies them through SetStopLoss / SetTakeProfit.
Parameters
LowerRsiPeriod, LowerRsiThreshold – map to inp1_Lo_RSIperiod / inp1_Ro_Value.
BearishVolume, BearishStopLossPips, BearishTakeProfitPips – map to inp6_VolumeSize, inp6_StopLossPips, inp6_TakeProfitPips.
CandleType – timeframe source for all calculations.
Implementation Notes
The MACD divergence threshold is derived from the current instrument point size and equals 3 pips, matching the 0.0003 default
used by the MQL version.
Candle, MACD and price history are stored in bounded lists (600 elements) to reproduce the divergence scanning windows without
allocating large arrays.
The strategy uses SubscribeCandles(...).Bind(...) to update all indicators in a single pass and processes only finished
candles, just like the original once-per-bar block execution.
Pip distances are converted into absolute price offsets before calling SetStopLoss and SetTakeProfit, reproducing the
point format rules declared at the top of the MQL source.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MACD Divergence RSI strategy: RSI filter + MACD signal line crossover.
/// Buys when RSI below threshold and MACD crosses above signal.
/// Sells when RSI above threshold and MACD crosses below signal.
/// </summary>
public class MacdDivergenceRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private decimal _prevMacd;
private decimal _prevSignal;
private decimal _prevRsi;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public MacdDivergenceRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = 0;
_prevSignal = 0;
_prevRsi = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = 12 }, LongMa = { Length = 26 } },
SignalMa = { Length = 9 }
};
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(macd, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished) return;
if (!macdValue.IsFinal || !rsiValue.IsFinal) return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue typed) return;
if (typed.Macd is not decimal macdMain || typed.Signal is not decimal signal) return;
var rsi = rsiValue.ToDecimal();
if (_hasPrev)
{
if (_prevMacd <= _prevSignal && macdMain > signal && rsi < 40 && Position <= 0)
BuyMarket();
else if (_prevMacd >= _prevSignal && macdMain < signal && rsi > 60 && Position >= 0)
SellMarket();
}
_prevMacd = macdMain;
_prevSignal = signal;
_prevRsi = rsi;
_hasPrev = true;
}
}
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
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class macd_divergence_rsi_strategy(Strategy):
def __init__(self):
super(macd_divergence_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._prev_macd = 0.0
self._prev_signal = 0.0
self._prev_rsi = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
def OnReseted(self):
super(macd_divergence_rsi_strategy, self).OnReseted()
self._prev_macd = 0.0
self._prev_signal = 0.0
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(macd_divergence_rsi_strategy, self).OnStarted2(time)
self._has_prev = False
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = 12
macd.Macd.LongMa.Length = 26
macd.SignalMa.Length = 9
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, rsi, self._process_candle).Start()
def _process_candle(self, candle, macd_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal or not rsi_value.IsFinal:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
macd_main = float(macd_value.Macd)
signal = float(macd_value.Signal)
rsi_val = float(rsi_value)
if self._has_prev:
if self._prev_macd <= self._prev_signal and macd_main > signal and rsi_val < 40 and self.Position <= 0:
self.BuyMarket()
elif self._prev_macd >= self._prev_signal and macd_main < signal and rsi_val > 60 and self.Position >= 0:
self.SellMarket()
self._prev_macd = macd_main
self._prev_signal = signal
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return macd_divergence_rsi_strategy()