Ver en GitHub

Estrategia TrueSort 1001

TrueSort 1001 es un estricto sistema de seguimiento de tendencias que refleja el asesor experto MQL original. La estrategia observa cinco promedios móviles simples y solo actúa cuando permanecen perfectamente ordenados durante tres velas completas consecutivas. Un índice direccional promedio en aumento (ADX) confirma el impulso antes de que se abra cualquier operación. Una vez en el mercado, la posición está protegida por un trailing stop adaptativo medido en pasos de precio y la operación se cierra tan pronto como las medias móviles pierden su alineación.

Lógica

Filtro de tendencia e impulso

  • Se calculan cinco SMA (10, 20, 50, 100 y 200 períodos de forma predeterminada) en el período de tiempo seleccionado.
  • Para configuraciones largas, las SMA rápidas deben estar estrictamente por encima de las más lentas en cada una de las últimas tres velas terminadas: SMA10 > SMA20 > SMA50 > SMA100 > SMA200.
  • Para configuraciones cortas se requiere el orden opuesto en las mismas tres velas.
  • ADX con período AdxPeriod debe permanecer por encima de AdxThreshold y el valor actual debe ser mayor que la vela anterior, lo que garantiza que la fuerza de la tendencia esté aumentando.

Condiciones de entrada

  1. No hay ninguna posición abierta.
  2. Tres velas históricas satisfacen la regla de pedido descrita anteriormente.
  3. El filtro ADX pasa.
  4. Una orden de mercado de Volume lotes se envía inmediatamente al cierre de la vela actual.

Condiciones de salida

  • Desincronización de media móvil: cuando la vela actual se cierra y la pila MA ya no está estrictamente ordenada en la dirección de la operación, la posición se liquida.
  • Protección final: StopLossPoints se convierten en distancia de precio absoluta multiplicando por el instrumento PriceStep. Para operaciones largas, el stop se inicializa al máximo entre SMA100 y Close - distance. Para cortos es el mínimo entre SMA100 y Close + distance. Después de cada vela, el stop se ajusta al precio, pero nunca se afloja. Si el precio cruza el stop, la posición se cierra en el mercado.

Notas adicionales

  • Todas las decisiones se toman únicamente sobre velas terminadas; se ignoran las velas sin terminar.
  • El algoritmo almacena los últimos tres valores SMA internamente para replicar la lógica shift del script MQL original sin solicitar el historial del indicador.
  • Los valores ADX se procesan a través de BindEx y se intenta operar solo cuando la estrategia está en línea y los datos están completamente formados.

Parámetros

Nombre Predeterminado Descripción
Volume 0.1 Tamaño del pedido en lotes para cada entrada al mercado.
StopLossPoints 100 Distancia del trailing-stop expresada en incrementos del precio del instrumento. 0 desactiva el seguimiento.
Sma10Length 10 Período del SMA más rápido.
Sma20Length 20 Período del segundo SMA.
Sma50Length 50 Período del medio SMA.
Sma100Length 100 Periodo utilizado tanto para la alineación como para la referencia de parada inicial.
Sma200Length 200 El SMA más lento confirma la tendencia a largo plazo.
AdxPeriod 14 Período del indicador ADX.
AdxThreshold 25 Nivel mínimo ADX y condición ascendente necesaria antes de las entradas.
CandleType TimeSpan.FromHours(1).TimeFrame() Serie de velas utilizada para todos los cálculos de indicadores.

Detalles de implementación

  • El código se basa en la suscripción de vela de alto nivel StockSharp y vincula seis indicadores (cinco SMA y ADX) en una sola canalización.
  • Los buffers de historial con longitud tres almacenan los últimos valores de SMA, evitando llamadas a GetValue() y manteniendo la paridad exacta con los turnos de MQL.
  • Los trailingstops se gestionan manualmente; StartProtection() todavía está activado, por lo que la infraestructura estándar está lista en caso de que se necesiten más protecciones.
  • Los comentarios dentro del código explican cada paso en inglés para facilitar el mantenimiento.
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;
	}
}