Открыть на GitHub

Стратегия HarVesteR

Обзор

HarVesteR — это трендовая стратегия, портированная из одноимённого советника MetaTrader. В основе лежит сочетание подтверждения MACD и двух простых скользящих средних, которые задают направление и управляют выходами. Дополнительный фильтр ADX позволяет торговать только в период устойчивого тренда.

Параметры по умолчанию полностью повторяют исходный советник: MACD(12, 24, 9), SMA с периодом 50 для сопровождения позиции, SMA с периодом 100 для фильтра тренда и поэтапный тейк-профит, который закрывает половину позиции после прохода цены на два начальных риска.

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

  1. Определение направления — SMA(100) используется как порог. Если закрытие бара находится ниже этой средней, стратегия готовится покупать; если выше — готовится продавать. После совершения сделки разрешение сбрасывается и появляется вновь только после обратного пробоя средней, что исключает серию входов без отката.
  2. Подтверждение MACD — сигнал считается действительным лишь в том случае, если линия MACD находится на нужной стороне нулевого уровня и за последние MACD Confirmation Bars свечей хотя бы раз переходила на противоположную сторону. Это повторяет цикл поиска противоположных значений в MQL-версии.
  3. Условия входа — для длинной позиции требуется, чтобы закрытие плюс ценовой отступ были выше обеих средних, MACD был положительным, а при активном фильтре ADX превышал 50. Для короткой позиции условия зеркальные.
  4. Начальный стоп — стоп размещается на минимуме (для покупок) или максимуме (для продаж) последних Stop Bars завершённых свечей, что соответствует вызовам iLowest/iHighest со смещением на один бар.
  5. Сопровождение позиции — когда цена проходит расстояние, равное Risk Multiplier умноженному на первоначальный риск, половина позиции закрывается, а стоп переносится в безубыток. Остаток позиции закрывается, когда SMA(50) пересекает скорректированное на отступ закрытие в противоположном направлении.
  6. Защитный выход — любое касание текущего стопа немедленно закрывает всю позицию.

Параметры

Название Описание Значение по умолчанию
Fast EMA Период быстрой EMA внутри MACD. 12
Slow EMA Период медленной EMA внутри MACD. 24
Signal EMA Период сглаживания сигнальной линии MACD. 9
MACD Confirmation Bars Максимальное число свечей между противоположными значениями MACD. 6
Trend SMA Период SMA, контролирующей сопровождение позиции. 50
Filter SMA Период SMA, определяющей направление торговли. 100
Offset (points) Ценовой отступ в пунктах для сравнений с SMA. 10
Stop Bars Количество прошлых свечей для расчёта стоп-уровня. 6
Risk Multiplier Множитель начального риска для срабатывания частичного тейк-профита. 2.0
Use ADX Включение фильтра ADX>50. Выключен
ADX Period Период ADX при активном фильтре. 14
Candle Type Тип свечей, поступающих в индикаторы (по умолчанию часовые). 1 час

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

  • Перевод точек в цену выполняется через Security.Step или Security.PriceStep. Если у инструмента отсутствует шаг цены, используется запасное значение 0.0001, что соответствует форексному «Point» оригинального советника.
  • Частичное закрытие реализовано рыночными ордерами на половину текущей позиции, как и в исходной MQL-логике с делением лота.
  • Метод StartProtection() вызывается при запуске, чтобы активировать встроенную защиту позиций до появления первых сделок.
  • При отключённом фильтре ADX стратегия подставляет фиктивное значение 60, поэтому поведение полностью совпадает с исторической версией.

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

  1. Перед запуском задайте свойство Volume — оно определяет базовый размер сделок и объём частичных выходов.
  2. Настройте Candle Type под ваш рабочий таймфрейм. Оригинальная методика рассчитана на часовой интервал, но возможны эксперименты на других периодах с последующей оптимизацией параметров.
  3. Наибольшее влияние на частоту и качество сделок оказывают MACD Confirmation Bars, Offset (points) и Risk Multiplier; начинайте оптимизацию именно с них.
using System;



using StockSharp.Algo.Indicators;

using StockSharp.Algo.Strategies;

using StockSharp.BusinessEntities;

using StockSharp.Messages;



namespace StockSharp.Samples.Strategies;



public class HarVesteRMacdTrendStrategy : Strategy

{

	private readonly StrategyParam<int> _fastPeriod;

	private readonly StrategyParam<int> _slowPeriod;

	private readonly StrategyParam<DataType> _candleType;



	private decimal _prevFast; private decimal _prevSlow; private bool _hasPrev;

	private int _cooldown;



	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }

	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

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



	public HarVesteRMacdTrendStrategy()

	{

		_fastPeriod = Param(nameof(FastPeriod), 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");

	}



	/// <inheritdoc />

	protected override void OnReseted()

	{

		base.OnReseted();

		_prevFast = default;

		_prevSlow = default;

		_hasPrev = default;

		_cooldown = default;

	}



	/// <inheritdoc />

	protected override void OnStarted2(DateTime time)

	{

		base.OnStarted2(time);

		_hasPrev = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };

		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription.Bind(fast, slow, ProcessCandle).Start();

	}



	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)

	{

		if (candle.State != CandleStates.Finished) return;

		if (!IsFormedAndOnlineAndAllowTrading()) return;

		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldown > 0)

		{

			_cooldown--;

			_prevFast = fast; _prevSlow = slow;

			return;

		}



		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)

		{

			var volume = Volume + Math.Abs(Position);

			BuyMarket(volume);

			_cooldown = 2;

		}

		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)

		{

			var volume = Volume + Math.Abs(Position);

			SellMarket(volume);

			_cooldown = 2;

		}

		_prevFast = fast; _prevSlow = slow;

	}

}