Ver no GitHub

Estratégia TrueSort 1001

TrueSort 1001 é um sistema estrito de acompanhamento de tendências que reflete o consultor especialista MQL original. A estratégia observa cinco médias móveis simples e só atua quando elas permanecem perfeitamente ordenadas por três velas consecutivas concluídas. Um Índice Direcional Médio crescente (ADX) confirma o impulso antes de qualquer negociação ser aberta. Uma vez no mercado, a posição é protegida por um trailing stop adaptativo medido em etapas de preço e a negociação é fechada assim que as médias móveis perdem o alinhamento.

Lógica

Filtro de tendência e impulso

  • Cinco SMAs (10, 20, 50, 100 e 200 períodos por padrão) são calculados no período selecionado.
  • Para configurações longas, os SMAs rápidos devem estar estritamente acima dos mais lentos em cada uma das últimas três velas finalizadas: SMA10 > SMA20 > SMA50 > SMA100 > SMA200.
  • Para configurações curtas, a ordem oposta é necessária nas mesmas três velas.
  • ADX com período AdxPeriod deve ficar acima de AdxThreshold e o valor atual deve ser maior que a vela anterior, garantindo que a força da tendência esteja aumentando.

Condições de entrada

  1. Nenhuma posição está aberta.
  2. Três velas históricas satisfazem a regra de ordenação descrita acima.
  3. O filtro ADX é aprovado.
  4. Uma ordem de mercado de Volume lotes é enviada imediatamente no fechamento da vela atual.

Condições de saída

  • Dessincronização da média móvel: quando a vela atual fecha e a pilha MA não está mais estritamente ordenada na direção da negociação, a posição é liquidada.
  • Proteção de rastreamento: StopLossPoints são convertidos em distância de preço absoluto multiplicando pelo instrumento PriceStep. Para negociações longas, o stop é inicializado no máximo entre SMA100 e Close - distance. Para shorts é o mínimo entre SMA100 e Close + distance. Após cada vela, o stop é reduzido em direção ao preço, mas nunca afrouxado. Se o preço ultrapassar o stop, a posição será fechada no mercado.

Notas adicionais

  • Todas as decisões são tomadas apenas em velas acabadas; velas inacabadas são ignoradas.
  • O algoritmo armazena os últimos três valores SMA internamente para replicar a lógica shift do script MQL original sem solicitar o histórico do indicador.
  • Os valores de ADX são processados via BindEx e a negociação é tentada apenas quando a estratégia está online e os dados estão totalmente formados.

Parâmetros

Nome Padrão Descrição
Volume 0.1 Tamanho do pedido em lotes para cada entrada no mercado.
StopLossPoints 100 Distância do trailing-stop expressa em etapas de preço do instrumento. 0 desativa o rastreamento.
Sma10Length 10 Período do mais rápido SMA.
Sma20Length 20 Período do segundo SMA.
Sma50Length 50 Período do meio SMA.
Sma100Length 100 Período utilizado tanto para alinhamento quanto para referência de parada inicial.
Sma200Length 200 Mais lento SMA confirmando a tendência de longo prazo.
AdxPeriod 14 Período do indicador ADX.
AdxThreshold 25 Nível mínimo ADX e condição ascendente necessária antes das entradas.
CandleType TimeSpan.FromHours(1).TimeFrame() Série de velas usada para todos os cálculos de indicadores.

Detalhes de implementação

  • O código depende da assinatura de vela de alto nível StockSharp e vincula seis indicadores (cinco SMAs e ADX) em um único pipeline.
  • Os buffers de histórico com comprimento três armazenam os valores SMA mais recentes, evitando chamadas para GetValue() enquanto mantêm a paridade exata com as mudanças MQL.
  • Os trailing stops são gerenciados manualmente; StartProtection() ainda está ativado, portanto a infraestrutura padrão estará pronta caso sejam necessárias proteções adicionais.
  • Os comentários dentro do código explicam cada etapa em inglês para facilitar a manutenção.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Trend-following strategy requiring strict moving average alignment
/// and rising volatility (ATR) before entering trades.
/// Exits when alignment is lost.
/// </summary>
public class TrueSort1001Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _midLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevMid;
	private decimal _prevSlow;
	private decimal _prevAtr;
	private decimal _entryPrice;

	public TrueSort1001Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");

		_midLength = Param(nameof(MidLength), 50)
			.SetDisplay("Mid SMA", "Medium SMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 200)
			.SetDisplay("Slow SMA", "Slow SMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for volatility filter.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int MidLength
	{
		get => _midLength.Value;
		set => _midLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_prevFast = 0;
		_prevMid = 0;
		_prevSlow = 0;
		_prevAtr = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = 0;
		_prevMid = 0;
		_prevSlow = 0;
		_prevAtr = 0;
		_entryPrice = 0;

		var fast = new SimpleMovingAverage { Length = FastLength };
		var mid = new SimpleMovingAverage { Length = MidLength };
		var slow = new SimpleMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, mid, slow, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, mid);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal midVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevMid == 0 || _prevSlow == 0)
		{
			_prevFast = fastVal;
			_prevMid = midVal;
			_prevSlow = slowVal;
			_prevAtr = atrVal;
			return;
		}

		var close = candle.ClosePrice;
		var bullishAligned = fastVal > midVal && midVal > slowVal;
		var bearishAligned = fastVal < midVal && midVal < slowVal;
		var atrRising = atrVal > _prevAtr;

		// Exit on alignment lost
		if (Position > 0 && !bullishAligned)
		{
			SellMarket();
		}
		else if (Position < 0 && !bearishAligned)
		{
			BuyMarket();
		}

		// Entry on alignment + rising ATR
		if (Position == 0)
		{
			if (bullishAligned && atrRising && close > fastVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (bearishAligned && atrRising && close < fastVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevMid = midVal;
		_prevSlow = slowVal;
		_prevAtr = atrVal;
	}
}