Die Ausbruch-BB-Strategie repliziert den MetaTrader Expert Advisor Breakthrough_BB innerhalb der StockSharp High-Level-API. Das System kombiniert Bollinger Bänder mit einem schnellen einfachen gleitenden Durchschnitt, um explosive Ausbrüche zu erfassen, die auftreten, nachdem sich der Preis nahe den Bandgrenzen komprimiert hat. Trades werden ausschließlich auf abgeschlossenen Kerzen generiert, um Signale deterministisch zu halten und das ursprüngliche MQL5-Verhalten widerzuspiegeln.
Handelslogik
Trendfilter: Ein einfacher gleitender Durchschnitt (SMA) mit konfigurierbarem Zeitraum validiert die Trendrichtung. Die Strategie vergleicht den letzten SMA-Wert mit dem SMA-Wert von vier Kerzen zuvor. Long-Trades erfordern einen steigenden SMA, während Shorts einen fallenden Verlauf erfordern.
Bollinger Bänder Ausbruch: Die Strategie beobachtet, wie der Schlusskurs von vier Kerzen zuvor mit dem oberen oder unteren Bollinger Band interagierte, und vergleicht ihn mit dem jüngsten Schlusskurs. Ein gültiger Ausbruch tritt auf, wenn der Preis zwischen diesen zwei Zeitstempeln von innerhalb des Bandes nach außen wechselt.
Einzelpositionsmodell: Der Algorithmus hält maximal eine offene Position. Jeder offene Trade wird geschlossen, bevor neue Einstiege bewertet werden, um überlappende Engagements zu verhindern.
Einstiegsbedingungen
Long-Setup
Der Schlusskurs von vier abgeschlossenen Kerzen zuvor lag unter dem oberen Bollinger Band.
Der jüngste Schlusskurs schloss über dem aktuellen oberen Bollinger Band.
Der auf der letzten Kerze berechnete SMA-Wert ist größer als der SMA-Wert von vier Kerzen zuvor (positive Steigung).
Keine Position ist aktuell offen.
Short-Setup
Der Schlusskurs von vier abgeschlossenen Kerzen zuvor lag über dem unteren Bollinger Band.
Der jüngste Schlusskurs schloss unter dem aktuellen unteren Bollinger Band.
Der auf der letzten Kerze berechnete SMA-Wert ist niedriger als der SMA-Wert von vier Kerzen zuvor (negative Steigung).
Keine Position ist aktuell offen.
Wenn eine Einstiegsbedingung erfüllt ist, sendet die Strategie eine Marktorder mit dem konfigurierten Volumenparameter.
Ausstiegsregeln
Long-Position Ausstieg: Wenn ein Long-Trade aktiv ist und der letzte Schlusskurs unter die Bollinger-Mittellinie fällt, wird die Position sofort mit einer Markt-Verkaufsorder geschlossen.
Short-Position Ausstieg: Wenn ein Short-Trade offen ist und der letzte Schlusskurs über die Bollinger-Mittellinie steigt, wird die Position mit einer Markt-Kauforder gedeckt.
Diese Ausstiegsregeln imitieren den ursprünglichen Expert Advisor, der Trades entfernte, wann immer der Markt zur Bandmittellinie zurückkehrte.
Indikatoren
Einfacher gleitender Durchschnitt (SMA): Definiert die Richtungsneigung und liefert den Steigungsvergleich über ein Vier-Kerzen-Intervall.
Bollinger Bänder: Liefert die obere, mittlere und untere Hülle, die zur Erkennung von Ausbruchseinstiegen und zur Verwaltung von Ausstiegen verwendet werden.
Parameter
Name
Beschreibung
Standard
Optimierbar
MaPeriod
Länge des SMA für den Trendfilter.
9
✔
BandsPeriod
Rückblicklänge für Bollinger-Band-Berechnungen.
28
✔
Deviation
Standardabweichungsmultiplikator für Bollinger Bänder.
1.6
✔
Volume
Ordergröße (in Lots oder Kontrakten, je nach Instrument).
1
✔
CandleType
Von der Strategie verarbeiteter Kerzenaggregationstyp.
Zeitrahmen 1 Stunde
✖
Alle Parameter exponieren StockSharp StrategyParam-Metadaten, sodass sie in der UI angepasst oder im Designer optimiert werden können.
Datenanforderungen
Funktioniert mit jedem Instrument, das Kerzendaten bereitstellt, die mit dem ausgewählten CandleType kompatibel sind.
Signale werden nur auf abgeschlossenen Kerzen ausgewertet. Unvollständige Kerzen werden ignoriert, um die Logik deterministisch zu halten.
Die Standardkonfiguration verwendet stündliche Kerzen, aber jeder von der Datenquelle unterstützte Zeitrahmen kann angegeben werden.
Zusätzliche Hinweise
Der Algorithmus verzichtet auf Indikator-Historien-Abfragen und pflegt stattdessen einen rollierenden Vier-Kerzen-Cache für Schluss- und SMA-Werte, entsprechend den Projektrichtlinien.
Schutzfunktionen wie Stop-Loss oder Take-Profit können bei Bedarf über StartProtection hinzugefügt werden; sie sind kein Teil der ursprünglichen MQL-Implementierung und werden daher hier weggelassen.
Da die Strategie Marktorders ausgibt, stellen Sie ausreichende Liquidität auf dem gewählten Instrument sicher, um Slippage zu minimieren.
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()