Открыть на GitHub

Стратегия ABH_BH_MFI

Общее описание

ABH_BH_MFI — перенос советника MetaTrader «Expert_ABH_BH_MFI» на платформу StockSharp с использованием высокоуровневого API. Стратегия ищет бычьи и медвежьи паттерны «беременная» (Harami) и подтверждает их показаниями индикатора Money Flow Index (MFI). Покупка производится при формировании бычьей беременной на падающем рынке, когда MFI остаётся подавленным. Продажа выполняется при формировании медвежьей беременной на восходящем рынке с высоким значением MFI. В отличие от MQL-версии, все блоки сигналов, управления капиталом и сопровождения позиций реализованы средствами StockSharp.

Логика торговли

1. Определение паттернов Harami

  • В обработчике сохраняются две последние полностью сформированные свечи.
  • Бычий Harami:
    • Свеча два бара назад — длинная чёрная (медвежья), её тело превышает средний размер тела.
    • Предыдущая свеча — белая (бычья), её тело полностью помещается внутри тела предыдущей чёрной свечи.
    • Средняя точка (High+Low)/2 старой свечи ниже скользящей средней закрытий, что подтверждает нисходящий фон.
  • Медвежий Harami строится зеркально: длинная белая свеча в прошлом, новая чёрная свеча внутри её тела и средняя точка выше скользящей средней закрытий.

2. Подтверждение по MFI

  • Индикатор MFI использует параметр MfiPeriod (по умолчанию 37) из оригинального советника.
  • Для открытия длинной позиции MFI предыдущей свечи должен быть ниже BullishThreshold (по умолчанию 40).
  • Для открытия короткой позиции MFI должен быть выше BearishThreshold (по умолчанию 60).

3. Правила выхода

  • Длинная позиция закрывается, если MFI пересекает снизу вверх ExitLowerLevel (по умолчанию 30) или ExitUpperLevel (по умолчанию 70).
  • Короткая позиция закрывается, когда MFI пересекает сверху вниз ExitUpperLevel либо пробивает снизу вверх ExitLowerLevel. Эти условия один в один повторяют проверки MFI(1) и MFI(2) в MQL-коде.

4. Управление рисками

  • Метод StartProtection устанавливает фиксированные стоп-лосс и тейк-профит в шагах цены, если заданы StopLossPoints и TakeProfitPoints. Нулевые значения отключают защиту, как и в оригинале.
  • Размер позиции задаётся через свойство Volume. При реверсе стратегия автоматически добирает объём, чтобы закрыть обратную позицию и открыть новую.

Параметры

Параметр Значение по умолчанию Назначение
CandleType Таймфрейм 1 час Основная свечная серия для анализа.
MfiPeriod 37 Период Money Flow Index.
BodyAveragePeriod 11 Длина SMA для оценки средних размеров тел и тренда закрытий.
BullishThreshold 40 Максимальный MFI для подтверждения бычьего сигнала.
BearishThreshold 60 Минимальный MFI для подтверждения медвежьего сигнала.
ExitLowerLevel 30 Нижний уровень MFI для фиксации позиций.
ExitUpperLevel 70 Верхний уровень MFI для фиксации позиций.
StopLossPoints 0 Стоп-лосс в шагах цены (0 — без стопа).
TakeProfitPoints 0 Тейк-профит в шагах цены (0 — без цели).

Особенности реализации

  • Свечи подписываются через SubscribeCandles, и расчёты выполняются только для завершённых баров (CandleStates.Finished).
  • Индикатор MFI привязан с помощью .Bind(_mfi, ProcessCandle), что избавляет от прямого обращения к буферам значений.
  • Две простые скользящие средние воспроизводят функции AvgBody и CloseAvg из MQL и кэшируют результаты, исключая доступ к историческим данным.
  • Перед отправкой заявок вызывается IsFormedAndOnlineAndAllowTrading(), что соответствует рекомендациям StockSharp по контролю состояния стратегии.

Отличия от версии MetaTrader

  • Модуль фиксированного лота заменён использованием стандартного свойства Volume, поскольку дополнительная прослойка не требуется.
  • Блок TrailingNone не выполнял действий, поэтому в переносе отсутствует логика трейлинг-стопа; при необходимости можно добавить её отдельно.
  • Журнальные сообщения сведены к минимуму. При тестировании или отладке можно добавить дополнительные LogInfo.

Рекомендации по использованию

  1. Перед стартом задайте инструмент и подходящий CandleType.
  2. При необходимости оптимизируйте уровни MFI под актив и таймфрейм.
  3. Установите ненулевые StopLossPoints и TakeProfitPoints, если брокер требует защитные заявки.
  4. Следите за создаваемыми графиками: стратегия отображает свечи, индикатор 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>
/// ABH BH MFI strategy: Harami pattern with MFI confirmation.
/// </summary>
public class AbhBhMfiStrategy : 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 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 AbhBhMfiStrategy()
	{
		_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), 40m)
			.SetDisplay("Oversold", "MFI oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 60m)
			.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();
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_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];

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

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

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