1 # Train ML/DL model to predict stock prices for the next day 2 ''' 3 Designed an Stacked GRU and LSTM based model to predict Future price of the stocks 4 In this implementation I used following TI 5 Technical Analysis: 6 Use of Technical Indicators: 7 1. SMA (OHLC) 8 2. RSI (Relative Strength Index) 9 3. Stochastic Oscillator 10 ''' 11 import os 12 import math 13 import numpy as np 14 import argparse 15 import pandas as pd 16 from sklearn.metrics import mean_squared_error 17 import tensorflow as tf 18 from tensorflow.keras.models import load_model 19 import matplotlib.pyplot as plt 20 import seaborn as sns 21 from sklearn.preprocessing import MinMaxScaler 22 from config import DATA_PATH 23 24 sns.set() 25 parser = argparse.ArgumentParser() 26 parser.add_argument('--stock_name',type=str, default="AAPL") 27 parser.add_argument('--load_all',action='store_true', help='For complete dataset (without TA)') 28 parser.add_argument('--use_ta',action='store_true', help='with technical indicators') 29 parser.add_argument('--indicator',type=str, default='RSI', help='technical indicators, (RSI, Stochastic)') 30 parser.add_argument('--epoch',type=int, default=5, help='number of iterations') 31 parser.add_argument('--not_train',action='store_true', help='load save model') 32 arg = parser.parse_args() 33 34 test_set_size_percentage = 10 35 36 def create_dataset(dataset, step_size): 37 data_X, data_Y = [], [] 38 for i in range(len(dataset)-step_size-1): 39 a = dataset[i:(i+step_size), 0] 40 data_X.append(a) 41 data_Y.append(dataset[i + step_size, 0]) 42 return np.array(data_X), np.array(data_Y) 43 44 ''' 45 DATA_PATH: Path to the Dataset (change in config.py accordingly) 46 stock_name: Stock Name present in Stock List dataframe (AAPL, DIS, MKT, YHOO, etc) 47 ''' 48 df = pd.read_csv(DATA_PATH + '/prices-split-adjusted.csv') 49 if arg.stock_name not in list(df['symbol']): 50 raise ValueError(arg.stock_name +" "+ "is not listed in Stock List") 51 52 print('Number of Unique Stocks: ', len(list(set(df.symbol)))) # Total Stocks Name in List (501) 53 print(list(set(df.symbol))[:10]) # 10 Stock Names 54 55 stock_price = df[df['symbol']== arg.stock_name] 56 sns.relplot(x='date', y='close',kind='line', legend='full' ,data=stock_price[:100]) # plot for 100 samples 57 sns.relplot(x='date', y='volume',kind='line', legend='full' ,data=stock_price[:100]) 58 df_stocks = stock_price.reindex(index = stock_price.index[::-1]) 59 obs = np.arange(1, len(df_stocks) + 1, 1) 60 plt.show() 61 62 # drop columns from dataframe 63 df_stocks.drop(['symbol'], 1, inplace=True) 64 df_stocks.drop(['date'], 1, inplace=True) 65 66 # Technical Indicators for Analysis 67 OHLC_avg = df_stocks[['open', 'high', 'low', 'close']].mean(axis=1) # SMA 68 HLC_avg = df_stocks[['high', 'low', 'close']].mean(axis=1) 69 close_val = df_stocks[['close']] 70 71 if arg.use_ta: 72 if arg.indicator == 'RSI': 73 diff = close_val.diff(1) 74 up = diff.where(diff > 0, 0.0) 75 dn = -diff.where(diff < 0, 0.0) 76 min_periods = 14 77 emaup = up.ewm(alpha=1/14, min_periods=min_periods, adjust=False).mean() 78 emadn = dn.ewm(alpha=1/14, min_periods=min_periods, adjust=False).mean() 79 rs = emaup / emadn # (https://www.investopedia.com/terms/r/rsi.asp) 80 codn = np.where(emadn == 0, 100, 100-(100/(1+rs))) 81 codn = codn.flatten() 82 rsi = pd.Series(codn, index=close_val.index) 83 rsi = rsi.fillna(40, axis=0) 84 rsi = pd.Series(rsi, name='RSI') 85 86 if arg.indicator == 'Stoch': 87 min_periods = 14 88 smin = df_stocks['low'].rolling(14, min_periods=min_periods).min() 89 smax = df_stocks['high'].rolling(14, min_periods=min_periods).max() 90 stoch_k = 100 * (df_stocks['close'] - smin) / (smax - smin) # (https://www.investopedia.com/terms/s/stochasticoscillator.asp) 91 stoch_k = stoch_k.fillna(40, axis=0) 92 stoch = pd.Series(stoch_k, name='Stochastic') 93 stoch_obs = np.arange(1, len(stoch) + 1, 1) 94 95 if arg.indicator is None: 96 raise ValueError("No technical indicator is given, please use correct indicators to proceed") 97 98 # Train and Test Split (With and Without Using Technical Indicators) 99 if arg.load_all: 100 ohlc_avg = np.reshape(OHLC_avg.values, (len(OHLC_avg),1)) 101 scaler = MinMaxScaler(feature_range=(0,1)) 102 ohlc_avg_norm = scaler.fit_transform(ohlc_avg) 103 104 train_ohlc = int(len(ohlc_avg_norm) * 0.9) 105 test_ohlc = len(ohlc_avg_norm) - train_ohlc 106 train_ohlc, test_ohlc = ohlc_avg_norm[0:train_ohlc,:], ohlc_avg_norm[train_ohlc:len(OHLC_avg),:] 107 x_train, y_train = create_dataset(train_ohlc, 1) 108 x_test, y_test = create_dataset(test_ohlc, 1) 109 110 elif arg.indicator == 'RSI': 111 RSI_avg = np.reshape(rsi.values, (len(rsi),1)) 112 scaler = MinMaxScaler(feature_range=(0, 1)) 113 rsi_avg_norm = scaler.fit_transform(RSI_avg) 114 115 train_rsi = int(len(rsi_avg_norm) * 0.9) 116 test_rsi = len(rsi_avg_norm) - train_rsi 117 train_rsi, test_rsi = rsi_avg_norm[0:train_rsi,:], rsi_avg_norm[train_rsi:len(rsi),:] 118 x_train, y_train = create_dataset(train_rsi, 1) 119 x_test, y_test = create_dataset(test_rsi, 1) 120 else: 121 stoch_avg = np.reshape(stoch.values, (len(stoch),1)) 122 scaler = MinMaxScaler(feature_range=(0,1)) 123 stoch_avg_norm = scaler.fit_transform(stoch_avg) 124 125 train_stoch = int(len(stoch_avg_norm) * 0.9) 126 test_stoch = len(stoch_avg_norm) - train_stoch 127 train_stoch, test_stoch = stoch_avg_norm[0:train_stoch,:], stoch_avg_norm[train_stoch:len(stoch),:] 128 x_train, y_train = create_dataset(train_stoch, 1) 129 x_test, y_test = create_dataset(test_stoch, 1) 130 131 # RESHAPING TRAIN AND TEST DATA 132 x_train = np.reshape(x_train, (x_train.shape[0], 1, x_train.shape[1])) 133 x_test = np.reshape(x_test, (x_test.shape[0], 1, x_test.shape[1])) 134 step_size = 1 135 136 # Model 137 input_shape = (x_train.shape[1], x_train.shape[2]) 138 input_layer = tf.keras.Input(shape=input_shape) 139 x = tf.keras.layers.GRU(256, input_shape=(1, step_size), return_sequences=True)(input_layer) 140 x = tf.keras.layers.Dropout(0.4)(x) 141 x = tf.keras.layers.LSTM(256)(x) 142 x = tf.keras.layers.Dropout(0.4)(x) 143 x = tf.keras.layers.Dense(64, activation='relu')(x) 144 x = tf.keras.layers.Dense(1)(x) 145 146 model = tf.keras.Model(inputs=input_layer, outputs=x) 147 model.summary() 148 149 # Load saved model or training a new model 150 if os.path.exists('{}_stock_prices__{}_{}.h5'.format(arg.stock_name, arg.indicator, arg.epoch)): 151 history = load_model('{}_stock_prices__{}_{}.h5'.format(arg.stock_name, arg.indicator, arg.epoch)) 152 else: 153 model.compile(loss='mse', optimizer='adam', metrics=["mse"]) 154 history = model.fit(x_train, y_train, epochs=arg.epoch, batch_size=2, verbose=1, validation_data = (x_test,y_test)) 155 if arg.indicator: 156 model.save('{}_stock_prices__{}_{}.h5'.format(arg.stock_name, arg.indicator, arg.epoch)) # save model 157 else: 158 model.save('{}_stock_prices__{}.h5'.format(arg.stock_name, arg.epoch)) 159 160 plt.plot(history.history['mean_squared_error']) 161 plt.plot(history.history['val_mean_squared_error']) 162 plt.title('model mean squared error') 163 plt.ylabel('accuracy') 164 plt.xlabel('epoch') 165 plt.legend(['train', 'test'], loc='upper left') 166 plt.show() 167 168 # summarize history for loss 169 plt.plot(history.history['loss']) 170 plt.plot(history.history['val_loss']) 171 plt.title('model loss') 172 plt.ylabel('loss') 173 plt.xlabel('epoch') 174 plt.legend(['train', 'test'], loc='upper left') 175 plt.show() 176 177 trainPredict = model.predict(x_train) 178 testPredict = model.predict(x_test) 179 180 if arg.not_train: 181 def model_score(model, x_train, y_train, x_test, y_test): 182 trainScore = model.evaluate(x_train, y_train, verbose=0) 183 print('Train Score: %.5f MSE (%.2f RMSE)' % (trainScore[0], math.sqrt(trainScore[0]))) 184 testScore = model.evaluate(x_test, y_test, verbose=0) 185 print('Test Score: %.5f MSE (%.2f RMSE)' % (testScore[0], math.sqrt(testScore[0]))) 186 return trainScore[0], testScore[0] 187 model_score(model, x_train, y_train, x_test, y_test) 188 189 # DE-NORMALIZING FOR PLOTTING 190 trainPredict = scaler.inverse_transform(trainPredict) 191 y_train = scaler.inverse_transform([y_train]) 192 testPredict = scaler.inverse_transform(testPredict) 193 y_test = scaler.inverse_transform([y_test]) 194 195 # PREDICT FUTURE VALUES 196 last_val = testPredict[-1] 197 last_val_scaled = last_val/last_val 198 next_val = model.predict(np.reshape(last_val_scaled, (1,1,1))) 199 print("Last Day Value:", np.asscalar(last_val)) 200 print("Next Day Value:", np.asscalar(last_val*next_val))