Прорыв по ширине полос Боллинджера
Стратегия Bollinger Band Width Breakout отслеживает резкое расширение полос Боллинджера. Когда значения выходят за пределы обычного диапазона, цена часто начинает новое движение.
Тестирование показывает среднегодичную доходность около 109%. Стратегию лучше запускать на крипторынке.
Позиция открывается, как только индикатор пробивает полосу, построенную по последним данным и множителю отклонения. Возможны сделки в обе стороны со стопом.
Система подходит трейдерам импульсных стратегий, ищущим ранние прорывы. Сделки закрываются, когда индикатор возвращается к среднему. По умолчанию используется BollingerLength = 20.
Подробности
- Условия входа: индикатор превышает среднее на величину множителя отклонения.
- Длинные/короткие: оба направления.
- Условия выхода: индикатор возвращается к среднему.
- Стопы: да.
- Значения по умолчанию:
BollingerLength= 20BollingerDeviation= 2.0mAvgPeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)StopMultiplier= 2
- Фильтры:
- Категория: Breakout
- Направление: оба
- Индикаторы: Bollinger
- Стопы: да
- Сложность: средняя
- Таймфрейм: краткосрочный
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: средний
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>
/// Strategy that trades on Bollinger Band width breakouts.
/// When Bollinger Band width increases significantly above its average,
/// it enters position in the direction determined by price movement.
/// </summary>
public class BollingerWidthBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _avgPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stopMultiplier;
private BollingerBands _bollinger;
private SimpleMovingAverage _widthAverage;
private AverageTrueRange _atr;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Period for width average calculation.
/// </summary>
public int AvgPeriod
{
get => _avgPeriod.Value;
set => _avgPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for breakout detection.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop-loss ATR multiplier.
/// </summary>
public int StopMultiplier
{
get => _stopMultiplier.Value;
set => _stopMultiplier.Value = value;
}
/// <summary>
/// Initialize <see cref="BollingerWidthBreakoutStrategy"/>.
/// </summary>
public BollingerWidthBreakoutStrategy()
{
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Length", "Period of the Bollinger Bands indicator", "Indicators")
.SetOptimize(10, 50, 5);
_bollingerDeviation = Param(nameof(BollingerDeviation), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier for Bollinger Bands", "Indicators")
.SetOptimize(1.0m, 3.0m, 0.5m);
_avgPeriod = Param(nameof(AvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Average Period", "Period for Bollinger width average calculation", "Indicators")
.SetOptimize(10, 50, 5);
_multiplier = Param(nameof(Multiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Standard deviation multiplier for breakout detection", "Indicators")
.SetOptimize(1.0m, 3.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_stopMultiplier = Param(nameof(StopMultiplier), 2)
.SetGreaterThanZero()
.SetDisplay("Stop Multiplier", "ATR multiplier for stop-loss", "Risk Management")
.SetOptimize(1, 5, 1);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_bollinger = new BollingerBands
{
Length = BollingerLength,
Width = BollingerDeviation
};
_widthAverage = new SMA { Length = AvgPeriod };
_atr = new AverageTrueRange { Length = BollingerLength };
// Create subscription
var subscription = SubscribeCandles(CandleType);
// Bind Bollinger Bands
subscription
.BindEx(_bollinger, _atr, ProcessBollinger)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
// Create chart area for visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void ProcessBollinger(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFinal || !atrValue.IsFinal || bollingerValue.IsEmpty || atrValue.IsEmpty)
return;
// Calculate Bollinger Band width
if (bollingerValue is not BollingerBandsValue bollingerTyped)
return;
if (bollingerTyped.UpBand is not decimal upperBand)
return;
if (bollingerTyped.LowBand is not decimal lowerBand)
return;
var lastWidth = upperBand - lowerBand;
// Process width through average
var widthAvgValue = _widthAverage.Process(new DecimalIndicatorValue(_widthAverage, lastWidth, candle.ServerTime) { IsFinal = true });
var avgWidth = widthAvgValue.ToDecimal();
// Skip if indicators are not formed yet
if (!_widthAverage.IsFormed)
{
return;
}
// Bollinger width breakout detection
if (lastWidth > avgWidth * (1m + Multiplier / 10m))
{
// Determine direction based on price and bands
var upperDistance = (candle.ClosePrice - upperBand).Abs();
var lowerDistance = (candle.ClosePrice - lowerBand).Abs();
var priceDirection = upperDistance < lowerDistance;
if (priceDirection && Position == 0)
{
BuyMarket();
}
else if (!priceDirection && Position == 0)
{
SellMarket();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class bollinger_band_width_breakout_strategy(Strategy):
"""
Strategy that trades on Bollinger Band width breakouts.
When Bollinger Band width increases significantly above its average,
it enters position in the direction determined by price movement.
"""
def __init__(self):
super(bollinger_band_width_breakout_strategy, self).__init__()
self._bollinger_length = self.Param("BollingerLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Length", "Period of the Bollinger Bands indicator", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 5)
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier for Bollinger Bands", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._avg_period = self.Param("AvgPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Average Period", "Period for Bollinger width average calculation", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 5)
self._multiplier = self.Param("Multiplier", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("Multiplier", "Standard deviation multiplier for breakout detection", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._stop_multiplier = self.Param("StopMultiplier", 2) \
.SetGreaterThanZero() \
.SetDisplay("Stop Multiplier", "ATR multiplier for stop-loss", "Risk Management") \
.SetCanOptimize(True) \
.SetOptimize(1, 5, 1)
self._bollinger = None
self._width_average = None
self._atr = None
@property
def BollingerLength(self):
return self._bollinger_length.Value
@BollingerLength.setter
def BollingerLength(self, value):
self._bollinger_length.Value = value
@property
def BollingerDeviation(self):
return self._bollinger_deviation.Value
@BollingerDeviation.setter
def BollingerDeviation(self, value):
self._bollinger_deviation.Value = value
@property
def AvgPeriod(self):
return self._avg_period.Value
@AvgPeriod.setter
def AvgPeriod(self, value):
self._avg_period.Value = value
@property
def Multiplier(self):
return self._multiplier.Value
@Multiplier.setter
def Multiplier(self, value):
self._multiplier.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopMultiplier(self):
return self._stop_multiplier.Value
@StopMultiplier.setter
def StopMultiplier(self, value):
self._stop_multiplier.Value = value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(bollinger_band_width_breakout_strategy, self).OnReseted()
def OnStarted2(self, time):
super(bollinger_band_width_breakout_strategy, self).OnStarted2(time)
# Create indicators
self._bollinger = BollingerBands()
self._bollinger.Length = self.BollingerLength
self._bollinger.Width = self.BollingerDeviation
self._width_average = SimpleMovingAverage()
self._width_average.Length = self.AvgPeriod
self._atr = AverageTrueRange()
self._atr.Length = self.BollingerLength
# Create subscription
subscription = self.SubscribeCandles(self.CandleType)
# Bind Bollinger Bands and ATR
subscription.BindEx(self._bollinger, self._atr, self.ProcessBollinger).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
# Create chart area for visualization
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._bollinger)
self.DrawOwnTrades(area)
def ProcessBollinger(self, candle, bollinger_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFinal or not atr_value.IsFinal or bollinger_value.IsEmpty or atr_value.IsEmpty:
return
# Calculate Bollinger Band width
if bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
upper_band = float(bollinger_value.UpBand)
lower_band = float(bollinger_value.LowBand)
last_width = upper_band - lower_band
# Process width through average
avg_result = process_float(self._width_average, last_width, candle.ServerTime, True)
avg_width = float(avg_result)
# Skip if indicators are not formed yet
if not self._width_average.IsFormed:
return
# Check if trading is allowed
# Bollinger width breakout detection
if last_width > avg_width * (1.0 + self.Multiplier / 10.0):
# Determine direction based on price and bands
upper_distance = abs(float(candle.ClosePrice) - upper_band)
lower_distance = abs(float(candle.ClosePrice) - lower_band)
price_direction = upper_distance < lower_distance
if price_direction and self.Position == 0:
self.BuyMarket()
elif not price_direction and self.Position == 0:
self.SellMarket()
def CreateClone(self):
return bollinger_band_width_breakout_strategy()