CodeX
Published in

CodeX

Algorithmic Trading with Stochastic Oscillator in Python

Learn to implement and backtest one of the most popular trading indicators with python

Photo by Chris Liverani on Unsplash

Stochastic Oscillator

%K = 100 * ((14 DAY CLOSING PRICE - 14 DAY LOWEST PRICE) - (14 DAY HIGHEST PRICE - 14 DAY LOWEST PRICE))
IF %K LINE < 20 AND %D LINE < 20 AND %K LINE < %D LINE => BUY
IF %K LINE > 80 AND %D LINE > 80 AND %K LINE > %D LINE => SELL

Implementation in Python

1. Importing Packages
2. Extracting Data from Alpha Vantage
3. Extracting the Stochastic Oscillator values
4. Stochastic Oscillator Plot
5. Creating the Trading Strategy
6. Plotting the Trading Lists
7. Creating our Position
8. Backtesting
9. SPY ETF Comparison

Step-1: Importing Packages

import pandas as pd
import numpy as np
import requests
from termcolor import colored as cl
from math import floor
import matplotlib.pyplot as plt
plt.rcParams[‘figure.figsize’] = (20, 10)
plt.style.use(‘fivethirtyeight’)

Step-2: Extracting Data from Alpha Vantage

def get_historical_data(symbol, start_date = None):
api_key = open(r'api_key.txt')
api_url = f'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&apikey={api_key}&outputsize=full'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'Close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
for i in df.columns:
df[i] = df[i].astype(float)
df.index = pd.to_datetime(df.index)
df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
if start_date:
df = df[df.index >= start_date]
return df

nflx = get_historical_data('NFLX', '2020-01-01')
nflx
Image by Author

Step-3: Extracting the Stochastic Oscillator values

def get_stoch(symbol, k_period, d_period, start_date):
api_key = open(r'api_key.txt')
url = f'https://www.alphavantage.co/query?function=STOCH&symbol={symbol}&interval=daily&fastkperiod={k_period}&slowdperiod={d_period}&apikey={api_key}'
raw = requests.get(url).json()
df = pd.DataFrame(raw['Technical Analysis: STOCH']).T.iloc[::-1]
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
df = df.astype(float)
return df['SlowK'], df['SlowD']

nflx['%k'], nflx['%d'] = get_stoch('NFLX', 14, 3, '2020-01-01')
nflx = nflx.dropna()
nflx.head()
Image by Author

Step-4: Stochastic Oscillator Plot

def plot_stoch(symbol, price, k, d):
ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(price)
ax1.set_title(f'{symbol} STOCK PRICE')
ax2.plot(k, color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(d, color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title(f'{symbol} STOCH')
ax2.legend()
plt.show()

plot_stoch('NFLX', nflx['Close'], nflx['%k'], nflx['%d'])
Image by Author

Step-5: Creating the trading strategy

def implement_stoch_strategy(prices, k, d):    
buy_price = []
sell_price = []
stoch_signal = []
signal = 0

for i in range(len(prices)):
if k[i] < 20 and d[i] < 20 and k[i] < d[i]:
if signal != 1:
buy_price.append(prices[i])
sell_price.append(np.nan)
signal = 1
stoch_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)
elif k[i] > 80 and d[i] > 80 and k[i] > d[i]:
if signal != -1:
buy_price.append(np.nan)
sell_price.append(prices[i])
signal = -1
stoch_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)

return buy_price, sell_price, stoch_signal

buy_price, sell_price, stoch_signal = implement_stoch_strategy(nflx['Close'], nflx['%k'], nflx['%d'])

