Auf GitHub ansehen

Exp BrainTrend2 AbsolutelyNoLagLwma X2MACandle MMRec Strategy

Overview

This strategy recreates the multi-module MetaTrader Expert Advisor by combining three filters in the StockSharp high level API:

  1. BrainTrend2 inspiration – an Average True Range (ATR) channel detects volatility contraction and expansion phases.
  2. AbsolutelyNoLagLwma approximation – a linear weighted moving average (LWMA) tracks the dominant direction with minimal lag.
  3. X2MACandle replica – a fast and a slow exponential moving average (EMA) pair evaluates candle colour to validate momentum.

A position is opened only when all filters point in the same direction. ATR driven stop-loss and take-profit targets manage the exit process and emulate the original MMRec money management concept.

Trading Logic

  • Bullish setup: the candle closes above the LWMA while the fast EMA is higher than the slow EMA. A fresh long entry is allowed only after the previous bullish bias disappeared, preventing multiple orders on identical signals.
  • Bearish setup: the candle closes below the LWMA while the fast EMA is lower than the slow EMA. Short positions obey the same confirmation and cooldown rules as the long side.
  • Risk management: ATR defines dynamic exit levels. Both stop-loss and take-profit scale with the current volatility and are re-evaluated on every candle. If the market touches either level, the strategy closes the entire position by market order.

Parameters

Name Description
CandleType Timeframe of the working candle series. Defaults to 6-hour candles to mirror the original EA defaults.
AtrPeriod Lookback period used by the ATR volatility filter.
LwmaLength Period of the linear weighted moving average trend filter.
FastMaLength Period of the fast EMA used for candle colouring.
SlowMaLength Period of the slow EMA used for candle colouring.
StopLossAtrMultiplier Multiplier applied to ATR to calculate the protective stop distance.
TakeProfitAtrMultiplier Multiplier applied to ATR to determine the take-profit distance.

All parameters are exposed through StrategyParam<T> so that they can be optimised inside StockSharp.

Notes

  • The original Expert Advisor relies on proprietary indicator buffers. This port uses standard StockSharp indicators that reproduce the same directional cues without relying on external scripts.
  • Money management is simplified to full-position exits because StockSharp strategies typically operate on portfolio-sized orders. The ATR driven distances provide the adaptive behaviour expected from the MMRec module.
  • Commentary in the code is in English as required by the conversion guidelines.
namespace StockSharp.Samples.Strategies;

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;

