Ver en GitHub

Estrategia X Trader

Esta estrategia implementa un sistema contrarian de cruce de medias móviles escrito originalmente en MQL como X trader. Utiliza dos medias móviles simples y abre posiciones en dirección opuesta al cruce. El riesgo se gestiona usando take-profit y stop-loss fijos en puntos absolutos mediante StartProtection.

Cómo funciona

  1. Suscribirse a datos de velas del marco temporal especificado.
  2. Calcular dos medias móviles con períodos configurables.
  3. Rastrear los últimos dos valores de cada media para detectar un cruce.
  4. Cuando la media rápida cruza por encima de la media lenta y permanece por encima durante dos barras mientras dos barras atrás estaba por debajo, se abre una posición corta.
  5. Cuando la media rápida cruza por debajo de la media lenta y permanece por debajo durante dos barras mientras dos barras atrás estaba por encima, se abre una posición larga.
  6. Solo puede estar abierta una posición al mismo tiempo. La protección cierra automáticamente las operaciones cuando el precio se mueve la cantidad configurada de take-profit o stop-loss.

Parámetros

  • CandleType – serie de velas a utilizar.
  • Ma1Period – período de la primera media móvil.
  • Ma2Period – período de la segunda media móvil.
  • TakeProfitPoints – objetivo de beneficio en puntos de precio.
  • StopLossPoints – límite de pérdida en puntos de precio.

Indicador

  • SimpleMovingAverage – usado dos veces con períodos diferentes.

Gestión de riesgos

StartProtection se habilita en OnStarted y aplica los valores de take-profit y stop-loss a todas las posiciones.

using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// X Trader Strategy - contrarian moving average cross.
/// </summary>
public class XTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _ma1Period;
	private readonly StrategyParam<int> _ma2Period;

	private decimal _ma1Prev;
	private decimal _ma1Prev2;
	private decimal _ma2Prev;
	private decimal _ma2Prev2;
	private bool _hasPrev2;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int Ma1Period { get => _ma1Period.Value; set => _ma1Period.Value = value; }
	public int Ma2Period { get => _ma2Period.Value; set => _ma2Period.Value = value; }

	public XTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_ma1Period = Param(nameof(Ma1Period), 16)
			.SetGreaterThanZero()
			.SetDisplay("MA1 Period", "Period of the first moving average", "Parameters");

		_ma2Period = Param(nameof(Ma2Period), 10)
			.SetGreaterThanZero()
			.SetDisplay("MA2 Period", "Period of the second moving average", "Parameters");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_ma1Prev = 0;
		_ma1Prev2 = 0;
		_ma2Prev = 0;
		_ma2Prev2 = 0;
		_hasPrev2 = false;
	}

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

		var ma1 = new ExponentialMovingAverage { Length = Ma1Period };
		var ma2 = new ExponentialMovingAverage { Length = Ma2Period };

		SubscribeCandles(CandleType)
			.Bind(ma1, ma2, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal ma1Value, decimal ma2Value)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_ma1Prev == 0)
		{
			_ma1Prev = ma1Value;
			_ma2Prev = ma2Value;
			return;
		}

		if (!_hasPrev2)
		{
			_ma1Prev2 = _ma1Prev;
			_ma2Prev2 = _ma2Prev;
			_ma1Prev = ma1Value;
			_ma2Prev = ma2Value;
			_hasPrev2 = true;
			return;
		}

		// Contrarian: sell when MA1 crosses above MA2, buy when MA1 crosses below
		var sellSignal = ma1Value > ma2Value && _ma1Prev > _ma2Prev && _ma1Prev2 < _ma2Prev2;
		var buySignal = ma1Value < ma2Value && _ma1Prev < _ma2Prev && _ma1Prev2 > _ma2Prev2;

		if (sellSignal && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}
		else if (buySignal && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}

		_ma1Prev2 = _ma1Prev;
		_ma2Prev2 = _ma2Prev;
		_ma1Prev = ma1Value;
		_ma2Prev = ma2Value;
	}
}