Step-6: Plotting the trading signals

ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(nflx['Close'], color = 'skyblue', label = 'NFLX')
ax1.plot(nflx.index, buy_price, marker = '^', color = 'green', markersize = 10, label = 'BUY SIGNAL', linewidth = 0)
ax1.plot(nflx.index, sell_price, marker = 'v', color = 'r', markersize = 10, label = 'SELL SIGNAL', linewidth = 0)
ax1.legend(loc = 'upper left')
ax1.set_title('NFLX STOCK PRICE')
ax2.plot(nflx['%k'], color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(nflx['%d'], color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title('NFLX STOCH')
ax2.legend()
plt.show()
Image by Author

Step-7: Creating our Position

position = []
for i in range(len(stoch_signal)):
if stoch_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(nflx['Close'])):
if stoch_signal[i] == 1:
position[i] = 1
elif stoch_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

k = nflx['%k']
d = nflx['%d']
close_price = nflx['Close']
stoch_signal = pd.DataFrame(stoch_signal).rename(columns = {0:'stoch_signal'}).set_index(nflx.index)
position = pd.DataFrame(position).rename(columns = {0:'stoch_position'}).set_index(nflx.index)

frames = [close_price, k, d, stoch_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy.tail()
Image by Author

Step-8: Backtesting

nflx_ret = pd.DataFrame(np.diff(nflx['Close'])).rename(columns = {0:'returns'})
stoch_strategy_ret = []

for i in range(len(nflx_ret)):
try:
returns = nflx_ret['returns'][i]*strategy['stoch_position'][i]
stoch_strategy_ret.append(returns)
except:
pass

stoch_strategy_ret_df = pd.DataFrame(stoch_strategy_ret).rename(columns = {0:'stoch_returns'})

investment_value = 100000
number_of_stocks = floor(investment_value/nflx['Close'][-1])
stoch_investment_ret = []

for i in range(len(stoch_strategy_ret_df['stoch_returns'])):
returns = number_of_stocks*stoch_strategy_ret_df['stoch_returns'][i]
stoch_investment_ret.append(returns)

stoch_investment_ret_df = pd.DataFrame(stoch_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(stoch_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the STOCH strategy by investing $100k in NFLX : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the STOCH strategy : {}%'.format(profit_percentage), attrs = ['bold']))
Profit gained from the STOCH strategy by investing $100k in NFLX : 45001.44
Profit percentage of the STOCH strategy : 45%

Step-9: SPY ETF Comparison

def get_benchmark(start_date, investment_value):
spy = get_historical_data('SPY', start_date)['Close']
benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})

investment_value = investment_value
number_of_stocks = floor(investment_value/spy[-1])
benchmark_investment_ret = []

for i in range(len(benchmark['benchmark_returns'])):
returns = number_of_stocks*benchmark['benchmark_returns'][i]
benchmark_investment_ret.append(returns)

benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)
investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('STOCH Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))
Benchmark profit by investing $100k : 21780.0
Benchmark Profit percentage : 21%
STOCH Strategy profit is 24% higher than the Benchmark Profit

Final Thoughts!

Full code:

import pandas as pd
import numpy as np
import requests
from termcolor import colored as cl
from math import floor
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (20, 10)
plt.style.use('fivethirtyeight')

def get_historical_data(symbol, start_date = None):
api_key = open(r'api_key.txt')
api_url = f'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&apikey={api_key}&outputsize=full'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'Close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
for i in df.columns:
df[i] = df[i].astype(float)
df.index = pd.to_datetime(df.index)
df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
if start_date:
df = df[df.index >= start_date]
return df

nflx = get_historical_data('NFLX', '2020-01-01')
nflx.head()

def get_stoch(symbol, k_period, d_period, start_date):
api_key = open(r'api_key.txt')
url = f'https://www.alphavantage.co/query?function=STOCH&symbol={symbol}&interval=daily&fastkperiod={k_period}&slowdperiod={d_period}&apikey={api_key}'
raw = requests.get(url).json()
df = pd.DataFrame(raw['Technical Analysis: STOCH']).T.iloc[::-1]
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
df = df.astype(float)
return df['SlowK'], df['SlowD']

nflx['%k'], nflx['%d'] = get_stoch('NFLX', 14, 3, '2020-01-01')
nflx = nflx.dropna()
nflx.head()

def plot_stoch(symbol, price, k, d):
ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(price)
ax1.set_title(f'{symbol} STOCK PRICE')
ax2.plot(k, color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(d, color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title(f'{symbol} STOCH')
ax2.legend()
plt.show()

plot_stoch('NFLX', nflx['Close'], nflx['%k'], nflx['%d'])

def implement_stoch_strategy(prices, k, d):
buy_price = []
sell_price = []
stoch_signal = []
signal = 0

for i in range(len(prices)):
if k[i] < 20 and d[i] < 20 and k[i] < d[i]:
if signal != 1:
buy_price.append(prices[i])
sell_price.append(np.nan)
signal = 1
stoch_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)
elif k[i] > 80 and d[i] > 80 and k[i] > d[i]:
if signal != -1:
buy_price.append(np.nan)
sell_price.append(prices[i])
signal = -1
stoch_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)

return buy_price, sell_price, stoch_signal

buy_price, sell_price, stoch_signal = implement_stoch_strategy(nflx['Close'], nflx['%k'], nflx['%d'])

ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(nflx['Close'], color = 'skyblue', label = 'NFLX')
ax1.plot(nflx.index, buy_price, marker = '^', color = 'green', markersize = 10, label = 'BUY SIGNAL', linewidth = 0)
ax1.plot(nflx.index, sell_price, marker = 'v', color = 'r', markersize = 10, label = 'SELL SIGNAL', linewidth = 0)
ax1.legend(loc = 'upper left')
ax1.set_title('NFLX STOCK PRICE')
ax2.plot(nflx['%k'], color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(nflx['%d'], color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title('NFLX STOCH')
ax2.legend()
plt.show()

position = []
for i in range(len(stoch_signal)):
if stoch_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(nflx['Close'])):
if stoch_signal[i] == 1:
position[i] = 1
elif stoch_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

k = nflx['%k']
d = nflx['%d']
close_price = nflx['Close']
stoch_signal = pd.DataFrame(stoch_signal).rename(columns = {0:'stoch_signal'}).set_index(nflx.index)
position = pd.DataFrame(position).rename(columns = {0:'stoch_position'}).set_index(nflx.index)

frames = [close_price, k, d, stoch_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy.tail()

nflx_ret = pd.DataFrame(np.diff(nflx['Close'])).rename(columns = {0:'returns'})
stoch_strategy_ret = []

for i in range(len(nflx_ret)):
try:
returns = nflx_ret['returns'][i]*strategy['stoch_position'][i]
stoch_strategy_ret.append(returns)
except:
pass

stoch_strategy_ret_df = pd.DataFrame(stoch_strategy_ret).rename(columns = {0:'stoch_returns'})

investment_value = 100000
number_of_stocks = floor(investment_value/nflx['Close'][-1])
stoch_investment_ret = []

for i in range(len(stoch_strategy_ret_df['stoch_returns'])):
returns = number_of_stocks*stoch_strategy_ret_df['stoch_returns'][i]
stoch_investment_ret.append(returns)

stoch_investment_ret_df = pd.DataFrame(stoch_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(stoch_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the STOCH strategy by investing $100k in NFLX : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the STOCH strategy : {}%'.format(profit_percentage), attrs = ['bold']))

def get_benchmark(start_date, investment_value):
spy = get_historical_data('SPY', start_date)['Close']
benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})

investment_value = investment_value
number_of_stocks = floor(investment_value/spy[-1])
benchmark_investment_ret = []

for i in range(len(benchmark['benchmark_returns'])):
returns = number_of_stocks*benchmark['benchmark_returns'][i]
benchmark_investment_ret.append(returns)

benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)
investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('STOCH Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Nikhil Adithyan

Founder @CodeX (medium.com/codex), a medium publication connected with code and technology | Top Writer | Connect with me on LinkedIn: https://bit.ly/3yNuwCJ