Bollinger Bands Sofisticadas
Esta estratégia opera rompimentos de Bollinger Bands. Compra quando o preço de fechamento cruza acima da banda superior e vende quando o preço de fechamento cruza abaixo da banda inferior.
Detalhes
- Condições de entrada:
- Comprado: fechamento cruza acima da banda superior
- Vendido: fechamento cruza abaixo da banda inferior
- Condições de saída: cruzamento inverso
- Tipo: Rompimento
- Indicadores: Bollinger Bands
- Período: 1 minuto (padrão)
- Stops: nenhum
Regra verificável e padrões. As bandas de Bollinger usam os últimos 20 fechamentos concluídos, incluindo o candle de sinal, com dois desvios-padrão populacionais. BollingerPeriod=20, DeviationMultiplier=2, CandleType=1 minuto. Sem posição, compra quando o fechamento cruza acima da banda superior e vende a descoberto quando cruza abaixo da banda inferior. Encerra toda a posição comprada quando o fechamento retorna para dentro da banda superior e toda a vendida quando retorna para dentro da banda inferior. As bandas anterior e atual usam somente os dados fechados dos respectivos candles. Sem stops, alvos ou reversão no mesmo candle.
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades crossings of the upper/lower Bollinger bands and closes on a reverse crossing
/// back inside the broken band. Band values use completed closes, including the signal bar.
/// </summary>
public class FancyBollingerBandsStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _closes = new();
private decimal? _previousClose;
private decimal? _previousUpper;
private decimal? _previousLower;
private bool _entryRequested;
private bool _exitRequested;
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public decimal DeviationMultiplier { get => _deviationMultiplier.Value; set => _deviationMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FancyBollingerBandsStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Number of completed closes in each band calculation.", "Bollinger");
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for both bands.", "Bollinger");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe.", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_closes.Clear();
_previousClose = null;
_previousUpper = null;
_previousLower = null;
_entryRequested = false;
_exitRequested = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_closes.Enqueue(candle.ClosePrice);
while (_closes.Count > BollingerPeriod)
_closes.Dequeue();
if (_closes.Count < BollingerPeriod)
return;
var mean = _closes.Average();
var variance = _closes.Sum(price => (price - mean) * (price - mean)) / BollingerPeriod;
var deviation = (decimal)Math.Sqrt((double)variance) * DeviationMultiplier;
var upper = mean + deviation;
var lower = mean - deviation;
var close = candle.ClosePrice;
if (_previousClose is decimal previous && _previousUpper is decimal previousUpper
&& _previousLower is decimal previousLower)
{
if (Position != 0m)
{
_entryRequested = false;
if (!_exitRequested)
{
if (Position > 0m && previous >= previousUpper && close <= upper)
{
_exitRequested = true;
SellMarket(Position);
}
else if (Position < 0m && previous <= previousLower && close >= lower)
{
_exitRequested = true;
BuyMarket(Math.Abs(Position));
}
}
}
else
{
_exitRequested = false;
if (!_entryRequested)
{
if (previous <= previousUpper && close > upper)
{
_entryRequested = true;
BuyMarket(Volume);
}
else if (previous >= previousLower && close < lower)
{
_entryRequested = true;
SellMarket(Volume);
}
}
}
}
_previousClose = close;
_previousUpper = upper;
_previousLower = lower;
}
}
import clr
import math
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class fancy_bollinger_bands_strategy(Strategy):
"""
Buy an upper Bollinger-band close crossover and sell a lower-band crossover.
Exit when the close crosses back inside the broken band.
"""
def __init__(self):
super(fancy_bollinger_bands_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Period", "Number of completed closes in each band calculation.", "Bollinger")
self._deviation_multiplier = self.Param("DeviationMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for both bands.", "Bollinger")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Candle timeframe.", "General")
self._closes = []
self._previous_close = None
self._previous_upper = None
self._previous_lower = None
self._entry_requested = False
self._exit_requested = False
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(fancy_bollinger_bands_strategy, self).OnReseted()
self._closes = []
self._previous_close = None
self._previous_upper = None
self._previous_lower = None
self._entry_requested = False
self._exit_requested = False
def OnStarted2(self, time):
super(fancy_bollinger_bands_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.Bind(self.on_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_candle(self, candle):
if candle.State != CandleStates.Finished:
return
period = int(self._bollinger_period.Value)
close = float(candle.ClosePrice)
self._closes.append(close)
if len(self._closes) > period:
self._closes.pop(0)
if len(self._closes) < period:
return
mean = sum(self._closes) / period
variance = sum((price - mean) ** 2 for price in self._closes) / period
offset = math.sqrt(variance) * float(self._deviation_multiplier.Value)
upper = mean + offset
lower = mean - offset
if self._previous_upper is not None and self._previous_lower is not None:
previous = self._previous_close
position = self.Position
if position != 0:
self._entry_requested = False
if not self._exit_requested:
if position > 0 and previous >= self._previous_upper and close <= upper:
self._exit_requested = True
self.SellMarket(position)
elif position < 0 and previous <= self._previous_lower and close >= lower:
self._exit_requested = True
self.BuyMarket(Math.Abs(position))
else:
self._exit_requested = False
if not self._entry_requested:
if previous <= self._previous_upper and close > upper:
self._entry_requested = True
self.BuyMarket(self.Volume)
elif previous >= self._previous_lower and close < lower:
self._entry_requested = True
self.SellMarket(self.Volume)
self._previous_close = close
self._previous_upper = upper
self._previous_lower = lower
def CreateClone(self):
return fancy_bollinger_bands_strategy()