Ver en GitHub

Engulfing MFI Confirmation Strategy

This strategy replicates the MetaTrader expert "Expert_ABE_BE_MFI" by combining Japanese candlestick engulfing patterns with confirmation from the Money Flow Index (MFI) oscillator. A long position is opened when a bullish engulfing candle appears while money flow stays in an oversold zone. A short position is opened when a bearish engulfing candle forms under overbought money-flow conditions. Positions are closed when MFI crosses dynamic exit thresholds, signalling momentum reversals.

Core Idea

  1. Pattern detection – the body of the current finished candle must fully engulf the previous candle in the direction of the trade.
  2. Volume confirmation – the MFI indicator (length configurable, default 37) must be below the oversold level (40) for long entries or above the overbought level (60) for short entries.
  3. Momentum exits – open positions are closed when MFI crosses key reversal levels (30 and 70) in the opposite direction, mimicking the original voting logic of the MQL expert.

Indicators

  • Money Flow Index (MFI) – calculates volume-adjusted momentum. The strategy stores the last two MFI readings to detect level crossings.
  • Candlestick Body Analysis – no additional indicator is registered; engulfing detection uses the latest two completed candles.

Trading Rules

Long Entry

  • Previous candle is bearish and current candle is bullish.
  • Current candle body opens below or equal to the previous close and closes above or equal to the previous open (strict engulfing).
  • Latest MFI value is below the configurable OversoldLevel (default 40).

Short Entry

  • Previous candle is bullish and current candle is bearish.
  • Current candle body opens above or equal to the previous close and closes below or equal to the previous open.
  • Latest MFI value is above the configurable OverboughtLevel (default 60).

Exit Conditions

  • Close Short when MFI crosses above ExitLongLevel (30) or ExitShortLevel (70) from below.
  • Close Long when MFI crosses below ExitShortLevel (70) or ExitLongLevel (30) from above.

These exit thresholds recreate the double voting logic of the original expert, ensuring that extended moves in money flow trigger timely liquidation of positions.

Trade Management

  • Market orders (BuyMarket / SellMarket) are used for entries and exits.
  • No explicit stop-loss or take-profit is used; risk management relies on the MFI reversal signals.

Parameters

Name Description Default Range / Notes
CandleType Candle timeframe used for analysis. 1 minute Any supported candle type.
MfiPeriod Length of the Money Flow Index. 37 Must be > 0; matches original EA default.
OversoldLevel MFI level that confirms bullish engulfing setups. 40 Enable optimization if needed.
OverboughtLevel MFI level that confirms bearish engulfing setups. 60 Enable optimization if needed.
ExitLongLevel Lower MFI boundary for detecting reversals. 30 Used for both long exits and short confirmations.
ExitShortLevel Upper MFI boundary for detecting reversals. 70 Used for both short exits and long confirmations.

Notes on Conversion

  • The original MQL expert aggregated “votes” from engulfing patterns and MFI filters. The C# strategy reproduces the same decision flow by directly converting the voting rules into discrete entry and exit conditions.
  • Money management and trailing modules from the MQL version are omitted; StockSharp position sizing is controlled by the strategy volume.
  • All indicator bindings leverage the high-level API (SubscribeCandles().Bind(...)) as required.

Usage Tips

  • Optimise MfiPeriod, OversoldLevel, and OverboughtLevel to adapt the strategy to specific markets.
  • Combine with risk controls (protective stops) via StartProtection in the host application if additional safety is required.
  • Ensure sufficient historical data so that the Money Flow Index is fully formed before enabling trading.
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;
	}
}