Ver en GitHub

Exp RJTX Matches Smoothed Duplex

Overview

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

  1. 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.
  2. Comparison – once enough history is available, the current smoothed close is compared with the smoothed open recorded Period bars earlier.
  3. 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;
	}
}