RSI Overbought/Oversold
This system trades reversals using the Relative Strength Index. When RSI drops below the oversold level, it buys after closing any shorts. When RSI climbs above the overbought level, it sells after closing longs.
Testing indicates an average annual return of about 61%. It performs best in the crypto market.
Positions exit when RSI returns to a neutral zone or the stop-loss is reached.
Details
- Entry Criteria: RSI below
OversoldLevelor aboveOverboughtLevel. - Long/Short: Both directions.
- Exit Criteria: RSI crosses
NeutralLevelor stop. - Stops: Yes.
- Default Values:
RsiPeriod= 14OverboughtLevel= 70OversoldLevel= 30NeutralLevel= 50CandleType= TimeSpan.FromMinutes(5)StopLossPercent= 2.0m
- Filters:
- Category: Oscillator
- Direction: Both
- Indicators: RSI
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday
- Seasonality: No
- Neural Networks: No
- Divergence: Yes
- Risk Level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// RSI Overbought/Oversold strategy.
/// Buys when RSI is oversold, sells when RSI is overbought.
/// </summary>
public class RsiOverboughtOversoldStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _overboughtLevel;
private readonly StrategyParam<int> _oversoldLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
/// <summary>
/// RSI calculation period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI level considered overbought.
/// </summary>
public int OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// RSI level considered oversold.
/// </summary>
public int OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="RsiOverboughtOversoldStrategy"/>.
/// </summary>
public RsiOverboughtOversoldStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetRange(5, 30)
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicators");
_overboughtLevel = Param(nameof(OverboughtLevel), 70)
.SetRange(60, 80)
.SetDisplay("Overbought Level", "RSI overbought threshold", "Indicators");
_oversoldLevel = Param(nameof(OversoldLevel), 30)
.SetRange(20, 40)
.SetDisplay("Oversold Level", "RSI oversold threshold", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cooldown = 0;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
if (Position == 0 && rsiValue <= OversoldLevel)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && rsiValue >= OverboughtLevel)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && rsiValue >= OverboughtLevel)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && rsiValue <= OversoldLevel)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_overbought_oversold_strategy(Strategy):
"""
RSI Overbought/Oversold strategy.
Buys when RSI is oversold, sells when RSI is overbought.
"""
def __init__(self):
super(rsi_overbought_oversold_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14).SetDisplay("RSI Period", "Period for RSI calculation", "Indicators")
self._overbought_level = self.Param("OverboughtLevel", 70).SetDisplay("Overbought Level", "RSI overbought threshold", "Indicators")
self._oversold_level = self.Param("OversoldLevel", 30).SetDisplay("Oversold Level", "RSI oversold threshold", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(rsi_overbought_oversold_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(rsi_overbought_oversold_strategy, self).OnStarted2(time)
self._cooldown = 0
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def _process_candle(self, candle, rsi_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
rv = float(rsi_val)
ob = self._overbought_level.Value
os_level = self._oversold_level.Value
cd = self._cooldown_bars.Value
if self.Position == 0 and rv <= os_level:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and rv >= ob:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and rv >= ob:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and rv <= os_level:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return rsi_overbought_oversold_strategy()