Ver en GitHub

Extreme EA (Conversión a StockSharp)

La estrategia Extreme EA es un Asesor Experto de seguimiento de tendencia originalmente escrito para MetaTrader. Combina dos medias móviles con un filtro de Índice de Canal de Materias Primas (CCI) y un módulo de gestión adaptativa del dinero. Este porte mantiene la lógica de trading intacta mientras expone todos los controles importantes a través de la API de alto nivel de StockSharp. La estrategia opera solo en velas cerradas y es compatible con múltiples marcos temporales ejecutando las medias móviles y el CCI en suscripciones de velas independientes.

Descripción de la Estrategia

  1. Filtro de tendencia: Dos medias móviles se calculan en el MaCandleType configurable. La media rápida rastrea el momentum a corto plazo mientras que la media lenta define la pendiente de la tendencia dominante. La estrategia verifica la pendiente de la media lenta usando los dos valores previos para imitar los desplazamientos del array CopyBuffer del código MQL.
  2. Filtro de momentum: El CCI se evalúa en su propio marco temporal (CciCandleType) y fuente de precio. El último valor completado se almacena en caché y se reutiliza hasta que aparece una nueva vela CCI, lo que coincide con el comportamiento de los buffers de MetaTrader.
  3. Reglas de entrada:
    • Entrar en largo cuando la MA lenta sube, la MA rápida sube y el CCI cae por debajo del nivel inferior.
    • Entrar en corto cuando la MA lenta baja, la MA rápida baja y el CCI sube por encima del nivel superior.
  4. Reglas de salida:
    • Cerrar todos los largos si la MA lenta deja de subir.
    • Cerrar todos los cortos si la MA lenta deja de bajar.

Gestión de Riesgo

  • MaximumRisk controla el tamaño de posición objetivo basado en el capital actual del portafolio y el último precio. Si el volumen calculado es cero o los valores del portafolio no están disponibles, la estrategia recurre al Volume configurado o al mínimo de la bolsa.
  • DecreaseFactor reduce el volumen calculado después de dos o más operaciones perdedoras consecutivas. La reducción refleja la fórmula original lot = lot - lot * losses / DecreaseFactor.
  • HistoryDays limita cuánto tiempo se recuerda una racha de pérdidas. Si una operación de cierre ocurre después del número especificado de días, la racha se reinicia antes de aplicar la reducción.
  • MaxPositions limita la pirámide acotando la exposición neta por dirección. Cuando se alcanza el límite, las nuevas entradas se suprimen hasta que la exposición disminuye.

Parámetros

Nombre Tipo Predeterminado Descripción
MaximumRisk decimal 0.05 Fracción del capital usado para dimensionar cada nueva operación.
DecreaseFactor decimal 6 Factor de reducción por racha de pérdidas. Establecer en 0 para deshabilitar.
HistoryDays int 60 Número de días preservados al contar pérdidas consecutivas.
MaxPositions int 3 Máximo de entradas simultáneas por dirección.
FastMaPeriod int 15 Período para la media móvil rápida.
SlowMaPeriod int 75 Período para la media móvil lenta.
CciPeriod int 12 Período de lookback para el CCI.
CciUpperLevel decimal 50 Umbral CCI superior usado para cortos.
CciLowerLevel decimal -50 Umbral CCI inferior usado para largos.
MaCandleType DataType 15m Marco temporal para ambas medias móviles y ejecución.
CciCandleType DataType 30m Marco temporal para el filtro CCI.
MaMethod MaMethod Exponential Método de suavizado (Simple, Exponential, Smoothed, LinearWeighted).
MaPriceMode AppliedPriceMode Median Entrada de precio para las medias móviles.
CciPriceMode AppliedPriceMode Typical Entrada de precio para el CCI.

Notas de Implementación

  • La estrategia se suscribe al marco temporal de medias móviles una vez y opcionalmente a una segunda suscripción para el CCI. Cuando ambos marcos temporales coinciden, una sola suscripción alimenta ambos componentes, reproduciendo el flujo de trabajo original de un solo gráfico.
  • Los valores previos de los indicadores se almacenan en campos privados para emular las comparaciones ma_slow_array[1], ma_slow_array[2] y ma_fast_array[0] sin recurrir a buffers de indicadores manuales.
  • El dimensionamiento de posición se normaliza contra el paso de volumen del instrumento, mínimo y máximo para evitar órdenes rechazadas.
  • El módulo de riesgo registra los precios de entrada y salida para estimar el PnL realizado por posición completada, lo que reemplaza el bucle HistoryDealGet usado en MetaTrader.

Diferencias respecto a la Versión MQL

  • Las funciones específicas de MetaTrader como FreeMarginCheck, MarginCheck y HistorySelect se aproximan con las métricas de portafolio de StockSharp y el rastreador interno de rachas de pérdidas.
  • El porte de StockSharp opera en posiciones netas. Las órdenes de cierre aplanan toda la exposición en la dirección relevante, alineándose con el modelo de posición consolidada.
  • Las rutinas de registro del EA original se omitieron en favor de los diagnósticos integrados de StockSharp.
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>
/// Extreme EA strategy using fast/slow EMA crossover with CCI filter.
/// Buys when both EMAs rising and CCI below lower level (oversold bounce).
/// Sells when both EMAs falling and CCI above upper level (overbought reversal).
/// </summary>
public class ExtremeEaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _cciUpperLevel;
	private readonly StrategyParam<decimal> _cciLowerLevel;

	private ExponentialMovingAverage _fastMa;
	private ExponentialMovingAverage _slowMa;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevFast2;
	private decimal _prevSlow2;
	private bool _hasPrev;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// CCI indicator length.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Upper CCI threshold for sell entries.
	/// </summary>
	public decimal CciUpperLevel
	{
		get => _cciUpperLevel.Value;
		set => _cciUpperLevel.Value = value;
	}

	/// <summary>
	/// Lower CCI threshold for buy entries.
	/// </summary>
	public decimal CciLowerLevel
	{
		get => _cciLowerLevel.Value;
		set => _cciLowerLevel.Value = value;
	}

	/// <summary>
	/// Initialize strategy parameters.
	/// </summary>
	public ExtremeEaStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast EMA period", "Indicator");

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow EMA period", "Indicator");

		_cciPeriod = Param(nameof(CciPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI lookback period", "Indicator");

		_cciUpperLevel = Param(nameof(CciUpperLevel), 50m)
			.SetDisplay("CCI Upper", "Upper CCI threshold for sell", "Levels");

		_cciLowerLevel = Param(nameof(CciLowerLevel), -50m)
			.SetDisplay("CCI Lower", "Lower CCI threshold for buy", "Levels");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_fastMa = null;
		_slowMa = null;
		_prevFast = 0;
		_prevSlow = 0;
		_prevFast2 = 0;
		_prevSlow2 = 0;
		_hasPrev = false;
	}

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

		_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
		_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fastMa, _slowMa, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fastMa.IsFormed || !_slowMa.IsFormed)
		{
			_prevFast2 = _prevFast;
			_prevSlow2 = _prevSlow;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		if (!_hasPrev)
		{
			_prevFast2 = _prevFast;
			_prevSlow2 = _prevSlow;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		var slowIsRising = _prevSlow > _prevSlow2;
		var slowIsFalling = _prevSlow < _prevSlow2;
		var fastIsRising = fastValue > _prevFast;
		var fastIsFalling = fastValue < _prevFast;

		// Buy: fast crosses above slow
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
		}
		// Sell: fast crosses below slow
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
		}

		_prevFast2 = _prevFast;
		_prevSlow2 = _prevSlow;
		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}