Открыть на GitHub

Стратегия MA Break Impulse Buy

Обзор

Стратегия реализует логику советника «M.A break mt4 buy» на высокоуровневом API StockSharp. Алгоритм ищет мощный бычий импульс после спокойной консолидации: сначала проверяются фильтры на основе экспоненциальных скользящих средних (EMA), затем анализируется «тихая» зона и, наконец, подтверждается пробой свечой, которая касается пробивной EMA. Торговля ведётся только на покупку.

Логика работы

  1. Фильтры тренда на EMA
    • На предыдущей закрытой свече (shift = 1) сравниваются две пары EMA.
    • EMA(FirstFastPeriod) должна быть выше EMA(FirstSlowPeriod).
    • EMA(SecondFastPeriod) должна быть выше EMA(SecondSlowPeriod).
  2. Выбор импульсной свечи
    • Импульсная свеча — последняя завершённая свеча (shift 1).
    • Её цена открытия должна находиться выше EMA с периодом TrendMaPeriod.
    • Минимум свечи должен коснуться либо пробить EMA с периодом BreakoutMaPeriod.
    • Свеча должна быть бычьей (Close > Open).
    • Диапазон свечи обязан находиться между CandleMinSize и CandleMaxSize (параметры переводятся из пунктов в цену через Security.PriceStep).
    • Верхняя тень не может превышать UpperWickLimit процентов от диапазона, а нижняя тень должна быть не меньше LowerWickFloor процентов.
  3. Тихие бары и сила импульса
    • Перед импульсной свечой анализируются QuietBarsCount свечей (shift ≥ 2); фиксируется максимальный диапазон (High-Low).
    • Полученный диапазон должен превышать QuietBarsMinRange (в пунктах, затем преобразованных в цену).
    • Тело импульсной свечи (Close - Open) обязано быть не меньше ImpulseStrength × quietRange.
  4. Управление позицией
    • При выполнении всех условий и отсутствии открытых позиций отправляется рыночная заявка на покупку.
    • StartProtection автоматически выставляет стоп-лосс и тейк-профит, используя значения в пунктах, переведённые через PriceStep.

Параметры

Имя Значение по умолчанию Описание
FirstFastPeriod 20 Период быстрого EMA в первом трендовом фильтре.
FirstSlowPeriod 30 Период медленного EMA в первом фильтре.
SecondFastPeriod 30 Период быстрого EMA во втором фильтре.
SecondSlowPeriod 50 Период медленного EMA во втором фильтре.
TrendMaPeriod 30 EMA, которую должна превысить цена открытия импульсной свечи.
BreakoutMaPeriod 20 EMA, которую должен коснуться минимум импульсной свечи.
QuietBarsCount 2 Количество «тихих» свечей до импульса.
QuietBarsMinRange 0.0 Минимальный диапазон тихих свечей в пунктах.
ImpulseStrength 1.1 Множитель для проверки размера тела импульса.
UpperWickLimit 100.0 Максимальная длина верхней тени в процентах от диапазона.
LowerWickFloor 0.0 Минимальная длина нижней тени в процентах от диапазона.
CandleMinSize 0.0 Минимальный допустимый диапазон импульсной свечи (в пунктах).
CandleMaxSize 100.0 Максимальный допустимый диапазон импульсной свечи (в пунктах).
VolumeSize 0.01 Объём сделки, нормализуется по VolumeStep.
StopLossPips 20.0 Размер стоп-лосса в пунктах (конвертируется через PriceStep).
TakeProfitPips 20.0 Размер тейк-профита в пунктах (конвертируется через PriceStep).
CandleType 15 минут Тип свечей, запрашиваемый у коннектора.

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

  • Используется высокоуровневый Bind, поэтому индикаторы автоматически обновляются вместе со свечами.
  • Все расчёты производятся только по закрытым свечам (CandleStates.Finished).
  • Значения в пунктах переводятся в цены через Security.PriceStep. Если инструмент не предоставляет шаг цены, применяется запасное значение 1, что соответствует поведению исходного MQL-кода.
  • StartProtection запускается один раз при старте, обеспечивая защитные ордера для каждой новой позиции.
  • В буфере хранится не более QuietBarsCount + 3 свечей, чего достаточно для оценки тихой зоны и импульсной свечи.

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

  • Убедитесь, что инструмент предоставляет PriceStep, VolumeStep, а также ограничения по объёму. Это необходимо для корректного перевода пунктов и нормализации объёма.
  • Настройте периоды EMA и ImpulseStrength под конкретный рынок: снижение множителя делает алгоритм чувствительнее, повышение — фильтрует только сильные пробои.
  • Стратегия предполагает наличие только одной длинной позиции. Внешние сделки по тому же инструменту могут блокировать новые входы.
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// MA Break Impulse Buy strategy: EMA crossover with volume surge.
/// Buys on EMA cross up with above-average volume, sells on EMA cross down.
/// </summary>
public class MABreakImpulseBuyStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;

	private decimal _prevClose;
	private decimal _prevEma;
	private decimal _prevVolume;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }

	public MABreakImpulseBuyStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0;
		_prevEma = 0;
		_prevVolume = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0;
		_prevEma = 0;
		_prevVolume = 0;
		_hasPrev = false;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_hasPrev)
		{
			var volumeUp = candle.TotalVolume > _prevVolume;

			if (_prevClose <= _prevEma && candle.ClosePrice > emaValue && volumeUp && Position <= 0)
				BuyMarket();
			else if (_prevClose >= _prevEma && candle.ClosePrice < emaValue && Position >= 0)
				SellMarket();
		}

		_prevClose = candle.ClosePrice;
		_prevEma = emaValue;
		_prevVolume = candle.TotalVolume;
		_hasPrev = true;
	}
}