View on GitHub

BBStrategy Strategy

Overview

BBStrategy is a Bollinger Bands breakout system converted from the MetaTrader expert advisor "BBStrategy". The strategy tracks two sets of Bollinger Bands with the same period but different deviation multipliers. When price pierces the outer band the algorithm arms a trade, but an actual entry is postponed until price returns into the inner band. This behaviour attempts to avoid buying overextended breakouts or selling deeply oversold conditions while still capturing the continuation move after a volatility expansion.

Core Logic

  1. Subscribe to candles and calculate two Bollinger Bands:
    • Outer band uses a configurable deviation multiplier (default 3.0).
    • Inner band uses a lower deviation (default 2.0).
  2. Detect when the closing price finishes outside the outer band:
    • Above the upper outer band arms a long setup.
    • Below the lower outer band arms a short setup.
  3. Enter only if the next completed candle closes back inside the inner band in the direction of the breakout. While the price waits to re-enter, the strategy stays in a "wait" state for the corresponding direction.
  4. Submit a single market order when conditions align and there are no open positions or active orders. Existing opposite positions are closed by increasing the volume of the market order.
  5. Optional take-profit and stop-loss distances (expressed in points) are converted to absolute price offsets and managed via the built-in protection helper.

Parameters

Name Description
Order Volume Trade size for each position.
Bollinger Period Number of candles used for both Bollinger Band calculations.
Inner Deviation Deviation multiplier for the inner band that validates pullbacks.
Outer Deviation Deviation multiplier for the outer band that detects breakouts.
Stop-Loss Points Protective stop distance in points (0 disables the stop).
Take-Profit Points Take-profit distance in points (0 disables the target).
Candle Type Candle timeframe for calculations.

Notes

  • The strategy trades a single position at a time and ignores new signals while orders are active.
  • For risk management the helper converts MetaTrader "points" into actual price increments based on the instrument tick size.
  • Chart drawings include candles, both Bollinger Bands and the strategy's own trades for easier visual debugging.
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bollinger Bands breakout strategy.
/// When price breaks above outer band, waits for re-entry into inner band then goes long.
/// When price breaks below outer band, waits for re-entry into inner band then goes short.
/// </summary>
public class BBStrategy : Strategy
{
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _innerDeviation;
	private readonly StrategyParam<decimal> _outerDeviation;
	private readonly StrategyParam<DataType> _candleType;

	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	public decimal InnerDeviation
	{
		get => _innerDeviation.Value;
		set => _innerDeviation.Value = value;
	}

	public decimal OuterDeviation
	{
		get => _outerDeviation.Value;
		set => _outerDeviation.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public BBStrategy()
	{
		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");

		_innerDeviation = Param(nameof(InnerDeviation), 2m)
			.SetDisplay("Inner Dev", "Inner band deviations", "Indicators");

		_outerDeviation = Param(nameof(OuterDeviation), 3m)
			.SetDisplay("Outer Dev", "Outer band deviations", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle series", "General");
	}

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

		var innerBand = new BollingerBands
		{
			Length = BollingerPeriod,
			Width = InnerDeviation
		};

		var outerBand = new BollingerBands
		{
			Length = BollingerPeriod,
			Width = OuterDeviation
		};

		var waitDirection = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(innerBand, outerBand, (candle, innerVal, outerVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!innerBand.IsFormed || !outerBand.IsFormed)
					return;

				if (innerVal.IsEmpty || outerVal.IsEmpty)
					return;

				var innerBb = innerVal as IBollingerBandsValue;
				var outerBb = outerVal as IBollingerBandsValue;

				if (innerBb == null || outerBb == null)
					return;

				var innerUpper = innerBb.UpBand ?? 0;
				var innerLower = innerBb.LowBand ?? 0;
				var outerUpper = outerBb.UpBand ?? 0;
				var outerLower = outerBb.LowBand ?? 0;

				if (innerUpper == 0 || innerLower == 0 || outerUpper == 0 || outerLower == 0)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var price = candle.ClosePrice;
				var signal = 0;

				// Detect outer band breakout
				if (price > outerUpper)
					waitDirection = 1;
				else if (price < outerLower)
					waitDirection = -1;

				// Check re-entry into inner band
				if (waitDirection > 0 && price < innerUpper && price > innerLower)
				{
					signal = 1;
					waitDirection = 0;
				}
				else if (waitDirection < 0 && price > innerLower && price < innerUpper)
				{
					signal = -1;
					waitDirection = 0;
				}

				if (signal == 1 && Position <= 0)
					BuyMarket();
				else if (signal == -1 && Position >= 0)
					SellMarket();
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, innerBand);
			DrawIndicator(area, outerBand);
			DrawOwnTrades(area);
		}
	}
}