布林 RSI 策略
本策略结合布林带过度扩张与RSI动量信号。 当价格收于布林带外且RSI出现背离时,往往预示即将反转。 系统在此背离处逆势建仓,价格重新回到带内或RSI反向穿越即平仓。 采用紧凑的百分比止损,以防波动进一步扩大。
测试表明年均收益约为 148%,该策略在外汇市场表现最佳。
细节
- 入场条件:指标信号
- 多/空:均可
- 退出条件:止损或反向信号
- 止损:是,按百分比
- 默认值:
CandleType= 15分钟StopLossPercent= 2BollingerPeriod= 20BollingerDeviation= 2RsiPeriod= 14 收盘价高于上轨且高于上一次上轨外的收盘价,而RSI低于当时的值,即为看跌背离并做空;下轨下方的镜像情形做多,并反转相反持仓。价格收回通道内或RSI回穿50时平仓。
- 过滤器:
- 类别:均值回归
- 方向:双向
- 指标:布林带, 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()