Стратегия Bollinger Bands
Стратегия торгует пробои полос Боллинджера. Покупает, когда цена закрывается выше верхней полосы, и продаёт, когда закрывается ниже нижней полосы. Выход осуществляется при пересечении простой скользящей средней или при срабатывании стоп-лосса.
Детали
- Условия входа:
- Long: закрытие выше верхней полосы Боллинджера
- Short: закрытие ниже нижней полосы Боллинджера
- Направление: Оба
- Условия выхода:
- Long: закрытие ниже SMA или достижение стоп-лосса
- Short: закрытие выше SMA или достижение стоп-лосса
- Стопы: процент от цены входа
- Значения по умолчанию:
BbLength= 120BbDeviation= 2mSmaLength= 110StopLossPercent= 6mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Фильтры:
- Категория: Пробой
- Направление: Оба
- Индикаторы: Bollinger Bands, SMA
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Среднесрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Bollinger Bands strategy.
/// A close above the upper Bollinger band goes long and a close below the lower band goes short, reversing an opposite position.
/// A long closes when the close falls below SMA(SmaLength) and a short when it rises above it, and a percent stop limits the loss.
/// </summary>
public class BollingerBandsStrategy : Strategy
{
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbDeviation;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Bollinger period.
/// </summary>
public int BbLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger standard deviation multiplier.
/// </summary>
public decimal BbDeviation
{
get => _bbDeviation.Value;
set => _bbDeviation.Value = value;
}
/// <summary>
/// Period of the exit SMA.
/// </summary>
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BollingerBandsStrategy()
{
_bbLength = Param(nameof(BbLength), 120)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger period", "Bollinger");
_bbDeviation = Param(nameof(BbDeviation), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Deviation", "Bollinger standard deviation multiplier", "Bollinger");
_smaLength = Param(nameof(SmaLength), 110)
.SetGreaterThanZero()
.SetDisplay("SMA Length", "Period of the exit SMA", "Exit");
_stopLossPercent = Param(nameof(StopLossPercent), 6m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <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 = BbLength, Width = BbDeviation };
var sma = new SimpleMovingAverage { Length = SmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, sma, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed || !smaValue.IsFormed)
return;
var bands = (BollingerBandsValue)bollingerValue;
if (bands.UpBand is not decimal upper || bands.LowBand is not decimal lower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var sma = smaValue.GetValue<decimal>();
if (close > upper && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close < lower && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close < sma)
SellMarket(Position);
else if (Position < 0 && close > sma)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import BollingerBands, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bollinger_bands_strategy(Strategy):
"""
Bollinger Bands strategy.
A close above the upper Bollinger band goes long and a close below the lower band goes short, reversing an opposite position.
A long closes when the close falls below SMA(SmaLength) and a short when it rises above it, and a percent stop limits the loss.
"""
def __init__(self):
super(bollinger_bands_strategy, self).__init__()
self._bb_length = self.Param("BbLength", 120).SetGreaterThanZero().SetDisplay("BB Length", "Bollinger period", "Bollinger")
self._bb_deviation = self.Param("BbDeviation", 2.0).SetGreaterThanZero().SetDisplay("BB Deviation", "Bollinger standard deviation multiplier", "Bollinger")
self._sma_length = self.Param("SmaLength", 110).SetGreaterThanZero().SetDisplay("SMA Length", "Period of the exit SMA", "Exit")
self._stop_loss_percent = self.Param("StopLossPercent", 6.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(bollinger_bands_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(self._bb_deviation.Value)
sma = SimpleMovingAverage()
sma.Length = self._sma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, sma, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, bollinger_value, sma_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed or not sma_value.IsFormed:
return
upper = bollinger_value.UpBand
lower = bollinger_value.LowBand
if upper is None or lower is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
sma = sma_value.GetValue[Decimal](None)
if close > upper and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close < lower and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close < sma:
self.SellMarket(self.Position)
elif self.Position < 0 and close > sma:
self.BuyMarket(-self.Position)
def CreateClone(self):
return bollinger_bands_strategy()