Открыть на GitHub

Roman Direction Flip

Эта стратегия воспроизводит оригинального советника MQL, опубликованного как roman.mq5. Алгоритм постоянно находится в позиции и меняет направление сделки только после закрытия предыдущей. Пока позиция приносит прибыль, стратегия повторяет ту же сторону; при срабатывании стоп-лосса происходит переход на противоположное направление. Версия StockSharp использует поток котировок уровня 1 и опирается на лучшие цены bid/ask, чтобы имитировать выходы по пунктам, как в MetaTrader.

Логика стратегии

  1. Начальное направление. При запуске параметр StartWithBuy определяет, будет ли первая сделка покупкой или продажей. Решение сохраняется во внутреннем флаге _nextTradeBuy, чтобы использовать его в следующих входах.
  2. Открытие позиции. Когда стратегия находится вне рынка и нет ожидающих заявок, она отправляет рыночный ордер в заранее выбранном направлении. Для покупок запоминается текущий лучший ask, для продаж — лучший bid. Такое поведение повторяет MetaTrader, где ордера исполняются по противоположной стороне стакана.
  3. Мониторинг открытой позиции. После исполнения ордера стратегия продолжает получать обновления Level 1. Каждое сообщение содержит новые значения bid/ask, благодаря чему алгоритм рассчитывает нереализованный результат в шагах цены (пунктах). В качестве делителя используется Security.PriceStep; если шаг неизвестен, применяется значение по умолчанию 1.
  4. Правило тейк-профита. Когда плавающая прибыль достигает или превышает значение TakeProfitSteps, позиция закрывается через ClosePosition(). Флаг _nextTradeBuy не изменяется, поэтому следующая сделка повторяет только что успешное направление.
  5. Правило стоп-лосса. Когда плавающий убыток достигает или превышает StopLossSteps, позиция закрывается, а _nextTradeBuy переключается. Следующая сделка открывается в противоположную сторону, что соответствует исходному советнику, где логическое значение bs менялось при убытке.
  6. Защита от повторной отправки заявок. Переменная _orderPending не позволяет отправлять новые заявки, пока предыдущий ордер ещё обрабатывается. Флаг сбрасывается в OnPositionChanged после изменения позиции.

Такой цикл обеспечивает постоянное присутствие в рынке и смену направления только после убыточных сделок. Фактически стратегия реализует «переключатель тренда»: после стоп-лосса она предполагает смену тренда и пытается следовать новой стороне.

Параметры

  • OrderVolume (decimal, по умолчанию 0.1) — объём каждого рыночного ордера. Настройте значение в соответствии с желаемым размером позиции в боевой или тестовой среде.
  • TakeProfitSteps (int, по умолчанию 46) — количество шагов цены, необходимых для срабатывания тейк-профита. Шаги соответствуют Security.PriceStep, поэтому на инструменте с тиком 0.01 стандартное значение равно 0.46 денежной единицы.
  • StopLossSteps (int, по умолчанию 31) — максимально допустимое неблагоприятное движение (в шагах цены), после которого позиция закрывается и направление меняется.
  • StartWithBuy (bool, по умолчанию true) — определяет, будет ли первая сделка длинной (true) или короткой (false). Далее направление зависит от результата предыдущей позиции.

Все параметры созданы через StrategyParam<T>, поддерживают оптимизацию (кроме булевой настройки) и снабжены метаданными SetDisplay для корректного отображения в интерфейсе.

Данные и исполнение

  • Используется подписка SubscribeLevel1() для получения лучших bid/ask. Свечи и индикаторы не требуются.
  • Для входов применяются BuyMarket/SellMarket, для выходов — ClosePosition(), что делает поведение максимально близким к версии MQL с немедленными рыночными ордерами.
  • Последние значения bid/ask хранятся локально, чтобы воспроизводить расчёт прибыли в пунктах, аналогичный переменной _Point в MetaTrader.

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

  • Фиксированные тейк-профит и стоп-лосс в шагах цены гарантируют наличие заранее заданных уровней выхода в каждой сделке.
  • Переключение направления после убытка может приводить к частым переворотам на боковом рынке, поэтому объём OrderVolume следует подбирать с учётом допустимого риска.
  • Стратегия почти всегда находится в позиции, поэтому чувствительна к гэпам и резким скачкам котировок; при необходимости добавьте внешние защитные механизмы.

Значения по умолчанию

  • OrderVolume = 0.1
  • TakeProfitSteps = 46
  • StopLossSteps = 31
  • StartWithBuy = true

Фильтры

  • Категория: Следование тренду / переключение направления
  • Направление: Длинные и короткие позиции
  • Индикаторы: Нет
  • Стопы: Да (фиксированные тейк-профит и стоп-лосс)
  • Сложность: Базовая
  • Таймфрейм: Тиковые котировки уровня 1
  • Сезонность: Нет
  • Нейросети: Нет
  • Дивергенции: Нет
  • Уровень риска: Высокий (почти постоянное присутствие в позиции)

Примечания

  • В оригинальном советнике направление сохранялось в логической переменной bs. В портированной версии используется _nextTradeBuy, дополнительно добавлена защита от повторных заявок.
  • Точность шага цены имеет значение: если инструмент торгуется дробными пунктами, скорректируйте значения тейк-профита и стоп-лосса, чтобы получить желаемые денежные параметры.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Roman Direction Flip strategy. Alternates direction on momentum reversals.
/// Uses Momentum indicator zero-line crossover.
/// </summary>
public class RomanDirectionFlipStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momPeriod;
	private decimal? _prevMom;

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

	public RomanDirectionFlipStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_momPeriod = Param(nameof(MomPeriod), 12).SetGreaterThanZero().SetDisplay("Momentum Period", "Momentum lookback", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMom = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevMom = null;
		var mom = new Momentum { Length = MomPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(mom, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal momVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevMom = momVal; return; }
		if (_prevMom == null) { _prevMom = momVal; return; }
		if (_prevMom.Value < 100m && momVal >= 100m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevMom.Value > 100m && momVal <= 100m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevMom = momVal;
	}
}