View on GitHub

Bollinger Supertrend Strategy

This strategy blends Bollinger Bands with the Supertrend indicator to pinpoint entries during strong directional moves. Bollinger Bands gauge volatility expansion while the Supertrend line tracks the overall trend and acts as a trailing stop.

Testing indicates an average annual return of about 79%. It performs best in the stocks market.

A long trade triggers when price closes above the upper Bollinger Band and remains above the Supertrend line, confirming momentum and trend alignment. A short trade occurs when price closes below the lower band while staying under the Supertrend level. Trades are exited once price crosses back through the Supertrend, indicating momentum has faded.

Because the system waits for breakouts beyond normal volatility, it suits traders looking to capture sustained runs rather than quick reversals. The Supertrend stop dynamically adjusts to market swings, helping manage risk without manual intervention.

Details

  • Entry Criteria:
    • Long: Close > upper Bollinger Band && Close > Supertrend
    • Short: Close < lower Bollinger Band && Close < Supertrend
  • Long/Short: Both sides.
  • Exit Criteria:
    • Long: Exit when price crosses below Supertrend
    • Short: Exit when price crosses above Supertrend
  • Stops: Yes, via Supertrend trailing stop.
  • Default Values:
    • BollingerPeriod = 20
    • BollingerDeviation = 2
    • SupertrendPeriod = 10
    • SupertrendMultiplier = 3
    • CandleType = TimeSpan.FromMinutes(15) A long closes when price closes below the Supertrend line and a short when it closes above it. An entry signal against an open position reverses it.
  • Filters:
    • Category: Trend
    • Direction: Both
    • Indicators: Bollinger Bands, Supertrend
    • Stops: Yes
    • Complexity: Intermediate
    • Timeframe: Intraday
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk Level: Medium
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bollinger Supertrend strategy.
/// A close above the upper Bollinger band that is also above the Supertrend line goes long and a close below the lower band that is also
/// below the Supertrend goes short, reversing an opposite position. The Supertrend is the trailing stop: a long closes when price closes
/// below the line and a short when it closes above it.
/// </summary>
public class BollingerSupertrendStrategy : Strategy
{
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerDeviation;
	private readonly StrategyParam<int> _supertrendPeriod;
	private readonly StrategyParam<decimal> _supertrendMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	/// <summary>
	/// Period of the Bollinger Bands.
	/// </summary>
	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	/// <summary>
	/// Standard deviation multiplier of the bands.
	/// </summary>
	public decimal BollingerDeviation
	{
		get => _bollingerDeviation.Value;
		set => _bollingerDeviation.Value = value;
	}

	/// <summary>
	/// ATR period of Supertrend.
	/// </summary>
	public int SupertrendPeriod
	{
		get => _supertrendPeriod.Value;
		set => _supertrendPeriod.Value = value;
	}

	/// <summary>
	/// ATR multiplier of Supertrend.
	/// </summary>
	public decimal SupertrendMultiplier
	{
		get => _supertrendMultiplier.Value;
		set => _supertrendMultiplier.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public BollingerSupertrendStrategy()
	{
		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("BB Period", "Period of the Bollinger Bands", "Bollinger");

		_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
			.SetGreaterThanZero()
			.SetDisplay("BB Deviation", "Standard deviation multiplier of the bands", "Bollinger");

		_supertrendPeriod = Param(nameof(SupertrendPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Supertrend Period", "ATR period of Supertrend", "Supertrend");

		_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 3m)
			.SetGreaterThanZero()
			.SetDisplay("Supertrend Multiplier", "ATR multiplier of Supertrend", "Supertrend");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

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

		var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };
		var supertrend = new SuperTrend { Length = SupertrendPeriod, Multiplier = SupertrendMultiplier };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(bollinger, supertrend, ProcessCandle)
			.Start();

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

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

		if (!bollingerValue.IsFormed || !supertrendValue.IsFormed)
			return;

		var bands = (BollingerBandsValue)bollingerValue;

		if (bands.UpBand is not decimal upper || bands.LowBand is not decimal lower || supertrendValue is not SuperTrendIndicatorValue trend)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var line = trend.Value;
		var close = candle.ClosePrice;

		if (close > upper && close > line && Position <= 0)
			BuyMarket(Volume + Math.Abs(Position));
		else if (close < lower && close < line && Position >= 0)
			SellMarket(Volume + Math.Abs(Position));
		else if (Position > 0 && close < line)
			SellMarket(Position);
		else if (Position < 0 && close > line)
			BuyMarket(-Position);
	}
}