Ver no GitHub

Estratégia Exp BrainTrend2 AbsolutelyNoLagLwma X2MACandle MMRec

Visão geral

Esta estratégia recria o Consultor Especialista multi-módulo do MetaTrader combinando três filtros na API de alto nível do StockSharp:

  1. Inspiração BrainTrend2 – um canal Average True Range (ATR) detecta fases de contração e expansão de volatilidade.
  2. Aproximação AbsolutelyNoLagLwma – uma média móvil ponderada linearmente (LWMA) rastreia a direção dominante com mínimo lag.
  3. Réplica X2MACandle – um par de média móvil exponencial (EMA) rápida e lenta avalia a cor das velas para validar o momentum.

Uma posição é aberta apenas quando todos os filtros apontam na mesma direção. Os alvos de stop-loss e take-profit impulsionados por ATR gerenciam o processo de saída e emulam o conceito original de gestão monetária MMRec.

Lógica de trading

  • Configuração altista: a vela fecha acima da LWMA enquanto a EMA rápida está mais alta que a EMA lenta. Uma nova entrada comprada só é permitida após o viés altista anterior desaparecer, evitando ordens múltiplas em sinais idênticos.
  • Configuração baixista: a vela fecha abaixo da LWMA enquanto a EMA rápida está mais baixa que a EMA lenta. Posições vendidas obedecem as mesmas regras de confirmação e resfriamento que o lado comprado.
  • Gestão de risco: o ATR define níveis de saída dinâmicos. Tanto o stop-loss quanto o take-profit escalam com a volatilidade atual e são reavaliados em cada vela. Se o mercado tocar qualquer um dos níveis, a estratégia fecha toda a posição com uma ordem de mercado.

Parâmetros

Nome Descrição
CandleType Período da série de velas de trabalho. Padrão são velas de 6 horas para refletir os padrões originais do EA.
AtrPeriod Período de lookback usado pelo filtro de volatilidade ATR.
LwmaLength Período da média móvil ponderada linearmente para o filtro de tendência.
FastMaLength Período da EMA rápida usada para coloração de velas.
SlowMaLength Período da EMA lenta usada para coloração de velas.
StopLossAtrMultiplier Multiplicador aplicado ao ATR para calcular a distância do stop de proteção.
TakeProfitAtrMultiplier Multiplicador aplicado ao ATR para determinar a distância do take-profit.

Todos os parâmetros são expostos através de StrategyParam<T> para que possam ser otimizados dentro do StockSharp.

Notas

  • O Consultor Especialista original depende de buffers de indicadores proprietários. Este port usa indicadores padrão do StockSharp que reproduzem as mesmas dicas direcionais sem depender de scripts externos.
  • O gerenciamento monetário é simplificado para saídas de posição completa porque as estratégias do StockSharp tipicamente operam com ordens do tamanho do portfólio. As distâncias impulsionadas por ATR fornecem o comportamento adaptativo esperado do módulo MMRec.
  • Os comentários no código estão em inglês conforme exigido pelas diretrizes de conversão.
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;
		}
	}
}