Ver no GitHub

Waddah Attar Trend

This strategy converts the original MQL expert "Exp_Waddah_Attar_Trend" into the StockSharp high-level API. It uses the Waddah Attar Trend indicator, which multiplies the difference between two exponential moving averages (fast and slow) by an additional smoothing moving average. The indicator outputs a color state: green when the trend value rises and magenta when it falls. A change of this color triggers trades.

Long positions are opened when the color switches from down to up. Short positions are opened when it switches from up to down. The strategy works on both sides and supports protective stop-loss and take-profit expressed as percentages of the entry price.

Details

  • Entry Criteria: Color change of Waddah Attar Trend (MACD difference multiplied by MA).
  • Long/Short: Both directions.
  • Exit Criteria: Opposite color change or protective stops.
  • Stops: Yes.
  • Default Values:
    • FastLength = 12
    • SlowLength = 26
    • MaLength = 9
    • SignalBar = 1
    • TrendMode = Direct
    • StopLossPercent = 1.0
    • TakeProfitPercent = 2.0
    • CandleType = TimeSpan.FromHours(4)
  • Filters:
    • Category: Trend
    • Direction: Both
    • Indicators: MACD, MA
    • Stops: Yes
    • Complexity: Intermediate
    • Timeframe: H4
    • Seasonality: No
    • Neural Networks: No
    • Divergence: No
    • Risk Level: Medium
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;

using StockSharp.Algo;
using StockSharp.Algo.Candles;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on the Waddah Attar Trend indicator.
/// Opens a long position when the trend color changes from down to up and
/// opens a short position when the color switches from up to down.
/// </summary>
public class WaddahAttarTrendStrategy : Strategy
{
	public enum TrendModes
	{
		/// <summary>
		/// Direct mode: buy on color change to up, sell on color change to down.
		/// </summary>
		Direct,
		/// <summary>
		/// Reverse mode: buy on color change to down, sell on color change to up.
		/// </summary>
		Reverse
	}

	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _maLength;
	private readonly StrategyParam<int> _signalBar;
	private readonly StrategyParam<TrendModes> _trendMode;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<decimal> _takeProfitPercent;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevTrend;
	private decimal[] _colors = Array.Empty<decimal>();
	private int _bufferIndex;

	/// <summary>
	/// Fast ExponentialMovingAverage length for MACD.
	/// </summary>
	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	/// <summary>
	/// Slow ExponentialMovingAverage length for MACD.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	/// <summary>
	/// Smoothing moving average length.
	/// </summary>
	public int MaLength
	{
		get => _maLength.Value;
		set => _maLength.Value = value;
	}

	/// <summary>
	/// Number of bars back used to detect signals.
	/// </summary>
	public int SignalBar
	{
		get => _signalBar.Value;
		set => _signalBar.Value = value;
	}

	/// <summary>
	/// Determines how indicator colors are interpreted.
	/// </summary>
	public TrendModes TrendMode
	{
		get => _trendMode.Value;
		set => _trendMode.Value = value;
	}

	/// <summary>
	/// Stop loss percentage from entry price.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

	/// <summary>
	/// Take profit percentage from entry price.
	/// </summary>
	public decimal TakeProfitPercent
	{
		get => _takeProfitPercent.Value;
		set => _takeProfitPercent.Value = value;
	}

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

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public WaddahAttarTrendStrategy()
	{
		_fastLength = Param(nameof(FastLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast ExponentialMovingAverage Length", "Fast ExponentialMovingAverage period for MACD", "Indicator")
			
			.SetOptimize(5, 20, 1);

		_slowLength = Param(nameof(SlowLength), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow ExponentialMovingAverage Length", "Slow ExponentialMovingAverage period for MACD", "Indicator")
			
			.SetOptimize(20, 40, 1);

		_maLength = Param(nameof(MaLength), 9)
			.SetGreaterThanZero()
			.SetDisplay("MA Length", "Smoothing moving average period", "Indicator")
			
			.SetOptimize(5, 20, 1);

		_signalBar = Param(nameof(SignalBar), 1)
			.SetGreaterThanZero()
			.SetDisplay("Signal Bar", "Bar offset for signal detection", "General");

		_trendMode = Param(nameof(TrendMode), TrendModes.Direct)
			.SetDisplay("Trend Mode", "Interpretation of indicator colors", "General");

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

		_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit %", "Take profit percentage", "Risk Management")
			
			.SetOptimize(1m, 10m, 1m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles used", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevTrend = 0m;
		_colors = Array.Empty<decimal>();
		_bufferIndex = 0;
	}

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

		_colors = new decimal[SignalBar + 2];
		_bufferIndex = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowLength };
		var ma = new SimpleMovingAverage { Length = MaLength };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(fastEma, slowEma, ma, ProcessCandle)
			.Start();

		StartProtection(new Unit(TakeProfitPercent, UnitTypes.Percent), new Unit(StopLossPercent, UnitTypes.Percent));
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal maValue)
	{
		// Work only with finished candles
		if (candle.State != CandleStates.Finished)
			return;

		// Ensure strategy is ready to trade
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macd = fast - slow;
		var trend = macd * maValue;
		var color = trend >= _prevTrend ? 0m : 1m;

		_colors[_bufferIndex % _colors.Length] = color;
		_bufferIndex++;

		if (_bufferIndex <= SignalBar + 1)
		{
			_prevTrend = trend;
			return;
		}

		var signalIndex = (_bufferIndex - SignalBar - 1) % _colors.Length;
		var prevSignalIndex = (_bufferIndex - SignalBar - 2) % _colors.Length;

		var signalColor = _colors[signalIndex];
		var prevSignalColor = _colors[prevSignalIndex];

		if (TrendMode == TrendModes.Direct)
		{
			if (prevSignalColor == 0m && signalColor > 0m && Position <= 0)
				BuyMarket();
			else if (prevSignalColor == 1m && signalColor < 1m && Position >= 0)
				SellMarket();
		}
		else
		{
			if (prevSignalColor == 1m && signalColor < 1m && Position <= 0)
				BuyMarket();
			else if (prevSignalColor == 0m && signalColor > 0m && Position >= 0)
				SellMarket();
		}

		_prevTrend = trend;
	}
}