Auf GitHub ansehen

Morning/Evening Star with MFI Confirmation Strategy

Overview

This strategy replicates the logic of the MetaTrader expert Expert_AMS_ES_MFI, combining multi-candle reversal patterns with momentum confirmation from the Money Flow Index (MFI). It monitors three-candle Morning Star and Evening Star formations on the selected timeframe and filters the signals using MFI thresholds to confirm exhaustion of the current swing before entering trades. Momentum reversals detected by MFI crossings are also used to close open positions.

Trading Logic

  • Data Source: Finished candles of the configured timeframe and their associated MFI values.
  • Indicators:
    • Money Flow Index (MFI) – period is configurable (default 49).
  • Entry Rules:
    • Long: Detect a Morning Star pattern (strong bearish candle, small-bodied middle candle, strong bullish candle closing above the midpoint of the first) and require the previous candle's MFI to be below the bullish confirmation threshold (default 40).
    • Short: Detect an Evening Star pattern (strong bullish candle, small-bodied middle candle, strong bearish candle closing below the midpoint of the first) and require the previous candle's MFI to be above the bearish confirmation threshold (default 60).
    • When flipping positions, the strategy first closes the opposite exposure before opening the new trade.
  • Exit Rules:
    • Long Exit: Close the position when the MFI crosses above the upper exit level (default 70) or drops below the lower exit level (default 30), signalling either overbought momentum or a failed reversal.
    • Short Exit: Close the position when the MFI crosses above the lower exit level (default 30) or above the upper exit level (default 70), signalling growing bullish momentum.
  • Order Type: Market orders using the strategy volume configured in the StockSharp environment.

Parameters

Name Description Default
CandleType Timeframe of the candles used for analysis. 1-hour candles
MfiPeriod Period of the MFI indicator. 49
BullishMfiThreshold MFI level that confirms Morning Star signals. 40
BearishMfiThreshold MFI level that confirms Evening Star signals. 60
UpperExitLevel MFI level used for overbought exit detection. 70
LowerExitLevel MFI level used for oversold exit detection. 30

All parameters can be optimised inside StockSharp Designer/Optimizer.

Usage Notes

  1. Attach the strategy to the desired security and set the CandleType to match the chart timeframe from the original MQL expert.
  2. Configure the risk parameters, such as strategy volume or broker-specific order size, via the StockSharp platform.
  3. Enable the strategy. It will automatically subscribe to candles, calculate MFI values, and manage positions according to the rules above.

Origin

The strategy is a direct conversion of the MQL5 expert advisor located in MQL/323, preserving its pattern and MFI-based decision logic while adapting it to the StockSharp high-level API.

namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Morning/Evening Star + MFI strategy.
/// Buys on morning star with low MFI, sells on evening star with high MFI.
/// </summary>
public class MorningEveningMfiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _mfiPeriod;
	private readonly StrategyParam<decimal> _mfiLow;
	private readonly StrategyParam<decimal> _mfiHigh;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private ICandleMessage _prevCandle;
	private ICandleMessage _prevPrevCandle;
	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 MfiLow { get => _mfiLow.Value; set => _mfiLow.Value = value; }
	public decimal MfiHigh { get => _mfiHigh.Value; set => _mfiHigh.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public MorningEveningMfiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_mfiPeriod = Param(nameof(MfiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("MFI Period", "MFI period", "Indicators");
		_mfiLow = Param(nameof(MfiLow), 40m)
			.SetDisplay("MFI Low", "MFI oversold threshold", "Signals");
		_mfiHigh = Param(nameof(MfiHigh), 60m)
			.SetDisplay("MFI High", "MFI overbought threshold", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCandle = null;
		_prevPrevCandle = null;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCandle = null;
		_prevPrevCandle = null;
		_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++;

		if (_prevCandle != null && _prevPrevCandle != null)
		{
			var prevBody = Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice);
			var prevRange = _prevCandle.HighPrice - _prevCandle.LowPrice;
			var isSmallBody = prevRange > 0 && prevBody < prevRange * 0.3m;
			var firstMidpoint = (_prevPrevCandle.OpenPrice + _prevPrevCandle.ClosePrice) / 2m;

			var firstBearish = _prevPrevCandle.OpenPrice > _prevPrevCandle.ClosePrice;
			var currBullish = candle.ClosePrice > candle.OpenPrice;
			var isMorningStar = firstBearish && isSmallBody && currBullish && candle.ClosePrice > firstMidpoint;

			var firstBullish = _prevPrevCandle.ClosePrice > _prevPrevCandle.OpenPrice;
			var currBearish = candle.OpenPrice > candle.ClosePrice;
			var isEveningStar = firstBullish && isSmallBody && currBearish && candle.ClosePrice < firstMidpoint;

			if (isMorningStar && mfiValue < MfiLow && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (isEveningStar && mfiValue > MfiHigh && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevPrevCandle = _prevCandle;
		_prevCandle = candle;
	}
}