Ver no GitHub

Estratégia envolvente de confirmação de IMF

Esta estratégia replica o especialista MetaTrader "Expert_ABE_BE_MFI" combinando padrões de engolfo de velas japonesas com a confirmação do oscilador Money Flow Index (MFI). Uma posição longa é aberta quando uma vela envolvente de alta aparece enquanto o fluxo de dinheiro permanece em uma zona de sobrevenda. Uma posição curta é aberta quando uma vela de baixa se forma sob condições de fluxo de dinheiro sobrecomprado. As posições são fechadas quando a IMF ultrapassa os limites de saída dinâmicos, sinalizando reversões de impulso.

Ideia Central

  1. Pattern detection – the body of the current finished candle must fully engulf the previous candle in the direction of the trade.
  2. Confirmação de volume – o indicador MFI (comprimento configurável, padrão 37) deve estar abaixo do nível de sobrevenda (40) para entradas longas ou acima do nível de sobrecompra (60) para entradas curtas.
  3. Saídas dinâmicas – as posições abertas são fechadas quando a IMF cruza os principais níveis de reversão (30 e 70) na direção oposta, imitando a lógica de votação original do especialista MQL.

Indicadores

  • Índice de fluxo de dinheiro (MFI) – calcula o momentum ajustado pelo volume. A estratégia armazena as duas últimas leituras da MFI para detectar passagens de nível.
  • Análise Corporal de Vela – nenhum indicador adicional é registrado; a detecção de engolfamento usa as duas últimas velas concluídas.

Regras de negociação

Entrada longa

  • A vela anterior é de baixa e a vela atual é de alta.
  • O corpo da vela atual abre abaixo ou igual ao fechamento anterior e fecha acima ou igual à abertura anterior (engolfo estrito).
  • Latest MFI value is below the configurable OversoldLevel (default 40).

Entrada curta

  • A vela anterior é de alta e a vela atual é de baixa.
  • Current candle body opens above or equal to the previous close and closes below or equal to the previous open.
  • O valor MFI mais recente está acima do configurável OverboughtLevel (padrão 60).

Condições de saída

  • Fechar Short quando a IMF cruza acima de ExitLongLevel (30) ou ExitShortLevel (70) de baixo.
  • Fechar Long quando a IMF cruza abaixo de ExitShortLevel (70) ou ExitLongLevel (30) de cima.

Estes limiares de saída recriam a lógica de voto duplo do perito original, garantindo que movimentos prolongados no fluxo de dinheiro desencadeiam a liquidação atempada de posições.

Gestão Comercial

  • As ordens de mercado (BuyMarket / SellMarket) são utilizadas para entradas e saídas.
  • No explicit stop-loss or take-profit is used; a gestão do risco depende dos sinais de reversão das IMFs.

Parâmetros

Nome Descrição Padrão Faixa/Notas
CandleType Candle timeframe used for analysis. 1 minuto Qualquer tipo de vela compatível.
MfiPeriod Length of the Money Flow Index. 37 Deve ser > 0; matches original EA default.
OversoldLevel Nível de IMF que confirma configurações de alta envolvente. 40 Ative a otimização, se necessário.
OverboughtLevel Nível de MFI que confirma configurações de baixa. 60 Enable optimization if needed.
ExitLongLevel Lower MFI boundary for detecting reversals. 30 Usado para saídas longas e confirmações curtas.
ExitShortLevel Upper MFI boundary for detecting reversals. 70 Used for both short exits and long confirmations.

Notas sobre conversão

  • O especialista original MQL agregou “votos” de padrões envolventes e filtros MFI. A estratégia C# reproduz o mesmo fluxo de decisão convertendo diretamente as regras de votação em condições discretas de entrada e saída.
  • Money management and trailing modules from the MQL version are omitted; O dimensionamento da posição StockSharp é controlado pelo volume da estratégia.
  • Todas as vinculações de indicadores aproveitam o API (SubscribeCandles().Bind(...)) de alto nível conforme necessário.

Dicas de uso

  • Otimize MfiPeriod, OversoldLevel e OverboughtLevel para adaptar a estratégia a mercados específicos.
  • Combine com controles de risco (paradas de proteção) via StartProtection no aplicativo host se segurança adicional for necessária.
  • Garanta dados históricos suficientes para que o Índice de Fluxo de Dinheiro esteja totalmente formado antes de permitir a negociação.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Engulfing MFI Confirmation strategy: Engulfing pattern with MFI filter.
/// Bullish engulfing + oversold MFI for long, bearish engulfing + overbought MFI for short.
/// </summary>
public class EngulfingMfiConfirmationStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _mfiPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevMfi;
	private bool _hasPrevMfi;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public EngulfingMfiConfirmationStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_mfiPeriod = Param(nameof(MfiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("MFI Period", "Money Flow Index period", "Indicators");
		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "MFI oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "MFI overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevMfi = 0m;
		_hasPrevMfi = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevMfi = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var mfi = new MoneyFlowIndex { Length = MfiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(mfi, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 2)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			if (curr is null || prev is null)
				return;

			var bullishEngulfing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice <= prev.ClosePrice
				&& curr.ClosePrice >= prev.OpenPrice;

			var bearishEngulfing = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice >= prev.ClosePrice
				&& curr.ClosePrice <= prev.OpenPrice;

			if (bullishEngulfing && mfiValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (bearishEngulfing && mfiValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		// Exit on MFI crossing
		if (_hasPrevMfi)
		{
			if (Position > 0 && _prevMfi >= Overbought && mfiValue < Overbought && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
			else if (Position < 0 && _prevMfi <= Oversold && mfiValue > Oversold && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevMfi = mfiValue;
		_hasPrevMfi = true;
	}
}