While the name stochastic oscillator may sound intimidating, this powerful trading indicator provides a simple way to identify momentum, trend reversals, or overbought and oversold positions. It’s a versatile formula that can be used in conjunction with other indicators to provide powerful new combinations.

Here, we’ll just look at the basic stochastic oscillator and walk through some examples in Python.

## Stochastic Oscillator 101

The fundamental premise of the stochastic oscillator is that a stock’s closing price tends to be closer to the recent highs if it’s trending upward, and closer to the recent lows if it’s trending downward. This is an oscillator, so it is range bound, in this case between 0–100, with the maximum value occurring when the close occurs at the high, and the minimum occurring if it closes at the low.

In it’s most basic form, it consists of two values: %K (“percent-K”) and %D (“percent-D”). The first gives us our indicator, and the second is a simple-moving average of the %K indicator, which operates much like a signal line with MACD strategies.

To calculate the %K value, we take the highest high from the previous periods (typically, 14 days), the lowest low from the same timeframe, and the most recent close. We subtract the close from the our lowest low, and divide it by the difference between the highest high and lowest low. Often, this is scaled up by 100 so that values are always between 0–100.

So in psuedocode we have:

K = 100 * (Price - min(Lows)) / (max(Highs) - min(Lows))

Or, if you prefer the math:

$$K_t = 100 \bigg(\frac{P_t - \textrm{min} \bf{L}^N}{\textrm{max} \bf{H}^N - \textrm{min} \bf{L}^N}\bigg)$$

where 𝐾_𝑡 is our %K at time 𝑡, 𝑃_𝑡 is the closing price, 𝐋^𝐍 and 𝐇^𝐍 are vectors of the lows and maximums with a length 𝑁.

So, if we have N=3, then we have 3 days in our price history. We may have lows of $11.52,$11.36, $11.58, which would be our L^N values. For highs, we might have something like$11.96, $11.83,$12.27. In this example we'd have:

$$\textrm{max} \bf{H}^N = max(11.96, 11.83, 12.27) = \12.27$$ $$\textrm{min} \bf{L}^N = min(11.52, 11.36, 11.58) = \11.36$$

If our closing price on the day we're calculating this is $12.01, then we have: $$K_t = 100 \bigg(\frac{12.01-11.36}{12.27-11.36} \bigg) = 71.4$$ %D, the other part of the stochastic oscillator, is just the simple moving average (SMA) of our K_t values for the last M days. D = SMA(K, M) $$D_t = \frac{1}{M} \sum_{t=1}^M K_t$$ If we have M=5 and our last 5 K_t values are 68.3, 67.6, 71.2, 72.1, 71.4, then %D would be 70.1. $$D_t = \frac{1}{5}\big(68.3 + 67.6 + 71.2 + 72.1 + 71.4 \big) = 70.1$$ That's it! Despite the intimidating name, you can see that the stochastic oscillator is really quite easy to calculate. If we're trading it, we don't want to calculate it by hand like this every single day, instead, we want our computer to do it automatically so we can feed that into our algorithms. Here's how we'd run this calculation in Python. ## Calculating the Stochastic Oscillator in Python To do this, we’ll import our standard packages into Python. import pandas as pd import numpy as np import matplotlib.pyplot as plt import yfinance as yf The next step is to build a function to calculate the stochastic oscillator and test it on some data. def calc_k_d(data, N=14, M=3): data['low_N'] = data['Low'].rolling(N).min() data['high_N'] = data['High'].rolling(N).max() data['K'] = 100 * (data['Close'] - data['low_N']) / \ (data['high_N'] - data['low_N']) data['D'] = data['K'].rolling(M).mean() return data That’s really all the code it takes to get this powerful indicator in place! With that, let’s check it out on some historical data and see how it looks. ticker = 'GD' start = '2018-01-01' end = '2018-06-30' yfObj = yf.Ticker(ticker) data = yfObj.history(start=start, end=end) N = 14 M = 3 data = calc_k_d(data, N, M) # Plot results fig, ax = plt.subplots(2, figsize=(12, 8), sharex=True) ax[0].plot(data['Close'], label=f'{ticker}') ax[0].set_title(f'Price for {ticker}') ax[0].set_ylabel('Price') ax[0].legend() ax[1].plot(data['K'], label='%K') ax[1].plot(data['D'], label='%D') ax[1].set_title(f'Stochastic Oscillator for {ticker}') ax[1].set_xlabel('Date') ax[1].set_ylabel('Indicator Value') ax[1].legend() plt.show() There are a some key take-aways from the chart above that will help illustrate how we can build this signal into our trading system. ## Overbought and Oversold Positions First, like the RSI, this indicator can be used to determine overbought/oversold points when we expect the stock to reverse price. When using the stochastic oscillator this way, we’re typically looking for %K above 80 to short, and below 20 to buy and take advantage of the reversal in price. While no indicator is perfect, this one nailed a series of overbought positions starting at the beginning of the time series in mid-January, and again in mid-March, as well as late April and late May. ## Stochastic Oscillator and Momentum Our %K can also be used as a momentum indicator. The value is scaled between 0–100 with 50 being the centerline. This is interpreted to mean that %K values greater than 50 have upward momentum, while values below 50 have downward momentum. You may see this combined with other breakout signals to confirm trends in one direction or another and generate the buy/sell signals. ## Fast and Slow %D You may notice that the %K and %D cross over one another frequently. This can be used as a trading signal, however, because of the repeated movements between the two, traders often prefer to use a smoothed average of %D. This can lead to some talking about %D-fast and %D-slow. In this case, %D-fast is just the %D we have used so far (to confuse matters, some will call this %K-slow instead). %D-slow is the simple moving average of %D-fast, so it will change more slowly. These are then used to provide trading signals when one crosses over the other. data['D_slow'] = data['D'].rolling(M).mean() fig, ax = plt.subplots(2, figsize=(12, 8), sharex=True) ax[0].plot(data['Close'], label=f'{ticker}') ax[0].set_title(f'Price for {ticker}') ax[0].set_ylabel('Price') ax[0].legend() ax[1].plot(data['K'], label='%K') ax[1].plot(data['D'], label='%$D_{fast}$') ax[1].plot(data['D_slow'], label='%$D_{slow}\$')
ax[1].set_title(f'Stochastic Oscillator for {ticker}')
ax[1].set_xlabel('Date')
ax[1].set_ylabel('Indicator Value')
ax[1].legend()

plt.show()