Прорыв наклона RSI
Стратегия RSI Slope Breakout отслеживает скорость изменения индекса относительной силы. Необычно крутой наклон свидетельствует о начале нового тренда.
Тестирование показывает среднегодичную доходность около 136%. Стратегию лучше запускать на фондовом рынке.
Сделка открывается, когда наклон превышает свой типичный уровень на несколько стандартных отклонений и выполняется в направлении ускорения с защитным стопом.
Стратегия предназначена для активных трейдеров, стремящихся рано войти в тренд. Позиции закрываются, когда наклон возвращается к нормальным значениям. Значение по умолчанию RsiPeriod = 14.
Подробности
- Условие входа: Индикатор превышает среднее значение на величину коэффициента отклонения.
- Лонг/Шорт: Оба направления.
- Условие выхода: Индикатор возвращается к среднему.
- Стопы: Да.
- Значения по умолчанию:
RsiPeriod= 14LookbackPeriod= 20DeviationMultiplier= 2mStopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Прорыв
- Направление: Оба
- Индикаторы: 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>
/// 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()