Hybrides Trendfolge-System, das Bollinger Band-Umkehrungen, zwei gleitende Durchschnitte höherer Zeitrahmen und Swing-Punkte eines ZigZag-Detektors kombiniert. Bei jedem Signal werden zwei Positionen eröffnet: eine mit einem berechneten Take-Profit-Ziel und eine zweite "Läufer"-Position, die auf Trailing- und Break-even-Logik basiert.
Details
Einstiegskriterien:
Long: Die vorherige Kerze schloss oberhalb des vorherigen unteren Bollinger-Bandes, nachdem sie zwei Kerzen zuvor darunter schloss, der aktuelle Schlusskurs liegt ebenfalls über diesem unteren Band, und der Preis liegt oberhalb beider gleitender Durchschnitte höherer Zeitrahmen.
Short: Die vorherige Kerze schloss unterhalb des vorherigen oberen Bollinger-Bandes, nachdem sie zwei Kerzen zuvor darüber schloss, der aktuelle Schlusskurs liegt ebenfalls unter diesem oberen Band, und der Preis liegt unterhalb beider gleitender Durchschnitte höherer Zeitrahmen.
Positionsmanagement:
Pro Signal werden zwei Positionen mit First Volume (mit Take-Profit) und Second Volume (Läufer) eröffnet.
Stops sind am jüngsten ZigZag-Swing-Extrem minus/plus Pivot Offset (pts) verankert.
Der Break-even-Schutz verschiebt den Stop auf den Einstieg plus einem Offset, sobald der unrealisierte Gewinn Break-even Threshold (pts) + Break-even Offset (pts) überschreitet.
Der Trailing Stop bewegt sich, wenn der Preis um Trailing Step (pts) über den bestehenden Stop hinaus vorrückt, und hält dabei einen Abstand von Trailing Stop (pts).
Take Profit:
Der Take-Profit der ersten Position wird als Prozentsatz (Take Profit %) des Abstands zwischen Einstieg und Stop berechnet.
Die Läufer-Position hat kein festes Ziel und wird über Stop, Trailing oder entgegengesetzte Signale beendet.
Zusätzliche Logik:
Entgegengesetzte Signale schließen sofort alle offenen Positionen in der anderen Richtung, bevor neue Trades eingegangen werden.
Die Signalverarbeitung verwendet geschlossene Kerzen; Teildaten werden ignoriert.
Die Strategie benötigt Kerzen-Abonnements auf drei verschiedenen Zeitrahmen, um die Filter auszuwerten und Ausstiege zu verwalten.
Die Swing-Erkennung approximiert die MetaTrader ZigZag-Logik, indem Mindesttiefe, Abweichung und Backstep-Regeln vor der Aktualisierung der Pivot-Niveaus durchgesetzt werden.
Die Volumina können unabhängig voneinander angepasst werden, um das Verhältnis des Take-Profit-Beins zum Läufer-Bein zu optimieren.
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;
/// <summary>
/// Bollinger Band Two MA ZigZag strategy (simplified). Uses EMA crossover
/// with Highest/Lowest channel for swing-based entries.
/// </summary>
public class BollingerBandTwoMaZigZagStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaFastLength;
private readonly StrategyParam<int> _emaSlowLength;
private readonly StrategyParam<int> _channelLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaFastLength
{
get => _emaFastLength.Value;
set => _emaFastLength.Value = value;
}
public int EmaSlowLength
{
get => _emaSlowLength.Value;
set => _emaSlowLength.Value = value;
}
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
public BollingerBandTwoMaZigZagStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_emaFastLength = Param(nameof(EmaFastLength), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators");
_emaSlowLength = Param(nameof(EmaSlowLength), 30)
.SetGreaterThanZero()
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators");
_channelLength = Param(nameof(ChannelLength), 20)
.SetGreaterThanZero()
.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFast = new ExponentialMovingAverage { Length = EmaFastLength };
var emaSlow = new ExponentialMovingAverage { Length = EmaSlowLength };
decimal prevFast = 0, prevSlow = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaFast, emaSlow, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastVal;
prevSlow = slowVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastVal;
prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
// EMA crossover combined with price confirmation
var bullishCross = prevFast <= prevSlow && fastVal > slowVal;
var bearishCross = prevFast >= prevSlow && fastVal < slowVal;
if (bullishCross && Position <= 0 && close > fastVal)
BuyMarket();
else if (bearishCross && Position >= 0 && close < fastVal)
SellMarket();
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaFast);
DrawIndicator(area, emaSlow);
DrawOwnTrades(area);
}
}
}
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 bollinger_band_two_ma_zig_zag_strategy(Strategy):
def __init__(self):
super(bollinger_band_two_ma_zig_zag_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ema_fast_length = self.Param("EmaFastLength", 10) \
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators")
self._ema_slow_length = self.Param("EmaSlowLength", 30) \
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaFastLength(self):
return self._ema_fast_length.Value
@property
def EmaSlowLength(self):
return self._ema_slow_length.Value
def OnReseted(self):
super(bollinger_band_two_ma_zig_zag_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(bollinger_band_two_ma_zig_zag_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self.EmaFastLength
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self.EmaSlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema_fast, ema_slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_fast)
self.DrawIndicator(area, ema_slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
close = float(candle.ClosePrice)
bullish_cross = self._prev_fast <= self._prev_slow and fv > sv
bearish_cross = self._prev_fast >= self._prev_slow and fv < sv
if bullish_cross and self.Position <= 0 and close > fv:
self.BuyMarket()
elif bearish_cross and self.Position >= 0 and close < fv:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return bollinger_band_two_ma_zig_zag_strategy()