This strategy is a C# conversion of the "Macd diver and rsi" MetaTrader 5 expert advisor. It keeps the original two-stage signal idea: the Relative Strength Index (RSI) detects oversold or overbought extremes, while the MACD histogram confirms that momentum is turning back in the direction of the trade. Long and short sides are configured independently so the behaviour can be tuned for bullish and bearish setups separately.
The strategy operates on a single candle subscription (configurable timeframe) and trades the charted security directly through market orders. All indicator processing uses the high-level StockSharp API via BindEx, matching the project rules.
Trading Logic
Indicator preparation
Two RSI indicators are created, one for the long leg and one for the short leg, with individual lengths and thresholds.
Two MovingAverageConvergenceDivergenceSignal indicators mirror the MACD settings for long and short trades. Their histogram component is used to confirm momentum reversals.
Entry rules
Long setup: when the long RSI value is at or below the oversold threshold and the long MACD histogram crosses above zero (changes sign from negative to positive), a bullish position is opened. If a short position is active it is closed and reversed in the same market order.
Short setup: when the short RSI value is at or above the overbought threshold and the short MACD histogram crosses below zero, a bearish position is opened. Existing long exposure is flattened before the new short is established.
Risk management
After each entry the strategy records the close price of the signal bar as the reference price.
Stop-loss and take-profit levels are projected from that price using pip distances defined separately for long and short trades.
Pips are converted to price units with the instrument PriceStep, and automatically scaled by 10 for symbols with 3 or 5 decimals to mirror MT5 behaviour.
On every completed candle the high/low range is checked against these levels. Hitting either level immediately closes the position with a market order.
Trade management
The position state is cleared whenever the position size returns to zero (either because a stop/take-profit was reached or the strategy was reversed by an opposite signal).
No partial exits or trailing adjustments are performed; the position is managed only via the static stop-loss and take-profit levels.
Parameters
Parameter
Description
CandleType
Timeframe of the candle subscription used for signals.
LongRsiPeriod, ShortRsiPeriod
RSI lengths for long and short detection.
LongRsiThreshold, ShortRsiThreshold
RSI thresholds that enable entries (oversold for longs, overbought for shorts).
Distance of stop-loss and take-profit orders in pips. Zero disables the respective level.
Notes
The strategy requires instruments with a non-zero PriceStep. If the step is missing the pip calculation falls back to 0.0001 to prevent division-by-zero.
Because both sides use independent indicator instances, you can tune bullish and bearish behaviour separately, for example by tightening the overbought threshold while keeping the oversold side more permissive.
The code adds English comments and documentation to clarify the trading process and satisfy the project guidelines.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MACD Diver And RSI strategy: RSI extremes + MACD histogram crossover.
/// Buys when RSI below 30 and MACD histogram crosses above zero.
/// Sells when RSI above 70 and MACD histogram crosses below zero.
/// </summary>
public class MacdDiverAndRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private decimal _prevHistogram;
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 MacdDiverAndRsiStrategy()
{
_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();
_prevHistogram = 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 histogram = macdMain - signal;
var rsi = rsiValue.ToDecimal();
if (_hasPrev)
{
if (_prevHistogram <= 0 && histogram > 0 && _prevRsi < 35 && Position <= 0)
BuyMarket();
else if (_prevHistogram >= 0 && histogram < 0 && _prevRsi > 65 && Position >= 0)
SellMarket();
}
_prevHistogram = histogram;
_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_diver_and_rsi_strategy(Strategy):
def __init__(self):
super(macd_diver_and_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._prev_histogram = 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_diver_and_rsi_strategy, self).OnReseted()
self._prev_histogram = 0.0
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(macd_diver_and_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)
histogram = macd_main - signal
rsi_val = float(rsi_value)
if self._has_prev:
if self._prev_histogram <= 0 and histogram > 0 and self._prev_rsi < 35 and self.Position <= 0:
self.BuyMarket()
elif self._prev_histogram >= 0 and histogram < 0 and self._prev_rsi > 65 and self.Position >= 0:
self.SellMarket()
self._prev_histogram = histogram
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return macd_diver_and_rsi_strategy()