top of page

Cats and Dogs in the Stock Market (IV)

Now that we have a convolutional neural network that can discriminate if a day was bullish or bearish given trading information up to 15:30 hours in a normal trading day, we can create an algorithm to try and exploit these predictions.

Using Quantconnect´s algorithm framework we can back-test this strategy and determine with a good level of confidence if our predictions are good or not.

First of all let´s import the necessary modules:

#Necessary modules:
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
#Machine Learning modules:
from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten, AveragePooling2D
from sklearn.preprocessing import robust_scale,minmax_scale
from keras.utils import to_categorical

These include the machine learning modules we will use as well as the immediate execution model from Quantconnect built-in tools.

We have to initialize the algorithm:

class CNN_Daily_Predictions(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2015, 5, 1)
        symbols = ["CAT"]
        for symbol in symbols:
            self.AddEquity(symbol, Resolution.Minute)
        self.UniverseSettings.Resolution = Resolution.Minute        

For the time being we will use a single symbol, Caterpillar from DOW30 industrial index. CAT is by the way a dividend champion, that is, they have increased their dividend distribution for 25 years in a row. Also, CAT is cat; we could also try with DOG...

The rest of the code sets the resolution of the universe to receive a data slice every minute, use a built-in brokerage and reality modeling module, use a weighted factor for our trade signals (in this case it will be generally 1, as we only have a single symbol) and a simple risk reduction model that will set a trailing stop loss limit at 1% trailing returns.

The alpha model (module that generates buy or sell signals) has to be defined and initialized:

class CnnVisualPredictionAlphaModel(AlphaModel):
    def __init__(self):
        # Initiate first day of operation.
        self.has_started = False
        self.opening_prices = {}
        self.price_differences = {}        
        #For fast trials this can prevent the model from ever training
        #the neural network:
        self.next_year = -1
        self.trained = False        
        #Machine learning model features:
        self.features = 15
        self.prediction_cut = 360
        self.slack = 3 #Needed to force the algorithm to operate 3 
        #minutes from the actual market close, as that is our 
        #prediction point.
        self.min_confidence = 0.7
        self.image_size = (self.features, self.prediction_cut)
        self.cnn_model = Sequential()
        self.categorical_encoding ={}

Now the alpha model needs an Update method, this is called every time a new time slice arrives, a new packet of price and volume data, here we train, predict and operate the market according to our prediction:

def Update(self, algorithm, data):        
        #Initialize every slice of data:
        insights = []        
        if not data.HasData: return []        
        if self.next_year == int(algorithm.Time.year) or self.trained==False:
            #To be substituted by using algorithm.Train() in the future:
            self.next_year = int(algorithm.Time.year) + 1
            algorithm.Debug("Model Trained:" + str(algorithm.Time.year))
            self.trained = True        
        #Obtain opening prices:
        if algorithm.Time.hour == 9 and algorithm.Time.minute == 31:
            self.opening_prices = {}
            for symbol in data.Keys:
                self.opening_prices[symbol] = data[symbol].Open                
        if algorithm.Time.hour == 15 and algorithm.Time.minute == 30:
            self.price_differences = {}
            for symbol in data.Keys:
                self.price_differences[symbol] = data[symbol].Close - self.opening_prices[symbol]                
            for symbol in data.Keys:
                prediction, confidence = self.predict(algorithm, symbol, self.prediction_cut)               
                #Market operation section:
                if prediction == str() or confidence < self.min_confidence: continue
                if prediction == True and self.price_differences[symbol]<=0: direction = InsightDirection.Up
                elif prediction == False and self.price_differences[symbol]>=0: direction = InsightDirection.Down
                else: continue
                #Controlled by self.prediction_cut and self.slack.
                insights.append(Insight(symbol, timedelta(minutes=(390-self.prediction_cut-self.slack)), InsightType.Price, direction, 0.02, confidence, "CNN_Visual", 1))            
            return insights

We are going to retrain our convolutional neural network every year with new data keeping only past 5 years data. Then we will predict the end of the day with our 360 minute time window, that is, from 15:30 to 16:30 in a normal trading day. These hard-coded timing values can be substituted by built-in close and open calendar values, but for this trial hard typing them is good enough. The train_model() and predict() functions will utilize the model and the prediction from our previous post. The code for them is quite long, so we will show it in the final installment of this series (only if the results are good, if not, we will hide the complete model and claim a clean trading career).

A certain level of prediction confidence has been used also, as the last layer of our convolutional neural network will express the prediction result as a percentage, if we want to use only the most secure predictions we can increase min_confidence value, or decrease it to take everything that is above 50%, which can generate a lot of erroneous predictions.

Running this algorithm in the back-test yields the following results in terms of prediction capability:

The key performance of the trade signals says we are able to predict correctly 56% of the time the direction of the last 30 minutes of trading. For this single symbol (CAT) 77 signals have been generated, with a long to short ratio of 1.34 (so it is upwards biased). The magnitude score is bad as our prediction is a flat 2% change, which is high compared to what we can expect for 30 minutes of trading of a high-volume company with strong fundamentals. Each signal is worth, in average, 4 dollars. The results are not stellar but are not bad either; there is some prediction power in the convolutional neural network.

Equity results are not very good, unfortunately:

In five years, the strategy will incur in a 1% loss, it probably means that our correct predictions do not yield much and our erroneous predictions are very bad. The trading fees incurred are also very significant we compared to the maximum gains. There are also a couple of 1% gain days, and the extreme loosing days are smaller in comparison.

These results are to be expected, as we have only 77 "action" days in 5 years of trading and very basic execution and risk control. As the results are not disastrous in the prediction, it is worth trying a more involved convolutional neural network and extend it with additional symbols and better operational tools.

We will discuss these improvements and further research in our next post.

46 views0 comments

Recent Posts

See All


bottom of page