Bollinger Stochastic Strategie
Bollinger Stochastic kombiniert Bollinger Bänder mit dem Stochastik-Oszillator, um überdehnte Bewegungen zu identifizieren. Wenn der Preis die äußere Bande berührt, während sich der Oszillator in einer Extremzone befindet, deutet dies auf einen möglichen Rückprall hin.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 133%. Die Strategie funktioniert am besten auf dem Kryptomarkt.
Das System handelt gegen diese Extreme: Long, wenn der Preis die untere Bande mit überverkauftem Stochastik berührt, und Short an der oberen Bande mit überkauftem Stochastik.
Ein prozentualer Stop begrenzt das Risiko, falls die Mean Reversion ausbleibt.
Details
- Einstiegskriterien: Indikatorsignal
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 minuteStopLossPercent= 2BollingerPeriod= 20BollingerDeviation= 2StochPeriod= 14StochDPeriod= 3StochOversold= 20StochOverbought= 80 Eine Kerze, deren Tief das untere Band berührt, während Stochastik-%K unter StochOversold liegt, eröffnet einen Long; eine Kerze, deren Hoch das obere Band berührt, während %K über StochOverbought liegt, einen Short. Ein Gegensignal dreht die Position.
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: Bollinger Bands, Stochastic
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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 Stochastic strategy.
/// A candle that touches the lower band while stochastic %K is below StochOversold goes long; a candle that touches the upper band
/// while %K is above StochOverbought goes short. An opposite signal reverses the position, and a percent stop limits the loss.
/// </summary>
public class BollingerStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of the Bollinger Bands.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier of the bands.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Lookback period of stochastic %K.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of stochastic %D.
/// </summary>
public int StochDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
/// <summary>
/// Stochastic level for longs.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// Stochastic level for shorts.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BollingerStochasticStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Period of the Bollinger Bands", "Bollinger");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Deviation", "Standard deviation multiplier of the bands", "Bollinger");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Lookback period of stochastic %K", "Stochastic");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Smoothing period of stochastic %D", "Stochastic");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Oversold Level", "Stochastic level for longs", "Stochastic");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Overbought Level", "Stochastic level for shorts", "Stochastic");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };
var stochastic = new StochasticOscillator
{
K = { Length = StochPeriod },
D = { Length = StochDPeriod },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, stochastic, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, stochastic);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed || !stochasticValue.IsFormed)
return;
var bands = (BollingerBandsValue)bollingerValue;
var stoch = (IStochasticOscillatorValue)stochasticValue;
if (bands.UpBand is not decimal upper || bands.LowBand is not decimal lower || stoch.K is not decimal k)
return;
var signal = 0;
if (candle.LowPrice <= lower && k < StochOversold)
signal = 1;
else if (candle.HighPrice >= upper && k > StochOverbought)
signal = -1;
if (signal == 0 || !IsFormedAndOnlineAndAllowTrading())
return;
if (signal > 0 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (signal < 0 && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import BollingerBands, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class bollinger_stochastic_strategy(Strategy):
"""
Bollinger Stochastic strategy.
A candle that touches the lower band while stochastic %K is below StochOversold goes long; a candle that touches the upper band
while %K is above StochOverbought goes short. An opposite signal reverses the position, and a percent stop limits the loss.
"""
def __init__(self):
super(bollinger_stochastic_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20).SetGreaterThanZero().SetDisplay("BB Period", "Period of the Bollinger Bands", "Bollinger")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0).SetGreaterThanZero().SetDisplay("BB Deviation", "Standard deviation multiplier of the bands", "Bollinger")
self._stoch_period = self.Param("StochPeriod", 14).SetGreaterThanZero().SetDisplay("Stochastic Period", "Lookback period of stochastic %K", "Stochastic")
self._stoch_d_period = self.Param("StochDPeriod", 3).SetGreaterThanZero().SetDisplay("Stochastic %D", "Smoothing period of stochastic %D", "Stochastic")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Oversold Level", "Stochastic level for longs", "Stochastic")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Overbought Level", "Stochastic level for shorts", "Stochastic")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(bollinger_stochastic_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bollinger_period.Value
bollinger.Width = Decimal(self._bollinger_deviation.Value)
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_period.Value
stochastic.D.Length = self._stoch_d_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, stochastic, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, stochastic)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, bollinger_value, stochastic_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed or not stochastic_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None or stochastic_value.K is None:
return
upper = bollinger_value.UpBand
lower = bollinger_value.LowBand
k = stochastic_value.K
signal = 0
if candle.LowPrice <= lower and k < Decimal(self._stoch_oversold.Value):
signal = 1
elif candle.HighPrice >= upper and k > Decimal(self._stoch_overbought.Value):
signal = -1
if signal == 0 or not self.IsFormedAndOnlineAndAllowTrading():
return
if signal > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif signal < 0 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return bollinger_stochastic_strategy()