Auf GitHub ansehen

Breakthrough BB Strategy

Overview

The Breakthrough BB Strategy replicates the MetaTrader expert advisor Breakthrough_BB within the StockSharp high-level API. The system combines Bollinger Bands with a fast simple moving average to capture explosive breakouts that occur after price compresses near the band boundaries. Trades are generated exclusively on completed candles to keep signals deterministic and to mirror the original MQL5 behaviour.

Trading Logic

  • Trend filter: A simple moving average (SMA) with configurable period validates the trend direction. The strategy compares the latest SMA value with the SMA value from four bars earlier. Long trades require the SMA to slope upward, while shorts require a downward slope.
  • Bollinger Bands breakout: The strategy observes how the close from four bars ago interacted with the Bollinger upper or lower band and compares it with the most recent closing price. A valid breakout occurs when price moves from inside the band to outside the band between those two timestamps.
  • Single position model: The algorithm keeps at most one open position. Any open trade is closed before evaluating new entries to prevent overlapping exposure.

Entry Conditions

Long setup

  1. The closing price from four completed candles ago was below the upper Bollinger Band.
  2. The most recent closing price finished above the current upper Bollinger Band.
  3. The SMA value calculated on the latest candle is greater than the SMA value from four candles ago (positive slope).
  4. No position is currently open.

Short setup

  1. The closing price from four completed candles ago was above the lower Bollinger Band.
  2. The most recent closing price finished below the current lower Bollinger Band.
  3. The SMA value calculated on the latest candle is lower than the SMA value from four candles ago (negative slope).
  4. No position is currently open.

When an entry condition is satisfied the strategy sends a market order using the configured volume parameter.

Exit Rules

  • Long position exit: If a long trade is active and the latest close falls below the Bollinger middle line, the position is closed immediately with a market sell order.
  • Short position exit: If a short trade is open and the latest close climbs above the Bollinger middle line, the position is covered with a market buy order.

These exit rules mimic the original expert advisor, which removed trades whenever the market reverted back inside the band midline.

Indicators

  • Simple Moving Average (SMA): Defines the directional bias and provides the slope comparison over a four-candle interval.
  • Bollinger Bands: Supplies the upper, middle, and lower envelopes used to detect breakout entries and manage exits.

Parameters

Name Description Default Optimizable
MaPeriod Length of the SMA used for the trend filter. 9
BandsPeriod Lookback length for Bollinger Band calculations. 28
Deviation Standard deviation multiplier applied to Bollinger Bands. 1.6
Volume Order size (in lots or contracts, depending on the instrument). 1
CandleType Candle aggregation type processed by the strategy. 1 hour time frame

All parameters expose StockSharp StrategyParam metadata so they can be adjusted in the UI or optimized in the designer.

Data Requirements

  • Works with any instrument that provides candle data compatible with the selected CandleType.
  • Signals are evaluated only on finished candles. Incomplete candles are ignored to keep the logic deterministic.
  • The default configuration uses hourly candles, but any timeframe supported by the data source can be supplied.

Additional Notes

  • The algorithm refrains from using indicator history lookups and instead maintains a rolling four-bar cache for close and SMA values, staying within project guidelines.
  • Protective features such as stop-loss or take-profit can be added via StartProtection if desired; they are not part of the original MQL implementation and are therefore omitted here.
  • Because the strategy issues market orders, ensure sufficient liquidity on the chosen instrument to minimize slippage.
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 Bollinger Bands breakouts with a moving average trend filter.
/// </summary>
public class BreakthroughBbStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<int> _bandsPeriod;
	private readonly StrategyParam<decimal> _deviation;
	private readonly StrategyParam<DataType> _candleType;

	private SimpleMovingAverage _sma;
	private BollingerBands _bollingerBands;

	private decimal? _closeLag0;
	private decimal? _closeLag1;
	private decimal? _closeLag2;
	private decimal? _closeLag3;

	private decimal? _maLag0;
	private decimal? _maLag1;
	private decimal? _maLag2;
	private decimal? _maLag3;

	/// <summary>
	/// Moving average period that defines the long term trend.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Bollinger Bands lookback period.
	/// </summary>
	public int BandsPeriod
	{
		get => _bandsPeriod.Value;
		set => _bandsPeriod.Value = value;
	}

	/// <summary>
	/// Bollinger Bands width measured in standard deviations.
	/// </summary>
	public decimal Deviation
	{
		get => _deviation.Value;
		set => _deviation.Value = value;
	}


	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="BreakthroughBbStrategy"/>.
	/// </summary>
	public BreakthroughBbStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Simple moving average length", "Parameters")
			;

		_bandsPeriod = Param(nameof(BandsPeriod), 28)
			.SetGreaterThanZero()
			.SetDisplay("Bands Period", "Bollinger Bands lookback", "Parameters")
			;

		_deviation = Param(nameof(Deviation), 1.6m)
			.SetGreaterThanZero()
			.SetDisplay("Deviation", "Bollinger Bands width in deviations", "Parameters")
			;


		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle series processed by the strategy", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_sma = default;
		_bollingerBands = default;

		_closeLag0 = null;
		_closeLag1 = null;
		_closeLag2 = null;
		_closeLag3 = null;

		_maLag0 = null;
		_maLag1 = null;
		_maLag2 = null;
		_maLag3 = null;
	}

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

		_sma = new SimpleMovingAverage { Length = MaPeriod };
		_bollingerBands = new BollingerBands
		{
			Length = BandsPeriod,
			Width = Deviation
		};

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(_sma, _bollingerBands, ProcessCandle)
			.Start();
	}

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

		var smaValue = smaIndValue.IsFormed ? smaIndValue.ToDecimal() : 0m;
		var bb = bbValue as IBollingerBandsValue;
		var middleBand = bb?.MovingAverage ?? 0m;
		var upperBand = bb?.UpBand ?? 0m;
		var lowerBand = bb?.LowBand ?? 0m;

		var close = candle.ClosePrice;
		var maPrev4 = _maLag2;
		var closePrev4 = _closeLag2;

		if (_sma is null || _bollingerBands is null)
		{
			UpdateHistory(close, smaValue);
			return;
		}

		if (!_sma.IsFormed || !_bollingerBands.IsFormed)
		{
			UpdateHistory(close, smaValue);
			return;
		}

		if (Position > 0 && close < middleBand)
		{
			SellMarket();
			UpdateHistory(close, smaValue);
			return;
		}

		if (Position < 0 && close > middleBand)
		{
			BuyMarket();
			UpdateHistory(close, smaValue);
			return;
		}

		if (maPrev4 is null || closePrev4 is null)
		{
			UpdateHistory(close, smaValue);
			return;
		}

		if (Position == 0)
		{
			if (closePrev4.Value < upperBand && close > upperBand && smaValue > maPrev4.Value)
			{
				BuyMarket();
				UpdateHistory(close, smaValue);
				return;
			}

			if (closePrev4.Value > lowerBand && close < lowerBand && smaValue < maPrev4.Value)
			{
				SellMarket();
				UpdateHistory(close, smaValue);
				return;
			}
		}

		UpdateHistory(close, smaValue);
	}

	private void UpdateHistory(decimal close, decimal maValue)
	{
		_maLag3 = _maLag2;
		_maLag2 = _maLag1;
		_maLag1 = _maLag0;
		_maLag0 = maValue;

		_closeLag3 = _closeLag2;
		_closeLag2 = _closeLag1;
		_closeLag1 = _closeLag0;
		_closeLag0 = close;
	}
}