View on GitHub

X2MA Digit DM 361 Strategy

This strategy combines two moving averages with the Average Directional Index (ADX). A long position is opened when the fast moving average is above the slow moving average and the positive directional index (+DI) is greater than the negative directional index (-DI). A short position is opened when the fast moving average is below the slow moving average and -DI is greater than +DI.

The strategy uses percentage based stop-loss and take-profit protections. Candles for calculations are taken from the specified timeframe.

Parameters

  • Fast MA Length – length of the fast moving average.
  • Slow MA Length – length of the slow moving average.
  • ADX Length – period for Average Directional Index calculation.
  • Stop Loss % – stop-loss size in percent of entry price.
  • Take Profit % – take-profit size in percent of entry price.
  • Candle Type – candle timeframe used for processing.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;



/// <summary>
/// Strategy based on two moving averages and directional movement index.
/// Long when fast MA is above slow MA and +DI exceeds -DI.
/// Short when fast MA is below slow MA and -DI exceeds +DI.
/// </summary>
public class X2MaDigitDm361Strategy : Strategy
{
	private readonly StrategyParam<int> _fastMaLength;
	private readonly StrategyParam<int> _slowMaLength;
	private readonly StrategyParam<int> _adxLength;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<decimal> _takeProfitPercent;

	/// <summary>
	/// Fast moving average length.
	/// </summary>
	public int FastMaLength
	{
		get => _fastMaLength.Value;
		set => _fastMaLength.Value = value;
	}

	/// <summary>
	/// Slow moving average length.
	/// </summary>
	public int SlowMaLength
	{
		get => _slowMaLength.Value;
		set => _slowMaLength.Value = value;
	}

	/// <summary>
	/// ADX calculation length.
	/// </summary>
	public int AdxLength
	{
		get => _adxLength.Value;
		set => _adxLength.Value = value;
	}

	/// <summary>
	/// Candle type for strategy calculation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Stop loss percent.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

	/// <summary>
	/// Take profit percent.
	/// </summary>
	public decimal TakeProfitPercent
	{
		get => _takeProfitPercent.Value;
		set => _takeProfitPercent.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="X2MaDigitDm361Strategy"/>.
	/// </summary>
	public X2MaDigitDm361Strategy()
	{
		_fastMaLength = Param(nameof(FastMaLength), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA Length", "Length of fast moving average", "Moving Averages")
			
			.SetOptimize(5, 25, 1);

		_slowMaLength = Param(nameof(SlowMaLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA Length", "Length of slow moving average", "Moving Averages")
			
			.SetOptimize(3, 15, 1);

		_adxLength = Param(nameof(AdxLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("ADX Length", "Length of Average Directional Index", "Directional Movement")
			
			.SetOptimize(7, 28, 7);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles timeframe", "General");

		_stopLossPercent = Param(nameof(StopLossPercent), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss %", "Stop loss percent", "Risk Management")
			
			.SetOptimize(0.5m, 3m, 0.5m);

		_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit %", "Take profit percent", "Risk Management")
			
			.SetOptimize(1m, 5m, 0.5m);
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

		var fastMa = new SMA { Length = FastMaLength };
		var slowMa = new SMA { Length = SlowMaLength };
		var adx = new AverageDirectionalIndex { Length = AdxLength };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(fastMa, slowMa, adx, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(TakeProfitPercent, UnitTypes.Percent),
			stopLoss: new Unit(StopLossPercent, UnitTypes.Percent),
			isStopTrailing: false,
			useMarketOrders: true);

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastMa);
			DrawIndicator(area, slowMa);
			DrawIndicator(area, adx);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastMaValue, IIndicatorValue slowMaValue, IIndicatorValue adxValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (!fastMaValue.IsFinal || !slowMaValue.IsFinal || !adxValue.IsFinal)
			return;

		var fast = fastMaValue.ToDecimal();
		var slow = slowMaValue.ToDecimal();
		var adx = (AverageDirectionalIndexValue)adxValue;
		var plusDi = adx.Dx.Plus;
		var minusDi = adx.Dx.Minus;

		if (plusDi is null || minusDi is null)
			return;

		if (fast > slow && plusDi > minusDi && Position <= 0)
		{
			BuyMarket();
		}
		else if (fast < slow && minusDi > plusDi && Position >= 0)
		{
			SellMarket();
		}
	}
}