Bollinger ダイバージェンス戦略
Bollinger Divergence は、価格がバンドを突き抜けると同時に反対側のバンドが 収縮し始める極端な状況を探します。価格のモメンタムとボラティリティの間の このダイバージェンスは、レンジの中央へのスナップバックに先行することが よくあります。
ローソク足が下限バンドを下回って終値を付け、かつ上限バンドが少なくとも 設定されたパーセンテージ分狭まると、ロングシグナルが発生します。 ショートの場合、パターンは上限バンドを中心に反転します。ポジションは Bollinger の中央ラインへの素早い戻りを目標とし、オプションで固定の 利確設定もあります。
このセットアップは、レンジ相場やボラティリティの急上昇が落ち着き始めた
後に最も効果を発揮します。CandlePercent パラメーターは、取引を許可する前に
反対側のバンドがどれだけ収縮しなければならないかを制御し、強いトレンド時の
ダマシを回避するのに役立ちます。
詳細
- データ: 価格ローソク足。
- エントリー条件:
- ロング: 終値が下限バンドを下回り、かつ上限バンドが
CandlePercent収縮。 - ショート: 終値が上限バンドを上回り、かつ下限バンドが
CandlePercent収縮。
- ロング: 終値が下限バンドを下回り、かつ上限バンドが
- エグジット条件:
- 中央バンドへの回帰、または利確パーセンテージ。
- ストップ: ハードストップなし;利確または手動エグジットに依存。
- デフォルト値:
BBLength= 20BBMultiplier= 2.0CandlePercent= 30TakeProfit= 5
- フィルター:
- カテゴリ: 平均回帰
- 方向: ロング/ショート
- インジケーター: Bollinger Bands
- 複雑さ: シンプル
- リスクレベル: 中
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 Divergence strategy.
/// Goes long when a candle closes below the lower band while the upper band contracts, and short when a candle closes
/// above the upper band while the lower band contracts. The contraction of the opposite band over the bar must reach
/// CandlePercent percent of the signal candle's range. Positions exit on a return to the middle band or at the
/// TakeProfit percentage.
/// </summary>
public class BollingerDivergenceStrategy : Strategy
{
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<decimal> _candlePercent;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevUpperBand;
private decimal? _prevLowerBand;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Required contraction of the opposite band, in percent of the signal candle's range.
/// </summary>
public decimal CandlePercent
{
get => _candlePercent.Value;
set => _candlePercent.Value = value;
}
/// <summary>
/// Take profit percentage. 0 disables it.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BollingerDivergenceStrategy()
{
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands");
_bbMultiplier = Param(nameof(BBMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands");
_candlePercent = Param(nameof(CandlePercent), 30m)
.SetNotNegative()
.SetDisplay("Candle Percent", "Required contraction of the opposite band in percent of the candle range", "Signals");
_takeProfit = Param(nameof(TakeProfit), 5m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUpperBand = null;
_prevLowerBand = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevUpperBand = null;
_prevLowerBand = null;
var bollinger = new BollingerBands
{
Length = BBLength,
Width = BBMultiplier
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
StartProtection(TakeProfit > 0 ? new Unit(TakeProfit, UnitTypes.Percent) : new Unit(), new Unit(), useMarketOrders: true);
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 prevUpper = _prevUpperBand;
var prevLower = _prevLowerBand;
_prevUpperBand = upperBand;
_prevLowerBand = lowerBand;
if (prevUpper is not decimal lastUpper || prevLower is not decimal lastLower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var minContraction = (candle.HighPrice - candle.LowPrice) * CandlePercent / 100m;
var upperContraction = lastUpper - upperBand;
var lowerContraction = lowerBand - lastLower;
var longSignal = close < lowerBand && upperContraction > 0 && upperContraction >= minContraction;
var shortSignal = close > upperBand && lowerContraction > 0 && lowerContraction >= minContraction;
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close >= middleBand)
SellMarket(Position);
else if (Position < 0 && close <= middleBand)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bollinger_divergence_strategy(Strategy):
"""
Bollinger Divergence strategy.
Goes long when a candle closes below the lower band while the upper band contracts, and short when a candle closes
above the upper band while the lower band contracts. The contraction of the opposite band over the bar must reach
CandlePercent percent of the signal candle's range. Positions exit on a return to the middle band or at the
TakeProfit percentage.
"""
def __init__(self):
super(bollinger_divergence_strategy, self).__init__()
self._bb_length = self.Param("BBLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands")
self._candle_percent = self.Param("CandlePercent", 30.0) \
.SetNotNegative() \
.SetDisplay("Candle Percent", "Required contraction of the opposite band in percent of the candle range", "Signals")
self._take_profit = self.Param("TakeProfit", 5.0) \
.SetNotNegative() \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._prev_upper_band = None
self._prev_lower_band = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(bollinger_divergence_strategy, self).OnReseted()
self._prev_upper_band = None
self._prev_lower_band = None
def OnStarted2(self, time):
super(bollinger_divergence_strategy, self).OnStarted2(time)
self._prev_upper_band = None
self._prev_lower_band = None
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(self._bb_multiplier.Value)
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, self._process_candle).Start()
take_profit = float(self._take_profit.Value)
self.StartProtection(
Unit(Decimal(take_profit), UnitTypes.Percent) if take_profit > 0 else Unit(),
Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bollinger_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None or bollinger_value.MovingAverage is None:
return
upper_band = float(bollinger_value.UpBand)
lower_band = float(bollinger_value.LowBand)
middle_band = float(bollinger_value.MovingAverage)
prev_upper = self._prev_upper_band
prev_lower = self._prev_lower_band
self._prev_upper_band = upper_band
self._prev_lower_band = lower_band
if prev_upper is None or prev_lower is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
min_contraction = (float(candle.HighPrice) - float(candle.LowPrice)) * float(self._candle_percent.Value) / 100.0
upper_contraction = prev_upper - upper_band
lower_contraction = lower_band - prev_lower
long_signal = close < lower_band and upper_contraction > 0 and upper_contraction >= min_contraction
short_signal = close > upper_band and lower_contraction > 0 and lower_contraction >= min_contraction
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close >= middle_band:
self.SellMarket(self.Position)
elif self.Position < 0 and close <= middle_band:
self.BuyMarket(-self.Position)
def CreateClone(self):
return bollinger_divergence_strategy()