Ver no GitHub

Estratégia Ma SAR ADX Bind

Visão geral

Esta estratégia é uma conversão para a API de alto nível do StockSharp do consultor especialista original MaSarADX.mq5 do MetaTrader 5. O sistema combina um filtro de tendência de média móvel simples com sinais do Índice de Movimento Direcional (ADX) e o trailing stop Parabolic SAR. As decisões de trading são avaliadas apenas em candles concluídos, replicando o comportamento do "primeiro tick de uma nova barra" da versão MQL. Quando o fechamento do candle está alinhado tanto com a tendência da média móvel quanto com o equilíbrio direcional do ADX, uma posição é aberta. O Parabolic SAR orienta tanto a direção do trade quanto as saídas forçando uma liquidação total quando o preço cruza para o lado oposto dos pontos SAR.

Indicadores e dados

  • Média Móvel Simples (SMA) – fornece o filtro de direção de tendência primária. Comprimento padrão: 100 candles.
  • Índice Direcional Médio (ADX) – fornece +DI e −DI para confirmar a força direcional. Comprimento padrão: 14.
  • Parabolic SAR – atua como uma sobreposição de stop-and-reverse e define as condições de saída. Aceleração padrão: 0.02; aceleração máxima: 0.10.
  • Candles – qualquer timeframe pode ser solicitado. Por padrão a estratégia assina candles de 1 hora, mas o parâmetro pode ser ajustado para corresponder ao símbolo e regime de teste.

A implementação assina fluxos de candles do StockSharp e vincula os três indicadores usando o helper BindEx de modo que cada callback recebe valores sincronizados para o mesmo candle.

Lógica de trading

Entrada comprada

  1. O fechamento do candle está acima da média móvel.
  2. +DI é maior ou igual a −DI, indicando pressão direcional de alta.
  3. O fechamento do candle está acima do valor do Parabolic SAR.
  4. Não há posição comprada atualmente aberta (Position <= 0).

Quando todas as regras se alinham, uma ordem de compra de mercado é enviada pelo volume base configurado mais qualquer tamanho necessário para cobrir uma posição vendida.

Entrada vendida

  1. O fechamento do candle está abaixo da média móvel.
  2. +DI é menor ou igual a −DI, indicando pressão direcional de baixa.
  3. O fechamento do candle está abaixo do valor do Parabolic SAR.
  4. Não há posição vendida atualmente aberta (Position >= 0).

Uma ordem de venda de mercado é colocada quando todas as regras de venda correspondem.

Saídas

  • As posições compradas são fechadas imediatamente assim que o preço cai abaixo do Parabolic SAR.
  • As posições vendidas são cobertas quando o preço sobe acima do Parabolic SAR.

Nenhum nível separado de stop-loss ou take-profit é adicionado; o cruzamento do SAR é o único sinal de saída, seguindo o consultor especialista original. Como as saídas são avaliadas antes de novas entradas, a estratégia não vai inverter de vendido para comprado (ou vice-versa) no mesmo candle, espelhando o ciclo de abertura/fechamento em duas etapas do script MQL.

Parâmetros

Nome Descrição Padrão Notas
MaPeriod Comprimento da média móvel simples usada para definir o filtro de tendência. 100 Otimizável, deve ser maior que zero.
AdxPeriod Período do cálculo de ADX que produz +DI e −DI. 14 Otimizável, deve ser maior que zero.
SarStep Fator de aceleração e incremento para o Parabolic SAR. 0.02 Equivalente ao parâmetro step do MQL.
SarMax Fator máximo de aceleração para Parabolic SAR. 0.10 Espelha a configuração maximum do MQL.
Volume Tamanho de ordem base para novas entradas. 1 Substitui o dimensionamento de lotes baseado em margem da versão MetaTrader. O tamanho real da ordem é Volume + |Position| para que as reversões liquidem a exposição existente.
CandleType O tipo de dados de candles assinado através do StockSharp. 1 hora Ajustável para qualquer timeframe.

