Predicting Trend Reversal in Algorithmic Trading using Stochastic Oscillator in Python

stochastic oscillator
to signal trend reversals in the Stock Market

The stochastic oscillator (SO) is a momentum indicator used to signal trend reversals in the stock market. The SO presents the location of the closing price of a stock in relation to the high and low range of the price of a stock over a period of time, typically a 14-day period.

Most Stochastic indicators have a pre-defined 80% Overbought line and a 20% Oversold line on the chart. With the crossing of the two lines, Lane’s SO gave a “signal” during a market. 

The stochastic oscillator has 2 primary signals that work to build a trading signal. These signals, referred to as the fast and slow signals, oscillate between values of 0 and 100. Within this range, there are certain threshold levels used to describe buying and selling pressure. Typically, these are set to 20 (oversold) and 80 (overbought).

Remember that SO does not contain all of the data necessary for proper analysis of stock price action. It doesn’t factor in Volume at all. Without Volume, you really are analyzing half of the equation. So add a Volume indicator to your indicator set-up, for confirmation of the signals you use in the SO.

Let’s focus on the SO plotting example in Python.

We use yfinance library to request the previous 6-months pricing history for $NVDA

!pip install pandas-datareader

!pip install pandas_ta

!pip install yfinance

import pandas_datareader as pdr

Let’s request data via Yahoo public API
data = pdr.get_data_yahoo(‘NVDA’)

print(data.info())

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1258 entries, 2017-06-07 to 2022-06-03
Data columns (total 6 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   High       1258 non-null   float64
 1   Low        1258 non-null   float64
 2   Open       1258 non-null   float64
 3   Close      1258 non-null   float64
 4   Volume     1258 non-null   float64
 5   Adj Close  1258 non-null   float64
dtypes: float64(6)
memory usage: 68.8 KB
None

import yfinance as yf

We request historical data for the past 5 years

data = yf.Ticker(“NVDA”).history(period=’5y’)

print(data.info())

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1260 entries, 2017-06-05 to 2022-06-03
Data columns (total 7 columns):
 #   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   Open          1260 non-null   float64
 1   High          1260 non-null   float64
 2   Low           1260 non-null   float64
 3   Close         1260 non-null   float64
 4   Volume        1260 non-null   int64  
 5   Dividends     1260 non-null   float64
 6   Stock Splits  1260 non-null   float64
dtypes: float64(6), int64(1)
memory usage: 78.8 KB
None

import pandas_ta as ta

df = yf.Ticker(‘nvda’)
df = df.history(period=’6mo’)[[‘Open’, ‘High’, ‘Low’, ‘Close’]]

df.tail()

Define time periods
k_period = 14
d_period = 3

Add a “n_high” column with max value of previous 14 periods
df[‘n_high’] = df[‘High’].rolling(k_period).max()

Add an “n_low” column with min value of previous 14 periods
df[‘n_low’] = df[‘Low’].rolling(k_period).min()

Use the min/max values to calculate the %k (as a percentage)
df[‘%K’] = (df[‘Close’] – df[‘n_low’]) * 100 / (df[‘n_high’] – df[‘n_low’])

Use the %k to calculates a SMA over the past 3 values of %k
df[‘%D’] = df[‘%K’].rolling(d_period).mean()

Add some indicators
df.ta.stoch(high=’high’, low=’low’, k=14, d=3, append=True)

Let’s plot the final charts

import plotly.graph_objects as go
from plotly.subplots import make_subplots

df.columns = [x.lower() for x in df.columns]

Create our primary chart
the rows/cols arguments tell plotly we want two figures

fig = make_subplots(rows=2, cols=1)

Create our Candlestick chart with an overlaid price line

fig.append_trace(
go.Candlestick(
x=df.index,
open=df[‘open’],
high=df[‘high’],
low=df[‘low’],
close=df[‘close’],
increasing_line_color=’#ff9900′,
decreasing_line_color=’black’,
showlegend=False
), row=1, col=1 # <———— upper chart
)


fig.append_trace(
go.Scatter(
x=df.index,
y=df[‘open’],
line=dict(color=’#ff9900′, width=1),
name=’open’,
), row=1, col=1 # <———— upper chart
)

fig.append_trace(
go.Scatter(
x=df.index,
y=df[‘stochk_14_3_3’],
line=dict(color=’#ff9900′, width=2),
name=’fast’,
), row=2, col=1 # <———— lower chart
)

fig.append_trace(
go.Scatter(
x=df.index,
y=df[‘stochd_14_3_3’],
line=dict(color=’#000000′, width=2),
name=’slow’
), row=2, col=1 # <———— lower chart
)

fig.update_yaxes(range=[-10, 110], row=2, col=1)

Add upper/lower bounds

fig.add_hline(y=0, col=1, row=2, line_color=”#666″, line_width=2)
fig.add_hline(y=100, col=1, row=2, line_color=”#666″, line_width=2)

Add overbought/oversold

fig.add_hline(y=20, col=1, row=2, line_color=’#336699′, line_width=2, line_dash=’dash’)
fig.add_hline(y=80, col=1, row=2, line_color=’#336699′, line_width=2, line_dash=’dash’)

Make it pretty

layout = go.Layout(
plot_bgcolor=’#efefef’,
# Font Families
font_family=’Monospace’,
font_color=’#000000′,
font_size=20,
xaxis=dict(
rangeslider=dict(
visible=False
)
)
)
fig.update_layout(layout)

View our chart in the system default HTML viewer (Chrome, Firefox, etc.)

fig.show()

Stochastic oscillator plot $NVIDIA

This plot shows the following:

  1. A Candlestick chart showing the pricing data over our trading period
  2. A SO chart showing our %k, %d, and 80/20 lines over that same period.

Remark: There is a data omission on the l.h.s. of the lower chart. This reflects the 14-day period over which our %k value is generated. During that period, we don’t have prior data to generate the %k for so we end up with a NaN value.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: