Expert AML MFI Strategy повторяет советник MetaTrader 5 «Expert_AML_MFI» с использованием высокоуровневого API StockSharp. В центре внимания — свечная модель Meeting Lines (встречные линии), подтверждаемая осциллятором Money Flow Index (MFI). Стратегия автоматически поддерживает статистику по свечам, определяет бычьи и медвежьи разворотные комбинации и управляет позициями при пересечениях MFI уровней перепроданности и перекупленности.
Логика работы
Подготовка данных. Подписка на выбранный таймфрейм (по умолчанию H1). Стратегия хранит две последние завершённые свечи и скользящее среднее модуля свечного тела. Средний размер тела рассчитывается индикатором SimpleMovingAverage, который применяется к абсолютной разнице между ценами открытия и закрытия — полностью аналогично оригиналу на MT5.
Определение паттерна. Вспомогательные методы выявляют Bullish Meeting Lines и Bearish Meeting Lines:
Бычий сигнал: длинная «чёрная» свеча, за которой следует длинная «белая» свеча с закрытием около предыдущего (не далее 10% от среднего тела).
Медвежий сигнал: длинная «белая» свеча и за ней длинная «чёрная» свеча с почти совпадающими ценами закрытия.
Подтверждение MFI. Предыдущее значение MFI должно быть ниже уровня входа в лонг (по умолчанию 40) либо выше уровня входа в шорт (по умолчанию 60).
Управление позицией. Сохраняются два последних значения MFI, чтобы фиксировать пересечения уровней перепроданности (30) и перекупленности (70):
Пересечение любого из уровней вверх закрывает короткие позиции.
Пересечение уровня 30 сверху вниз или уровня 70 снизу вверх закрывает длинные позиции.
Исполнение ордеров. При подтверждённом паттерне стратегия закрывает противоположную позицию (если она есть) и открывает новую рыночную сделку заданным базовым объёмом.
Параметры
Имя
Описание
Значение по умолчанию
CandleType
Таймфрейм для подписки на свечи.
Часовые свечи
MfiPeriod
Период расчёта MFI.
12
BodyAveragePeriod
Количество свечей в среднем по телам.
4
BullishEntryLevel
Максимальное значение MFI для бычьего входа.
40
BearishEntryLevel
Минимальное значение MFI для медвежьего входа.
60
OversoldLevel
Уровень перепроданности для выходов.
30
OverboughtLevel
Уровень перекупленности для выходов.
70
TradeVolume
Базовый объём новой позиции.
1
Каждый параметр оформлен через StrategyParam, поэтому доступна оптимизация в Designer.
Индикаторы и визуализация
Money Flow Index — подключён к подписке на свечи и отображается на графике при наличии области.
Simple Moving Average от модулей тел — служебный индикатор для оценки «длины» свечей.
Дополнительно
Метод StartProtection() активирует стандартную защиту позиции один раз при запуске.
Команды BuyMarket и SellMarket сначала закрывают противоположную позицию, после чего открывают новую, что повторяет поведение оригинального эксперта.
Python-версия умышленно не создаётся, согласно требованиям репозитория.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Meeting Lines + MFI strategy.
/// Buys on bullish meeting lines with low MFI, sells on bearish meeting lines with high MFI.
/// </summary>
public class ExpertAmlMfiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _mfiLow;
private readonly StrategyParam<decimal> _mfiHigh;
private ICandleMessage _prevCandle;
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 ExpertAmlMfiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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 level for bullish entry", "Signals");
_mfiHigh = Param(nameof(MfiHigh), 60m)
.SetDisplay("MFI High", "MFI level for bearish entry", "Signals");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
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 (_prevCandle != null)
{
var avgBody = (Math.Abs(candle.ClosePrice - candle.OpenPrice) +
Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice)) / 2m;
if (avgBody > 0)
{
var prevBearish = _prevCandle.OpenPrice > _prevCandle.ClosePrice;
var currBullish = candle.ClosePrice > candle.OpenPrice;
var closesNear = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;
if (prevBearish && currBullish && closesNear && mfiValue < MfiLow && Position <= 0)
BuyMarket();
var prevBullish = _prevCandle.ClosePrice > _prevCandle.OpenPrice;
var currBearish = candle.OpenPrice > candle.ClosePrice;
var closesNear2 = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;
if (prevBullish && currBearish && closesNear2 && mfiValue > MfiHigh && Position >= 0)
SellMarket();
}
}
_prevCandle = candle;
}
}
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 expert_aml_mfi_strategy(Strategy):
def __init__(self):
super(expert_aml_mfi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._mfi_period = self.Param("MfiPeriod", 14)
self._mfi_low = self.Param("MfiLow", 40.0)
self._mfi_high = self.Param("MfiHigh", 60.0)
self._prev_candle = None
@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 MfiLow(self):
return self._mfi_low.Value
@MfiLow.setter
def MfiLow(self, value):
self._mfi_low.Value = value
@property
def MfiHigh(self):
return self._mfi_high.Value
@MfiHigh.setter
def MfiHigh(self, value):
self._mfi_high.Value = value
def OnReseted(self):
super(expert_aml_mfi_strategy, self).OnReseted()
self._prev_candle = None
def OnStarted2(self, time):
super(expert_aml_mfi_strategy, self).OnStarted2(time)
self._prev_candle = None
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
mfi_val = float(mfi_value)
if self._prev_candle is not None:
avg_body = (abs(float(candle.ClosePrice) - float(candle.OpenPrice))
+ abs(float(self._prev_candle.ClosePrice) - float(self._prev_candle.OpenPrice))) / 2.0
if avg_body > 0:
prev_bearish = float(self._prev_candle.OpenPrice) > float(self._prev_candle.ClosePrice)
curr_bullish = float(candle.ClosePrice) > float(candle.OpenPrice)
closes_near = abs(float(candle.ClosePrice) - float(self._prev_candle.ClosePrice)) < avg_body * 0.3
if prev_bearish and curr_bullish and closes_near and mfi_val < self.MfiLow and self.Position <= 0:
self.BuyMarket()
prev_bullish = float(self._prev_candle.ClosePrice) > float(self._prev_candle.OpenPrice)
curr_bearish = float(candle.OpenPrice) > float(candle.ClosePrice)
closes_near2 = abs(float(candle.ClosePrice) - float(self._prev_candle.ClosePrice)) < avg_body * 0.3
if prev_bullish and curr_bearish and closes_near2 and mfi_val > self.MfiHigh and self.Position >= 0:
self.SellMarket()
self._prev_candle = candle
def CreateClone(self):
return expert_aml_mfi_strategy()