Bollinger RSI 戦略
Bollinger RSI は、ボリンジャーバンドの過伸展と RSI のモメンタムシグナルを組み合わせます。 価格がバンドの外側でクローズしているが RSI がダイバージェンスを示している場合、反転が近いことが多いです。
テストでは平均年間リターンは約148%を示しています。外国為替市場で最も良いパフォーマンスを発揮します。
このシステムはそのダイバージェンスで逆張りトレードを行い、価格がバンド内に戻るか RSI が反対にクロスしたときにイグジットします。
ボラティリティがさらに拡大する場合に備え、タイトなパーセントストップがエクスポージャーを制限します。
詳細
- エントリー条件: インジケーターシグナル
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは反対シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15 minuteStopLossPercent= 2BollingerPeriod= 20BollingerDeviation= 2RsiPeriod= 14 上側バンドより上の終値が前回のバンド外終値より高いのに、RSIが当時より低ければ弱気ダイバージェンスとして売り、下側バンドの下での逆の状況では買い、反対ポジションはドテンします。価格がバンド内に戻って引けるか、RSIが50を逆方向に横切ったら決済します。
- フィルター:
- カテゴリ: 平均回帰
- 方向: 両方
- インジケーター: Bollinger Bands, RSI
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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 RSI strategy.
/// A close above the upper band that is higher than the previous close above that band while RSI is lower than it was then
/// is a bearish divergence and goes short; the mirror below the lower band goes long, reversing an opposite position.
/// The position closes once price closes back inside the bands or RSI crosses back over 50, and a percent stop limits the loss.
/// </summary>
public class BollingerRsiStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
// The previous close outside each band and RSI at that close.
private decimal? _upperClose;
private decimal _upperRsi;
private decimal? _lowerClose;
private decimal _lowerRsi;
/// <summary>
/// Period of the Bollinger Bands.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier of the bands.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Period of RSI.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.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 BollingerRsiStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Period of the Bollinger Bands", "Indicators");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier of the bands", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period of RSI", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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 OnReseted()
{
base.OnReseted();
_upperClose = null;
_upperRsi = default;
_lowerClose = null;
_lowerRsi = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_upperClose = null;
_upperRsi = default;
_lowerClose = null;
_lowerRsi = default;
var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, rsi, 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);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsi);
}
}
}
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 rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed || !rsiValue.IsFormed)
return;
var bands = (BollingerBandsValue)bollingerValue;
if (bands.UpBand is not decimal upper || bands.LowBand is not decimal lower)
return;
var rsi = rsiValue.GetValue<decimal>();
var close = candle.ClosePrice;
var signal = 0;
if (close > upper)
{
if (_upperClose is decimal previousClose && close > previousClose && rsi < _upperRsi)
signal = -1;
_upperClose = close;
_upperRsi = rsi;
}
else if (close < lower)
{
if (_lowerClose is decimal previousClose && close < previousClose && rsi > _lowerRsi)
signal = 1;
_lowerClose = close;
_lowerRsi = rsi;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (signal > 0)
{
if (Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
else if (signal < 0)
{
if (Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
else if (Position > 0)
{
if (close >= lower || rsi > 50m)
SellMarket(Position);
}
else if (Position < 0)
{
if (close <= upper || rsi < 50m)
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class bollinger_rsi_strategy(Strategy):
"""
Bollinger RSI strategy.
A close above the upper band that is higher than the previous close above that band while RSI is lower than it was then
is a bearish divergence and goes short; the mirror below the lower band goes long, reversing an opposite position.
The position closes once price closes back inside the bands or RSI crosses back over 50, and a percent stop limits the loss.
"""
def __init__(self):
super(bollinger_rsi_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20).SetGreaterThanZero().SetDisplay("Bollinger Period", "Period of the Bollinger Bands", "Indicators")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0).SetGreaterThanZero().SetDisplay("Bollinger Deviation", "Standard deviation multiplier of the bands", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "Period of RSI", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
# The previous close outside each band and RSI at that close.
self._upper_close = None
self._upper_rsi = Decimal(0)
self._lower_close = None
self._lower_rsi = Decimal(0)
def OnReseted(self):
super(bollinger_rsi_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(bollinger_rsi_strategy, self).OnStarted2(time)
self._reset_state()
bollinger = BollingerBands()
bollinger.Length = self._bollinger_period.Value
bollinger.Width = Decimal(self._bollinger_deviation.Value)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, rsi, 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.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
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, rsi_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed or not rsi_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
upper = bollinger_value.UpBand
lower = bollinger_value.LowBand
rsi = rsi_value.GetValue[Decimal](None)
close = candle.ClosePrice
signal = 0
if close > upper:
if self._upper_close is not None and close > self._upper_close and rsi < self._upper_rsi:
signal = -1
self._upper_close = close
self._upper_rsi = rsi
elif close < lower:
if self._lower_close is not None and close < self._lower_close and rsi > self._lower_rsi:
signal = 1
self._lower_close = close
self._lower_rsi = rsi
if not self.IsFormedAndOnlineAndAllowTrading():
return
middle = Decimal(50)
if signal > 0:
if self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif signal < 0:
if self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0:
if close >= lower or rsi > middle:
self.SellMarket(self.Position)
elif self.Position < 0:
if close <= upper or rsi < middle:
self.BuyMarket(-self.Position)
def CreateClone(self):
return bollinger_rsi_strategy()