Открыть на GitHub

Стратегия «Previous Candle Breakdown»

Обзор

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

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

  1. После закрытия каждой опорной свечи (по умолчанию 4 часа) запоминается максимум и минимум предыдущей свечи, к которым добавляется/вычитается отступ IndentSteps * Security.PriceStep.
  2. По данным тиковых сделок контролируется достижение уровней. Достижение верхней границы открывает длинную позицию, пробой нижней — короткую.
  3. Дополнительный фильтр по скользящим средним требует, чтобы быстрая SMA (с учётом возможного сдвига) была выше медленной для лонгов и ниже для шортов. Нулевой период любой SMA отключает фильтр.
  4. Торговля разрешена только внутри временного окна между StartTime и EndTime; поддерживаются сессии, которые переходят через полночь.
  5. Фиксация риска выполняется непрерывно: стоп-лосс, тейк-профит и трейлинг-стоп могут закрыть позицию раньше, чем появится сигнал на разворот.

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

  • StopLossSteps / TakeProfitSteps — расстояние до стоп-лосса и тейк-профита в шагах цены (distance = steps * Security.PriceStep).
  • TrailingStopSteps / TrailingStepSteps — включают трейлинг-стоп. После прохождения цены на величину трейлинга стоп активируется и сдвигается при каждом дополнительном движении не меньше указанного шага.
  • ProfitClose — закрывает все позиции, если плавающая прибыль Position * (последняя цена - PositionPrice) превышает порог (0 — отключено).
  • MaxNetPosition — ограничивает абсолютное значение суммарной позиции. Величину сделки задаёт свойство Volume у стратегии.

Параметры

Параметр Описание
CandleType Таймфрейм свечей для расчёта уровней.
IndentSteps Отступ от максимума/минимума предыдущей свечи в шагах цены.
FastMaPeriod / FastMaShift Период и сдвиг быстрой SMA.
SlowMaPeriod / SlowMaShift Период и сдвиг медленной SMA.
StopLossSteps Расстояние до стоп-лосса.
TakeProfitSteps Расстояние до тейк-профита.
TrailingStopSteps Размер трейлинг-стопа (0 — отключён).
TrailingStepSteps Минимальный дополнительный профит для переноса трейлинг-стопа; при включённом трейлинге должен быть > 0.
ProfitClose Порог плавающей прибыли для полного закрытия.
MaxNetPosition Максимально допустимая нетто-позиция.
StartTime / EndTime Временные границы торговли.

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

  • Управляйте размером заявок через свойство Volume. Перенос расчёта лота по риску из MQL-версии не выполнялся.
  • В расчётах используются простые скользящие средние (SMA). При необходимости можно добавить другие типы усреднения.
  • Порог ProfitClose измеряется в ценовых единицах (объём × изменение цены); подберите значение под конкретный инструмент.
  • Стратегия работает в неттинговом режиме: встречные сделки автоматически закрывают текущую позицию перед открытием новой.
  • При включённом трейлинг-стопе параметр TrailingStepSteps должен быть положительным, иначе стратегия не запустится.

Отличия от оригинального MQL-советника

  • Не реализовано управление объёмом по фиксированным лотам или проценту риска; в StockSharp за это отвечает Volume или внешние модули.
  • Поддерживаются только SMA; выбор метода сглаживания, доступный в MetaTrader, не перенесён.
  • Закрытие по прибыли использует плавающий PnL на основе средней цены позиции — комиссии и свапы брокера не учитываются.
  • Сообщения о результатах сделок упрощены и делегированы стандартному логированию StockSharp.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class PreviousCandleBreakdownLevelsStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private decimal? _prevHigh, _prevLow;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public PreviousCandleBreakdownLevelsStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 8).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 21).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow period", "Indicators");
	}

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

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevHigh = null; _prevLow = null;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }
		if (_prevHigh == null) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }
		var close = candle.ClosePrice;
		if (fast > slow && close > _prevHigh.Value && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (fast < slow && close < _prevLow.Value && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice;
	}
}