Diese Strategie ist eine C#-Portierung des MetaTrader-Expertenberaters BollingerBandsEA (Version 3.0). Es werden Mean-Reversion-Setups gehandelt, die auftreten, nachdem der Preis während der aktiven Handelssitzung über die Bollinger-Bänder hinausgeht.
Handelslogik
Abonnieren Sie die primäre Intraday-Kerzenserie (standardmäßig 15-Minuten-Kerzen) und eine tägliche Kerzenserie, die zum Erstellen des Trendfilters verwendet wird.
Berechnen Sie Bollinger-Bänder (Länge 20, Breite 2,0) für die Intraday-Serie und ein 100-Perioden-SMA für Tagesabschlüsse.
Verfolgen Sie die aktuellen und vorherigen Tageshochs/-tiefs und behalten Sie die vorherigen Bollinger-Bandwerte für die Signalauswertung bei.
Erlauben Sie nur Eingaben innerhalb des Handelssitzungsfensters: von SessionStartOffsetMinutes nach der Eröffnung des Handelstages bis SessionEndOffsetMinutes vor dem Ende des Handelstages.
Überspringen Sie den Handel, sobald der kumulierte PnL für den aktuellen Tag positiv wird, und ahmen Sie so den täglichen Stop von EA nach.
Geben Sie eine Short-Position ein, wenn die vorherige Kerze bärisch ist, über dem oberen Band schließt, der aktuelle Schlusskurs über diesem Band bleibt, die Bandbreite breit genug ist, der Preis unter dem täglichen SMA liegt und der Preis über dem aktuellen oder vorherigen Tageshoch gehandelt wird.
Geben Sie eine Long-Position ein, wenn die vorherige Kerze bullisch ist, unter dem unteren Band schließt, der aktuelle Schlusskurs unter diesem Band bleibt, die Bandbreite breit genug ist, der Preis über dem täglichen SMA liegt und der Preis unter dem aktuellen oder vorherigen Tagestief liegt.
Die Positionsgröße wird entweder durch das konfigurierte feste Volumen oder durch eine risikobasierte Größenbestimmung bestimmt, die den Abstand zum Stop-Loss in Punkten verwendet.
Ausstiege werden durch die Überprüfung von Stop-Loss, Take-Profit, optionalem Schließen auf dem mittleren Band, einem optionalen Trailing-Stop und der optionalen Break-Even-Logik durchgeführt. Verlierergeschäfte können auch nach einer konfigurierbaren Haltezeit liquidiert werden.
Parameter
Parameter
Beschreibung
CandleType
Für den Handel verwendete Intraday-Kerzenserien.
BollingerLength
Zeitraum des gleitenden Durchschnitts der Bollinger-Bänder.
BollingerWidth
Breitenmultiplikator der Bollinger-Bänder.
DailyMaLength
Länge des täglichen SMA-Filters.
StopLossPoints
Stop-Loss-Distanz, ausgedrückt in Instrumentenpunkten.
UseRiskVolume
Ermöglicht eine risikobasierte Positionsgrößenbestimmung.
RiskPercent
Kontoprozentsatz, der für die risikobasierte Dimensionierung verwendet wird.
FixedVolume
Festes Fallback-Volumen, wenn die Risikogrößenbestimmung deaktiviert oder nicht möglich ist.
SessionStartOffsetMinutes
Minuten nach Beginn der Sitzung, bevor Einträge zulässig sind.
SessionEndOffsetMinutes
Minuten vor Sitzungsende, wenn Einträge blockiert sind.
CloseOnMiddleBand
Ausstiegsposition, wenn der Preis das mittlere Band von Bollinger überschreitet.
EnableTrailing
Ermöglicht Trailing-Stop-Anpassungen.
TrailingFactor
Distanzmultiplikator erforderlich, bevor der Stopp nachgestellt wird.
EnableBreakEven
Ermöglicht die Verschiebung des Stops auf den Einstiegspreis.
BreakEvenFactor
Gewinnmultiplikator erforderlich, um den Stop auf die Gewinnschwelle zu bringen.
CloseLosingAfterMinutes
Schließt verlustbringende Trades, nachdem sie für die angegebene Minute gehalten wurden.
Notizen
Schützende Stop-Loss- und Take-Profit-Orders werden simuliert, indem bei jeder Aktualisierung die Candle-Extreme überprüft werden. Passen Sie diesen Abschnitt an, wenn börsenseitige Schutzanordnungen erforderlich sind.
Die risikobasierte Größenbestimmung hängt von Security.Step und Security.StepPrice ab. Fehlen diese Werte, greift die Strategie auf das Festvolumen zurück.
Der tägliche Gewinnstopp nutzt die Strategie PnL, daher müssen realisierte und variable PnL in derselben Währung wie das Portfolio erfolgen.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades reversals from Bollinger Bands.
/// Buys when price closes below lower band and sells when price closes above upper band.
/// </summary>
public class BollingerBandsSessionReversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerWidth;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the Bollinger Bands moving average.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Width multiplier for Bollinger Bands.
/// </summary>
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public BollingerBandsSessionReversalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle series", "General");
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators");
}
/// <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 = BollingerLength,
Width = BollingerWidth
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (bbValue is not IBollingerBandsValue bb)
return;
var middle = bb.MovingAverage ?? 0m;
var upper = bb.UpBand ?? 0m;
var lower = bb.LowBand ?? 0m;
if (middle == 0m || upper == 0m || lower == 0m)
return;
var price = candle.ClosePrice;
// Reversal: buy when price falls below lower band
if (price < lower && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Reversal: sell when price rises above upper band
else if (price > upper && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit at middle band
else if (Position > 0 && price >= middle)
{
SellMarket();
}
else if (Position < 0 && price <= middle)
{
BuyMarket();
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bollinger_bands_session_reversal_strategy(Strategy):
def __init__(self):
super(bollinger_bands_session_reversal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle series", "General")
self._bollinger_length = self.Param("BollingerLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators")
self._bollinger_width = self.Param("BollingerWidth", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerLength(self):
return self._bollinger_length.Value
@BollingerLength.setter
def BollingerLength(self, value):
self._bollinger_length.Value = value
@property
def BollingerWidth(self):
return self._bollinger_width.Value
@BollingerWidth.setter
def BollingerWidth(self, value):
self._bollinger_width.Value = value
def OnStarted2(self, time):
super(bollinger_bands_session_reversal_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.BollingerLength
bollinger.Width = self.BollingerWidth
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, self._process_candle).Start()
def _process_candle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
middle = bb_value.MovingAverage
if upper is None or lower is None or middle is None:
return
up = float(upper)
lo = float(lower)
mid = float(middle)
if up == 0 or lo == 0 or mid == 0:
return
price = float(candle.ClosePrice)
if price < lo and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif price > up and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and price >= mid:
self.SellMarket()
elif self.Position < 0 and price <= mid:
self.BuyMarket()
def CreateClone(self):
return bollinger_bands_session_reversal_strategy()