The strategy recreates the behaviour of the MetaTrader 5 expert advisor Exp_RJTX_Matches_Smoothed_Duplex.mq5. Two independent RJTX signal blocks analyse smoothed open and close prices on their respective time-frames. Each block classifies every finished candle as bullish or bearish depending on whether the smoothed close rises above the smoothed open from Period bars ago. Bullish "matches" trigger entries for the long module, while bearish matches manage the short module.
Signal generation
Smoothing – both blocks feed candle opens and closes into the selected smoothing algorithm. The same method is applied to open and close streams but separate instances are used to keep the internal buffers independent.
Comparison – once enough history is available, the current smoothed close is compared with the smoothed open recorded Period bars earlier.
Match detection – if the close is greater, the candle receives a bullish match; otherwise it becomes bearish. Signals are evaluated after shifting by SignalBar closed candles, just like the MT5 buffer access.
Position management
The long block opens a long position (covering any existing short if allowed) when a bullish match reaches the evaluation window. A bearish match closes the long position if long exits are enabled.
The short block mirrors this logic: a bearish match opens a short trade (closing long exposure if permitted) and a bullish match covers the short.
StockSharp strategies are netted. Therefore, opposite modules close the current position before opening a new one, instead of maintaining two independent hedged positions like the MT5 version. Disable the corresponding Allow ... Close parameter to forbid automatic covering.
Risk management
Stops and profit targets are expressed in price steps (PriceStep × points). For every finished candle the strategy checks whether the bar range touches the active stop-loss or take-profit level and closes the corresponding position immediately. This emulates the behaviour of MT5 protective orders without relying on broker-managed orders.
Parameters
Section
Parameter
Default
Description
Long
LongCandleType
H4
Time-frame used by the long RJTX block.
Long
LongVolume
0.1
Volume opened when a long signal is executed.
Long
LongAllowOpen
true
Enable opening long positions.
Long
LongAllowClose
true
Enable closing long positions on bearish matches.
Long
LongStopLossPoints
1000
Stop-loss distance for long trades in price steps (0 disables the check).
Long
LongTakeProfitPoints
2000
Take-profit distance for long trades in price steps (0 disables the check).
Long
LongSignalBar
1
Shift applied when reading RJTX buffers (0 = current closed candle).
Long
LongPeriod
10
Number of bars between the current smoothed close and the historical smoothed open.
Long
LongMethod
Sma
Smoothing algorithm used for the long block (Sma, Ema, Smma, Lwma, Jjma, Jurx, Parma, T3, Vidya, Ama).
Long
LongLength
12
Length of the smoothing filter applied to open/close series.
Long
LongPhase
15
Phase parameter for Jurik-style filters (kept for compatibility).
Short
ShortCandleType
H4
Time-frame used by the short RJTX block.
Short
ShortVolume
0.1
Volume opened when a short signal is executed.
Short
ShortAllowOpen
true
Enable opening short positions.
Short
ShortAllowClose
true
Enable closing short positions on bullish matches.
Short
ShortStopLossPoints
1000
Stop-loss distance for short trades in price steps (0 disables the check).
Short
ShortTakeProfitPoints
2000
Take-profit distance for short trades in price steps (0 disables the check).
Short
ShortSignalBar
1
Shift applied when reading RJTX buffers for the short block.
Short
ShortPeriod
10
Number of bars between the current smoothed close and the historical smoothed open.
Short
ShortMethod
Sma
Smoothing algorithm used for the short block.
Short
ShortLength
12
Length of the smoothing filter applied to short signals.
Short
ShortPhase
15
Phase parameter for Jurik-style filters in the short block.
Notes
Jjma maps to the Jurik Moving Average. Jurx, Parma, and Vidya are approximated with Zero-Lag EMA, Arnaud Legoux MA, and EMA respectively because StockSharp does not expose identical filters from the SmoothAlgorithms library.
The stop-loss / take-profit logic is evaluated on candle extremes. Intrabar spikes shorter than the candle's high/low will not trigger exits.
Signals are processed on finished candles only; intrabar matches are ignored in line with the MT5 IsNewBar behaviour.
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>
/// Exp RJTX Matches Smoothed Duplex strategy using SmoothedMA crossover.
/// Buys when fast SmoothedMA crosses above slow SmoothedMA.
/// Sells on reverse crossover.
/// </summary>
public class ExpRjtxMatchesSmoothedDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SmoothedMovingAverage _fast;
private SmoothedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast smoothed MA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow smoothed MA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpRjtxMatchesSmoothedDuplexStrategy"/> class.
/// </summary>
public ExpRjtxMatchesSmoothedDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast smoothed MA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow smoothed MA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SmoothedMovingAverage { Length = FastPeriod };
_slow = new SmoothedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SmoothedMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_rjtx_matches_smoothed_duplex_strategy(Strategy):
"""
SmoothedMA crossover strategy with SL/TP and cooldown.
Buys when fast SmoothedMA crosses above slow, sells on reverse.
"""
def __init__(self):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast smoothed MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow smoothed MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).OnStarted2(time)
fast = SmoothedMovingAverage()
fast.Length = self._fast_period.Value
slow = SmoothedMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_rjtx_matches_smoothed_duplex_strategy()