Notas de implementação

  • O processamento de indicadores usa o pipeline de alto nível BindEx do StockSharp, garantindo que SMA, ADX e SAR sejam atualizados em sincronia perfeita sem buffering manual.
  • As saídas são executadas mesmo se AllowTrading estiver temporariamente desabilitado, mantendo os controles de risco ativos em todos os momentos.
  • Helpers de graficação estão incluídos: o painel principal mostra preço, SMA e SAR, enquanto um painel secundário mostra o indicador ADX para diagnósticos.
  • Declarações de log descrevem cada decisão de trading com os valores subjacentes do indicador para simplificar testes futuros e depuração.

Diretrizes de uso

  1. Anexe a estratégia a um título e portfólio no Designer ou Backtester.
  2. Ajuste o tipo de candle para corresponder ao seu horizonte de trading (p.ex., candles M15, H1, ou D1).
  3. Ajuste o período da média móvel, o período do ADX e os parâmetros SAR para se adaptar à volatilidade do instrumento.
  4. Defina o parâmetro Volume para o tamanho de posição preferido. Se precisar do gerenciamento de dinheiro adaptativo usado no script original, integre seu próprio dimensionamento baseado em portfólio antes de enviar ordens.
  5. Execute a estratégia. Trades serão acionados somente após todos os indicadores terem produzido valores históricos suficientes para estarem formados.

