Стратегия воспроизводит логику советника MetaTrader "Expert_ABE_BE_MFI". Торговые сигналы формируются при появлении свечного паттерна поглощения, подтверждённого индикатором Money Flow Index (MFI). Покупка открывается, когда бычье поглощение сопровождается нахождением MFI в зоне перепроданности. Продажа открывается, когда медвежье поглощение возникает при MFI в зоне перекупленности. Закрытие позиций осуществляется при пересечении ключевых уровней MFI, что указывает на смену импульса.
Основная идея
Обнаружение паттерна – тело текущей завершённой свечи должно полностью поглощать тело предыдущей свечи в направлении сделки.
Подтверждение объёмом – индикатор MFI (период настраивается, по умолчанию 37) должен быть ниже уровня перепроданности (40) для покупок или выше уровня перекупленности (60) для продаж.
Выход по импульсу – открытые позиции закрываются при пересечении уровня 30 или 70 индикатором MFI в противоположную сторону, что имитирует «систему голосов» оригинального советника.
Индикаторы
Money Flow Index (MFI) – вычисляет объёмный импульс. Стратегия хранит два последних значения MFI для определения пересечений уровней.
Анализ свечных тел – дополнительные индикаторы не используются; проверка поглощения выполняется по двум последним свечам.
Правила торговли
Вход в лонг
Предыдущая свеча – медвежья, текущая – бычья.
Тело текущей свечи открывается не выше закрытия предыдущей и закрывается не ниже открытия предыдущей свечи (строгое поглощение).
Текущее значение MFI ниже параметра OversoldLevel (по умолчанию 40).
Вход в шорт
Предыдущая свеча – бычья, текущая – медвежья.
Тело текущей свечи открывается не ниже закрытия предыдущей и закрывается не выше её открытия.
Текущее значение MFI выше параметра OverboughtLevel (по умолчанию 60).
Условия выхода
Закрыть шорт, если MFI пересёк снизу вверх уровень ExitLongLevel (30) или ExitShortLevel (70).
Закрыть лонг, если MFI пересёк сверху вниз уровень ExitShortLevel (70) или ExitLongLevel (30).
Эти пороговые значения позволяют воспроизвести двойную систему голосования из оригинального эксперта, реагируя на продолжительные изменения денежного потока.
Управление сделками
Для входа и выхода используются рыночные ордера (BuyMarket / SellMarket).
Стоп-лоссы и тейк-профиты не применяются; управление риском возлагается на сигналы разворота MFI.
Параметры
Имя
Описание
Значение по умолчанию
Диапазон / примечания
CandleType
Тип/таймфрейм свечей для расчётов.
1 минута
Любой поддерживаемый тип свечей.
MfiPeriod
Период индикатора MFI.
37
Должен быть > 0; соответствует настройке в MQL.
OversoldLevel
Уровень MFI для подтверждения бычьего сигнала.
40
При необходимости включите оптимизацию.
OverboughtLevel
Уровень MFI для подтверждения медвежьего сигнала.
60
При необходимости включите оптимизацию.
ExitLongLevel
Нижний уровень MFI для фиксации прибыли.
30
Используется при закрытии лонга и подтверждении шорта.
ExitShortLevel
Верхний уровень MFI для фиксации прибыли.
70
Используется при закрытии шорта и подтверждении лонга.
Особенности конверсии
Оригинальный советник суммировал «голоса» от паттернов и фильтров. В C# логика преобразована в явные условия входа и выхода.
Модули мани-менеджмента и трейлинг-стопа из MQL не перенесены; объём задаётся параметром Volume стратегии StockSharp.
Используется высокоуровневый API (SubscribeCandles().Bind(...)) в соответствии с требованиями.
Рекомендации по применению
Оптимизируйте MfiPeriod, OversoldLevel и OverboughtLevel для конкретного инструмента.
При необходимости дополните стратегию защитными стопами через StartProtection в хост-приложении.
Перед запуском убедитесь, что истории достаточно для формирования MFI.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
class engulfing_mfi_confirmation_strategy(Strategy):
def __init__(self):
super(engulfing_mfi_confirmation_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._mfi_period = self.Param("MfiPeriod", 14)
self._oversold = self.Param("Oversold", 30.0)
self._overbought = self.Param("Overbought", 70.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._prev_mfi = 0.0
self._has_prev_mfi = False
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MfiPeriod(self):
return self._mfi_period.Value
@MfiPeriod.setter
def MfiPeriod(self, value):
self._mfi_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(engulfing_mfi_confirmation_strategy, self).OnReseted()
self._candles.clear()
self._prev_mfi = 0.0
self._has_prev_mfi = False
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(engulfing_mfi_confirmation_strategy, self).OnStarted2(time)
self._candles.clear()
self._has_prev_mfi = False
self._candles_since_trade = self.SignalCooldownCandles
mfi = MoneyFlowIndex()
mfi.Length = self.MfiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mfi, self._process_candle).Start()
def _process_candle(self, candle, mfi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
mfi_val = float(mfi_value)
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
if curr is None or prev is None:
return
bullish_engulfing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) <= float(prev.ClosePrice)
and float(curr.ClosePrice) >= float(prev.OpenPrice))
bearish_engulfing = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) >= float(prev.ClosePrice)
and float(curr.ClosePrice) <= float(prev.OpenPrice))
if bullish_engulfing and mfi_val < self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish_engulfing and mfi_val > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
if self._has_prev_mfi:
if self.Position > 0 and self._prev_mfi >= self.Overbought and mfi_val < self.Overbought and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and self._prev_mfi <= self.Oversold and mfi_val > self.Oversold and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
self._prev_mfi = mfi_val
self._has_prev_mfi = True
def CreateClone(self):
return engulfing_mfi_confirmation_strategy()