Machine Learning with Quantiacs

November, 27 2020

The Python Quantiacs toolbox allows you to use machine learning methods for coding trading algorithms. We describe an example based on keras and use a Long Short-Term Memory (LSTM) model for time series forecasting.

 

All Quantiacs system files should contain a definition of the settings:

 

def mySettings():    

    settings = {}
    
    settings['markets']  = ['F_ES']
    settings['slippage'] = 0.05
    settings['budget']   = 1000000
    settings['lookback'] = 504
    settings['beginInSample'] = '20140101'
    settings['endInSample']   = '20170101'    

    return settings

These settings define one asset to be traded, the E-Mini S&P 500 Index Futures, set slippage to be 5% of the daily range of the asset, fix initial capital to 1 M USD, define a lookback period of 504 days and set the initial and final days of the simulation.

In addition, we define an auxiliary function which creates and trains the LSTM network. First of all, we import the required modules:

import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
import tensorflow as tf

Next, we define the function:

def createAndTrain(DATE, CLOSE, settings):    

    lst_dates  = DATE.tolist()
    lst_dates  = lst_dates[1:]Quantiacs
    price_data = CLOSE[1:, :]    
    average    = np.average(price_data)
    std_dev    = np.std(price_data)
    price_data = (price_data - average) / std_dev    

    return_data = (CLOSE[1:, :] - CLOSE[:- 1, :]) / CLOSE[:- 1, :]    

    # define training set and target:
    trainX = np.reshape(price_data, (price_data.shape[0], 1, price_data.shape[1]))
    trainY = return_data    

    # create and fit the LSTM network:
    model = Sequential()
    model.add(LSTM(4, input_dim=1))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=0)    
    settings['mean']  = average
    settings['std']   = std_dev
    settings['model'] = model    

    return

The function performs supervised learning by defining a training set where the features are the normalized CLOSE prices of the assets. Normalization takes place by subtracting from the CLOSE the mean and dividing by the standard deviation. The target variable is represented by the relative returns of the assets.

Next, it creates a LSTM network and fits it with the training set. The function expands the settings for the trading system by defining three new objects:

    settings['mean']  = average
    settings['std']   = std_dev
    settings['model'] = model

which are used by the trading system.

The trading logic is defined in the trading system function:

def myTradingSystem(DATE, CLOSE, exposure, equity, settings):    

    lookBack = settings['lookback']    
    if 'model' not in settings:
        createAndTrain(DATE[:lookBack - 2], CLOSE[:lookBack - 2], settings)    

    model   = settings['model']
    average = settings['mean']
    std_dev = settings['std']    

    testX = (CLOSE[lookBack-1:] - average) / std_dev
    testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))    
    testY = model.predict(testX)    
    newDelta = testY[0]
    nMarkets = CLOSE.shape[1]
    pos = np.ones((1, nMarkets))
    if newDelta >= 0:
        pos[0] = 1
    else:
        pos[0] = -1    

    return pos, settings

The trading system calls the auxiliary function which builds the model using a slice of the data and later applies the model for predicting the relative returns. If the predicted return is positive the system goes long, otherwise it goes short.

The main function call is:

if __name__ == '__main__':
    from quantiacsToolbox import runts    
    inter_op_parallelism_threads = 1
    tf.random.set_seed(1)     
    results = runts(__file__)

The same logic of defining an auxiliary function for performing intermediate operations can be used for simpler methods, like performing a linear regression.

If you are interested in statistics and machine learning, and want to test your ideas on global financial markets, submit your code before end of 2020 and take part to the Q14 contest on Quantiacs!

The Full Strategy

import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
import tensorflow as tf

def createAndTrain(DATE, CLOSE, settings): 
   
    lst_dates  = DATE.tolist()
    lst_dates  = lst_dates[1:]
    price_data = CLOSE[1:, :]    
    average    = np.average(price_data)
    std_dev    = np.std(price_data)
    price_data = (price_data - average) / std_dev    
    return_data = (CLOSE[1:, :] - CLOSE[:- 1, :]) / CLOSE[:- 1, :]
    
    # define training set and target:
    trainX = np.reshape(price_data, (price_data.shape[0], 1, price_data.shape[1]))
    trainY = return_data    

    # create and fit the LSTM network:
    model = Sequential()
    model.add(LSTM(4, input_dim=1))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=0)  
  
    settings['mean']  = average
    settings['std']   = std_dev
    settings['model'] = model    

    return

def myTradingSystem(DATE, CLOSE, exposure, equity, settings):    

    lookBack = settings['lookback']   
 
    if 'model' not in settings:
        createAndTrain(DATE[:lookBack - 2], CLOSE[:lookBack - 2], settings)  
  
    model   = settings['model']
    average = settings['mean']
    std_dev = settings['std']    

    testX = (CLOSE[lookBack-1:] - average) / std_dev
    testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))    
    testY = model.predict(testX)    
    newDelta = testY[0]

    nMarkets = CLOSE.shape[1]
    pos = np.ones((1, nMarkets))

    if newDelta >= 0:
        pos[0] = 1
    else:
        pos[0] = -1    

    return pos, settings

def mySettings():    

    settings = {}
    
    settings['markets']  = ['F_ES']
    settings['slippage'] = 0.05
    settings['budget']   = 1000000
    settings['lookback'] = 504
    settings['beginInSample'] = '20140101'
    settings['endInSample']   = '20170101'    

    return settings

if __name__ == '__main__':Quantiacs
    from quantiacsToolbox import runts    
    inter_op_parallelism_threads = 1
    tf.random.set_seed(1)    
    results = runts(__file__)


Quantiacs