Stochastic RSI Cross
Strategy based on Stochastic RSI crossover
Testing indicates an average annual return of about 112%. It performs best in the forex market.
Stochastic RSI Cross watches the %K and %D lines of StochRSI. Bullish crosses near oversold levels trigger buys, bearish crosses near overbought trigger sells, and opposite crosses exit.
Because StochRSI oscillates quickly, signals can be frequent. Many traders require the cross to happen near an extreme to filter out noise.
Details
- Entry Criteria: Signals based on RSI, Stochastic.
- Long/Short: Both directions.
- Exit Criteria: Opposite signal or stop.
- Stops: Yes.
- Default Values:
RsiPeriod= 14StochPeriod= 14KPeriod= 3DPeriod= 3StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: RSI, Stochastic
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- 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>
/// Strategy based on Stochastic Oscillator K/D crossover.
/// Buys when %K crosses above %D in oversold zone.
/// Sells when %K crosses below %D in overbought zone.
/// </summary>
public class StochasticRsiCrossStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevK;
private decimal _prevD;
private bool _hasPrevValues;
private int _cooldown;
/// <summary>
/// K period.
/// </summary>
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
/// <summary>
/// D period.
/// </summary>
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="StochasticRsiCrossStrategy"/>.
/// </summary>
public StochasticRsiCrossStrategy()
{
_kPeriod = Param(nameof(KPeriod), 14)
.SetDisplay("K Period", "Period for %K line", "Indicators")
.SetOptimize(10, 20, 2);
_dPeriod = Param(nameof(DPeriod), 3)
.SetDisplay("D Period", "Period for %D line", "Indicators")
.SetOptimize(3, 5, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_prevK = default;
_prevD = default;
_hasPrevValues = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stoch = new StochasticOscillator
{
K = { Length = KPeriod },
D = { Length = DPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stoch, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, stoch);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var stoch = (IStochasticOscillatorValue)stochValue;
if (stoch.K is not decimal k || stoch.D is not decimal d)
return;
if (!_hasPrevValues)
{
_hasPrevValues = true;
_prevK = k;
_prevD = d;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevK = k;
_prevD = d;
return;
}
// %K crosses above %D in oversold zone (< 20) - buy
if (_prevK <= _prevD && k > d && k < 20 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 5;
}
// %K crosses below %D in overbought zone (> 80) - sell
else if (_prevK >= _prevD && k < d && k > 80 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 5;
}
_prevK = k;
_prevD = d;
}
}
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 StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class stochastic_rsi_cross_strategy(Strategy):
"""
Stochastic K/D crossover strategy.
Buys when %K crosses above %D in oversold zone.
Sells when %K crosses below %D in overbought zone.
"""
def __init__(self):
super(stochastic_rsi_cross_strategy, self).__init__()
self._k_period = self.Param("KPeriod", 14).SetDisplay("K Period", "Period for %K line", "Indicators")
self._d_period = self.Param("DPeriod", 3).SetDisplay("D Period", "Period for %D line", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_k = 0.0
self._prev_d = 0.0
self._has_prev = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stochastic_rsi_cross_strategy, self).OnReseted()
self._prev_k = 0.0
self._prev_d = 0.0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(stochastic_rsi_cross_strategy, self).OnStarted2(time)
stoch = StochasticOscillator()
stoch.K.Length = self._k_period.Value
stoch.D.Length = self._d_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stoch, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, stoch)
self.DrawOwnTrades(area)
def _process_candle(self, candle, stoch_val):
if candle.State != CandleStates.Finished:
return
if stoch_val.K is None or stoch_val.D is None:
return
k = float(stoch_val.K)
d = float(stoch_val.D)
if not self._has_prev:
self._has_prev = True
self._prev_k = k
self._prev_d = d
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_k = k
self._prev_d = d
return
# %K crosses above %D in oversold zone (< 20)
if self._prev_k <= self._prev_d and k > d and k < 20 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown = 5
# %K crosses below %D in overbought zone (> 80)
elif self._prev_k >= self._prev_d and k < d and k > 80 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown = 5
self._prev_k = k
self._prev_d = d
def CreateClone(self):
return stochastic_rsi_cross_strategy()