Ver no GitHub

Graal EMA Estratégia de Momento

Esta estratégia é uma conversão do consultor especialista MetaTrader 4 0Graal-CROSSmuvingi. Ele negocia reversões de tendência que ocorrem quando uma média móvel exponencial rápida (EMA) nos preços de fechamento cruza uma EMA mais lenta calculada nos preços de abertura. Um oscilador de impulso confirma a direção do rompimento e um take-profit de distância fixa replica o modelo de execução MT4 original.

Ideia de Negociação

  1. EMA rápido no fechamento rastreia a ação de preço mais recente.
  2. EMA lenta em aberto fica para trás e forma a linha de base do cruzamento.
  3. Oscilador de impulso (período 14) mede a intensidade com que o preço acelera para longe do valor neutro (100). A estratégia só é negociada quando o momentum se desvia de 100 em mais do que um filtro configurável e continua a fortalecer-se na mesma direção.
  4. Take Profit fecha negociações após uma distância predefinida medida em pontos do instrumento, espelhando o parâmetro MT4 TakeProfit.

Regras de entrada

  • Configuração longa
    • O EMA rápida cruza acima do EMA lenta na vela finalizada atual, enquanto a barra anterior tinha o EMA rápida abaixo ou igual ao EMA lenta.
    • O momento (valor menos 100) é maior que o limite MomentumFilter e também maior que a leitura do momento da barra anterior.
    • As posições curtas existentes são fechadas antes de abrir uma nova posição longa. O novo tamanho comprado é igual ao Volume configurado mais qualquer valor necessário para abrir uma posição vendida.
  • Configuração curta
    • A rápida EMA cruza abaixo da lenta EMA enquanto a barra anterior tinha a rápida EMA acima ou igual à lenta EMA.
    • O momento (valor menos 100) está abaixo do limite negativo MomentumFilter e menor que a leitura do momento da barra anterior.
    • As posições longas existentes são fechadas antes de abrir uma nova posição curta. O novo tamanho vendido é igual ao Volume configurado mais a quantidade necessária para cobrir uma posição comprada em aberto.

Regras de saída

  • As posições são fechadas automaticamente quando o preço atinge a meta de lucro calculada (TakeProfitPoints * PriceStep).
  • Um novo sinal oposto também inverte a posição imediatamente porque o tamanho da ordem sempre inclui a quantidade da posição atual.

Parâmetros

Nome Descrição Padrão
FastPeriod Comprimento do EMA nos preços de fechamento. 13
SlowPeriod Comprimento do EMA nos preços de abertura. 34
MomentumPeriod Lookback do oscilador de momento. 14
MomentumFilter Desvio mínimo de impulso absoluto de 100 necessário para negociar. 0,1
TakeProfitPoints Distância até a meta de lucro em faixas de preço (multiplicada por PriceStep). 200
CandleType Tipo de dados Candle usado para cálculos (período de 15 minutos por padrão). Período de 15 minutos
Volume Tamanho do pedido usado para novas entradas. O mecanismo o herda da classe base. 1

Notas de implementação

  • Os sinais são processados apenas em velas fechadas (CandleStates.Finished).
  • A estratégia assina o tipo de vela escolhido com SubscribeCandles e vincula EMA e indicadores de impulso por meio do API de alto nível.
  • O EMA lenta é atualizado manualmente com preços de abertura dentro do callback de ligação para replicar o comportamento do MT4 onde PRICE_OPEN foi usado.
  • O gerenciamento de lucro observa altos e baixos intrabares para emular a lógica de saída baseada em pontos do MT4.
  • StartProtection() é ativado no início para proteger contra posições abertas inesperadas antes que a estratégia comece a ser negociada.
using System;

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

namespace StockSharp.Samples.Strategies;

public class GraalEmaMomentumStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public GraalEmaMomentumStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var mom = new Momentum { Length = MomentumPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, mom, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal mom)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && mom > 0 && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevFast >= _prevSlow && fast < slow && mom < 0 && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevFast = fast;
		_prevSlow = slow;
	}
}