Bollinger Ausbruch-Strategie
Bollinger Ausbruch versucht, Bewegungen zu erfassen, die über die Bollinger Bands hinausgehen und weiter in dieselbe Richtung laufen. Wenn der Preis über das obere Band oder unter das untere Band schließt, tritt die Strategie in Richtung des Ausbruchs ein, sofern optionale Bestätigungen den Trade unterstützen.
RSI-, Aroon- und Moving-Average-Filter können aktiviert werden, um Momentum und Trend zu validieren. Ein optionaler Stop-Loss hilft, das Risiko zu kontrollieren. Positionen werden geschlossen, wenn der Preis das gegenüberliegende Band erreicht oder der Stop ausgelöst wird.
Dieser Ansatz bevorzugt Märkte, die zu starken Trends neigen, bei denen Band-Ausbrüche zu einer Fortsetzung statt zu Mean Reversion führen.
Details
- Daten: Kurskerzen.
- Einstiegskriterien:
- Long: Schlusskurs über oberem Band und alle aktivierten Filter bestätigen.
- Short: Schlusskurs unter unterem Band und alle aktivierten Filter bestätigen.
- Ausstiegskriterien: Berührung des gegenüberliegenden Bandes oder Stop-Loss wenn
UseSL. - Stops: Optionaler Stop-Loss (
UseSL). - Standardwerte:
UseRSI= TrueUseAroon= FalseUseMA= TrueUseSL= True
- Filter:
- Kategorie: Ausbruch
- Richtung: Long/Short
- Indikatoren: Bollinger Bands, RSI, Aroon, Moving Average
- Komplexität: Moderat
- Risikolevel: Hoch
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>
/// Bollinger Breakout Strategy.
/// Buys when candle body extends below lower BB with RSI oversold filter.
/// Sells when candle body extends above upper BB with RSI overbought filter.
/// </summary>
public class BollingerBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiOversold;
private readonly StrategyParam<int> _rsiOverbought;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<decimal> _candlePercent;
private readonly StrategyParam<int> _cooldownBars;
private BollingerBands _bollinger;
private RelativeStrengthIndex _rsi;
private ExponentialMovingAverage _ma;
private decimal? _entryPrice;
private int _cooldownRemaining;
public BollingerBreakoutStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands");
_bbMultiplier = Param(nameof(BBMultiplier), 1.5m)
.SetDisplay("BB StdDev", "Standard deviation multiplier", "Bollinger Bands");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI Filter");
_rsiOversold = Param(nameof(RSIOversold), 45)
.SetDisplay("RSI Oversold", "RSI oversold level", "RSI Filter");
_rsiOverbought = Param(nameof(RSIOverbought), 55)
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI Filter");
_maLength = Param(nameof(MALength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving Average period", "Moving Average");
_candlePercent = Param(nameof(CandlePercent), 0.3m)
.SetDisplay("Candle %", "Candle body penetration percentage", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int RSIOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
public int RSIOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
public int MALength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
public decimal CandlePercent
{
get => _candlePercent.Value;
set => _candlePercent.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();
_bollinger = null;
_rsi = null;
_ma = null;
_entryPrice = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands
{
Length = BBLength,
Width = BBMultiplier
};
_rsi = new RelativeStrengthIndex { Length = RSILength };
_ma = new ExponentialMovingAverage { Length = MALength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bollinger, _rsi, _ma, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue rsiValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_bollinger.IsFormed || !_rsi.IsFormed || !_ma.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
if (rsiValue.IsEmpty || maValue.IsEmpty)
return;
var rsi = rsiValue.ToDecimal();
var maVal = maValue.ToDecimal();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var close = candle.ClosePrice;
var open = candle.OpenPrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var candleSize = high - low;
if (candleSize <= 0)
return;
var buyZone = candleSize * CandlePercent + low;
var sellZone = high - candleSize * CandlePercent;
// Buy: candle buy zone below lower BB, bearish candle, RSI oversold, price above MA
var buySignal = buyZone < lower && close < open && rsi < RSIOversold && close > maVal;
// Sell: candle sell zone above upper BB, bullish candle, RSI overbought, price below MA
var sellSignal = sellZone > upper && close > open && rsi > RSIOverbought && close < maVal;
// Early exit at middle band
if (Position > 0 && close >= middle)
{
SellMarket(Math.Abs(Position));
_entryPrice = null;
_cooldownRemaining = CooldownBars;
return;
}
else if (Position < 0 && close <= middle)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_cooldownRemaining = CooldownBars;
return;
}
// Entry signals
if (buySignal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = close;
_cooldownRemaining = CooldownBars;
}
else if (sellSignal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = close;
_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, RelativeStrengthIndex, ExponentialMovingAverage, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class bollinger_breakout_strategy(Strategy):
"""Bollinger Breakout Strategy with RSI and MA filters."""
def __init__(self):
super(bollinger_breakout_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_length = self.Param("BBLength", 20) \
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 1.5) \
.SetDisplay("BB StdDev", "Standard deviation multiplier", "Bollinger Bands")
self._rsi_length = self.Param("RSILength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI Filter")
self._rsi_oversold = self.Param("RSIOversold", 45) \
.SetDisplay("RSI Oversold", "RSI oversold level", "RSI Filter")
self._rsi_overbought = self.Param("RSIOverbought", 55) \
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI Filter")
self._ma_length = self.Param("MALength", 50) \
.SetDisplay("MA Length", "Moving Average period", "Moving Average")
self._candle_percent = self.Param("CandlePercent", 0.3) \
.SetDisplay("Candle %", "Candle body penetration percentage", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._bollinger = None
self._rsi = None
self._ma = None
self._entry_price = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bollinger_breakout_strategy, self).OnReseted()
self._bollinger = None
self._rsi = None
self._ma = None
self._entry_price = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(bollinger_breakout_strategy, self).OnStarted2(time)
self._bollinger = BollingerBands()
self._bollinger.Length = int(self._bb_length.Value)
self._bollinger.Width = float(self._bb_multiplier.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
self._ma = ExponentialMovingAverage()
self._ma.Length = int(self._ma_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._bollinger, self._rsi, self._ma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._bollinger)
self.DrawIndicator(area, self._ma)
self.DrawOwnTrades(area)
def _on_process(self, candle, bb_value, rsi_value, ma_value):
if candle.State != CandleStates.Finished:
return
if not self._bollinger.IsFormed or not self._rsi.IsFormed or not self._ma.IsFormed:
return
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
if rsi_value.IsEmpty or ma_value.IsEmpty:
return
rsi = float(IndicatorHelper.ToDecimal(rsi_value))
ma_val = float(IndicatorHelper.ToDecimal(ma_value))
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
close = float(candle.ClosePrice)
opn = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
middle = float(bb_value.MovingAverage)
candle_size = high - low
if candle_size <= 0:
return
candle_pct = float(self._candle_percent.Value)
cooldown = int(self._cooldown_bars.Value)
buy_zone = candle_size * candle_pct + low
sell_zone = high - candle_size * candle_pct
buy_signal = buy_zone < lower and close < opn and rsi < float(self._rsi_oversold.Value) and close > ma_val
sell_signal = sell_zone > upper and close > opn and rsi > float(self._rsi_overbought.Value) and close < ma_val
if self.Position > 0 and close >= middle:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = None
self._cooldown_remaining = cooldown
return
elif self.Position < 0 and close <= middle:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = None
self._cooldown_remaining = cooldown
return
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
def CreateClone(self):
return bollinger_breakout_strategy()