Auf GitHub ansehen

Two MA Other TimeFrame Correct Intersection Strategy

Overview

This strategy is a StockSharp port of the MetaTrader 5 expert advisor "Two MA Other TimeFrame Correct Intersection". The original EA relies on two moving averages that are each calculated on their own timeframe (for example H1 vs D1) while trade decisions are synchronized to the chart timeframe. The conversion keeps the multi-timeframe behaviour and opens long positions when the fast moving average crosses above the slow moving average. Conversely, short positions are opened when the fast average crosses below the slow one. All orders are executed at market price and the strategy always closes any opposite exposure before opening a new trade, matching the engine-driven execution model of the MQL5 script.

Trading logic

  • Subscribe to three candle streams: the primary trading timeframe, the fast-MA timeframe and the slow-MA timeframe.
  • Compute the fast and slow moving averages on their dedicated timeframes. Each moving average supports the same smoothing methods and price sources that were exposed by the original iCustom indicator.
  • Optionally apply a configurable horizontal shift to the moving-average outputs before they are compared, reproducing the ma_shift inputs of the EA.
  • Every time a candle on the primary trading timeframe finishes, check for a crossover between the most recent and the previous moving-average values:
    • If the fast MA was below the slow MA on the previous step and is now above it, close any short position and open (or reverse into) a long position.
    • If the fast MA was above the slow MA on the previous step and is now below it, close any long position and open (or reverse into) a short position.
  • All entries use the configured trade volume. When reversing an existing position the strategy increases the order size by the magnitude of the opposite exposure to ensure the position flips in a single market order.

Parameters

Parameter Description
TradeVolume Base volume for market entries. Used for both long and short trades.
CandleType Primary trading timeframe. Signals are evaluated whenever a candle of this type closes.
FastTimeFrame Timeframe used to build the fast moving average.
SlowTimeFrame Timeframe used to build the slow moving average.
FastLength Number of bars included in the fast moving average.
SlowLength Number of bars included in the slow moving average.
FastShift Horizontal shift applied to the fast moving average output before comparison.
SlowShift Horizontal shift applied to the slow moving average output before comparison.
FastMethod Smoothing algorithm for the fast moving average (simple, exponential, smoothed or linear weighted).
SlowMethod Smoothing algorithm for the slow moving average.
FastAppliedPrice Candle price used by the fast moving average (open, high, low, close, median, typical or weighted).
SlowAppliedPrice Candle price used by the slow moving average.

Implementation notes

  • The moving averages are processed through StockSharp high-level subscriptions (SubscribeCandles().Bind(...)) and keep running even when the trading timeframe differs from the calculation timeframe.
  • Shift parameters are implemented with small queues that delay the indicator output by the requested number of bars, replicating the behaviour of the ma_shift inputs.
  • The strategy uses StartProtection() to align with StockSharp account protection utilities, just like the original trading engine that guarded open positions.
  • Chart rendering adds the primary candles together with the fast and slow moving averages so the crossover signals remain visible during backtests.
  • There is no stop-loss, take-profit or trailing-stop module in the original EA. Traders can combine this module with separate money-management strategies if additional risk control is required.
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Dual moving average crossover strategy.
/// Converted from the "Two MA Other TimeFrame Correct Intersection" MQL5 expert advisor.
/// Uses fast and slow SMA on a single timeframe with crossover signals.
/// </summary>
public class TwoMAOtherTimeFrameCorrectIntersectionStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;

	private ExponentialMovingAverage _fastMa;
	private ExponentialMovingAverage _slowMa;
	private decimal? _prevFast;
	private decimal? _prevSlow;

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public TwoMAOtherTimeFrameCorrectIntersectionStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("Candle Type", "Primary timeframe used for signal evaluation", "General");

		_fastLength = Param(nameof(FastLength), 20)
		.SetDisplay("Fast MA Length", "Number of bars for the fast moving average", "Indicators")
		.SetGreaterThanZero();

		_slowLength = Param(nameof(SlowLength), 50)
		.SetDisplay("Slow MA Length", "Number of bars for the slow moving average", "Indicators")
		.SetGreaterThanZero();
	}

	/// <summary>
	/// Primary candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Number of bars used by the fast moving average.
	/// </summary>
	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	/// <summary>
	/// Number of bars used by the slow moving average.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = null;
		_prevSlow = null;

		_fastMa = new ExponentialMovingAverage { Length = FastLength };
		_slowMa = new ExponentialMovingAverage { Length = SlowLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
		.Bind(_fastMa, _slowMa, ProcessCandle)
		.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _fastMa);
			DrawIndicator(area, _slowMa);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_fastMa.IsFormed || !_slowMa.IsFormed)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		if (_prevFast is null || _prevSlow is null)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Fast crosses above slow => buy
		if (_prevFast < _prevSlow && fastValue > slowValue)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		// Fast crosses below slow => sell
		else if (_prevFast > _prevSlow && fastValue < slowValue)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_prevFast = null;
		_prevSlow = null;
		_fastMa = null;
		_slowMa = null;

		base.OnReseted();
	}
}