Открыть на GitHub

Стратегия стабилизации ордеров

Обзор

Стратегия стабилизации ордеров — это конверсия эксперта MetaTrader hjueiisyx8lp2o379e_www_forex-instruments_info.mq4. Изначальный робот выставляет пару стоповых ордеров вокруг текущей цены и ждёт пробоя. После открытия позиции система отслеживает размеры тел последних свечей, чтобы определить, застыл ли рынок, и закрывает сделку при потере импульса или достижении заданной прибыли.

Версия на C# воспроизводит те же правила с использованием высокоуровневого API StockSharp и работает только по закрытым свечам. Это делает поведение стратегии воспроизводимым как на тестах, так и в реальной торговле.

Правила торговли

  1. При отсутствии позиций и активных заявок стратегия размещает buy stop выше рынка и sell stop ниже рынка на расстоянии OrderDistancePoints пунктов (MetaTrader points).
  2. При исполнении стоп-заявки:
    • Открывается позиция объёмом OrderVolume лотов.
    • Противоположный стоп остаётся в стакане, чтобы поймать разворот в другую сторону.
  3. Пока позиция открыта, анализируются тела двух последних завершённых свечей:
    • Если тело последней свечи меньше StabilizationPoints, а плавающая прибыль превышает ProfitThreshold, позиция закрывается, а противоположный стоп отменяется.
    • Если две подряд свечи укладываются в предел StabilizationPoints, позиция закрывается вне зависимости от текущего результата.
    • Если прибыль достигает AbsoluteFixation, сделка закрывается немедленно.
  4. Стоп-заявки снимаются после истечения ExpirationMinutes, если параметр больше нуля.

Параметры

Имя Описание Значение по умолчанию
OrderVolume Объём сделки в лотах. 0.1
OrderDistancePoints Расстояние до стоповых ордеров в пунктах MetaTrader. 20
ProfitThreshold Минимальная прибыль (в валюте счёта) для выхода по стабилизации. -2
AbsoluteFixation Уровень прибыли (валюта счёта), при котором позиция закрывается безусловно. 30
StabilizationPoints Максимальный размер тела свечи, считающийся признаком флэта. 25
ExpirationMinutes Время жизни стоп-заявок в минутах, 0 — без ограничения. 20
CandleType Тип свечи для анализа стабилизации (по умолчанию 5-минутные). TimeFrame(5m)

Особенности конверсии

  • В оригинале расчёты велись по тикам; в порте используется завершённая свеча, что упрощает тестирование.
  • Пункты MetaTrader сопоставлены с PriceStep. Если шаг цены не задан, используется значение 1.
  • Прибыль пересчитывается через PriceStep и StepPrice, чтобы получить оценку в валюте счёта.
  • Все комментарии и описания параметров переведены на английский язык в соответствии с требованиями репозитория.

Использование

  1. Добавьте стратегию в свой проект StockSharp и назначьте инструмент с портфелем.
  2. Настройте параметры, уделив особое внимание таймфрейму свечей и расстоянию до стопов.
  3. Запустите стратегию — она автоматически выставит стоповые ордера и будет сопровождать позицию согласно описанным правилам.

Идеи для развития

  • Подбирать таймфрейм под волатильность инструмента, чтобы избежать ложных выходов.
  • Добавить фильтры волатильности (ATR, полосы Боллинджера) для работы только в активные периоды.
  • Реализовать трейлинг-стоп или частичную фиксацию прибыли при приближении к целевому уровню.
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>
/// Order stabilization strategy - trades breakouts after periods of low volatility.
/// When candle body is small (stabilization), waits for a directional breakout.
/// Goes long on bullish breakout candle after stabilization, short on bearish.
/// </summary>
public class OrderStabilizationStrategy : Strategy
{
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _stabilizationFactor;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevBody;
	private decimal _prevAtr;
	private bool _hasPrev;

	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal StabilizationFactor { get => _stabilizationFactor.Value; set => _stabilizationFactor.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public OrderStabilizationStrategy()
	{
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "ATR period for volatility", "Indicators");

		_stabilizationFactor = Param(nameof(StabilizationFactor), 0.5m)
			.SetDisplay("Stabilization Factor", "Body must be less than ATR * factor for stabilization", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevBody = 0m;
		_prevAtr = 0m;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(atr, ProcessCandle)
			.Start();
	}

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

		var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
		var threshold = atrValue * StabilizationFactor;

		if (!_hasPrev)
		{
			_prevBody = body;
			_prevAtr = atrValue;
			_hasPrev = true;
			return;
		}

		var prevThreshold = _prevAtr * StabilizationFactor;
		var wasStabilized = _prevBody < prevThreshold;

		// After stabilization, trade breakout candle
		if (wasStabilized && body > threshold)
		{
			var bullish = candle.ClosePrice > candle.OpenPrice;

			if (bullish && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();
				BuyMarket();
			}
			else if (!bullish && Position >= 0)
			{
				if (Position > 0)
					SellMarket();
				SellMarket();
			}
		}

		_prevBody = body;
		_prevAtr = atrValue;
	}
}