Auf GitHub ansehen

Previous Candle Breakout Strategy

This strategy replicates the classic "BreakOut" MetaTrader expert by Soubra2003. It monitors the high and low of the most recent completed candle and reacts whenever the current close breaks those reference levels. The approach is fully symmetric: long positions are opened on bullish breakouts, and short positions are opened on bearish breakdowns. Optional stop-loss and take-profit buffers expressed in price units allow the user to cap risk or lock in gains.

Overview

  • Subscribes to a single candle series (1-hour timeframe by default).
  • Stores the previous candle's high and low to act as breakout triggers.
  • Trades only at candle close to mirror the original tick-based logic without relying on intra-bar data.
  • Supports both long and short trades and always stays flat when no breakout condition is active.

Trading Rules

  1. Breakout entry / reversal
    • When the close of the current finished candle is strictly above the previous candle's high:
      • Any open short position is closed at market.
      • A new long position is opened immediately afterward (the reversal happens within the same candle processing step).
    • When the close is strictly below the previous candle's low:
      • Any open long position is closed at market.
      • A new short position is opened afterward.
  2. Protective exits (optional)
    • If a stop-loss offset is configured (> 0), the strategy exits a long when the close falls offset units below the entry price, or exits a short when the close rises offset units above the entry price.
    • If a take-profit offset is configured (> 0), the strategy exits a long when the close rises offset units above the entry price, or exits a short when the close falls offset units below the entry price.
  3. State reset
    • After every candle is processed, the most recent high and low become the new breakout reference levels.

Parameters

  • Candle Type – data type used for subscription (defaults to hourly time frame). Set this to the bar size that matches the chart used in MetaTrader for the original expert.
  • Stop Loss – distance in absolute price units between the entry price and the protective stop. Keep at 0 to disable stop-loss handling.
  • Take Profit – distance in absolute price units between the entry price and the profit target. Keep at 0 to disable take-profit handling.

Notes

  • The stop-loss and take-profit calculations are performed on candle close prices. The original MQL4 version attached static SL/TP levels to the orders; in StockSharp the exits are simulated by sending market orders once the thresholds are met.
  • Use instrument-specific price increments when configuring offsets. For example, if the instrument trades with 0.01 tick size and you want a 20-tick stop, set the stop-loss parameter to 0.20.
  • Because the logic always references the immediately preceding candle, the strategy works best on trending instruments or during high-volatility sessions where breakouts are meaningful.

Origin

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Breakout strategy that trades when the close crosses the previous candle's high or low.
/// Ported from the BreakOut.mq4 expert by Soubra2003.
/// </summary>
public class PreviousCandleBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _stopLossOffset;
	private readonly StrategyParam<decimal> _takeProfitOffset;

	private decimal? _previousHigh;
	private decimal? _previousLow;
	private decimal _entryPrice;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public decimal StopLossOffset { get => _stopLossOffset.Value; set => _stopLossOffset.Value = value; }
	public decimal TakeProfitOffset { get => _takeProfitOffset.Value; set => _takeProfitOffset.Value = value; }

	public PreviousCandleBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for candle subscription", "General");

		_stopLossOffset = Param(nameof(StopLossOffset), 1000m)
			.SetDisplay("Stop Loss", "Price distance for the stop-loss. Set 0 to disable.", "Risk")
			;

		_takeProfitOffset = Param(nameof(TakeProfitOffset), 1500m)
			.SetDisplay("Take Profit", "Price distance for the take-profit. Set 0 to disable.", "Risk")
			;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousHigh = null;
		_previousLow = null;
		_entryPrice = 0m;
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

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

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

		if (_previousHigh is null || _previousLow is null)
		{
			// Store the first finished candle to obtain reference high/low levels.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		var previousHigh = _previousHigh.Value;
		var previousLow = _previousLow.Value;
		var close = candle.ClosePrice;

		var breakoutAbove = close > previousHigh;
		var breakoutBelow = close < previousLow;

		// Manage protective exits while a position is open.
		if (Position > 0)
		{
			if (StopLossOffset > 0m && close <= _entryPrice - StopLossOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
			else if (TakeProfitOffset > 0m && close >= _entryPrice + TakeProfitOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
		}
		else if (Position < 0)
		{
			if (StopLossOffset > 0m && close >= _entryPrice + StopLossOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
			else if (TakeProfitOffset > 0m && close <= _entryPrice - TakeProfitOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
		}

		// Breakout above the previous high opens or reverses into a long position.
		if (breakoutAbove)
		{
			if (Position < 0)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}

			if (Position <= 0)
			{
				BuyMarket();
				_entryPrice = close;
			}
		}
		else if (breakoutBelow)
		{
			// Breakout below the previous low opens or reverses into a short position.
			if (Position > 0)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}

			if (Position >= 0)
			{
				SellMarket();
				_entryPrice = close;
			}
		}

		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
	}
}