Please note that this article is for educational and informational purposes only. All screenshots are for illustrative purposes only. The views and opinions expressed are those of the author and do not reflect or represent the views and opinions of Alpaca. Alpaca does not recommend any specific securities or investment strategies. Investing and investment strategies involve risk, including loss of value and the loss of principal. Past performance does not guarantee future returns or results. Please consider your objectives before investing.
This article first appeared on Medium, written by Victor S.
There are so many articles on predicting stock prices, but this article provides two things to the reader, that no other article talks about:
- The use of confidence intervals in stock trading to determine stop-loss and take-profit
- The use of Alpaca in stock trading to track profits and test trading strategies
Both of which provide important tools to the next generation of machine learning trading algorithms.
Concept:
The program will consist of three main parts:
- The Data Setup
The data will be accessed via the yfinance library, in daily intervals, The data will include the opening,high,low and closing price of the asset. The data will be normalized and then reshaped to fit the neural network
- The Neural Network
The neural network will be a convolutional LSTM network that can extract the feature and also access temporal features of the dataset. This network fits the data because some of the complex patterns are not only convolutional, they are also time based.
- Creating Orders
The Neural Network will predict the daily opening and closing prices. If the opening prices is larger than the closing price, the network will short sell the stock. If the closing price is larger than the opening price, the network will buy the stock.
After training the network, I will compute the loss of the network and use this value as a a confidence interval, to determine the stop loss and take profit values. I will use requests to access the Alpaca API to make orders.
With the key concept in place, let’s move to the code.
The Code
Step 1 | Prerequisites:
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras import callbacks
from sklearn.model_selection import train_test_split
from keras.layers import Flatten
from keras.layers import TimeDistributed
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from IPython.display import clear_output
import datetime
import statistics
import time
import os
import json
import yfinance as yf
from keras.models import model_from_json
import requests
from keras.models import load_model
from matplotlib import pyplot as plt
There are quite a lot of prerequisties of the program.They are spread out as so, to prevent importing the whole library and taking up space. Please note that importing clear_output from IPython.display is only for Jupyter notebooks. If you are using scripts it is not necessary to import this.
Step 2 | Access Data:
def data_setup(symbol,data_len,seq_len):
end = datetime.datetime.today().strftime('%Y-%m-%d')
start = datetime.datetime.strptime(end, '%Y-%m-%d') - datetime.timedelta(days=(data_len/0.463))
orig_dataset = yf.download(symbol,start,end)
close = orig_dataset['Close'].values
open_ = orig_dataset['Open'].values
high = orig_dataset['High'].values
low = orig_dataset['Low'].values
dataset,minmax = normalize_data(orig_dataset)
cols = dataset.columns.tolist()
data_seq = list()
for i in range(len(cols)):
if cols[i] < 4:
data_seq.append(dataset[cols[i]].values)
data_seq[i] = data_seq[i].reshape((len(data_seq[i]), 1))
data = hstack(data_seq)
n_steps = seq_len
X, y = split_sequences(data, n_steps)
n_features = X.shape[2]
n_seq = len(X)
n_steps = seq_len
print(X.shape)
X = X.reshape((n_seq,1, n_steps, n_features))
true_y = []
for i in range(len(y)):
true_y.append([y[i][0],y[i][1]])
return X,array(true_y),n_features,minmax,n_steps,close,open_,high,low
This function takes data from yfinance and splits it into its respective sections. It also reshapes data into the form:
(n_seq,1, n_steps, n_features)
A four-dimensional array to fit the Convolutional LSTM network.
Step 3 | Prepare Data:
Accessing the data is only half of the challenge. The rest is putting the data into the correct format, and splitting the data into training and testing datasets.
def split_sequences(sequences, n_steps):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps
if end_ix > len(sequences)-1:
break
seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
X.append(seq_x)
y.append(seq_y)
return array(X), array(y)
This function splits the sequence into time series data, by splitting the sequence into chunks of size n_steps.
def normalize_data(dataset):
cols = dataset.columns.tolist()
col_name = [0]*len(cols)
for i in range(len(cols)):
col_name[i] = i
dataset.columns = col_name
dtypes = dataset.dtypes.tolist()
# orig_answers = dataset[attr_row_predict].values
minmax = list()
for column in dataset:
dataset = dataset.astype({column: 'float32'})
for i in range(len(cols)):
col_values = dataset[col_name[i]]
value_min = min(col_values)
value_max = max(col_values)
minmax.append([value_min, value_max])
for column in dataset:
values = dataset[column].values
for i in range(len(values)):
values[i] = (values[i] - minmax[column][0]) / (minmax[column][1] - minmax[column][0])
dataset[column] = values
dataset[column] = values
return dataset,minmax
This function changes all data into a value between 0 and 1. This is as many stocks have skyrocketed or nosedived. Without normalizing, the neural network would learn from datapoints with higher values. This could create a blind spot and therefore affect predictions. The normalizing is done as so:
value = (value - minimum) / maximum
Where minimum and maximum are the minimum and maximum values of the feature.
def enviroment_setup(X,y):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
return X_train, X_test, y_train, y_test
This function uses sklearn’s train_test_split function to shuffle the data and divide into training and testing datasets.
Step 4 | Create Neural Network:
def initialize_network(n_steps,n_features,optimizer):
model = Sequential()
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation='relu'), input_shape=(None, n_steps, n_features)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2)))
model.add(TimeDistributed(Flatten()))
model.add(LSTM(50, activation='relu'))
model.add(Dense(2))
model.compile(optimizer=optimizer, loss='mse')
return model
This is the basic architecture of the Convolutional LSTM network. The optimizer that I found that works best with this network is Adam.
Step 5 | Train Neural Network:
def train_model(X_train,y_train,model,epochs):
dirx = 'something directory'
os.chdir(dirx)
h5='Stocks'+'_best_model'+'.h5'
checkpoint = callbacks.ModelCheckpoint(h5, monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=True, mode='auto', period=1)
earlystop = callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=epochs * 1/4, verbose=0, mode='auto', baseline=None, restore_best_weights=True)
callback = [earlystop,checkpoint]
json = 'Stocks'+'_best_model'+'.json'
model_json = model.to_json()
with open(json, "w") as json_file:
json_file.write(model_json)
history = model.fit(X_train, y_train, epochs=epochs, batch_size=len(X_train)//4, verbose=2,validation_split = 0.3, callbacks = callback)
return history
For the training function, I used the criminally underused Model Checkpoint callback to save the best weights of the model. Change the dirx variable to where you want to store your model.
Step 6 | Evaluation and Prediction:
def load_keras_model(dataset,model,loss,optimizer):
dirx = 'something directory'
os.chdir(dirx)
json_file = open(dataset+'_best_model'+'.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
model = model_from_json(loaded_model_json)
model.compile(optimizer=optimizer, loss=loss, metrics = None)
model.load_weights(dataset+'_best_model'+'.h5')
return model
def evaluation(exe_time,X_test, y_test,X_train, y_train,history,model,optimizer,loss):
model = load_keras_model('Stocks',model,loss,optimizer)
test_loss = model.evaluate(X_test, y_test, verbose=0)
train_loss = model.evaluate(X_train, y_train, verbose=0)
eval_test_loss = round(100-(test_loss*100),1)
eval_train_loss = round(100-(train_loss*100),1)
eval_average_loss = round((eval_test_loss + eval_train_loss)/2,1)
print("--- Training Report ---")
plot_loss(history)
print('Execution time: ',round(exe_time,2),'s')
print('Testing Accuracy:',eval_test_loss,'%')
print('Training Accuracy:',eval_train_loss,'%')
print('Average Network Accuracy:',eval_average_loss,'%')
return model,eval_test_loss
After saving the best weights, load the model again to ensure that you are using the best weights. The program then evaluates the program, based on data that it has not seen before. It then prints a set of variables to give comprehensive insight on the training of the network.
def market_predict(model,minmax,seq_len,n_features,n_steps,data,test_loss):
pred_data = data[-1].reshape((len(data[-1]),1, n_steps, n_features))
pred = model.predict(pred_data)[0]
appro_loss = list()
for i in range(len(pred)):
pred[i] = pred[i] * (minmax[i][1] - minmax[i][0]) + minmax[i][0]
appro_loss.append(((100-test_loss)/100) * (minmax[i][1] - minmax[i][0]))
return pred,appro_loss
This is the function that makes the prediction of the program. We have to perform the inverse of the normalization function to get the value in terms of USD.
Step 7 | Create Order:
BASE_URL = 'https://paper-api.alpaca.markets'
API_KEY = 'XXXXXXXX'
SECRET_KEY = 'XXXXXXXX'
ORDERS_URL = '{}/v2/orders'.format(BASE_URL)
HEADERS = {'APCA-API-KEY-ID':API_KEY,'APCA-API-SECRET-KEY':SECRET_KEY}
These are the basic parameters and endpoints to make alpaca orders. You can get your own API key and secret key here.
def create_order(pred_price,company,test_loss,appro_loss):
open_price,close_price = pred_price[0],pred_price[1]
if open_price > close_price:
side = 'sell'
elif open_price < close_price:
side = 'buy'
if side == 'buy':
order = {
'symbol':company,
'qty':round(20*(test_loss/100)),
'type':'stop_limit',
'time_in_force':'day',
'side': 'buy',
'take_profit': close_price + appro_loss,
'stop_loss': close_price - appro_loss
}
elif side == 'sell':
order = {
'symbol':company,
'qty':round(20*(test_loss/100)),
'type':'stop_limit',
'time_in_force':'day',
'side': 'sell',
'take_profit':close_price - appro_loss,
'stop_loss':close_price + appro_loss
}
r = requests.post(ORDERS_URL, json = order,headers = HEADERS)
print(r.content)
This function applies the take profit and stop loss idea:
Take profit and prevent loss as the close price fluctuates along the predicted closing price.
The length between the borders of the red area and the center is the loss value. The borders act as the stop loss and take profit value, as these are the value that the program predicts the price will fluctuate within.
Conclusion
Machine Learning and Stock Trading come hand in hand, as both are the prediction of complex patterns.
I hope that more people will use the Alpaca API and confidence intervals when it comes to algorithmic trading.
My links
If you want to see more of my content, click this link.
Alpaca does not prepare, edit, or endorse Third Party Content. Alpaca does not guarantee the accuracy, timeliness, completeness or usefulness of Third Party Content, and is not responsible or liable for any content, advertising, products, or other materials on or available from third party sites.
Brokerage services are provided by Alpaca Securities LLC ("Alpaca"), member FINRA/SIPC, a wholly-owned subsidiary of AlpacaDB, Inc. Technology and services are offered by AlpacaDB, Inc. This is not an offer, solicitation of an offer, or advice to buy or sell securities, or open a brokerage account in any jurisdiction where Alpaca is not registered (Alpaca is registered only in the United States).