While I was reading about stock prediction on the web, I saw people talking about using 1D CNN to predict the stock price. This caught my attention since CNN is specifically designed to process pixel data and used in image recognition and processing and it looked like a interesting challenge.

This solution is frontend only application using Tensorflow.js library and the best part is that it doesn’t require any server side. The data is available via “IEX Developer Platform” API service. The Image 1 above is from this stock prediction application.

The code is available on my Github repository.

All the work that need to be done can be set up in 6 steps:

Get the Data Generate Features Generate ML Model Train the ML Model Test the ML Model Predict with ML Model

So let’s start…

Get the Data

This data is gathered using the API from IEX. The API description is available on the following link. In this application I’m are using the chart endpoint which has predefined historical period of 1y (1 year). The placeholder %company% is where we replace the company symbol that we input in the application.

let url = 'https://api.iextrading.com/1.0/stock/%company%/chart/1y'

The result from this API is json array with historical data for the requested company. Below is some example from the response data.

[

...

{

"date":"2018-02-20",

"open":169.4694,

"high":171.6463,

"low":168.8489,

"close":169.2724,

"volume":33930540,

"unadjustedVolume":33930540,

"change":-0.5713,

"changePercent":-0.336,

"vwap":170.3546,

"label":"Feb 20, 18",

"changeOverTime":0

},

...

]

Generate Features

After the data is retrieved we need to process it and prepare the feature set and the label set. While I was researching, mostly I found ideas where they were using date fields as feature. However I didn’t like this because of two reasons. First, the date is constantly increasing as feature. Second, the dates are independent (not directly connected with stock price).

What I think is more connected with the current stock price is how the stock price changed in the past. So for example the stock price today is dependent on the stock price changes from the last 7 days. For that reason we define 7 features for our test set and each one of them is labeled with the stock price for the next day.

All this preprocessing of the data is made in processData function defined in helpers.js file. In our case timePortion variable has value 7.

...

// Create the train sets

for (let i = timePortion; i < size; i++) {

for (let j = (i - timePortion); j < i; j++) {

trainX.push(scaledFeatures[j]);

}

trainY.push(scaledFeatures[i]);

}

...

And what we get for trainX is flat array of values, but later we will reshape it to matrix with format:

[number_of_samples - 7, number_of_features]

Another important thing is that we first normalize our features using minMaxScaler function defined in helpers.js file. This will scale all the values between 0 and 1. This is important so the prediction model better fit our model and to be faster when there is a lot of data. If you want to know more about this min-max normalization you can find references at the end of this blog.

Generate ML Model

Next step is creating the CNN model. This is done by buildCnn function in prediction.js file. This step is really simplified using the Tensorflow library. What we need to do is define sequential (linear stack of layers) tensorflow model and then add the predefined layers in order to build our CNN model.

But what is CNN ?

CNN or Convolutional Neural Network is a class of deep neural networks, most commonly applied to analyzing visual imagery. That’s why using it for predicting stock price is unusual and interesting challenge.

The CNN has 4 important type of layers that makes it different. These are Convolution layer, ReLU layer, Pooling layer and Fully Connected Layer. Each of them has specific task to do. However, I won’t dive deep in explaining CNN here for now.

Let’s continue building the CNN with Tensorflow. We defined total of 7 layers:

inputLayer — has input size [7, 1] because we have 7 features conv1d— First convolutional layer averagePooling1d — First average pooling layer conv1d — Second convolutional layer averagePooling1d — Second pooling layer flatten — Reduce the dimension, reshape input to [number of samples, number of features] dense — Fully connected layer using linear activation function with 1 unit which returns 1 output value

Below is the code where we define all these layers in sequential tensorflow model.

After we build our model we proceed to the next step, which is training our model.

Train the ML Model

Now that we have created our model, we need to get ready and transform our data. That means transforming our train and label sets into tensor data, since tensorflow works with it’s own type of data known as tensor(s).

This is pretty simple step. We create the tensors and reshape our features data into [number_of_samples, timePortion, 1]. timePortion is 7 in this case.

...

let tensorData = {

tensorTrainX: tf.tensor1d(built.data.trainX).reshape([built.data.size, built.data.timePortion, 1]),

tensorTrainY: tf.tensor1d(built.data.trainY)

};

...

Now that we got our tensors we use them along with the model in cnn function. There, we first set up the optimization algorithm and the loss function. We use the “adam” algorithm as optimizer and “minSquaredError” as loss function.

model.compile({ optimizer: ‘adam’, loss: ‘meanSquaredError’ });

Unlike SGD (scholastic gradient descent), Adam optimizer use different learning rate for each weight.

The last thing we need to do here is to call the fit function on the tensorflow model and send the trainX (features) and trainY (labels) sets. We also set the options for epochs to 100.

Training your network on each item of the set once is one epoch.

...

// Train the model

model.fit(data.tensorTrainX, data.tensorTrainY, {

epochs: epochs

}).then(fucntion (result) {

...

When the training is over it returns the result and the model is prepared to be used for generating predictions.

Test the ML Model

In this step we already have our model prepared for making future predictions. What we will do first is use this model to predict on the same set that we trained our model. The idea behind this is that we can compare (visualize) how well our model fits the training set.

We create the prediction simply by calling predict function from the model.

var predictedX = model.predict(tensorData.tensorTrainX);

We get the predicted data by calling:

predictedX.data().then(function (pred) {

...

And since we have previously normalized (scaled) our features we need to run inverse min-max operation in order to get the real feature values. We do that by calling minMaxInverseScaler function from helpers.js where we send the predicted data along with min and max values.

var predictedXInverse = minMaxInverseScaler(pred, min, max);

Now we use the plotData function defined in plot.js so we can visualize both (actual and predicted) data sets. P.S. I used Chart.js library for visualization.

Image 2: Actual vs Predicted values for AAPL(Apple)

Predict with ML Model

What’s left to do is generating the test features for making the next day stock price prediction. This features are generated using generateNextDayPrediction function from helpers.js file.

let nextDayPrediction = generateNextDayPrediction(result.originalData, result.timePortion);

What this function does is pretty simple. It takes the last 7 stock price values from the given data and create the test feature set (for next day prediction).

Then we repeat the same steps where we transform this data into tensor data and reshape it into [number_of_samples, number_of_features, 1] which in this case it will be [1, 7, 1] since we have only one test example.

Next, we call the predict function from our model.

let predictedValue = model.predict(tensorNextDayPrediction);

We get the predicted value and run inverse min-max normalization

predictedValue.data().then(function (predValue) {

// Revert the scaled features, so we get the real values

let inversePredictedValue = minMaxInverseScaler(predValue, min, max);

...

predictedXInverse.data[predictedXInverse.data.length] = inversePredictedValue.data[0];

At the end we just add that value at the end of our predicted data (from the train set in the previous “Test the ML Model” section) so it can be visualized on the graph.

Conclusion

Hopefully this was clear and easy to understand. If you think that some part needs better explanation please feel free to add a comment or suggestion. For any questions feel free to contact me.

Hope you enjoyed it!

Useful Links