Открыть на GitHub

Previous Candle Breakdown 2

Стратегия пробоя, повторяющая логику советника MetaTrader «Previous Candle Breakdown 2». Алгоритм отслеживает последнюю закрытую свечу на настраиваемом таймфрейме и открывает сделки, когда цена пробивает её максимум или минимум на указанное количество пунктов. Дополнительные фильтры по скользящим средним, торговое окно по времени, выбор фиксированного объёма или расчёт объёма по проценту риска, а также многоуровневая защита позиции полностью воссоздают поведение оригинального MQL-кода внутри StockSharp.

Краткое описание

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

Параметры по умолчанию

  • IndentPips = 10
  • FastPeriod = 10, FastShift = 3, SlowPeriod = 30, SlowShift = 0, MaMethod = Simple
  • StopLossPips = 50, TakeProfitPips = 150
  • TrailingStopPips = 15, TrailingStepPips = 5
  • ProfitClose = 100 (денежный эквивалент суммарной реализованной и нереализованной прибыли)
  • MaxPositions = 10 (максимальный абсолютный объём в контрактах/лотах на сторону)
  • OrderVolume = 0 (отключено), RiskPercent = 5 (используется, если OrderVolume равен нулю и задан стоп-лосс)
  • StartTime = 09:09, EndTime = 19:19
  • CandleType = свечи с периодом 4 часа

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

  1. Подписаться на выбранный поток свечей и хранить последнюю завершённую свечу.
  2. Проверить, находится ли текущее время внутри торгового окна. При достижении ProfitClose сразу закрыть позицию.
  3. Рассчитать уровни пробоя, прибавив/вычтя отступ в пунктах к максимуму и минимуму предыдущей свечи.
  4. При пробое этих уровней и выполнении условий по скользящим средним открыть сделку, учитывая ограничение MaxPositions.
  5. Установить стартовые стоп-лосс и тейк-профит относительно цены входа. Активировать трейлинг-стоп, когда цена проходит в нужную сторону расстояние, превышающее трейлинг плюс шаг.
  6. На каждой свече контролировать достижение стоп-лосса или тейк-профита, обновлять трейлинг-стоп и сбрасывать защитные уровни после закрытия позиции.

Примечания

  • Расчёт «пункта» автоматически корректируется для инструментов с 3 или 5 знаками после запятой, как в MetaTrader.
  • При выборе риск-ориентированного объёма величина позиции оценивается на основе текущей стоимости портфеля и расстояния до стоп-лосса.
  • Пробой оценивается по значениям закрытой свечи, поэтому внутрисвечные экстремумы учитываются через High/Low свечи.
  • MaxPositions работает с чистой позицией стратегии: при использовании дробных объёмов параметр задаёт максимально допустимый модуль позиции в каждом направлении.
  • На график выводятся свечи, активные скользящие средние (если включены) и сделки, что облегчает визуальный контроль.
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>
/// Previous candle breakdown strategy v2.
/// Trades breakout of previous candle high/low filtered by EMA crossover.
/// </summary>
public class PreviousCandleBreakdown2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private decimal? _prevHigh;
	private decimal? _prevLow;
	private decimal? _prevFast;
	private decimal? _prevSlow;

	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 PreviousCandleBreakdown2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 10).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

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

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevHigh = null; _prevLow = null; _prevFast = null; _prevSlow = 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;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}
		if (_prevHigh == null || _prevLow == null || _prevFast == null || _prevSlow == null)
		{
			_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
			return;
		}
		var close = candle.ClosePrice;
		var bullTrend = fast > slow;
		var bearTrend = fast < slow;
		if (bullTrend && close > _prevHigh.Value && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		else if (bearTrend && close < _prevLow.Value && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}
		_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
	}
}