Diferenças em relação ao consultor especialista original

  • O cálculo de lotes baseado em margem foi substituído por um parâmetro fixo Volume para manter a estratégia broker-neutral dentro do StockSharp.
  • A gestão de trades, os valores do indicador e a ordem de avaliação (saída antes de entrada) seguem estritamente a lógica de referência do MetaTrader.
  • Todos os comentários dentro do código-fonte estão em inglês para cumprir com as diretrizes do projeto.
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>
/// Conversion of the MaSarADX MetaTrader strategy to StockSharp high level API.
/// Combines a moving average, ADX directional movement and Parabolic SAR for entries and exits.
/// </summary>
public class MaSarAdxBindStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<int> _adxPeriod;
	private readonly StrategyParam<decimal> _sarStep;
	private readonly StrategyParam<decimal> _sarMax;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _previousHigh;
	private decimal? _previousLow;
	private decimal? _previousClose;
	private decimal _smoothedPlusDm;
	private decimal _smoothedMinusDm;
	private decimal _smoothedTrueRange;
	private int _adxSamples;

	/// <summary>
	/// Moving average period used for the trend filter.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Average Directional Index calculation period.
	/// </summary>
	public int AdxPeriod
	{
		get => _adxPeriod.Value;
		set => _adxPeriod.Value = value;
	}

	/// <summary>
	/// Acceleration step for the Parabolic SAR indicator.
	/// </summary>
	public decimal SarStep
	{
		get => _sarStep.Value;
		set => _sarStep.Value = value;
	}

	/// <summary>
	/// Maximum acceleration factor for the Parabolic SAR indicator.
	/// </summary>
	public decimal SarMax
	{
		get => _sarMax.Value;
		set => _sarMax.Value = value;
	}


	/// <summary>
	/// Type of candles processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="MaSarAdxBindStrategy"/>.
	/// </summary>
	public MaSarAdxBindStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 120)
		.SetGreaterThanZero()
		.SetDisplay("MA Period", "Length of the trend moving average", "Indicators")
		
		.SetOptimize(20, 200, 10);

		_adxPeriod = Param(nameof(AdxPeriod), 18)
		.SetGreaterThanZero()
		.SetDisplay("ADX Period", "Length of the Average Directional Index", "Indicators")
		
		.SetOptimize(7, 28, 1);

		_sarStep = Param(nameof(SarStep), 0.02m)
		.SetRange(0.005m, 0.2m)
		.SetDisplay("SAR Step", "Acceleration step for Parabolic SAR", "Indicators")
		;

		_sarMax = Param(nameof(SarMax), 0.1m)
		.SetRange(0.05m, 1m)
		.SetDisplay("SAR Maximum", "Maximum acceleration for Parabolic SAR", "Indicators")
		;


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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousHigh = null;
		_previousLow = null;
		_previousClose = null;
		_smoothedPlusDm = 0m;
		_smoothedMinusDm = 0m;
		_smoothedTrueRange = 0m;
		_adxSamples = 0;
	}

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

		// Instantiate indicators used in the original MetaTrader script.
		var movingAverage = new SimpleMovingAverage
		{
			Length = MaPeriod
		};

		var parabolicSar = new ParabolicSar
		{
			Acceleration = SarStep,
			AccelerationStep = SarStep,
			AccelerationMax = SarMax
		};

		// Subscribe to candle data and bind indicator updates to a single handler.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(movingAverage, parabolicSar, ProcessCandle)
			.Start();

		// Draw the trading context for visual debugging when charts are available.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, movingAverage);
			DrawIndicator(area, parabolicSar);
			DrawOwnTrades(area);

		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal movingAverage, decimal sar)
	{
		// Work only with completed candles to mirror the original first-tick logic.
		if (candle.State != CandleStates.Finished)
			return;

		var (plusDi, minusDi, isReady) = UpdateDirectionalMovement(candle);
		if (!isReady)
			return;

		// Always allow risk exits even if trading is temporarily disabled.
		if (Position > 0 && candle.ClosePrice < sar)
		{
			SellMarket();
			return;
		}

		if (Position < 0 && candle.ClosePrice > sar)
		{
			BuyMarket();
			return;
		}

		// Entry conditions replicated from the MetaTrader version.
		var bullishSignal = candle.ClosePrice > movingAverage && plusDi >= minusDi && candle.ClosePrice > sar;
		var bearishSignal = candle.ClosePrice < movingAverage && plusDi <= minusDi && candle.ClosePrice < sar;

		if (bullishSignal && Position <= 0)
		{
			BuyMarket();
			return;
		}

		if (bearishSignal && Position >= 0)
		{
			SellMarket();
		}
	}

	private (decimal plusDi, decimal minusDi, bool isReady) UpdateDirectionalMovement(ICandleMessage candle)
	{
		if (_previousHigh is not decimal previousHigh ||
			_previousLow is not decimal previousLow ||
			_previousClose is not decimal previousClose)
		{
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			_previousClose = candle.ClosePrice;
			return (0m, 0m, false);
		}

		var upMove = candle.HighPrice - previousHigh;
		var downMove = previousLow - candle.LowPrice;
		var plusDm = upMove > downMove && upMove > 0m ? upMove : 0m;
		var minusDm = downMove > upMove && downMove > 0m ? downMove : 0m;
		var trueRange = Math.Max(
			candle.HighPrice - candle.LowPrice,
			Math.Max(
				Math.Abs(candle.HighPrice - previousClose),
				Math.Abs(candle.LowPrice - previousClose)));

		if (_adxSamples < AdxPeriod)
		{
			_smoothedPlusDm += plusDm;
			_smoothedMinusDm += minusDm;
			_smoothedTrueRange += trueRange;
			_adxSamples++;
		}
		else
		{
			_smoothedPlusDm = _smoothedPlusDm - (_smoothedPlusDm / AdxPeriod) + plusDm;
			_smoothedMinusDm = _smoothedMinusDm - (_smoothedMinusDm / AdxPeriod) + minusDm;
			_smoothedTrueRange = _smoothedTrueRange - (_smoothedTrueRange / AdxPeriod) + trueRange;
		}

		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
		_previousClose = candle.ClosePrice;

		if (_adxSamples < AdxPeriod || _smoothedTrueRange <= 0m)
			return (0m, 0m, false);

		return (
			100m * _smoothedPlusDm / _smoothedTrueRange,
			100m * _smoothedMinusDm / _smoothedTrueRange,
			true);
	}
}