Стратегия BB + RSI
Покупает, когда цена закрывается ниже нижней полосы Боллинджера и RSI находится ниже уровня покупки. Позиция закрывается, если RSI поднимается выше уровня выхода или цена падает от максимума на заданный процент.
Параметры
- Тип свечей
- Период полос Боллинджера
- Отклонение полос Боллинджера
- Период RSI
- Уровень покупки RSI
- Уровень выхода RSI
- Шаг трейлинг-стопа в процентах
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bollinger Bands and RSI strategy with trailing exit.
/// </summary>
public class BbRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<decimal> _bbDeviation;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiBuyLevel;
private readonly StrategyParam<decimal> _rsiExitLevel;
private readonly StrategyParam<decimal> _trailingStep;
private BollingerBands _bollingerBands;
private RelativeStrengthIndex _rsi;
private bool _inTrade;
private decimal _peakPrice;
/// <summary>
/// Candle type for calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BbPeriod
{
get => _bbPeriod.Value;
set => _bbPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands deviation.
/// </summary>
public decimal BbDeviation
{
get => _bbDeviation.Value;
set => _bbDeviation.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI level to enter long.
/// </summary>
public decimal RsiBuyLevel
{
get => _rsiBuyLevel.Value;
set => _rsiBuyLevel.Value = value;
}
/// <summary>
/// RSI level to exit long.
/// </summary>
public decimal RsiExitLevel
{
get => _rsiExitLevel.Value;
set => _rsiExitLevel.Value = value;
}
/// <summary>
/// Trailing step percentage.
/// </summary>
public decimal TrailingStep
{
get => _trailingStep.Value;
set => _trailingStep.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BbRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands")
.SetOptimize(10, 50, 5);
_bbDeviation = Param(nameof(BbDeviation), 1.5m)
.SetRange(0.5m, 5m)
.SetDisplay("BB Deviation", "Bollinger Bands deviation", "Bollinger Bands")
.SetOptimize(1m, 4m, 0.5m);
_rsiPeriod = Param(nameof(RsiPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "RSI")
.SetOptimize(7, 21, 2);
_rsiBuyLevel = Param(nameof(RsiBuyLevel), 48m)
.SetRange(0m, 100m)
.SetDisplay("RSI Buy Level", "RSI threshold to enter long", "RSI")
.SetOptimize(20m, 40m, 5m);
_rsiExitLevel = Param(nameof(RsiExitLevel), 52m)
.SetRange(0m, 100m)
.SetDisplay("RSI Exit Level", "RSI threshold to exit long", "RSI")
.SetOptimize(60m, 80m, 5m);
_trailingStep = Param(nameof(TrailingStep), 2m)
.SetRange(0.1m, 20m)
.SetDisplay("Trailing Step %", "Trailing stop step percent", "Risk")
.SetOptimize(0.5m, 5m, 0.5m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_inTrade = default;
_peakPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollingerBands = new BollingerBands { Length = BbPeriod, Width = BbDeviation };
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx([_bollingerBands, _rsi], ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollingerBands);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
if (values[0] is not BollingerBandsValue bbValue ||
bbValue.UpBand is not decimal upperBand ||
bbValue.LowBand is not decimal lowerBand ||
!values[1].IsFormed)
return;
var rsiValue = values[1].GetValue<decimal>();
var closePrice = candle.ClosePrice;
if (Position == 0)
{
// Long entry: close below lower BB and RSI oversold
if (closePrice < lowerBand && rsiValue < RsiBuyLevel)
{
BuyMarket();
_peakPrice = closePrice;
_inTrade = true;
}
// Short entry: close above upper BB and RSI overbought
else if (closePrice > upperBand && rsiValue > RsiExitLevel)
{
SellMarket();
_peakPrice = closePrice;
_inTrade = true;
}
}
else if (Position > 0 && _inTrade)
{
if (closePrice > _peakPrice)
_peakPrice = closePrice;
var trailingDrop = _peakPrice * (1m - TrailingStep / 100m);
if (closePrice <= trailingDrop || rsiValue > RsiExitLevel)
{
SellMarket();
_inTrade = false;
}
}
else if (Position < 0 && _inTrade)
{
if (closePrice < _peakPrice)
_peakPrice = closePrice;
var trailingRise = _peakPrice * (1m + TrailingStep / 100m);
if (closePrice >= trailingRise || rsiValue < RsiBuyLevel)
{
BuyMarket();
_inTrade = false;
}
}
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class bb_rsi_strategy(Strategy):
def __init__(self):
super(bb_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bb_period = self.Param("BbPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands")
self._bb_deviation = self.Param("BbDeviation", 1.5) \
.SetDisplay("BB Deviation", "Bollinger Bands deviation", "Bollinger Bands")
self._rsi_period = self.Param("RsiPeriod", 13) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI calculation period", "RSI")
self._rsi_buy_level = self.Param("RsiBuyLevel", 48.0) \
.SetDisplay("RSI Buy Level", "RSI threshold to enter long", "RSI")
self._rsi_exit_level = self.Param("RsiExitLevel", 52.0) \
.SetDisplay("RSI Exit Level", "RSI threshold to exit long", "RSI")
self._trailing_step = self.Param("TrailingStep", 2.0) \
.SetDisplay("Trailing Step %", "Trailing stop step percent", "Risk")
self._in_trade = False
self._peak_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(bb_rsi_strategy, self).OnReseted()
self._in_trade = False
self._peak_price = 0.0
def OnStarted2(self, time):
super(bb_rsi_strategy, self).OnStarted2(time)
bb = BollingerBands()
bb.Length = self._bb_period.Value
bb.Width = self._bb_deviation.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, rsi, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_value, rsi_value):
if candle.State != CandleStates.Finished:
return
bb = bb_value
upper = bb.UpBand
lower = bb.LowBand
if upper is None or lower is None:
return
if not rsi_value.IsFormed:
return
upper_v = float(upper)
lower_v = float(lower)
rsi_v = float(rsi_value)
close = float(candle.ClosePrice)
buy_level = float(self._rsi_buy_level.Value)
exit_level = float(self._rsi_exit_level.Value)
trail_step = float(self._trailing_step.Value)
if self.Position == 0:
if close < lower_v and rsi_v < buy_level:
self.BuyMarket()
self._peak_price = close
self._in_trade = True
elif close > upper_v and rsi_v > exit_level:
self.SellMarket()
self._peak_price = close
self._in_trade = True
elif self.Position > 0 and self._in_trade:
if close > self._peak_price:
self._peak_price = close
trailing_drop = self._peak_price * (1.0 - trail_step / 100.0)
if close <= trailing_drop or rsi_v > exit_level:
self.SellMarket()
self._in_trade = False
elif self.Position < 0 and self._in_trade:
if close < self._peak_price:
self._peak_price = close
trailing_rise = self._peak_price * (1.0 + trail_step / 100.0)
if close >= trailing_rise or rsi_v < buy_level:
self.BuyMarket()
self._in_trade = False
def CreateClone(self):
return bb_rsi_strategy()