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= 12SlowLength= 26MaLength= 9SignalBar= 1TrendMode= DirectStopLossPercent= 1.0TakeProfitPercent= 2.0CandleType= 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class waddah_attar_trend_strategy(Strategy):
MODE_DIRECT = 0
MODE_REVERSE = 1
def __init__(self):
super(waddah_attar_trend_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast EMA Length", "Fast EMA period for MACD", "Indicator")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow EMA Length", "Slow EMA period for MACD", "Indicator")
self._ma_length = self.Param("MaLength", 9) \
.SetDisplay("MA Length", "Smoothing moving average period", "Indicator")
self._signal_bar = self.Param("SignalBar", 1) \
.SetDisplay("Signal Bar", "Bar offset for signal detection", "General")
self._trend_mode = self.Param("TrendMode", 0) \
.SetDisplay("Trend Mode", "0=Direct, 1=Reverse", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles used", "General")
self._prev_trend = 0.0
self._colors = []
self._buffer_index = 0
@property
def fast_length(self):
return self._fast_length.Value
@property
def slow_length(self):
return self._slow_length.Value
@property
def ma_length(self):
return self._ma_length.Value
@property
def signal_bar(self):
return self._signal_bar.Value
@property
def trend_mode(self):
return self._trend_mode.Value
@property
def stop_loss_percent(self):
return self._stop_loss_percent.Value
@property
def take_profit_percent(self):
return self._take_profit_percent.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(waddah_attar_trend_strategy, self).OnReseted()
self._prev_trend = 0.0
self._colors = []
self._buffer_index = 0
def OnStarted2(self, time):
super(waddah_attar_trend_strategy, self).OnStarted2(time)
sb = int(self.signal_bar)
self._colors = [0.0] * (sb + 2)
self._buffer_index = 0
self._prev_trend = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = int(self.fast_length)
slow_ema = ExponentialMovingAverage()
slow_ema.Length = int(self.slow_length)
ma = SimpleMovingAverage()
ma.Length = int(self.ma_length)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, ma, self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(float(self.take_profit_percent), UnitTypes.Percent),
stopLoss=Unit(float(self.stop_loss_percent), UnitTypes.Percent))
def process_candle(self, candle, fast, slow, ma_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast)
slow = float(slow)
ma_value = float(ma_value)
macd = fast - slow
trend = macd * ma_value
color = 0.0 if trend >= self._prev_trend else 1.0
buf_len = len(self._colors)
self._colors[self._buffer_index % buf_len] = color
self._buffer_index += 1
sb = int(self.signal_bar)
if self._buffer_index <= sb + 1:
self._prev_trend = trend
return
signal_index = (self._buffer_index - sb - 1) % buf_len
prev_signal_index = (self._buffer_index - sb - 2) % buf_len
signal_color = self._colors[signal_index]
prev_signal_color = self._colors[prev_signal_index]
tm = int(self.trend_mode)
if tm == self.MODE_DIRECT:
if prev_signal_color == 0.0 and signal_color > 0.0 and self.Position <= 0:
self.BuyMarket()
elif prev_signal_color == 1.0 and signal_color < 1.0 and self.Position >= 0:
self.SellMarket()
else:
if prev_signal_color == 1.0 and signal_color < 1.0 and self.Position <= 0:
self.BuyMarket()
elif prev_signal_color == 0.0 and signal_color > 0.0 and self.Position >= 0:
self.SellMarket()
self._prev_trend = trend
def CreateClone(self):
return waddah_attar_trend_strategy()