Multi-Timeframe Bollinger Bands-Strategie
Wendet Bollinger Bands sowohl auf einem primären als auch auf einem höheren Zeitrahmen an. Handelt, wenn der Kurs die Bänder des höheren Zeitrahmens durchsticht, und filtert Einstiege optional mit einem langfristigen gleitenden Durchschnitt. Das Ziel ist es, Extremwerte gegen den übergeordneten Trend zu faden.
Die Strategie unterstützt sowohl Long- als auch Short-Positionen. Ein Stop-Loss-Prozentsatz kann für das Risikomanagement aktiviert werden. Der Einsatz mehrerer Zeitrahmen hilft, Trades gegen die dominante Marktstruktur zu vermeiden.
Details
- Einstiegskriterien:
- Long: Schluss unter dem unteren Band des höheren Zeitrahmens und über dem MA-Filter (falls aktiviert).
- Short: Schluss über dem oberen Band des höheren Zeitrahmens und unter dem MA-Filter (falls aktiviert).
- Ausstiegskriterien:
- Long: Preis schließt über dem oberen Band des aktuellen Zeitrahmens.
- Short: Preis schließt unter dem unteren Band des aktuellen Zeitrahmens.
- Indikatoren:
- Bollinger Bands auf zwei Zeitrahmen (Länge 20, Multiplikator 2)
- Optionaler EMA-Filter (Periode 200)
- Stops: Optionaler Stop-Loss über StartProtection (%-basiert).
- Standardwerte:
BBLength= 20BBMultiplier= 2.0UseMaFilter= FalseMaLength= 200SLPercent= 2
- Filter:
- Gegentrend mit MTF-Kontext
- Zeitrahmen: Haupt 5m, MTF 60m standardmäßig
- Indikatoren: Bollinger Bands, EMA
- Stops: Optional
- Komplexität: Moderat
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Multi-Timeframe Bollinger Bands Strategy.
/// Uses two BB periods: a short-period BB for exit signals
/// and a long-period BB (simulating higher timeframe) for entry signals.
/// Buys when price touches long-period lower BB, exits at short-period upper BB.
/// </summary>
public class MtfBbStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _bbShortLength;
private readonly StrategyParam<int> _bbLongLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<int> _cooldownBars;
private BollingerBands _bbShort;
private BollingerBands _bbLong;
private int _cooldownRemaining;
public MtfBbStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_bbShortLength = Param(nameof(BbShortLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Short Length", "Short-period Bollinger Bands", "Bollinger Bands");
_bbLongLength = Param(nameof(BbLongLength), 50)
.SetGreaterThanZero()
.SetDisplay("BB Long Length", "Long-period Bollinger Bands (MTF proxy)", "Bollinger Bands");
_bbMultiplier = Param(nameof(BBMultiplier), 2.0m)
.SetDisplay("BB StdDev", "Standard deviation multiplier", "Bollinger Bands");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int BbShortLength
{
get => _bbShortLength.Value;
set => _bbShortLength.Value = value;
}
public int BbLongLength
{
get => _bbLongLength.Value;
set => _bbLongLength.Value = value;
}
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bbShort = null;
_bbLong = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bbShort = new BollingerBands { Length = BbShortLength, Width = BBMultiplier };
_bbLong = new BollingerBands { Length = BbLongLength, Width = BBMultiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bbShort, _bbLong, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bbShort);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue bbShortValue, IIndicatorValue bbLongValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_bbShort.IsFormed || !_bbLong.IsFormed)
return;
if (bbShortValue.IsEmpty || bbLongValue.IsEmpty)
return;
var bbShort = (BollingerBandsValue)bbShortValue;
var bbLong = (BollingerBandsValue)bbLongValue;
if (bbShort.UpBand is not decimal shortUpper || bbShort.LowBand is not decimal shortLower)
return;
if (bbLong.UpBand is not decimal longUpper || bbLong.LowBand is not decimal longLower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Buy: price touches long-period lower BB (oversold on higher timeframe)
if (candle.ClosePrice <= longLower && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: price touches long-period upper BB (overbought on higher timeframe)
else if (candle.ClosePrice >= longUpper && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long at short-period upper BB
else if (Position > 0 && candle.ClosePrice >= shortUpper)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short at short-period lower BB
else if (Position < 0 && candle.ClosePrice <= shortLower)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class mtf_bb_strategy(Strategy):
"""Multi-Timeframe Bollinger Bands Strategy."""
def __init__(self):
super(mtf_bb_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._bb_short_length = self.Param("BbShortLength", 20) \
.SetDisplay("BB Short Length", "Short-period Bollinger Bands", "Bollinger Bands")
self._bb_long_length = self.Param("BbLongLength", 50) \
.SetDisplay("BB Long Length", "Long-period Bollinger Bands (MTF proxy)", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 2.0) \
.SetDisplay("BB StdDev", "Standard deviation multiplier", "Bollinger Bands")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._bb_short = None
self._bb_long = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mtf_bb_strategy, self).OnReseted()
self._bb_short = None
self._bb_long = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(mtf_bb_strategy, self).OnStarted2(time)
self._bb_short = BollingerBands()
self._bb_short.Length = int(self._bb_short_length.Value)
self._bb_short.Width = float(self._bb_multiplier.Value)
self._bb_long = BollingerBands()
self._bb_long.Length = int(self._bb_long_length.Value)
self._bb_long.Width = float(self._bb_multiplier.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._bb_short, self._bb_long, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._bb_short)
self.DrawOwnTrades(area)
def _on_process(self, candle, bb_short_value, bb_long_value):
if candle.State != CandleStates.Finished:
return
if not self._bb_short.IsFormed or not self._bb_long.IsFormed:
return
if bb_short_value.IsEmpty or bb_long_value.IsEmpty:
return
if bb_short_value.UpBand is None or bb_short_value.LowBand is None:
return
if bb_long_value.UpBand is None or bb_long_value.LowBand is None:
return
short_upper = float(bb_short_value.UpBand)
short_lower = float(bb_short_value.LowBand)
long_upper = float(bb_long_value.UpBand)
long_lower = float(bb_long_value.LowBand)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
if close <= long_lower and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif close >= long_upper and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and close >= short_upper:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and close <= short_lower:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return mtf_bb_strategy()