The Session Breakout strategy replicates the MetaTrader expert advisor "Session breakout". It watches the European morning sessi
on and measures its price range. When that range is sufficiently tight, the strategy prepares to trade breakouts during the U.S.
afternoon session using StockSharp's high-level API. The implementation enforces at most one long and one short entry per day a
nd automatically attaches protective orders (stop loss and take profit) to every position.
Trading logic
Reset the state at the beginning of every trading day and skip weekends. Mondays are optional and controlled by a parameter.
Track finished candles during the European session (default 06:00–12:00) and record the highest high and lowest low.
At the start of the U.S. session the captured range is classified as "small" when its width is less than SmallSessionThreshol dPips.
If the range is small, monitor U.S. session candles (default 12:00–16:00) and wait until at least one U.S. bar has closed (Eu ropeSessionStartHour + 5 to EuropeSessionStartHour + 10).
A long breakout is triggered when the entire candle stays above the European high plus a configurable buffer (BreakoutBuffer Pips). A short breakout requires the candle to stay below the European low minus the buffer.
After entering a position, the strategy attaches stop-loss and take-profit levels expressed in pips and prevents additional en
tries in the same direction for the rest of the day.
Parameters
Parameter
Description
Volume
Order volume used for both long and short breakouts.
EuropeSessionStartHour
Hour when the European range tracking begins.
EuropeSessionEndHour
Hour when the European range tracking stops.
UsSessionStartHour
Hour that marks the beginning of the U.S. session window.
UsSessionEndHour
Hour that marks the end of the U.S. session window.
SmallSessionThresholdPips
Maximum width (in pips) for the European range to qualify as a squeeze.
BreakoutBufferPips
Extra buffer added above/below the range before triggering breakouts.
TradeOnMonday
Enables trading on Mondays. Weekends are always skipped.
TakeProfitPips
Distance between the entry price and the take-profit level.
StopLossPips
Distance between the entry price and the stop-loss level.
CandleType
Candle series used for all calculations (15-minute candles by default).
Notes
The pip size is derived from the instrument PriceStep. Adjust the pip-based parameters to match the contract specification
s of the selected security.
Because orders are generated when a qualifying candle closes, fills happen at the close price of that candle in backtests. Liv
e fills may vary depending on market conditions.
Only one long and one short trade can be opened per day. The logic mirrors the original expert advisor behaviour while using S
tockSharp's position-based risk management helpers.
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>
/// Session breakout strategy that tracks a range during first hours and trades breakouts later.
/// Accumulates high/low during range hours (0-8), then trades breakouts during active hours (8-20).
/// </summary>
public class SessionBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _rangeStartHour;
private readonly StrategyParam<int> _rangeEndHour;
private readonly StrategyParam<int> _tradeEndHour;
private readonly StrategyParam<DataType> _candleType;
private DateTime _currentDate;
private decimal? _rangeHigh;
private decimal? _rangeLow;
private bool _rangeComplete;
private bool _tradedToday;
public int RangeStartHour { get => _rangeStartHour.Value; set => _rangeStartHour.Value = value; }
public int RangeEndHour { get => _rangeEndHour.Value; set => _rangeEndHour.Value = value; }
public int TradeEndHour { get => _tradeEndHour.Value; set => _tradeEndHour.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public SessionBreakoutStrategy()
{
_rangeStartHour = Param(nameof(RangeStartHour), 0)
.SetDisplay("Range Start", "Hour to start tracking range", "Sessions");
_rangeEndHour = Param(nameof(RangeEndHour), 8)
.SetDisplay("Range End", "Hour to stop tracking range", "Sessions");
_tradeEndHour = Param(nameof(TradeEndHour), 20)
.SetDisplay("Trade End", "Hour to stop trading", "Sessions");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentDate = default;
_rangeHigh = null;
_rangeLow = null;
_rangeComplete = false;
_tradedToday = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_currentDate = default;
_rangeHigh = null;
_rangeLow = null;
_rangeComplete = false;
_tradedToday = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var candleDate = candle.OpenTime.Date;
var hour = candle.OpenTime.Hour;
// Reset on new day
if (candleDate != _currentDate)
{
_currentDate = candleDate;
_rangeHigh = null;
_rangeLow = null;
_rangeComplete = false;
_tradedToday = false;
}
// Build range during accumulation hours
if (hour >= RangeStartHour && hour < RangeEndHour)
{
if (_rangeHigh == null || candle.HighPrice > _rangeHigh)
_rangeHigh = candle.HighPrice;
if (_rangeLow == null || candle.LowPrice < _rangeLow)
_rangeLow = candle.LowPrice;
return;
}
// Mark range as complete
if (!_rangeComplete && hour >= RangeEndHour && _rangeHigh != null && _rangeLow != null)
_rangeComplete = true;
if (!_rangeComplete || _rangeHigh == null || _rangeLow == null)
return;
// Trade during active hours
if (hour >= RangeEndHour && hour < TradeEndHour)
{
// Breakout above range high - buy
if (Position <= 0 && candle.ClosePrice > _rangeHigh.Value && !_tradedToday)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_tradedToday = true;
}
// Breakout below range low - sell
else if (Position >= 0 && candle.ClosePrice < _rangeLow.Value && !_tradedToday)
{
if (Position > 0)
SellMarket();
SellMarket();
_tradedToday = true;
}
}
// Close at end of day
if (hour >= TradeEndHour && Position != 0)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
}
}
}
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.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class session_breakout_strategy(Strategy):
def __init__(self):
super(session_breakout_strategy, self).__init__()
self._range_start = self.Param("RangeStartHour", 0).SetDisplay("Range Start", "Hour to start tracking range", "Sessions")
self._range_end = self.Param("RangeEndHour", 8).SetDisplay("Range End", "Hour to stop tracking range", "Sessions")
self._trade_end = self.Param("TradeEndHour", 20).SetDisplay("Trade End", "Hour to stop trading", "Sessions")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(session_breakout_strategy, self).OnReseted()
self._current_date = None
self._range_high = None
self._range_low = None
self._range_complete = False
self._traded_today = False
def OnStarted2(self, time):
super(session_breakout_strategy, self).OnStarted2(time)
self._current_date = None
self._range_high = None
self._range_low = None
self._range_complete = False
self._traded_today = False
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
candle_date = candle.OpenTime.Date
hour = candle.OpenTime.Hour
if self._current_date is None or candle_date != self._current_date:
self._current_date = candle_date
self._range_high = None
self._range_low = None
self._range_complete = False
self._traded_today = False
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if hour >= self._range_start.Value and hour < self._range_end.Value:
if self._range_high is None or high > self._range_high:
self._range_high = high
if self._range_low is None or low < self._range_low:
self._range_low = low
return
if not self._range_complete and hour >= self._range_end.Value and self._range_high is not None and self._range_low is not None:
self._range_complete = True
if not self._range_complete or self._range_high is None or self._range_low is None:
return
if hour >= self._range_end.Value and hour < self._trade_end.Value:
if self.Position <= 0 and close > self._range_high and not self._traded_today:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._traded_today = True
elif self.Position >= 0 and close < self._range_low and not self._traded_today:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._traded_today = True
if hour >= self._trade_end.Value and self.Position != 0:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
def CreateClone(self):
return session_breakout_strategy()