Fibonacci 与布林带策略
仅做多的均值回归策略,结合布林带与前一波段低点下方的斐波那契扩展。
默认值:BollingerPeriod = 20、BollingerDeviation = 2、FibLookback = 50、FibonacciLevel = 0.618、CandleType = 1分钟。只使用已完成 K 线。布林带按含信号 K 线的最近20个收盘价和总体标准差计算。波段为之前50根已完成 K 线的最高 High 和最低 Low,不含信号 K 线。
向下扩展为 F = previousLow − FibonacciLevel × (previousHigh − previousLow)。支持大于1的正系数,例如1.618。空仓时严格满足 Close < lowerBand 且 Close > F 才买入。当 Close >= upperBand 或 Close >= previousHigh 时平掉全部多仓。等于 F 不入场。不做空、不设止损单、不使用未来 K 线。
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()