The stochastic RSI — often abbreviated StochRSI — is much less intimidating than it sounds. This oscillator is simply the stochastic oscillator applied to an RSI signal.

It’s used in a very similar way as the indicators that it’s built on. It ranges from 0–100 with higher readings indicating overbought levels (potentially decreasing), and lower values indicating oversold levels (potentially increasing). StochRSI moves much more quickly than the standard RSI, so can generate a lot of trading signals, and traders may even smooth it (SMA-StochRSI) or combine it with other values to determine entry and exit points.

Let’s get to the calculations and show some examples of how we can use this.

Stochastic Oscillator + RSI = StochRSI

The StochRSI calculation takes place in a few simple steps.

1. Calculate Relative Strength (RS) factor by taking the average gains (e.g. days when prices closed up) divided by the average losses (e.g. days when prices closed down), multiplying by the number of periods (P) minus 1, and adding today's gain and loss (this is a smoothed moving average). Dividing by these gives the RS factor.
• RS[t] = (Gains[t] * (P-1) * mean(Gains[-P:t-1])) / (Losses[t] * (P-1) * mean(Losses[-P:t-1))

2. Plug RS into the RSI formula to get RSI.

• RSI[t] = 100 - 100 / (1 + RS[t])

3. Calculate StochRSI from the highs and lows of the RSI over the previous N periods.

• StochRSI[t] = 100 * (RSI[t] - min(RSI[-N:])) / (max(RSI[-N:]) - min(RSI[-N:]))

Or, if you like the math, we can write the steps as:

#### Step 1: Calculate RS

$$RS_t = \left\{ \begin{array}{11} \frac{\sum_{t=1}^P Gain_t}{\sum_{t=1}^P \mid Loss_t \mid} \quad \textrm{if} \; t = P\;\textrm{(initial value)} \\ \frac{Gain_t + (P - 1) / P \sum_{i=1}^{P-1} Gain_i}{\mid Loss_t \mid + (P-1) / P \sum_{i=1}^{P-1} \mid Loss_i \mid} \quad \textrm{if} \; t > P \end{array} \right.$$

#### Step 2: Calculate RSI

$$RSI_t = 100 - \bigg( \frac{100}{1 + RS_t} \bigg)$$

#### Step 3: Calculate StochRSI

$$K^{RSI}_t = 100 \bigg( \frac{RSI_t - \textrm{min}\; \bf{RSI}^N}{\textrm{max} \; \bf{RSI}^N- \textrm{min} \; \bf{RSI}^N} \bigg)$$

where t gives the time period, P is the look-back period for the RSI, N is the look-back period for the StochRSI, and the bold RSI indicate a vector of RSI values of length N. Most commonly you’ll see P=N=14 for the model, so we’ll use that in our example.

OK, so we walked through the calculation in words, pseudocode, and math, let’s put it into practice in Python.

## Stochastic RSI Example in Python

To start, we’ll import the usual packages.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf

From here, we can get some data and calculate the gains and losses to begin our RS calculation.

ticker = 'GE'
start = '2019-01-01'
end = '2019-12-31'
yfObj = yf.Ticker(ticker)
data = yfObj.history(start=start, end=end)
P = N = 14
# Drop extra columns for paucity
data.drop(['Open', 'High', 'Low', 'Volume', 'Dividends',
'Stock Splits'], inplace=True, axis=1)
# Calculate gains and losses
data['diff_close'] = data['Close'] - data['Close'].shift(1)
data['gain'] = np.where(data['diff_close']>0,
data['diff_close'], 0)
data['loss'] = np.where(data['diff_close']<0,
np.abs(data['diff_close']), 0)
data.head()

Now that we have our gains and losses, we need to calculate the averages of each. RS needs to be initialized with the simple moving average of the gains and losses at the time where t=P, or row 14 in our case.

This can be done in just a single line of Python using the rolling method.

data[['init_avg_gain', 'init_avg_loss']] = data[
['gain', 'loss']].rolling(P).mean()
data.iloc[10:15]

Now we have our initial average gain and loss values in the 14th row of our data frame. All the subsequent rows are going to be calculated using the second formula in our RS equation above.

avg_gain = np.zeros(len(data))
avg_loss = np.zeros(len(data))
for i, _row in enumerate(data.iterrows()):
row = _row[1]
if i < P - 1:
last_row = row.copy()
continue
elif i == P-1:
avg_gain[i] += row['init_avg_gain']
avg_loss[i] += row['init_avg_loss']
else:
avg_gain[i] += ((P - 1) * avg_gain[i-1] + row['gain']) / P
avg_loss[i] += ((P - 1) * avg_loss[i-1] + row['loss']) / P

last_row = row.copy()

data['avg_gain'] = avg_gain
data['avg_loss'] = avg_loss
data.tail()

To get the RSI, we simply need to take the ratio between our avg_gain and avg_loss values, then plug those into the RSI formula, and we're set!

data['RS'] = data['avg_gain'] / data['avg_loss']
data['RSI'] = 100 - 100 / (1 + data['RS'])
data.tail()

Now, we need to implement the stochastic oscillator formula on our RSI values.

Usually the stochastic oscillator compares the high print versus the low print over the previous 𝑁 periods, but in this case, we just look at the RSI value itself.

data['low_N'] = data['RSI'].rolling(N).min()
data['high_N'] = data['RSI'].rolling(N).max()
data['StochRSI'] = 100 * (data['RSI'] - data['low_N']) /
(data['high_N'] - data['low_N'])
data.tail()

Let’s put this all together in a simple function (or three).

def calc_RSI(data, P=14):
# Calculate gains and losses
data['diff_close'] = data['Close'] - data['Close'].shift(1)
data['gain'] = np.where(data['diff_close']>0, data['diff_close'], 0)
data['loss'] = np.where(data['diff_close']<0, np.abs(data['diff_close']), 0)
# Get initial values
data[['init_avg_gain', 'init_avg_loss']] = data[['gain', 'loss']].rolling(P).mean()
# Calculate smoothed avg gains and losses for all t > P
avg_gain = np.zeros(len(data))
avg_loss = np.zeros(len(data))
for i, _row in enumerate(data.iterrows()):
row = _row[1]
if i < P - 1:
last_row = row.copy()
continue
elif i == P-1:
avg_gain[i] += row['init_avg_gain']
avg_loss[i] += row['init_avg_loss']
else:
avg_gain[i] += ((P - 1) * avg_gain[i-1] + row['gain']) / P
avg_loss[i] += ((P - 1) * avg_loss[i-1] + row['loss']) / P

last_row = row.copy()

data['avg_gain'] = avg_gain
data['avg_loss'] = avg_loss

# Calculate RS and RSI
data['RS'] = data['avg_gain'] / data['avg_loss']
data['RSI'] = 100 - 100 / (1 + data['RS'])
return data

def calc_StochOscillator(data, N=14):
data['low_N'] = data['RSI'].rolling(N).min()
data['high_N'] = data['RSI'].rolling(N).max()
data['StochRSI'] = 100 * (data['RSI'] - data['low_N']) / \
(data['high_N'] - data['low_N'])
return data

def calc_StochRSI(data, P=14, N=14):
data = calc_RSI(data, P)
data = calc_StochOscillator(data, N)
return data

Now we can run this from beginning to end and plot the output

ticker = 'GE'
start = '2019-01-01'
end = '2019-12-31'

yfObj = yf.Ticker(ticker)
data = yfObj.history(start=start, end=end)
P = N = 14

# Drop extra columns for paucity
data.drop(['Open', 'High', 'Low', 'Volume', 'Dividends',
'Stock Splits'],inplace=True, axis=1)
data = calc_StochRSI(data)

# Plot the output
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
fig, ax = plt.subplots(3, figsize=(15, 8), sharex=True)

ax[0].plot(data.index, data['Close'], label=f'{ticker}',
c=colors[4])
ax[0].set_title(f'Price for {ticker}')
ax[0].set_ylabel('Price (USD)')

ax[1].plot(data.index, data['StochRSI'], label='StochRSI')
ax[1].plot(data.index, data['RSI'], label='RSI')
ax[1].axhline(80, label='Overbought', c=colors[3], linestyle=':')
ax[1].axhline(20, label='Oversold', c=colors[3], linestyle='-.')
ax[1].set_title(f'Stochastic RSI and RSI for {ticker}')
ax[1].set_ylabel('Level')
ax[1].legend(bbox_to_anchor=[1., 0.85])

ax[2].plot(data.index, data['StochRSI'], label='StochRSI')
ax[2].plot(data.index, data['StochRSI'].rolling(N).mean(),
label='Smoothed Stoch RSI')
ax[2].axhline(80, label='Overbought', c=colors[3], linestyle=':')
ax[2].axhline(20, label='Oversold', c=colors[3], linestyle='-.')
ax[2].set_title(f'StochRSI and Smoothed StochRSI for {ticker}')
ax[2].set_ylabel('Level')
ax[2].set_xlabel('Date')
ax[2].legend(bbox_to_anchor=[1, 0.85])
plt.tight_layout()
plt.show()

In the top plot, we have just the price, followed by a plot of the stochastic RSI and RSI. You can see here that the StochRSI is wild — it frequently jumps from 0 to 100 in a day or two which can lead to a plethora of trading signals, whereas the RSI itself only touches the overbought level twice in this year of data.

For this reason, traders often use a smoothed StochRSI — just like the %D value you see with the stochastic indicator itself — in conjunction with the StochRSI to make trading decisions.

## How to Trade the StochRSI

The easiest way to trade this is by buying when the StochRSI or the smoothed StochRSI is below the oversold level and selling when it is above the overbought level. This is standard practice in a mean reversion strategy.

Additionally, it can be used as a momentum indicator. If the level is above the centerline (50) then we have upward momentum we can buy. If it’s below 50, then it is moving downward and we have a sell/short signal.

Just like with the stochastic oscillator itself, we can also track the difference between the raw StochRSI and the smoothed StochRSI. If they cross or if the difference becomes too large, we can generate buy and sell signals for our strategy.