Ver en GitHub

X Trader Strategy

This strategy implements a contrarian moving average cross system originally written in MQL as X trader. It uses two simple moving averages and opens positions opposite to the direction of the cross. Risk is managed using fixed take‑profit and stop‑loss in absolute points via StartProtection.

How it works

  1. Subscribe to candle data of the specified time frame.
  2. Calculate two moving averages with configurable periods.
  3. Track the last two values of each average to detect a cross.
  4. When the fast average crosses above the slow average and remains above for two bars while two bars ago it was below, a short position is opened.
  5. When the fast average crosses below the slow average and remains below for two bars while two bars ago it was above, a long position is opened.
  6. Only one position may be open at a time. Protection automatically exits trades when price moves by the configured take‑profit or stop‑loss amount.

Parameters

  • CandleType – candle series to use.
  • Ma1Period – period of the first moving average.
  • Ma2Period – period of the second moving average.
  • TakeProfitPoints – profit target in price points.
  • StopLossPoints – loss limit in price points.

Indicator

  • SimpleMovingAverage – used twice with different periods.

Risk Management

StartProtection is enabled in OnStarted and applies the take‑profit and stop‑loss values to all positions.

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>
/// X Trader Strategy - contrarian moving average cross.
/// </summary>
public class XTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _ma1Period;
	private readonly StrategyParam<int> _ma2Period;

	private decimal _ma1Prev;
	private decimal _ma1Prev2;
	private decimal _ma2Prev;
	private decimal _ma2Prev2;
	private bool _hasPrev2;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int Ma1Period { get => _ma1Period.Value; set => _ma1Period.Value = value; }
	public int Ma2Period { get => _ma2Period.Value; set => _ma2Period.Value = value; }

	public XTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_ma1Period = Param(nameof(Ma1Period), 16)
			.SetGreaterThanZero()
			.SetDisplay("MA1 Period", "Period of the first moving average", "Parameters");

		_ma2Period = Param(nameof(Ma2Period), 10)
			.SetGreaterThanZero()
			.SetDisplay("MA2 Period", "Period of the second moving average", "Parameters");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_ma1Prev = 0;
		_ma1Prev2 = 0;
		_ma2Prev = 0;
		_ma2Prev2 = 0;
		_hasPrev2 = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ma1 = new ExponentialMovingAverage { Length = Ma1Period };
		var ma2 = new ExponentialMovingAverage { Length = Ma2Period };

		SubscribeCandles(CandleType)
			.Bind(ma1, ma2, ProcessCandle)
			.Start();
	}

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

		if (_ma1Prev == 0)
		{
			_ma1Prev = ma1Value;
			_ma2Prev = ma2Value;
			return;
		}

		if (!_hasPrev2)
		{
			_ma1Prev2 = _ma1Prev;
			_ma2Prev2 = _ma2Prev;
			_ma1Prev = ma1Value;
			_ma2Prev = ma2Value;
			_hasPrev2 = true;
			return;
		}

		// Contrarian: sell when MA1 crosses above MA2, buy when MA1 crosses below
		var sellSignal = ma1Value > ma2Value && _ma1Prev > _ma2Prev && _ma1Prev2 < _ma2Prev2;
		var buySignal = ma1Value < ma2Value && _ma1Prev < _ma2Prev && _ma1Prev2 > _ma2Prev2;

		if (sellSignal && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}
		else if (buySignal && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}

		_ma1Prev2 = _ma1Prev;
		_ma2Prev2 = _ma2Prev;
		_ma1Prev = ma1Value;
		_ma2Prev = ma2Value;
	}
}