Estrategia de Ruptura de Pendiente RSI
La estrategia de Ruptura de Pendiente RSI observa la tasa de cambio del RSI. Una pendiente inusualmente pronunciada sugiere que se está formando una nueva tendencia.
Las pruebas indican un rendimiento anual promedio de aproximadamente 136%. Funciona mejor en el mercado de acciones.
Las entradas ocurren cuando la pendiente supera su nivel típico en un múltiplo de la desviación estándar, tomando operaciones en la dirección de la aceleración con un stop protector.
Atrae a los traders activos que buscan una exposición temprana a la tendencia. Las posiciones se cierran cuando la pendiente regresa a lecturas normales. RsiPeriod predeterminado = 14.
Detalles
- Criterios de entrada: El indicador supera la media por el multiplicador de desviación.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: El indicador revierte a la media.
- Stops: Sí.
- Valores predeterminados:
RsiPeriod= 14LookbackPeriod= 20DeviationMultiplier= 2mStopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: RSI
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Strategy based on RSI (Relative Strength Index) Slope breakout
/// Enters positions when the slope of RSI exceeds average slope plus a multiple of standard deviation
/// </summary>
public class RsiSlopeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private RelativeStrengthIndex _rsi;
private decimal _prevRsiValue;
private decimal _currentSlope;
private decimal _avgSlope;
private decimal _stdDevSlope;
private decimal[] _slopes;
private int _currentIndex;
private bool _isInitialized;
/// <summary>
/// RSI period
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Lookback period for slope statistics calculation
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for breakout detection
/// </summary>
public decimal DeviationMultiplier
{
get => _deviationMultiplier.Value;
set => _deviationMultiplier.Value = value;
}
/// <summary>
/// Candle type
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop loss percentage
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Constructor
/// </summary>
public RsiSlopeBreakoutStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period for RSI indicator", "Indicator Parameters")
.SetOptimize(7, 21, 7);
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Period for slope statistics calculation", "Strategy Parameters")
.SetOptimize(10, 50, 5);
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for breakout detection", "Strategy Parameters")
.SetOptimize(1m, 3m, 0.5m);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevRsiValue = 0;
_currentSlope = 0;
_avgSlope = 0;
_stdDevSlope = 0;
_currentIndex = 0;
_isInitialized = false;
_slopes = new decimal[LookbackPeriod];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_slopes = new decimal[LookbackPeriod];
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
// Set up position protection
StartProtection(
takeProfit: null, // We'll handle exits via strategy logic
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent)
);
base.OnStarted2(time);
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if indicator is formed
if (!_rsi.IsFormed)
return;
// Initialize on first valid value
if (!_isInitialized)
{
_prevRsiValue = rsiValue;
_isInitialized = true;
return;
}
// Calculate current RSI slope (difference between current and previous RSI values)
_currentSlope = rsiValue - _prevRsiValue;
// Store slope in array and update index
_slopes[_currentIndex] = _currentSlope;
_currentIndex = (_currentIndex + 1) % LookbackPeriod;
// Calculate statistics once we have enough data
if (!IsFormedAndOnlineAndAllowTrading())
return;
CalculateStatistics();
// Trading logic
if (Math.Abs(_avgSlope) > 0) // Avoid division by zero
{
// Long signal: RSI slope exceeds average + k*stddev (slope is positive and we don't have a long position)
if (_currentSlope > 0 &&
_currentSlope > _avgSlope + DeviationMultiplier * _stdDevSlope &&
Position <= 0)
{
// Cancel existing orders
CancelActiveOrders();
// Enter long position
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
LogInfo($"Long signal: RSI Slope {_currentSlope} > Avg {_avgSlope} + {DeviationMultiplier}*StdDev {_stdDevSlope}");
}
// Short signal: RSI slope exceeds average + k*stddev in negative direction (slope is negative and we don't have a short position)
else if (_currentSlope < 0 &&
_currentSlope < _avgSlope - DeviationMultiplier * _stdDevSlope &&
Position >= 0)
{
// Cancel existing orders
CancelActiveOrders();
// Enter short position
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
LogInfo($"Short signal: RSI Slope {_currentSlope} < Avg {_avgSlope} - {DeviationMultiplier}*StdDev {_stdDevSlope}");
}
// Exit conditions - when slope returns to average
if (Position > 0 && _currentSlope < _avgSlope)
{
// Exit long position
SellMarket(Math.Abs(Position));
LogInfo($"Exit long: RSI Slope {_currentSlope} < Avg {_avgSlope}");
}
else if (Position < 0 && _currentSlope > _avgSlope)
{
// Exit short position
BuyMarket(Math.Abs(Position));
LogInfo($"Exit short: RSI Slope {_currentSlope} > Avg {_avgSlope}");
}
}
// Store current RSI value for next slope calculation
_prevRsiValue = rsiValue;
}
private void CalculateStatistics()
{
// Reset statistics
_avgSlope = 0;
decimal sumSquaredDiffs = 0;
// Calculate average slope
for (int i = 0; i < LookbackPeriod; i++)
{
_avgSlope += _slopes[i];
}
_avgSlope /= LookbackPeriod;
// Calculate standard deviation of slopes
for (int i = 0; i < LookbackPeriod; i++)
{
decimal diff = _slopes[i] - _avgSlope;
sumSquaredDiffs += diff * diff;
}
_stdDevSlope = (decimal)Math.Sqrt((double)(sumSquaredDiffs / LookbackPeriod));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class rsi_slope_breakout_strategy(Strategy):
"""
Strategy based on RSI (Relative Strength Index) Slope breakout
Enters positions when the slope of RSI exceeds average slope plus a multiple of standard deviation
"""
def __init__(self):
super(rsi_slope_breakout_strategy, self).__init__()
# Initialize strategy parameters
self._rsiPeriod = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "Period for RSI indicator", "Indicator Parameters") \
.SetCanOptimize(True) \
.SetOptimize(7, 21, 7)
self._lookbackPeriod = self.Param("LookbackPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Period", "Period for slope statistics calculation", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 5)
self._deviationMultiplier = self.Param("DeviationMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for breakout detection", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._stopLossPercent = self.Param("StopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
self._candleType = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
# Indicators and state variables
self._rsi = None
self._prevRsiValue = 0.0
self._currentSlope = 0.0
self._avgSlope = 0.0
self._stdDevSlope = 0.0
self._slopes = []
self._currentIndex = 0
self._isInitialized = False
@property
def RsiPeriod(self):
"""RSI period"""
return self._rsiPeriod.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsiPeriod.Value = value
@property
def LookbackPeriod(self):
"""Lookback period for slope statistics calculation"""
return self._lookbackPeriod.Value
@LookbackPeriod.setter
def LookbackPeriod(self, value):
self._lookbackPeriod.Value = value
@property
def DeviationMultiplier(self):
"""Standard deviation multiplier for breakout detection"""
return self._deviationMultiplier.Value
@DeviationMultiplier.setter
def DeviationMultiplier(self, value):
self._deviationMultiplier.Value = value
@property
def CandleType(self):
"""Candle type"""
return self._candleType.Value
@CandleType.setter
def CandleType(self, value):
self._candleType.Value = value
@property
def StopLossPercent(self):
"""Stop loss percentage"""
return self._stopLossPercent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stopLossPercent.Value = value
def OnReseted(self):
"""
Resets internal state when strategy is reset.
"""
super(rsi_slope_breakout_strategy, self).OnReseted()
self._prevRsiValue = 0
self._currentSlope = 0
self._avgSlope = 0
self._stdDevSlope = 0
self._slopes = [0.0] * self.LookbackPeriod
self._currentIndex = 0
self._isInitialized = False
def OnStarted2(self, time):
super(rsi_slope_breakout_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
self._slopes = [0.0] * self.LookbackPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self.ProcessCandle).Start()
# Setup chart visualization if available
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._rsi)
self.DrawOwnTrades(area)
# Set up position protection
self.StartProtection(
takeProfit=None,
stopLoss=Unit(self.StopLossPercent, UnitTypes.Percent)
)
def ProcessCandle(self, candle, rsi_value):
# Skip unfinished candles
if candle.State != CandleStates.Finished:
return
# Check if indicator is formed
if not self._rsi.IsFormed:
return
# Initialize on first valid value
if not self._isInitialized:
self._prevRsiValue = rsi_value
self._isInitialized = True
return
# Calculate current RSI slope (difference between current and previous RSI values)
self._currentSlope = rsi_value - self._prevRsiValue
# Store slope in array and update index
self._slopes[self._currentIndex] = self._currentSlope
self._currentIndex = (self._currentIndex + 1) % self.LookbackPeriod
# Calculate statistics once we have enough data
if not self.IsFormedAndOnlineAndAllowTrading():
self._prevRsiValue = rsi_value
return
self.CalculateStatistics()
# Trading logic
if abs(self._avgSlope) > 0: # Avoid division by zero
# Long signal: RSI slope exceeds average + k*stddev (slope is positive and we don't have a long position)
if self._currentSlope > 0 and \
self._currentSlope > self._avgSlope + self.DeviationMultiplier * self._stdDevSlope and \
self.Position <= 0:
# Cancel existing orders
self.CancelActiveOrders()
# Enter long position
volume = self.Volume + Math.Abs(self.Position)
self.BuyMarket(volume)
self.LogInfo(f"Long signal: RSI Slope {self._currentSlope} > Avg {self._avgSlope} + {self.DeviationMultiplier}*StdDev {self._stdDevSlope}")
# Short signal: RSI slope exceeds average + k*stddev in negative direction (slope is negative and we don't have a short position)
elif self._currentSlope < 0 and \
self._currentSlope < self._avgSlope - self.DeviationMultiplier * self._stdDevSlope and \
self.Position >= 0:
# Cancel existing orders
self.CancelActiveOrders()
# Enter short position
volume = self.Volume + Math.Abs(self.Position)
self.SellMarket(volume)
self.LogInfo(f"Short signal: RSI Slope {self._currentSlope} < Avg {self._avgSlope} - {self.DeviationMultiplier}*StdDev {self._stdDevSlope}")
# Exit conditions - when slope returns to average
if self.Position > 0 and self._currentSlope < self._avgSlope:
# Exit long position
self.SellMarket(Math.Abs(self.Position))
self.LogInfo(f"Exit long: RSI Slope {self._currentSlope} < Avg {self._avgSlope}")
elif self.Position < 0 and self._currentSlope > self._avgSlope:
# Exit short position
self.BuyMarket(Math.Abs(self.Position))
self.LogInfo(f"Exit short: RSI Slope {self._currentSlope} > Avg {self._avgSlope}")
# Store current RSI value for next slope calculation
self._prevRsiValue = rsi_value
def CalculateStatistics(self):
# Reset statistics
self._avgSlope = 0
sumSquaredDiffs = 0
# Calculate average slope
for i in range(self.LookbackPeriod):
self._avgSlope += self._slopes[i]
self._avgSlope /= self.LookbackPeriod
# Calculate standard deviation of slopes
for i in range(self.LookbackPeriod):
diff = self._slopes[i] - self._avgSlope
sumSquaredDiffs += diff * diff
self._stdDevSlope = Math.Sqrt(sumSquaredDiffs / self.LookbackPeriod)
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return rsi_slope_breakout_strategy()