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
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.
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.
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
Source: MQL/17306/BreakOut.mq4 (BreakOut expert advisor by Soubra2003)
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class previous_candle_breakout_strategy(Strategy):
def __init__(self):
super(previous_candle_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1)))
self._stop_loss_offset = self.Param("StopLossOffset", 1000.0)
self._take_profit_offset = self.Param("TakeProfitOffset", 1500.0)
self._previous_high = None
self._previous_low = None
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopLossOffset(self):
return self._stop_loss_offset.Value
@StopLossOffset.setter
def StopLossOffset(self, value):
self._stop_loss_offset.Value = value
@property
def TakeProfitOffset(self):
return self._take_profit_offset.Value
@TakeProfitOffset.setter
def TakeProfitOffset(self, value):
self._take_profit_offset.Value = value
def OnStarted2(self, time):
super(previous_candle_breakout_strategy, self).OnStarted2(time)
self._previous_high = None
self._previous_low = None
self._entry_price = 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._previous_high is None or self._previous_low is None:
self._previous_high = high
self._previous_low = low
return
previous_high = self._previous_high
previous_low = self._previous_low
breakout_above = close > previous_high
breakout_below = close < previous_low
sl = float(self.StopLossOffset)
tp = float(self.TakeProfitOffset)
if self.Position > 0:
if sl > 0.0 and close <= self._entry_price - sl:
self.SellMarket()
self._entry_price = 0.0
elif tp > 0.0 and close >= self._entry_price + tp:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if sl > 0.0 and close >= self._entry_price + sl:
self.BuyMarket()
self._entry_price = 0.0
elif tp > 0.0 and close <= self._entry_price - tp:
self.BuyMarket()
self._entry_price = 0.0
if breakout_above:
if self.Position < 0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position <= 0:
self.BuyMarket()
self._entry_price = close
elif breakout_below:
if self.Position > 0:
self.SellMarket()
self._entry_price = 0.0
if self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._previous_high = high
self._previous_low = low
def OnReseted(self):
super(previous_candle_breakout_strategy, self).OnReseted()
self._previous_high = None
self._previous_low = None
self._entry_price = 0.0
def CreateClone(self):
return previous_candle_breakout_strategy()