Elegante Bollinger Bands
Diese Strategie handelt Bollinger Bands-Ausbrüche. Sie kauft, wenn der Schlusskurs über das obere Band kreuzt, und verkauft, wenn der Schlusskurs unter das untere Band kreuzt.
Details
- Einstiegsbedingungen:
- Long: Schlusskurs kreuzt über das obere Band
- Short: Schlusskurs kreuzt unter das untere Band
- Ausstiegsbedingungen: umgekehrter Kreuzungspunkt
- Typ: Ausbruch
- Indikatoren: Bollinger Bands
- Zeitrahmen: 1 Minute (Standard)
- Stops: keine
Prüfbare Regel und Standardwerte. Die Bollinger-Bänder verwenden die letzten 20 abgeschlossenen Schlusskurse einschließlich der Signalkerze und das Populations-Standardabweichungsmaß mit Faktor 2. BollingerPeriod=20, DeviationMultiplier=2, CandleType=1 Minute. Ohne Position wird long eröffnet, wenn der Schlusskurs das obere Band von unten überschreitet, und short, wenn er das untere Band von oben unterschreitet. Die gesamte Long-Position schließt bei Rückkreuzung unter das obere Band; die gesamte Short-Position bei Rückkreuzung über das untere Band. Für das vorige und aktuelle Kreuzungssignal werden die jeweils abgeschlossenen Kerzen verwendet. Keine Stops, Gewinnziele oder Umkehr auf derselben Kerze.
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades crossings of the upper/lower Bollinger bands and closes on a reverse crossing
/// back inside the broken band. Band values use completed closes, including the signal bar.
/// </summary>
public class FancyBollingerBandsStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _closes = new();
private decimal? _previousClose;
private decimal? _previousUpper;
private decimal? _previousLower;
private bool _entryRequested;
private bool _exitRequested;
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public decimal DeviationMultiplier { get => _deviationMultiplier.Value; set => _deviationMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FancyBollingerBandsStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Number of completed closes in each band calculation.", "Bollinger");
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for both bands.", "Bollinger");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe.", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_closes.Clear();
_previousClose = null;
_previousUpper = null;
_previousLower = null;
_entryRequested = false;
_exitRequested = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_closes.Enqueue(candle.ClosePrice);
while (_closes.Count > BollingerPeriod)
_closes.Dequeue();
if (_closes.Count < BollingerPeriod)
return;
var mean = _closes.Average();
var variance = _closes.Sum(price => (price - mean) * (price - mean)) / BollingerPeriod;
var deviation = (decimal)Math.Sqrt((double)variance) * DeviationMultiplier;
var upper = mean + deviation;
var lower = mean - deviation;
var close = candle.ClosePrice;
if (_previousClose is decimal previous && _previousUpper is decimal previousUpper
&& _previousLower is decimal previousLower)
{
if (Position != 0m)
{
_entryRequested = false;
if (!_exitRequested)
{
if (Position > 0m && previous >= previousUpper && close <= upper)
{
_exitRequested = true;
SellMarket(Position);
}
else if (Position < 0m && previous <= previousLower && close >= lower)
{
_exitRequested = true;
BuyMarket(Math.Abs(Position));
}
}
}
else
{
_exitRequested = false;
if (!_entryRequested)
{
if (previous <= previousUpper && close > upper)
{
_entryRequested = true;
BuyMarket(Volume);
}
else if (previous >= previousLower && close < lower)
{
_entryRequested = true;
SellMarket(Volume);
}
}
}
}
_previousClose = close;
_previousUpper = upper;
_previousLower = lower;
}
}
import clr
import math
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class fancy_bollinger_bands_strategy(Strategy):
"""
Buy an upper Bollinger-band close crossover and sell a lower-band crossover.
Exit when the close crosses back inside the broken band.
"""
def __init__(self):
super(fancy_bollinger_bands_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Period", "Number of completed closes in each band calculation.", "Bollinger")
self._deviation_multiplier = self.Param("DeviationMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for both bands.", "Bollinger")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Candle timeframe.", "General")
self._closes = []
self._previous_close = None
self._previous_upper = None
self._previous_lower = None
self._entry_requested = False
self._exit_requested = False
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(fancy_bollinger_bands_strategy, self).OnReseted()
self._closes = []
self._previous_close = None
self._previous_upper = None
self._previous_lower = None
self._entry_requested = False
self._exit_requested = False
def OnStarted2(self, time):
super(fancy_bollinger_bands_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.Bind(self.on_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_candle(self, candle):
if candle.State != CandleStates.Finished:
return
period = int(self._bollinger_period.Value)
close = float(candle.ClosePrice)
self._closes.append(close)
if len(self._closes) > period:
self._closes.pop(0)
if len(self._closes) < period:
return
mean = sum(self._closes) / period
variance = sum((price - mean) ** 2 for price in self._closes) / period
offset = math.sqrt(variance) * float(self._deviation_multiplier.Value)
upper = mean + offset
lower = mean - offset
if self._previous_upper is not None and self._previous_lower is not None:
previous = self._previous_close
position = self.Position
if position != 0:
self._entry_requested = False
if not self._exit_requested:
if position > 0 and previous >= self._previous_upper and close <= upper:
self._exit_requested = True
self.SellMarket(position)
elif position < 0 and previous <= self._previous_lower and close >= lower:
self._exit_requested = True
self.BuyMarket(Math.Abs(position))
else:
self._exit_requested = False
if not self._entry_requested:
if previous <= self._previous_upper and close > upper:
self._entry_requested = True
self.BuyMarket(self.Volume)
elif previous >= self._previous_lower and close < lower:
self._entry_requested = True
self.SellMarket(self.Volume)
self._previous_close = close
self._previous_upper = upper
self._previous_lower = lower
def CreateClone(self):
return fancy_bollinger_bands_strategy()