Arpit Bollinger Band Strategy
Bollinger Band breakout strategy that waits for a close outside the bands two candles ago and enters when price breaks the extreme of that bar.
- Indicators: Bollinger Bands (EMA 20, deviation 1.5)
- Entry: Long when price closed below the lower band two bars ago and current high exceeds that bar's high. Short when price closed above the upper band two bars ago and current low falls below that bar's low.
- Stops: Stop placed beyond the current candle range with a 5% buffer and take profit based on a risk‑reward ratio.
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 Band strategy - buys when price crosses below lower band (reversal),
/// sells when price crosses above upper band (reversal). Uses cooldown between trades.
/// </summary>
public class ArpitBollingerBandStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerMultiplier;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevClose;
private decimal _prevUpper;
private decimal _prevLower;
private int _barIndex;
private int _lastTradeBar;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Bollinger Bands length.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BollingerMultiplier
{
get => _bollingerMultiplier.Value;
set => _bollingerMultiplier.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ArpitBollingerBandStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Length", "Bollinger Bands length", "Bollinger");
_bollingerMultiplier = Param(nameof(BollingerMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "StdDev multiplier", "Bollinger");
_cooldownBars = Param(nameof(CooldownBars), 350)
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevUpper = 0;
_prevLower = 0;
_barIndex = 0;
_lastTradeBar = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BollingerLength,
Width = BollingerMultiplier,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
if (bbValue.IsEmpty)
return;
var bb = (BollingerBandsValue)bbValue;
var upper = bb.UpBand ?? 0m;
var lower = bb.LowBand ?? 0m;
if (upper == 0 || lower == 0)
return;
var cooldownOk = _barIndex - _lastTradeBar > CooldownBars;
// Price crosses below lower band then returns above = buy reversal
var crossUpFromBelow = _prevClose <= _prevLower && _prevLower > 0 && candle.ClosePrice > lower;
// Price crosses above upper band then returns below = sell reversal
var crossDownFromAbove = _prevClose >= _prevUpper && _prevUpper > 0 && candle.ClosePrice < upper;
if (crossUpFromBelow && Position <= 0 && cooldownOk)
{
BuyMarket();
_lastTradeBar = _barIndex;
}
else if (crossDownFromAbove && Position >= 0 && cooldownOk)
{
SellMarket();
_lastTradeBar = _barIndex;
}
_prevClose = candle.ClosePrice;
_prevUpper = upper;
_prevLower = lower;
}
}
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.Indicators import BollingerBands
from StockSharp.Algo.Strategies import Strategy
class arpit_bollinger_band_strategy(Strategy):
"""Bollinger Band reversal strategy.
Buys when price crosses below lower band then returns above.
Sells when price crosses above upper band then returns below.
"""
def __init__(self):
super(arpit_bollinger_band_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._bollinger_length = self.Param("BollingerLength", 20) \
.SetDisplay("Length", "Bollinger Bands length", "Bollinger")
self._bollinger_multiplier = self.Param("BollingerMultiplier", 2.0) \
.SetDisplay("Multiplier", "StdDev multiplier", "Bollinger")
self._cooldown_bars = self.Param("CooldownBars", 350) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading")
self._prev_close = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._bar_index = 0
self._last_trade_bar = 0
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(arpit_bollinger_band_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._bar_index = 0
self._last_trade_bar = 0
def OnStarted2(self, time):
super(arpit_bollinger_band_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bollinger_length.Value
bollinger.Width = self._bollinger_multiplier.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
if bb_value.IsEmpty:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
if upper is None or lower is None:
return
upper = float(upper)
lower = float(lower)
if upper == 0 or lower == 0:
return
close = float(candle.ClosePrice)
cooldown_ok = self._bar_index - self._last_trade_bar > self._cooldown_bars.Value
cross_up_from_below = self._prev_close <= self._prev_lower and self._prev_lower > 0 and close > lower
cross_down_from_above = self._prev_close >= self._prev_upper and self._prev_upper > 0 and close < upper
if cross_up_from_below and self.Position <= 0 and cooldown_ok:
self.BuyMarket()
self._last_trade_bar = self._bar_index
elif cross_down_from_above and self.Position >= 0 and cooldown_ok:
self.SellMarket()
self._last_trade_bar = self._bar_index
self._prev_close = close
self._prev_upper = upper
self._prev_lower = lower
def CreateClone(self):
return arpit_bollinger_band_strategy()