Прорыв по ширине полос Боллинджера
Ширина полос Боллинджера измеряет расстояние между верхней и нижней полосами. Расширение ширины указывает на рост волатильности и возможное формирование тренда. Эта стратегия торгует прорывы, когда ширина увеличивается.
Тестирование показывает среднегодичную доходность около 151%. Стратегию лучше запускать на фондовом рынке.
Положение цены относительно средней полосы задаёт направление. Расширяющийся канал с ценой выше средней запускает покупки, а расширение при цене ниже средней — продажи.
Выход происходит при сужении ширины полос или достижении волатильного стопа.
Подробности
- Условия входа: ширина полос растёт, а цена относительно средней полосы определяет направление.
- Длинные/короткие позиции: обе стороны.
- Условия выхода: ширина полос сужается или стоп.
- Стопы: да.
- Значения по умолчанию:
BollingerPeriod= 20BollingerDeviation= 2.0mAtrMultiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Breakout
- Направление: обе стороны
- Индикаторы: полосы Боллинджера, ATR
- Стопы: да
- Сложность: базовая
- Таймфрейм: внутридневной
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that trades on Bollinger Bands Width expansion.
/// It identifies periods of increasing volatility (widening Bollinger Bands)
/// and trades in the direction of the trend as identified by price position relative to the middle band.
/// </summary>
public class BollingerBandWidthStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevWidth;
private int _cooldown;
/// <summary>
/// Period for Bollinger Bands calculation.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Deviation for Bollinger Bands calculation.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Type of candles used for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize the Bollinger Band Width strategy.
/// </summary>
public BollingerBandWidthStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetDisplay("Bollinger Period", "Period for Bollinger Bands calculation", "Indicators")
.SetOptimize(10, 30, 5);
_bollingerDeviation = Param(nameof(BollingerDeviation), 2.0m)
.SetDisplay("Bollinger Deviation", "Deviation for Bollinger Bands calculation", "Indicators")
.SetOptimize(1.5m, 2.5m, 0.25m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevWidth = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevWidth = 0;
_cooldown = 0;
var bollinger = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
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 bollingerValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upperBand ||
bb.LowBand is not decimal lowerBand ||
bb.MovingAverage is not decimal middleBand)
return;
var bbWidth = upperBand - lowerBand;
if (_prevWidth == 0)
{
_prevWidth = bbWidth;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevWidth = bbWidth;
return;
}
var isBBWidthExpanding = bbWidth > _prevWidth;
if (Position == 0 && isBBWidthExpanding)
{
if (candle.ClosePrice > middleBand)
{
BuyMarket();
_cooldown = CooldownBars;
}
else
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0 && !isBBWidthExpanding)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && !isBBWidthExpanding)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevWidth = bbWidth;
}
}
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_band_width_strategy(Strategy):
"""
Strategy that trades on Bollinger Bands Width expansion.
Identifies periods of increasing volatility (widening Bollinger Bands)
and trades in the direction of the trend as identified by price position relative to the middle band.
"""
def __init__(self):
super(bollinger_band_width_strategy, self).__init__()
self._bb_period = self.Param("BollingerPeriod", 20).SetDisplay("Bollinger Period", "Period for Bollinger Bands calculation", "Indicators")
self._bb_deviation = self.Param("BollingerDeviation", 2.0).SetDisplay("Bollinger Deviation", "Deviation for Bollinger Bands calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_width = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bollinger_band_width_strategy, self).OnReseted()
self._prev_width = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(bollinger_band_width_strategy, self).OnStarted2(time)
self._prev_width = 0.0
self._cooldown = 0
bb = BollingerBands()
bb.Length = self._bb_period.Value
bb.Width = self._bb_deviation.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bb_val):
if candle.State != CandleStates.Finished:
return
if not bb_val.IsFormed:
return
if bb_val.UpBand is None or bb_val.LowBand is None or bb_val.MovingAverage is None:
return
upper = float(bb_val.UpBand)
lower = float(bb_val.LowBand)
middle = float(bb_val.MovingAverage)
bb_width = upper - lower
close = float(candle.ClosePrice)
cd = self._cooldown_bars.Value
if self._prev_width == 0:
self._prev_width = bb_width
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_width = bb_width
return
is_expanding = bb_width > self._prev_width
if self.Position == 0 and is_expanding:
if close > middle:
self.BuyMarket()
self._cooldown = cd
else:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and not is_expanding:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and not is_expanding:
self.BuyMarket()
self._cooldown = cd
self._prev_width = bb_width
def CreateClone(self):
return bollinger_band_width_strategy()