Ver en GitHub

Estrategia de Three EMA

Descripción general

La estrategia reproduce el asesor experto de MetaTrader "ThreeEMA" apilando tres medias móviles exponenciales (EMA). Busca alineación direccional entre una EMA rápida, media y lenta en el mismo marco temporal. Cuando los promedios están estrictamente ordenados de forma ascendente (rápida por encima de la media por encima de la lenta), la estrategia abre o mantiene una posición larga. Cuando el orden se invierte (rápida por debajo de la media por debajo de la lenta), abre o mantiene una posición corta. Las compensaciones de stop-loss y take-profit protectores reflejan los parámetros MQL originales y se expresan en puntos de precio relativos al tamaño de tick del instrumento.

Comportamiento MQL original

La versión MQL instanciaba tres indicadores EMA (FastPeriod, MediumPeriod, SlowPeriod) y generaba señales de trading basadas en su ordenación relativa en la barra más recientemente cerrada:

  • Abrir largo / cerrar corto cuando FastEMA > MediumEMA > SlowEMA.
  • Abrir corto / cerrar largo cuando FastEMA < MediumEMA < SlowEMA.
  • El stop-loss y take-profit se aplicaban como distancias fijas en puntos desde el precio de entrada.

Las órdenes se enviaban con ejecución de mercado y el bloque de gestión de dinero usaba un tamaño de lote fijo. El módulo de trailing estaba deshabilitado.

Detalles de implementación de StockSharp

  • Usa la API de suscripción de velas de alto nivel. Tres indicadores ExponentialMovingAverage están vinculados a la suscripción del marco temporal principal para que cada vela terminada entregue todos los valores EMA simultáneamente.
  • Las decisiones de trading se evalúan solo en velas completamente formadas para evitar ruido intrabarra.
  • Siempre que aparece una pila direccional, la estrategia cancela cualquier orden vigente, cierra la exposición opuesta si es necesario, y abre una nueva posición de mercado en la dirección requerida.
  • StartProtection convierte las distancias de stop-loss y take-profit configuradas en puntos en compensaciones de precio reales usando el PriceStep del instrumento. Esto refleja el comportamiento protector del EA original.
  • La integración de gráficos dibuja velas y las tres EMA cuando hay un área de gráfico disponible, facilitando la validación visual de señales.

Parámetros

Nombre Predeterminado Descripción
CandleType Marco temporal de 1 minuto Marco temporal de la suscripción de velas usado para las EMA.
FastPeriod 5 Longitud de la EMA rápida. Debe ser menor que MediumPeriod.
MediumPeriod 12 Longitud de la EMA media. Debe estar entre los períodos rápido y lento.
SlowPeriod 24 Longitud de la EMA lenta. Debe ser el valor de período más alto.
StopLossPoints 400 Distancia de stop-loss protector expresada en puntos del instrumento (convertida a precio usando PriceStep). Cero para deshabilitar.
TakeProfitPoints 900 Distancia de take-profit en puntos del instrumento (convertida a precio usando PriceStep). Cero para deshabilitar.

Notas de uso

  1. Configure Volume antes de iniciar la estrategia para reflejar el tamaño de orden deseado (el EA original usaba lotes fijos).
  2. Asegúrese de que los períodos EMA permanezcan estrictamente crecientes; de lo contrario, se lanza una excepción durante OnStarted para coincidir con la validación del código fuente MQL.
  3. Debido a que la lógica siempre voltea posiciones cuando la pila EMA se invierte, la estrategia está continuamente expuesta al mercado cuando las condiciones alternan entre alineaciones alcistas y bajistas.
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;

public class ThreeEmaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ThreeEmaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}