Fibonacci- und Bollinger-Bands-Strategie
Long-Mean-Reversion mit Bollinger-Bändern und Fibonacci-Erweiterung unterhalb der vorherigen Handelsspanne.
Standardwerte: BollingerPeriod = 20, BollingerDeviation = 2, FibLookback = 50, FibonacciLevel = 0.618, CandleType = 1 Minute. Nur abgeschlossene Kerzen. Bollinger verwendet die letzten 20 Schlusskurse einschließlich Signalkerze und die Populationsstandardabweichung. Die Spanne stammt aus höchstem High und niedrigstem Low der vorherigen 50 abgeschlossenen Kerzen, ohne Signalkerze.
Erweiterung nach unten: F = previousLow − FibonacciLevel × (previousHigh − previousLow). Positive Koeffizienten über 1 wie 1.618 sind zulässig. Ohne Position strikt bei Close < lowerBand und Close > F kaufen. Long vollständig bei Close >= upperBand oder Close >= previousHigh schließen. Gleichheit mit F erlaubt keinen Einstieg. Keine Shorts, Stop-Aufträge oder zukünftigen Kerzen.
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>
/// Long-only mean reversion: below the lower Bollinger band while above the
/// preceding swing's Fibonacci price, with exits at the upper band or prior high.
/// </summary>
public class FibonacciBollingerBandsStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _fibLookback;
private readonly StrategyParam<decimal> _fibonacciLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _closes = new();
private readonly Queue<decimal> _previousHighs = new();
private readonly Queue<decimal> _previousLows = new();
private Order _pendingOrder;
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public decimal BollingerDeviation { get => _bollingerDeviation.Value; set => _bollingerDeviation.Value = value; }
public int FibLookback { get => _fibLookback.Value; set => _fibLookback.Value = value; }
public decimal FibonacciLevel { get => _fibonacciLevel.Value; set => _fibonacciLevel.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FibonacciBollingerBandsStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20).SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Number of completed closes for band calculation.", "Bollinger");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m).SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Number of population standard deviations.", "Bollinger");
_fibLookback = Param(nameof(FibLookback), 50).SetGreaterThanZero()
.SetDisplay("Fib Lookback", "Number of previous candles used for the swing range.", "Fibonacci");
_fibonacciLevel = Param(nameof(FibonacciLevel), 0.618m).SetGreaterThanZero()
.SetDisplay("Fibonacci Level", "Fraction of preceding swing range projected below its low.", "Fibonacci");
_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();
_previousHighs.Clear();
_previousLows.Clear();
_pendingOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
OrderRegistering += order => _pendingOrder = order;
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();
var complete = _closes.Count == BollingerPeriod
&& _previousHighs.Count == FibLookback && _previousLows.Count == FibLookback;
var pendingActive = _pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed);
if (complete && !pendingActive)
{
var mean = _closes.Average();
var variance = _closes.Sum(price => (price - mean) * (price - mean)) / BollingerPeriod;
var offset = (decimal)Math.Sqrt((double)variance) * BollingerDeviation;
var upper = mean + offset;
var lower = mean - offset;
var swingHigh = _previousHighs.Max();
var swingLow = _previousLows.Min();
var fib = swingLow - (swingHigh - swingLow) * FibonacciLevel;
if (Position > 0m && (candle.ClosePrice >= upper || candle.ClosePrice >= swingHigh))
SellMarket(Position);
else if (Position == 0m && candle.ClosePrice < lower && candle.ClosePrice > fib)
BuyMarket(Volume);
}
// The signal candle never contributes to the swing extremes used for itself.
_previousHighs.Enqueue(candle.HighPrice);
_previousLows.Enqueue(candle.LowPrice);
while (_previousHighs.Count > FibLookback) _previousHighs.Dequeue();
while (_previousLows.Count > FibLookback) _previousLows.Dequeue();
}
}
import clr
import math
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, OrderStates
from StockSharp.Algo.Strategies import Strategy
class fibonacci_bollinger_bands_strategy(Strategy):
"""Long-only Bollinger/Fibonacci entry with a previous-swing-high exit."""
def __init__(self):
super(fibonacci_bollinger_bands_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20).SetGreaterThanZero() \
.SetDisplay("Bollinger Period", "Number of closes in Bollinger bands.", "Bollinger")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0).SetGreaterThanZero() \
.SetDisplay("Bollinger Deviation", "Population standard-deviation multiplier.", "Bollinger")
self._fib_lookback = self.Param("FibLookback", 50).SetGreaterThanZero() \
.SetDisplay("Fib Lookback", "Previous swing bars (current excluded).", "Fibonacci")
self._fibonacci_level = self.Param("FibonacciLevel", 0.618).SetGreaterThanZero() \
.SetDisplay("Fibonacci Level", "Fraction of swing range projected below the prior low.", "Fibonacci")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Candle timeframe.", "General")
self._closes = []
self._previous_highs = []
self._previous_lows = []
self._pending_order = None
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(fibonacci_bollinger_bands_strategy, self).OnReseted()
self._closes = []
self._previous_highs = []
self._previous_lows = []
self._pending_order = None
def OnStarted2(self, time):
super(fibonacci_bollinger_bands_strategy, self).OnStarted2(time)
self.OrderRegistering += self._on_order_registering
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_order_registering(self, order):
self._pending_order = order
def on_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
band_length = int(self._bollinger_period.Value)
lookback = int(self._fib_lookback.Value)
self._closes.append(close)
if len(self._closes) > band_length:
self._closes.pop(0)
active = self._pending_order is not None and self._pending_order.State not in (
OrderStates.Done, OrderStates.Failed)
if len(self._closes) == band_length and len(self._previous_highs) == lookback and not active:
mean = sum(self._closes) / band_length
variance = sum((value - mean) ** 2 for value in self._closes) / band_length
offset = math.sqrt(variance) * float(self._bollinger_deviation.Value)
upper = mean + offset
lower = mean - offset
high = max(self._previous_highs)
low = min(self._previous_lows)
fib = low - float(self._fibonacci_level.Value) * (high - low)
if self.Position > 0 and (close >= upper or close >= high):
self.SellMarket(self.Position)
elif self.Position == 0 and close < lower and close > fib:
self.BuyMarket(self.Volume)
self._previous_highs.append(float(candle.HighPrice))
self._previous_lows.append(float(candle.LowPrice))
if len(self._previous_highs) > lookback:
self._previous_highs.pop(0)
if len(self._previous_lows) > lookback:
self._previous_lows.pop(0)
def CreateClone(self):
return fibonacci_bollinger_bands_strategy()