Estrategia BB + RSI
Esta estrategia compra cuando el precio cierra por debajo de la banda inferior de Bollinger Bands mientras el RSI está por debajo del nivel de compra. La posición se cierra cuando el RSI sube por encima del nivel de salida o cuando el precio cae desde el máximo el porcentaje de paso trailing especificado.
Parámetros
- Tipo de vela
- Período de Bollinger Bands
- Desviación de Bollinger Bands
- Período del RSI
- Nivel de compra del RSI
- Nivel de salida del RSI
- Porcentaje de paso trailing
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()