This strategy replicates the Exp_XRSIDeMarker_Histogram expert advisor. It trades reversals detected by a custom oscillator that blends a Relative Strength Index (RSI) with the DeMarker indicator and then smooths the result. The system can open or close long and short trades independently, and optional protective stops expressed in price steps are supported.
Indicator construction
Applied price – the RSI is calculated on the selected input (close, open, high, low, median, typical or weighted price) using the configured period.
DeMarker component – for each finished candle, the strategy measures the upward (deMax) and downward (deMin) pressure:
deMax = max(High_t - High_{t-1}, 0)
deMin = max(Low_{t-1} - Low_t, 0)
Both series are smoothed with a simple moving average whose length matches the RSI period.
DeMarker = deMaxAvg / (deMaxAvg + deMinAvg) (scaled to the 0–100 range).
Composite oscillator – the final value is (RSI + 100 * DeMarker) / 2.
Smoothing – the composite oscillator is passed through one of the supported moving averages (SMA, EMA, SMMA, LWMA or Jurik). If an unsupported smoothing mode from the original MQL version is selected, the indicator falls back to an EMA with the requested length. The Jurik option also honours the phase parameter.
Signal history – the strategy stores historical values and evaluates signals on the bar defined by SignalBar, mimicking the original EA which waited for the next candle before taking trades.
Trading logic
Bullish reversal
Condition: value at SignalBar+1 is below SignalBar+2 (down-slope) and the value at SignalBar turns back up (>=).
Actions:
Close existing short trades when CloseShortOnLongSignal is true.
Open a new long trade with TradeVolume (plus any quantity required to flip from a short) when AllowBuyEntries is enabled.
Bearish reversal
Condition: value at SignalBar+1 is above SignalBar+2 (up-slope) and the value at SignalBar turns down (<=).
Actions:
Close existing long trades when CloseLongOnShortSignal is true.
Open a new short trade when AllowSellEntries is enabled.
Signals are ignored until the indicator and DeMarker components are fully formed, and orders are placed only when the strategy is online and trading is permitted.
Risk management
StopLossTicks and TakeProfitTicks represent distances in price steps. The strategy multiplies these values by Security.PriceStep (falling back to 1 if the instrument step is unknown) and closes the position when the distance is reached inside the candle range.
Passing 0 disables the respective protection.
The TradeVolume parameter is used as the default order size and also to compute reversals (the opposite position is closed before a new one is opened).
Parameters
Parameter
Description
Default
TradeVolume
Volume used when opening new positions.
0.1
StopLossTicks
Protective stop in price steps.
1000
TakeProfitTicks
Profit target in price steps.
2000
AllowBuyEntries
Enable/disable entering long trades.
true
AllowSellEntries
Enable/disable entering short trades.
true
CloseLongOnShortSignal
Close longs when a short signal appears.
true
CloseShortOnLongSignal
Close shorts when a long signal appears.
true
CandleType
Timeframe used for analysis (default 4-hour candles).
H4
IndicatorPeriod
Look-back for both RSI and DeMarker components.
14
AppliedPriceSelection
Applied price used by the RSI calculation.
Close
SmoothingMethodSelection
Moving average used for smoothing (SMA/EMA/SMMA/LWMA/Jurik/Adaptive).
Sma
SmoothingLength
Period of the smoothing average.
5
SmoothingPhase
Phase argument passed to Jurik smoothing.
15
SignalBar
Number of closed bars back used for signal evaluation.
1
Notes vs. original EA
Money management modes from the MQL version (balance-based, free-margin-based, etc.) are replaced with a direct TradeVolume parameter.
Order slippage (Deviation) is not required because StockSharp uses market orders.
Advanced smoothing algorithms (Parabolic MA, T3, VIDYA, AMA) are not available in StockSharp and are mapped to the EMA via the Adaptive option.
All comments in the C# source are written in English, and the logic runs only on finished candles just like the original implementation.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// XRSI DeMarker Histogram strategy (simplified).
/// Uses RSI combined with momentum to detect reversals.
/// Buys on RSI reversal from oversold, sells on reversal from overbought.
/// </summary>
public class XrsidDeMarkerHistogramStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _smaPeriod;
private decimal _prevRsi;
private decimal _prevPrevRsi;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public XrsidDeMarkerHistogramStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI indicator period", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "Smoothing period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0m;
_prevPrevRsi = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_prevPrevRsi = 0;
_initialized = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, sma, (ICandleMessage candle, decimal rsiValue, decimal smaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!_initialized)
{
_prevPrevRsi = rsiValue;
_prevRsi = rsiValue;
_initialized = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevPrevRsi = _prevRsi;
_prevRsi = rsiValue;
return;
}
// Buy on RSI reversal from oversold (V-bottom)
var buySignal = _prevPrevRsi > _prevRsi && rsiValue >= _prevRsi && _prevRsi < 35m;
// Sell on RSI reversal from overbought (inverse V)
var sellSignal = _prevPrevRsi < _prevRsi && rsiValue <= _prevRsi && _prevRsi > 65m;
if (buySignal && Position <= 0)
{
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
SellMarket();
}
_prevPrevRsi = _prevRsi;
_prevRsi = rsiValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, sma);
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class xrsid_de_marker_histogram_strategy(Strategy):
def __init__(self):
super(xrsid_de_marker_histogram_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI indicator period", "Indicators")
self._sma_period = self.Param("SmaPeriod", 5) \
.SetDisplay("SMA Period", "Smoothing period", "Indicators")
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def SmaPeriod(self):
return self._sma_period.Value
def OnReseted(self):
super(xrsid_de_marker_histogram_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
def OnStarted2(self, time):
super(xrsid_de_marker_histogram_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
sma = SimpleMovingAverage()
sma.Length = self.SmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(rsi, sma, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, sma_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._initialized:
self._prev_prev_rsi = rv
self._prev_rsi = rv
self._initialized = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_prev_rsi = self._prev_rsi
self._prev_rsi = rv
return
buy_signal = self._prev_prev_rsi > self._prev_rsi and rv >= self._prev_rsi and self._prev_rsi < 35.0
sell_signal = self._prev_prev_rsi < self._prev_rsi and rv <= self._prev_rsi and self._prev_rsi > 65.0
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._prev_prev_rsi = self._prev_rsi
self._prev_rsi = rv
def CreateClone(self):
return xrsid_de_marker_histogram_strategy()