Ver en GitHub

Session Breakout Strategy

Overview

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();
		}
	}
}