Breakout strategy that mirrors the MetaTrader expert "Previous Candle Breakdown 2". The algorithm watches the most recently
completed candle on a configurable timeframe and triggers trades when price pierces its high or low by a user-defined pip offset.
Optional moving average filtering, strict trading hours, position sizing by fixed volume or percentage risk, and layered
protective exits replicate the behaviour of the original MQL version inside StockSharp.
Overview
Entry Logic: Enter long when price exceeds the previous candle high plus an indent. Enter short when price breaks below the
previous candle low minus the same indent.
Filters: Optional fast/slow moving averages with shift parameters require directional confirmation before trading. Trading is
also constrained to a start/end time window.
Position Sizing: Choose between a fixed order volume or dynamic sizing based on portfolio value and stop-loss distance.
Risk Controls: Static stop-loss and take-profit levels in pips, trailing stop with a step filter, and a global profit target
that closes all positions.
Scaling: The MaxPositions limit caps the absolute net position size for each direction.
ProfitClose = 100 (currency units of realised + unrealised PnL)
MaxPositions = 10 (absolute contract/lot count per side)
OrderVolume = 0 (disabled), RiskPercent = 5 (used when OrderVolume is zero and stop-loss is active)
StartTime = 09:09, EndTime = 19:19
CandleType = 4-hour time frame
Trading Rules
Subscribe to the configured candle series and record each finished candle.
Check whether the current time falls inside the allowed trading session. If ProfitClose is reached, immediately flatten.
Compute breakout levels by adding/subtracting the pip indent from the previous candle’s high and low.
When price breaks those levels and MA conditions (if enabled) are satisfied, open trades respecting the MaxPositions limit.
Set initial stop-loss and take-profit distances from the entry price and activate trailing stops once price has moved in favour
of the trade by at least the trailing distance plus step.
Continuously monitor candles: trigger stop-loss/take-profit exits when touched, trail stops as price advances, and reset
protective levels once positions are closed.
Notes
Pip calculations adjust automatically for 3 or 5 decimal instruments to mimic MetaTrader’s point-to-pip conversion.
When using percentage risk sizing, the algorithm estimates volume from current portfolio value and the configured stop-loss.
The breakout check uses finished candles, so intra-bar spikes are evaluated at candle close/high/low levels.
MaxPositions works with the strategy’s net position. If you use fractional volumes, the parameter represents the maximum
absolute net size allowed per direction.
Charts display candles, the active moving averages when enabled, and executed trades for visual confirmation.
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;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class previous_candle_breakdown2_strategy(Strategy):
def __init__(self):
super(previous_candle_breakdown2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(previous_candle_breakdown2_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(previous_candle_breakdown2_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
if self._prev_high is None or self._prev_low is None or self._prev_fast is None or self._prev_slow is None:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
bull_trend = fv > sv
bear_trend = fv < sv
if bull_trend and close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif bear_trend and close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return previous_candle_breakdown2_strategy()