Die BB Swing-Strategie ist ein getreuer Port des MetaTrader-Expert Advisors "BB SWING". Sie handelt Bollinger-Band-Pullbacks, die mit dem vorherrschenden Trend übereinstimmen, der durch zwei linear gewichtete gleitende Durchschnitte (LWMAs) definiert wird. Ein höherer Zeitrahmen-Momentum-Filter und ein sehr langsamer MACD helfen, die Stärke der Umkehr zu bestätigen, bevor eine Position eröffnet wird.
Handelslogik
Nur mit abgeschlossenen Kerzen des CandleType-Zeitrahmens arbeiten.
Die letzten vier abgeschlossenen Kerzen verfolgen, um aktuelle Extreme und Kerzenkörper zu inspizieren.
Warten, dass die schnelle LWMA über (für Longs) oder unter (für Shorts) der langsamen LWMA bleibt.
Prüfen, ob eines der letzten drei Tiefs das untere Bollinger-Band berührt (Long-Setup) oder eines der Hochs das obere Band berührt (Short-Setup).
Erfordern, dass die vorherige Kerze einen stärkeren Körper als ihre Vorgängerin hat, was Momentum weg vom Band signalisiert.
Die Trendstärke mit dem auf MomentumCandleType berechneten Momentum bestätigen. Die Strategie misst den absoluten Abstand zwischen der Momentum-Lesung und 100; der Abstand muss die konfigurierten Kauf-/Verkaufsschwellen bei einem der letzten drei Momentum-Werte überschreiten.
Die langfristige Richtung mit einem auf dem MacdCandleType-Zeitrahmen berechneten MACD validieren. Long-Einträge sind erlaubt, während die MACD-Hauptlinie über der Signallinie bleibt; Shorts erfordern das entgegengesetzte Verhältnis.
Wenn alle Bedingungen erfüllt sind, eine Marktposition mit dem aktuellen Martingal-Volumenschritt eingehen.
Positionsgrößenbestimmung und Skalierung
InitialVolume definiert das erste Einstiegsvolumen.
Jedes zusätzliche Add-on multipliziert das Basisvolumen mit LotExponent (volume = InitialVolume * LotExponent^n).
MaxTrades begrenzt die Anzahl der sequenziellen Add-ons, sodass die Gesamtpositionsgröße niemals InitialVolume * MaxTrades überschreitet.
Ausstiegs- und Schutzregeln
Feste StopLoss- und TakeProfit-Werte in Kursschritten ausgedrückt.
Optionale Break-Even-Logik (EnableBreakEven), die den Stop auf BreakEvenOffset verschiebt, sobald der Preis BreakEvenTrigger Schritte vorschreitet.
Klassischer Trailing Stop (EnableTrailingStop), der dem Extrempreis um TrailingStop Schritte folgt.
Geldverwaltungstools:
UseMoneyTakeProfit schließt Positionen, sobald der unrealisierte Gewinn in Kontowährung MoneyTakeProfit erreicht.
UsePercentTakeProfit schließt Positionen, sobald der Gewinn PercentTakeProfit Prozent des Startkapitals entspricht.
UseMoneyTrailing aktiviert einen Gewinn-Trail: sobald der Gewinn MoneyTrailTarget überschreitet, löst ein Rückgang von MoneyTrailStop einen Ausstieg aus.
UseEquityStop überwacht den Eigenkapital-Drawdown relativ zum während der Sitzung aufgezeichneten Eigenkapitalhöchststand. Ein Drawdown größer als EquityRiskPercent schließt alle Positionen.
Optionales CloseOnMacdCross steigt aus, wann immer die MACD-Hauptlinie die Signallinie gegen die aktuelle Positionsrichtung kreuzt.
Alle Schutzaktionen basieren auf Marktorders (BuyMarket / SellMarket), um die gesamte Position zu neutralisieren.
Parameter
Name
Beschreibung
InitialVolume
Basis-Trade-Volumen für den ersten Einstieg.
LotExponent
Multiplikator für das Volumen jedes zusätzlichen Einstiegs beim Skalieren.
Prozentsatz des Startkapitals der einen Ausstieg auslöst wenn UsePercentTakeProfit aktiv ist.
UseMoneyTrailing
Aktiviert geldbasiertes Trailing nach Erreichen eines Gewinnziels.
MoneyTrailTarget
Gewinnniveau das die Money-Trailing-Logik aktiviert.
MoneyTrailStop
Maximaler erlaubter Rückgang in Währung nach Aktivierung.
UseEquityStop
Aktiviert das Schließen von Positionen wenn der schwebende Drawdown einen Schwellenwert überschreitet.
EquityRiskPercent
Maximal erlaubter Eigenkapital-Drawdown in Prozent.
CloseOnMacdCross
Aktiviert MACD-basierte Ausstiegsfilterung.
CandleType
Primärer Zeitrahmen für Signalberechnungen.
MomentumCandleType
Höherer Zeitrahmen für den Momentum-Filter.
MacdCandleType
Sehr langsamer Zeitrahmen für den MACD-Ausstiegsfilter.
Hinweise
Die Strategie verarbeitet nur abgeschlossene Kerzen; sie reagiert nicht intrabar.
Alle Stop- und Zielberechnungen verwenden den vom angeschlossenen Exchange gemeldeten Instrument-Kursschritt. Stelle sicher, dass PriceStep korrekt konfiguriert ist für präzise Risikokontrolle.
Geld- und eigenkapitalbasierte Schutzmaßnahmen basieren auf den Strategie-Portfolio-Statistiken in StockSharp. Im Tester-Modus stelle sicher, dass der Portfolio-Feed aktiviert ist.
Im Gegensatz zum ursprünglichen MQL-Experten verwaltet diese C#-Implementierung eine einzelne aggregierte Position pro Richtung. Beim Skalieren wird die aggregierte Position erhöht statt mehrere diskrete Tickets zu öffnen.
Bollinger-Bänder verwenden eine feste Länge von 20 und eine Breite von 2 Standardabweichungen auf typischen Preisen, passend zum ursprünglichen Code.
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;
public class BbSwingStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public BbSwingStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bb_swing_strategy(Strategy):
def __init__(self):
super(bb_swing_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(bb_swing_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(bb_swing_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return bb_swing_strategy()