Стратегия возврата по RSI
Стратегия отслеживает индекс относительной силы (RSI) и измеряет его отклонение от среднего значения. Когда RSI отступает более чем на несколько стандартных отклонений от своего среднего, ожидается быстрый возврат к нему.
Тестирование показывает среднегодичную доходность около 61%. Стратегию лучше запускать на крипторынке.
Длинная сделка открывается, когда RSI падает ниже нижней границы, определённой средним минус Multiplier стандартных отклонений. Короткая сделка заключается, когда RSI поднимается выше верхней границы. Выход происходит при возвращении RSI к своей скользящей средней.
Метод подойдёт трейдерам, ищущим объективные сигналы перекупленности и перепроданности. Полоса, основанная на волатильности, адаптирует пороги к текущим условиям рынка, а стоп‑лосс ограничивает убытки.
Подробности
- Условия входа:
- Long: RSI < Avg − Multiplier * StdDev
- Short: RSI > Avg + Multiplier * StdDev
- Long/Short: обе стороны.
- Условия выхода:
- Long: выход при RSI > Avg
- Short: выход при RSI < Avg
- Стопы: да, процентный стоп‑лосс.
- Параметры по умолчанию:
RsiPeriod= 14AveragePeriod= 20Multiplier= 2.0mCandleType= 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>
/// RSI Mean Reversion Strategy.
/// Enter when RSI deviates from its average by a certain multiple of standard deviation.
/// Exit when RSI returns to its average.
/// </summary>
public class RsiMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _averagePeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private SimpleMovingAverage _rsiAverage;
private StandardDeviation _rsiStdDev;
private decimal _prevRsiValue;
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Period for RSI average calculation.
/// </summary>
public int AveragePeriod
{
get => _averagePeriod.Value;
set => _averagePeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for entry.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="RsiMeanReversionStrategy"/>.
/// </summary>
public RsiMeanReversionStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period for RSI calculation", "Strategy Parameters")
.SetOptimize(10, 20, 2);
_averagePeriod = Param(nameof(AveragePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Average Period", "Period for RSI average calculation", "Strategy Parameters")
.SetOptimize(10, 30, 5);
_multiplier = Param(nameof(Multiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("StdDev Multiplier", "Standard deviation multiplier for entry", "Strategy Parameters")
.SetOptimize(1.0m, 3.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "Strategy Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsiValue = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_rsiAverage = new SMA { Length = AveragePeriod };
_rsiStdDev = new StandardDeviation { Length = AveragePeriod };
// Create candle subscription
var subscription = SubscribeCandles(CandleType);
// Define custom indicator chain processing
subscription
.Bind(_rsi, ProcessRsi)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
// Enable position protection
StartProtection(
takeProfit: new Unit(5, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent)
);
}
private void ProcessRsi(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
// Process RSI through average and standard deviation indicators
var rsiAvgValue = _rsiAverage.Process(new DecimalIndicatorValue(_rsiAverage, rsiValue, candle.ServerTime) { IsFinal = true }).ToDecimal();
var rsiStdDevValue = _rsiStdDev.Process(new DecimalIndicatorValue(_rsiStdDev, rsiValue, candle.ServerTime) { IsFinal = true }).ToDecimal();
// Store previous RSI value for changes detection
decimal currentRsiValue = rsiValue;
if (!_rsiAverage.IsFormed || !_rsiStdDev.IsFormed)
{
_prevRsiValue = currentRsiValue;
return;
}
// Calculate bands
var upperBand = rsiAvgValue + Multiplier * rsiStdDevValue;
var lowerBand = rsiAvgValue - Multiplier * rsiStdDevValue;
LogInfo($"RSI: {currentRsiValue}, RSI Avg: {rsiAvgValue}, Upper: {upperBand}, Lower: {lowerBand}");
// Entry logic - mean reversion
if (Position == 0)
{
if (currentRsiValue < lowerBand)
{
BuyMarket();
}
else if (currentRsiValue > upperBand)
{
SellMarket();
}
}
_prevRsiValue = currentRsiValue;
}
}
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, UnitTypes, Unit, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class rsi_mean_reversion_strategy(Strategy):
"""
RSI Mean Reversion Strategy.
Enter when RSI deviates from its average by a certain multiple of standard deviation.
Exit when RSI returns to its average.
"""
def __init__(self):
super(rsi_mean_reversion_strategy, self).__init__()
# Initialize strategy parameters
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "Period for RSI calculation", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 20, 2)
self._average_period = self.Param("AveragePeriod", 20) \
.SetDisplay("Average Period", "Period for RSI average calculation", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 30, 5)
self._multiplier = self.Param("Multiplier", 2.0) \
.SetDisplay("StdDev Multiplier", "Standard deviation multiplier for entry", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "Strategy Parameters")
# Internal indicators
self._rsi = None
self._rsi_average = None
self._rsi_std_dev = None
self._prev_rsi_value = 0
@property
def RsiPeriod(self):
"""RSI period."""
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def AveragePeriod(self):
"""Period for RSI average calculation."""
return self._average_period.Value
@AveragePeriod.setter
def AveragePeriod(self, value):
self._average_period.Value = value
@property
def Multiplier(self):
"""Standard deviation multiplier for entry."""
return self._multiplier.Value
@Multiplier.setter
def Multiplier(self, value):
self._multiplier.Value = value
@property
def CandleType(self):
"""Type of candles to use."""
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
"""Called when the strategy starts."""
super(rsi_mean_reversion_strategy, self).OnStarted2(time)
# Create indicators
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
self._rsi_average = SimpleMovingAverage()
self._rsi_average.Length = self.AveragePeriod
self._rsi_std_dev = StandardDeviation()
self._rsi_std_dev.Length = self.AveragePeriod
# Create candle subscription
subscription = self.SubscribeCandles(self.CandleType)
# Define custom indicator chain processing
subscription.Bind(self._rsi, self.ProcessRsi).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)
# Enable position protection
self.StartProtection(
takeProfit=Unit(5, UnitTypes.Percent),
stopLoss=Unit(2, UnitTypes.Percent)
)
def OnReseted(self):
super(rsi_mean_reversion_strategy, self).OnReseted()
self._prev_rsi_value = 0
def ProcessRsi(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
# Process RSI through average and standard deviation indicators
rsi_avg_value = float(process_float(self._rsi_average, rsi_value, candle.ServerTime, candle.State == CandleStates.Finished))
rsi_std_dev_value = float(process_float(self._rsi_std_dev, rsi_value, candle.ServerTime, candle.State == CandleStates.Finished))
# Store previous RSI value for changes detection
current_rsi_value = rsi_value
# Check if indicators are formed
if not self._rsi_average.IsFormed or not self._rsi_std_dev.IsFormed:
self._prev_rsi_value = current_rsi_value
return
# Calculate bands
upper_band = rsi_avg_value + self.Multiplier * rsi_std_dev_value
lower_band = rsi_avg_value - self.Multiplier * rsi_std_dev_value
self.LogInfo("RSI: {0}, RSI Avg: {1}, Upper: {2}, Lower: {3}".format(
current_rsi_value, rsi_avg_value, upper_band, lower_band))
# Entry logic - only enter when flat (no exit logic in CS)
if self.Position == 0:
# Long Entry: RSI is below lower band
if current_rsi_value < lower_band:
self.BuyMarket()
# Short Entry: RSI is above upper band
elif current_rsi_value > upper_band:
self.SellMarket()
self._prev_rsi_value = current_rsi_value
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return rsi_mean_reversion_strategy()