Открыть на GitHub

Стратегия Nextbar

Обзор

Nextbar — это точная адаптация эксперта MetaTrader 4 nextbar.mq4. Советник сравнивает закрытие последней завершённой свечи с закрытием свечи, находящейся на заданном числе баров назад. Если расстояние между ценами превышает допустимый порог, система либо следует импульсу, либо работает против него (в зависимости от параметра направления). После входа выставляются симметричные тейк-профит и стоп-лосс, а также срабатывает принудительное закрытие по истечении фиксированного числа баров.

Реализация на StockSharp повторяет ту же логику, опираясь на высокоуровневый API стратегий. Обработка выполняется только на завершённых свечах, поэтому расчёты полностью соответствуют баровому режиму исходного эксперта.

Логика исходного MQL-советника

  • Расстояние импульса — сравнивается Close[1] и Close[bars2check+1]. Если модуль разницы не меньше minbar * Point, фиксируется торговый сигнал.
  • Флаг направления — входной параметр direction: значение 1 означает торговлю по тренду (покупка после роста, продажа после падения), 2 — контртрендовый подход (покупка после падения, продажа после роста).
  • Ограничение по сделкам — одновременно допускается только одна позиция. Новый вход возможен лишь на следующей свече после формирования сигнала.
  • Выход из позиции — лонг закрывается при достижении ценой тейк-профита выше точки входа либо стоп-лосса ниже неё; для шорта условия зеркальные. Если ни один уровень не сработал, позиция принудительно закрывается спустя bars2hold завершённых свечей.

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

  • Используются SubscribeCandles() и Bind для получения завершённых свечей на выбранном таймфрейме.
  • Поддерживается кратковременный буфер закрытий, позволяющий обращаться к свече с индексом bars2check + 1, как в MetaTrader.
  • Все параметры в пунктах переводятся в абсолютные цены через Security.PriceStep, что повторяет работу константы Point.
  • Рыночные ордера отправляются с объёмом Volume, а режим входа (Follow либо Reverse) задаётся параметром Direction.
  • Проверка тейк-профита, стоп-лосса и лимита по количеству баров выполняется один раз на каждую завершённую свечу, полностью воспроизводя оригинальный алгоритм.

Параметры

Параметр Описание Значение по умолчанию Примечания
CandleType Таймфрейм, по которому вычисляются сигналы. Часовые свечи Инструмент должен предоставлять свечи указанного типа.
BarsToCheck Количество завершённых свечей между опорным закрытием и текущим. 8 Аналог параметра bars2check.
BarsToHold Максимум завершённых свечей, на протяжении которых удерживается позиция. 10 Соответствует bars2hold. Закрытие происходит на свече, где счётчик достигает значения.
MinMovePoints Минимальное расстояние между закрытиями в пунктах MetaTrader. 77 Аналог minbar. Значение умножается на Security.PriceStep.
TakeProfitPoints Дистанция тейк-профита в пунктах. 115 Соответствует входному параметру profit. Ноль отключает цель.
StopLossPoints Дистанция стоп-лосса в пунктах. 115 Соответствует параметру loss. Ноль отключает защиту.
Direction Режим торговли: Follow (по тренду) или Reverse (контртренд). Follow Полностью повторяет direction (1 = тренд, 2 = контртренд).
Volume Объём рыночных ордеров. Значение Strategy.Volume Настраивается через стандартное свойство стратегии.

Рабочий процесс

  1. Дождаться завершения свечи и сохранить её цену закрытия.
  2. Получить закрытие свечи, расположенной на BarsToCheck баров раньше, и вычислить разницу.
  3. Если модуль разницы меньше MinMovePoints * PriceStep, сигнал игнорируется.
  4. Иначе:
    • В режиме Follow открыть лонг при росте и шорт при падении.
    • В режиме Reverse открыть лонг при падении и шорт при росте.
  5. На каждой последующей завершённой свече при открытой позиции:
    • Закрыть лонг, если закрытие выше входа на TakeProfitPoints или ниже на StopLossPoints.
    • Закрыть шорт, если закрытие ниже входа на TakeProfitPoints или выше на StopLossPoints.
    • Принудительно закрыть позицию после BarsToHold завершённых свечей с момента входа.

Практические рекомендации

  • Для корректного перевода пунктов в цену инструмент должен иметь заданный Security.PriceStep (при необходимости также StepPrice и настройки объёмов).
  • Стратегия работает с единственной позицией; заранее задайте Volume, соответствующий размеру ордера в MetaTrader.
  • Все решения принимаются по завершённым свечам, поэтому в тестах и реальной торговле необходимо использовать данные, которые предоставляют закрытые бары выбранного таймфрейма.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class NextbarStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevOpen;
	private decimal _prevClose;
	private bool _hasPrev;
	private int _cooldownRemaining;

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

	public NextbarStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA filter", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevOpen = default;
		_prevClose = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevOpen = 0;
		_prevClose = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!_hasPrev) { _prevOpen = candle.OpenPrice; _prevClose = close; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevOpen = candle.OpenPrice;
			_prevClose = close;
			return;
		}

		var prevBullish = _prevClose > _prevOpen;
		var prevBearish = _prevClose < _prevOpen;

		if (prevBullish && close > ema && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (prevBearish && close < ema && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevOpen = candle.OpenPrice;
		_prevClose = close;
	}
}