A estratégia monitora a contração e expansão das Bandas de Bollinger para explorar rompimentos de volatilidade. Define um squeeze como um período em que a distância entre as bandas superior e inferior se torna estreita em relação à banda do meio. Uma vez que a volatilidade se expande e o preço fecha fora da banda após um squeeze, o sistema entra na direção do rompimento.
As posições são abertas com ordens a mercado. Uma posição comprada é criada quando o preço fecha acima da banda superior após um squeeze, enquanto uma posição vendida é aberta quando o preço fecha abaixo da banda inferior. Apenas velas completadas são processadas, evitando sinais prematuros durante a formação.
O algoritmo rastreia mudanças na largura das bandas sem armazenar históricos completos de velas. Ao comparar a largura atual com a anterior, garante que uma expansão real ocorra antes de colocar ordens. Isso evita entradas durante fases prolongadas de baixa volatilidade onde nenhum rompimento se desenvolve.
Os parâmetros padrão usam uma Banda de Bollinger de 20 períodos com um multiplicador de largura de 2. O limiar de squeeze é definido em 0.05, o que significa que as bandas devem estar dentro de cinco por cento da linha do meio para registrar baixa volatilidade. O período da vela e todos os valores numéricos são totalmente configuráveis e suportam otimização no ambiente StockSharp.
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 Bands breakout strategy.
/// Enters on breakout above/below bands, exits at middle band.
/// </summary>
public class BbSqueezeStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevUpper;
private decimal _prevLower;
private bool _hasPrev;
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BbSqueezeStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Period of Bollinger Bands", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevUpper = 0;
_prevLower = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands { Length = BollingerPeriod, Width = 2m };
SubscribeCandles(CandleType).BindEx(bb, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished) return;
var bbVal = (BollingerBandsValue)value;
if (bbVal.UpBand is not decimal upper ||
bbVal.LowBand is not decimal lower ||
bbVal.MovingAverage is not decimal middle)
return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevClose = close;
_prevUpper = upper;
_prevLower = lower;
_hasPrev = true;
return;
}
// Cross above upper band => buy
if (_prevClose <= _prevUpper && close > upper && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Cross below lower band => sell
else if (_prevClose >= _prevLower && close < lower && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
// Exit long at middle
else if (Position > 0 && close < middle)
{
SellMarket();
}
// Exit short at middle
else if (Position < 0 && close > middle)
{
BuyMarket();
}
_prevClose = close;
_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 bb_squeeze_strategy(Strategy):
def __init__(self):
super(bb_squeeze_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetDisplay("Bollinger Period", "Period of Bollinger Bands", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles used", "General")
self._prev_close = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._has_prev = False
@property
def bollinger_period(self):
return self._bollinger_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bb_squeeze_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(bb_squeeze_strategy, self).OnStarted2(time)
bb = BollingerBands()
bb.Length = self.bollinger_period
bb.Width = 2.0
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def on_process(self, candle, value):
if candle.State != CandleStates.Finished:
return
upper = float(value.UpBand)
lower = float(value.LowBand)
middle = float(value.MovingAverage)
if upper == 0 or lower == 0:
return
close = float(candle.ClosePrice)
if not self._has_prev:
self._prev_close = close
self._prev_upper = upper
self._prev_lower = lower
self._has_prev = True
return
# Cross above upper band => buy
if self._prev_close <= self._prev_upper and close > upper and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Cross below lower band => sell
elif self._prev_close >= self._prev_lower and close < lower and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
# Exit long at middle
elif self.Position > 0 and close < middle:
self.SellMarket()
# Exit short at middle
elif self.Position < 0 and close > middle:
self.BuyMarket()
self._prev_close = close
self._prev_upper = upper
self._prev_lower = lower
def CreateClone(self):
return bb_squeeze_strategy()