A Estratégia de Rompimento BB replica o expert advisor MetaTrader Breakthrough_BB dentro da API de alto nível do StockSharp. O sistema combina Bandas de Bollinger com uma média móvel simples rápida para capturar rompimentos explosivos que ocorrem após a compressão do preço perto dos limites da banda. As operações são geradas exclusivamente em candles completos para manter sinais determinísticos e espelhar o comportamento original do MQL5.
Lógica de negociação
Filtro de tendência: Uma média móvel simples (SMA) com período configurável valida a direção da tendência. A estratégia compara o último valor da SMA com o valor da SMA de quatro barras antes. Operações compradas exigem que a SMA tenha inclinação ascendente, enquanto vendidas exigem inclinação descendente.
Rompimento das Bandas de Bollinger: A estratégia observa como o fechamento de quatro barras atrás interagiu com a banda superior ou inferior de Bollinger e compara com o preço de fechamento mais recente. Um rompimento válido ocorre quando o preço se move de dentro da banda para fora entre esses dois momentos.
Modelo de posição única: O algoritmo mantém no máximo uma posição aberta. Qualquer operação aberta é fechada antes de avaliar novas entradas para evitar exposição sobreposta.
Condições de entrada
Configuração comprada
O preço de fechamento de quatro candles atrás estava abaixo da Banda de Bollinger superior.
O preço de fechamento mais recente terminou acima da Banda de Bollinger superior atual.
O valor da SMA calculado no último candle é maior que o valor da SMA de quatro candles atrás (inclinação positiva).
Nenhuma posição está aberta atualmente.
Configuração vendida
O preço de fechamento de quatro candles atrás estava acima da Banda de Bollinger inferior.
O preço de fechamento mais recente terminou abaixo da Banda de Bollinger inferior atual.
O valor da SMA calculado no último candle é menor que o valor da SMA de quatro candles atrás (inclinação negativa).
Nenhuma posição está aberta atualmente.
Quando uma condição de entrada é satisfeita, a estratégia envia uma ordem a mercado usando o parâmetro de volume configurado.
Regras de saída
Saída de posição comprada: Se uma operação comprada estiver ativa e o último fechamento cair abaixo da linha média de Bollinger, a posição é fechada imediatamente com uma ordem de venda a mercado.
Saída de posição vendida: Se uma operação vendida estiver aberta e o último fechamento subir acima da linha média de Bollinger, a posição é coberta com uma ordem de compra a mercado.
Essas regras de saída imitam o expert advisor original, que removia operações sempre que o mercado revertia de volta para dentro da linha média da banda.
Indicadores
Média Móvel Simples (SMA): Define o viés direcional e fornece a comparação de inclinação em um intervalo de quatro candles.
Bandas de Bollinger: Fornece os envelopes superior, médio e inferior usados para detectar entradas por rompimento e gerenciar saídas.
Parâmetros
Nome
Descrição
Padrão
Otimizável
MaPeriod
Comprimento da SMA usada para o filtro de tendência.
9
✔
BandsPeriod
Comprimento de retrospectiva para cálculos das Bandas de Bollinger.
28
✔
Deviation
Multiplicador de desvio padrão aplicado às Bandas de Bollinger.
1.6
✔
Volume
Tamanho da ordem (em lotes ou contratos, dependendo do instrumento).
1
✔
CandleType
Tipo de agregação de candles processado pela estratégia.
Período de 1 hora
✖
Todos os parâmetros expõem metadados StrategyParam do StockSharp para que possam ser ajustados na interface ou otimizados no designer.
Requisitos de dados
Funciona com qualquer instrumento que forneça dados de candles compatíveis com o CandleType selecionado.
Os sinais são avaliados apenas em candles concluídos. Candles incompletos são ignorados para manter a lógica determinística.
A configuração padrão usa candles horários, mas qualquer período suportado pela fonte de dados pode ser fornecido.
Notas adicionais
O algoritmo evita consultas ao histórico do indicador e mantém um cache deslizante de quatro barras para valores de fechamento e SMA, seguindo as diretrizes do projeto.
Recursos de proteção como stop-loss ou take-profit podem ser adicionados via StartProtection se desejado; eles não fazem parte da implementação MQL original e, portanto, são omitidos aqui.
Como a estratégia emite ordens a mercado, garanta liquidez suficiente no instrumento escolhido para minimizar o deslizamento.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy that trades Bollinger Bands breakouts with a moving average trend filter.
/// </summary>
public class BreakthroughBbStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _bandsPeriod;
private readonly StrategyParam<decimal> _deviation;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private BollingerBands _bollingerBands;
private decimal? _closeLag0;
private decimal? _closeLag1;
private decimal? _closeLag2;
private decimal? _closeLag3;
private decimal? _maLag0;
private decimal? _maLag1;
private decimal? _maLag2;
private decimal? _maLag3;
/// <summary>
/// Moving average period that defines the long term trend.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands lookback period.
/// </summary>
public int BandsPeriod
{
get => _bandsPeriod.Value;
set => _bandsPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands width measured in standard deviations.
/// </summary>
public decimal Deviation
{
get => _deviation.Value;
set => _deviation.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="BreakthroughBbStrategy"/>.
/// </summary>
public BreakthroughBbStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Simple moving average length", "Parameters")
;
_bandsPeriod = Param(nameof(BandsPeriod), 28)
.SetGreaterThanZero()
.SetDisplay("Bands Period", "Bollinger Bands lookback", "Parameters")
;
_deviation = Param(nameof(Deviation), 1.6m)
.SetGreaterThanZero()
.SetDisplay("Deviation", "Bollinger Bands width in deviations", "Parameters")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series processed by the strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = default;
_bollingerBands = default;
_closeLag0 = null;
_closeLag1 = null;
_closeLag2 = null;
_closeLag3 = null;
_maLag0 = null;
_maLag1 = null;
_maLag2 = null;
_maLag3 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = MaPeriod };
_bollingerBands = new BollingerBands
{
Length = BandsPeriod,
Width = Deviation
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_sma, _bollingerBands, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaIndValue, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
var smaValue = smaIndValue.IsFormed ? smaIndValue.ToDecimal() : 0m;
var bb = bbValue as IBollingerBandsValue;
var middleBand = bb?.MovingAverage ?? 0m;
var upperBand = bb?.UpBand ?? 0m;
var lowerBand = bb?.LowBand ?? 0m;
var close = candle.ClosePrice;
var maPrev4 = _maLag2;
var closePrev4 = _closeLag2;
if (_sma is null || _bollingerBands is null)
{
UpdateHistory(close, smaValue);
return;
}
if (!_sma.IsFormed || !_bollingerBands.IsFormed)
{
UpdateHistory(close, smaValue);
return;
}
if (Position > 0 && close < middleBand)
{
SellMarket();
UpdateHistory(close, smaValue);
return;
}
if (Position < 0 && close > middleBand)
{
BuyMarket();
UpdateHistory(close, smaValue);
return;
}
if (maPrev4 is null || closePrev4 is null)
{
UpdateHistory(close, smaValue);
return;
}
if (Position == 0)
{
if (closePrev4.Value < upperBand && close > upperBand && smaValue > maPrev4.Value)
{
BuyMarket();
UpdateHistory(close, smaValue);
return;
}
if (closePrev4.Value > lowerBand && close < lowerBand && smaValue < maPrev4.Value)
{
SellMarket();
UpdateHistory(close, smaValue);
return;
}
}
UpdateHistory(close, smaValue);
}
private void UpdateHistory(decimal close, decimal maValue)
{
_maLag3 = _maLag2;
_maLag2 = _maLag1;
_maLag1 = _maLag0;
_maLag0 = maValue;
_closeLag3 = _closeLag2;
_closeLag2 = _closeLag1;
_closeLag1 = _closeLag0;
_closeLag0 = close;
}
}
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 SimpleMovingAverage, BollingerBands
from StockSharp.Algo.Strategies import Strategy
class breakthrough_bb_strategy(Strategy):
def __init__(self):
super(breakthrough_bb_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 9)
self._bands_period = self.Param("BandsPeriod", 28)
self._deviation = self.Param("Deviation", 1.6)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._close_lag0 = None
self._close_lag1 = None
self._close_lag2 = None
self._close_lag3 = None
self._ma_lag0 = None
self._ma_lag1 = None
self._ma_lag2 = None
self._ma_lag3 = None
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
@property
def BandsPeriod(self):
return self._bands_period.Value
@BandsPeriod.setter
def BandsPeriod(self, value):
self._bands_period.Value = value
@property
def Deviation(self):
return self._deviation.Value
@Deviation.setter
def Deviation(self, value):
self._deviation.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(breakthrough_bb_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.MaPeriod
self._bollinger = BollingerBands()
self._bollinger.Length = self.BandsPeriod
self._bollinger.Width = self.Deviation
self._close_lag0 = None
self._close_lag1 = None
self._close_lag2 = None
self._close_lag3 = None
self._ma_lag0 = None
self._ma_lag1 = None
self._ma_lag2 = None
self._ma_lag3 = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._sma, self._bollinger, self.ProcessCandle).Start()
def ProcessCandle(self, candle, sma_ind_value, bb_value):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma_ind_value) if sma_ind_value.IsFormed else 0.0
mid_band = float(bb_value.MovingAverage) if bb_value.MovingAverage is not None else 0.0
upper = float(bb_value.UpBand) if bb_value.UpBand is not None else 0.0
lower = float(bb_value.LowBand) if bb_value.LowBand is not None else 0.0
close = float(candle.ClosePrice)
if not self._sma.IsFormed or not self._bollinger.IsFormed:
self._update_history(close, sma_val)
return
# Exit on middle band cross
if self.Position > 0 and close < mid_band:
self.SellMarket()
self._update_history(close, sma_val)
return
if self.Position < 0 and close > mid_band:
self.BuyMarket()
self._update_history(close, sma_val)
return
ma_prev4 = self._ma_lag2
close_prev4 = self._close_lag2
if ma_prev4 is None or close_prev4 is None:
self._update_history(close, sma_val)
return
if self.Position == 0:
if close_prev4 < upper and close > upper and sma_val > ma_prev4:
self.BuyMarket()
self._update_history(close, sma_val)
return
if close_prev4 > lower and close < lower and sma_val < ma_prev4:
self.SellMarket()
self._update_history(close, sma_val)
return
self._update_history(close, sma_val)
def _update_history(self, close, ma_value):
self._ma_lag3 = self._ma_lag2
self._ma_lag2 = self._ma_lag1
self._ma_lag1 = self._ma_lag0
self._ma_lag0 = ma_value
self._close_lag3 = self._close_lag2
self._close_lag2 = self._close_lag1
self._close_lag1 = self._close_lag0
self._close_lag0 = close
def OnReseted(self):
super(breakthrough_bb_strategy, self).OnReseted()
self._close_lag0 = None
self._close_lag1 = None
self._close_lag2 = None
self._close_lag3 = None
self._ma_lag0 = None
self._ma_lag1 = None
self._ma_lag2 = None
self._ma_lag3 = None
def CreateClone(self):
return breakthrough_bb_strategy()