/// <summary>
/// Hybrid strategy that combines ATR based breakout confirmation with LWMA and EMA filters.
/// </summary>
public class ExpBrainTrend2AbsolutelyNoLagLwmaX2MACandleMmrecStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<int> _lwmaLength;
	private readonly StrategyParam<int> _fastMaLength;
	private readonly StrategyParam<int> _slowMaLength;
	private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
	private readonly StrategyParam<decimal> _takeProfitAtrMultiplier;

	private AverageTrueRange _atr;
	private WeightedMovingAverage _lwma;
	private ExponentialMovingAverage _fastEma;
	private ExponentialMovingAverage _slowEma;

	private bool _allowLongSignal;
	private bool _allowShortSignal;
	private decimal? _longEntryPrice;
	private decimal? _shortEntryPrice;

	/// <summary>
	/// Initializes a new instance of <see cref="ExpBrainTrend2AbsolutelyNoLagLwmaX2MACandleMmrecStrategy"/>.
	/// </summary>
	public ExpBrainTrend2AbsolutelyNoLagLwmaX2MACandleMmrecStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(6).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle series for the strategy", "General");

		_atrPeriod = Param(nameof(AtrPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "Average True Range lookback", "Indicators")
			
			.SetOptimize(5, 20, 1);

		_lwmaLength = Param(nameof(LwmaLength), 7)
			.SetGreaterThanZero()
			.SetDisplay("LWMA Length", "Linear weighted moving average length", "Indicators")
			
			.SetOptimize(5, 25, 1);

		_fastMaLength = Param(nameof(FastMaLength), 9)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA length used by the candle filter", "Indicators")
			
			.SetOptimize(5, 30, 1);

		_slowMaLength = Param(nameof(SlowMaLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA length used by the candle filter", "Indicators")
			
			.SetOptimize(10, 60, 2);

		_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss (ATR)", "Multiplier applied to ATR for protective stop", "Risk Management")
			
			.SetOptimize(1m, 4m, 0.5m);

		_takeProfitAtrMultiplier = Param(nameof(TakeProfitAtrMultiplier), 3m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit (ATR)", "Multiplier applied to ATR for profit target", "Risk Management")
			
			.SetOptimize(1.5m, 6m, 0.5m);
	}

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

	/// <summary>
	/// ATR period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// LWMA period.
	/// </summary>
	public int LwmaLength
	{
		get => _lwmaLength.Value;
		set => _lwmaLength.Value = value;
	}

	/// <summary>
	/// Fast EMA period used by the candle filter.
	/// </summary>
	public int FastMaLength
	{
		get => _fastMaLength.Value;
		set => _fastMaLength.Value = value;
	}

	/// <summary>
	/// Slow EMA period used by the candle filter.
	/// </summary>
	public int SlowMaLength
	{
		get => _slowMaLength.Value;
		set => _slowMaLength.Value = value;
	}

	/// <summary>
	/// Stop loss multiplier expressed in ATR units.
	/// </summary>
	public decimal StopLossAtrMultiplier
	{
		get => _stopLossAtrMultiplier.Value;
		set => _stopLossAtrMultiplier.Value = value;
	}

	/// <summary>
	/// Take profit multiplier expressed in ATR units.
	/// </summary>
	public decimal TakeProfitAtrMultiplier
	{
		get => _takeProfitAtrMultiplier.Value;
		set => _takeProfitAtrMultiplier.Value = value;
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

			_allowLongSignal = false;
			_allowShortSignal = false;
		_longEntryPrice = null;
		_shortEntryPrice = null;
	}

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

		_atr = new AverageTrueRange
		{
			Length = AtrPeriod
		};

		_lwma = new WeightedMovingAverage
		{
			Length = LwmaLength
		};

		_fastEma = new EMA
		{
			Length = FastMaLength
		};

		_slowEma = new EMA
		{
			Length = SlowMaLength
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_atr, _lwma, _fastEma, _slowEma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _lwma);
			DrawIndicator(area, _fastEma);
			DrawIndicator(area, _slowEma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal atrValue, decimal lwmaValue, decimal fastEmaValue, decimal slowEmaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (!_atr.IsFormed || !_lwma.IsFormed || !_fastEma.IsFormed || !_slowEma.IsFormed)
			return;

		var close = candle.ClosePrice;
		var high = candle.HighPrice;
		var low = candle.LowPrice;

		var bullishFilter = close > lwmaValue && fastEmaValue > slowEmaValue;
		var bearishFilter = close < lwmaValue && fastEmaValue < slowEmaValue;

		if (!bullishFilter)
			_allowLongSignal = true;

		if (!bearishFilter)
			_allowShortSignal = true;

		if (bullishFilter && Position <= 0 && _allowLongSignal)
		{
			BuyMarket(Volume + Math.Abs(Position));
			_allowLongSignal = false;
			_allowShortSignal = false;
		}
		else if (bearishFilter && Position >= 0 && _allowShortSignal)
		{
			SellMarket(Volume + Math.Abs(Position));
			_allowShortSignal = false;
			_allowLongSignal = false;
		}

		var atr = atrValue;

		if (Position > 0 && _longEntryPrice is decimal longPrice)
		{
			var stopPrice = longPrice - atr * StopLossAtrMultiplier;
			var targetPrice = longPrice + atr * TakeProfitAtrMultiplier;

			if (low <= stopPrice)
			{
				SellMarket(Position);
				_longEntryPrice = null;
			}
			else if (high >= targetPrice)
			{
				SellMarket(Position);
				_longEntryPrice = null;
			}
		}
		else if (Position < 0 && _shortEntryPrice is decimal shortPrice)
		{
			var stopPrice = shortPrice + atr * StopLossAtrMultiplier;
			var targetPrice = shortPrice - atr * TakeProfitAtrMultiplier;

			if (high >= stopPrice)
			{
				BuyMarket(-Position);
				_shortEntryPrice = null;
			}
			else if (low <= targetPrice)
			{
				BuyMarket(-Position);
				_shortEntryPrice = null;
			}
		}
	}

	/// <inheritdoc />
	protected override void OnOwnTradeReceived(MyTrade trade)
	{
		base.OnOwnTradeReceived(trade);

		if (trade.Order.Side == Sides.Buy)
		{
			_longEntryPrice = trade.Trade?.Price;
			if (Position <= 0)
				_shortEntryPrice = null;
		}
		else if (trade.Order.Side == Sides.Sell)
		{
			_shortEntryPrice = trade.Trade?.Price;
			if (Position >= 0)
				_longEntryPrice = null;
		}

		if (Position == 0)
		{
			_longEntryPrice = null;
			_shortEntryPrice = null;
		}
	}
}