La Estrategia de Rompimiento BB replica el asesor experto de MetaTrader Breakthrough_BB dentro de la API de alto nivel de StockSharp. El sistema combina Bandas de Bollinger con una media móvil simple rápida para capturar rupturas explosivas que ocurren después de que el precio se comprime cerca de los límites de la banda. Las operaciones se generan exclusivamente en velas completadas para mantener señales deterministas y reflejar el comportamiento original de MQL5.
Lógica de trading
Filtro de tendencia: Una media móvil simple (SMA) con período configurable valida la dirección de la tendencia. La estrategia compara el último valor de la SMA con el valor de la SMA de cuatro barras antes. Las operaciones largas requieren que la SMA tenga pendiente ascendente, mientras que los cortos requieren una pendiente descendente.
Ruptura de Bandas de Bollinger: La estrategia observa cómo el cierre de hace cuatro barras interactuó con la banda superior o inferior de Bollinger y lo compara con el precio de cierre más reciente. Una ruptura válida ocurre cuando el precio se mueve desde el interior de la banda hacia el exterior entre esos dos momentos.
Modelo de posición única: El algoritmo mantiene como máximo una posición abierta. Cualquier operación abierta se cierra antes de evaluar nuevas entradas para evitar exposición solapada.
Condiciones de entrada
Configuración larga
El precio de cierre de hace cuatro velas completas estaba por debajo de la Banda de Bollinger superior.
El precio de cierre más reciente terminó por encima de la Banda de Bollinger superior actual.
El valor de la SMA calculado en la última vela es mayor que el valor de la SMA de hace cuatro velas (pendiente positiva).
No hay ninguna posición abierta actualmente.
Configuración corta
El precio de cierre de hace cuatro velas completas estaba por encima de la Banda de Bollinger inferior.
El precio de cierre más reciente terminó por debajo de la Banda de Bollinger inferior actual.
El valor de la SMA calculado en la última vela es menor que el valor de la SMA de hace cuatro velas (pendiente negativa).
No hay ninguna posición abierta actualmente.
Cuando se cumple una condición de entrada, la estrategia envía una orden de mercado usando el parámetro de volumen configurado.
Reglas de salida
Salida de posición larga: Si una operación larga está activa y el último cierre cae por debajo de la línea media de Bollinger, la posición se cierra inmediatamente con una orden de venta a mercado.
Salida de posición corta: Si una operación corta está abierta y el último cierre sube por encima de la línea media de Bollinger, la posición se cubre con una orden de compra a mercado.
Estas reglas de salida imitan el asesor experto original, que eliminaba operaciones cada vez que el mercado revertía de vuelta dentro de la línea media de la banda.
Indicadores
Media Móvil Simple (SMA): Define el sesgo direccional y proporciona la comparación de pendiente en un intervalo de cuatro velas.
Bandas de Bollinger: Suministra las envolventes superior, media e inferior usadas para detectar entradas por ruptura y gestionar salidas.
Parámetros
Nombre
Descripción
Predeterminado
Optimizable
MaPeriod
Longitud de la SMA usada para el filtro de tendencia.
9
✔
BandsPeriod
Longitud de retrospectiva para los cálculos de Bandas de Bollinger.
28
✔
Deviation
Multiplicador de desviación estándar aplicado a las Bandas de Bollinger.
1.6
✔
Volume
Tamaño de la orden (en lotes o contratos, según el instrumento).
1
✔
CandleType
Tipo de agregación de velas procesado por la estrategia.
Marco temporal de 1 hora
✖
Todos los parámetros exponen metadatos StrategyParam de StockSharp para que puedan ajustarse en la interfaz o optimizarse en el diseñador.
Requisitos de datos
Funciona con cualquier instrumento que proporcione datos de velas compatibles con el CandleType seleccionado.
Las señales se evalúan solo en velas terminadas. Las velas incompletas se ignoran para mantener la lógica determinista.
La configuración predeterminada usa velas horarias, pero se puede suministrar cualquier marco temporal compatible con la fuente de datos.
Notas adicionales
El algoritmo evita búsquedas en el historial del indicador y en su lugar mantiene una caché deslizante de cuatro barras para valores de cierre y SMA, cumpliendo con las pautas del proyecto.
Las funciones de protección como stop-loss o take-profit pueden agregarse mediante StartProtection si se desea; no forman parte de la implementación MQL original y por lo tanto se omiten aquí.
Dado que la estrategia emite órdenes de mercado, asegúrese de contar con suficiente liquidez en el instrumento elegido para minimizar el deslizamiento.
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()