Cryptocurrency Price Prediction Using Deep Learning in TensorFlow

In this post, deep learning neural networks are applied to the problem of predicting Bitcoin and other cryptocurrency prices. A chartist approach is taken to predict future values; the network makes predictions based on historical trends in the price and trading volume. A 1D convolutional neural network (CNN) transforms an input volume consisting of historical prices from several major cryptocurrencies into future price information.

Data Gathering

To facilitate rapid prediction, pricing information is queried using the web API of Poloniex. A URL is provided to the API and a JSON containing the historical price information of a specified cryptocurrency is returned.

import numpy as np
import os
import pandas as pd
import urllib.request
    
def GetAPIUrl(cur, sts = 1420070400):
    '''
    Makes a URL for querying historical prices of a cyrpto from Poloniex
    cur:    3 letter abbreviation for cryptocurrency (BTC, LTC, etc)
    '''
    return 'https://poloniex.com/public?command=returnChartData&currencyPair=USDT_{:s}&start={:d}&end=9999999999&period=7200'.format(cur, sts)

def GetCurDF(cur, fp):
    '''
    cur:    3 letter abbreviation for cryptocurrency (BTC, LTC, etc)
    fp:     File path (to save price data to CSV)
    '''
    openUrl = urllib.request.urlopen(GetAPIUrl(cur))
    r = openUrl.read()
    openUrl.close()
    df = pd.read_json(r.decode())
    df['date'] = df['date'].astype(np.int64) // 1000000000
    return df

#%%Path to store cached currency data
datPath = 'CurDat/'
if not os.path.exists(datPath):
    os.mkdir(datPath)
#Different cryptocurrency types
cl = ['BTC', 'LTC', 'ETH', 'XMR']
#Columns of price data to use
CN = ['close', 'high', 'low', 'open', 'volume']
#Store data frames for each of above types
D = []
for ci in cl:
    dfp = os.path.join(datPath, ci + '.csv')
    try:
        df = pd.read_csv(dfp, sep = ',')
    except FileNotFoundError:
        df = GetCurDF(ci, dfp)
    D.append(df)
#%%Only keep range of data that is common to all currency types
cr = min(Di.shape[0] for Di in D)
for i in range(len(cl)):
    D[i] = D[i][(D[i].shape[0] - cr):]

After execution, D[i] is a pandas Dataframe containing historical price data for the cryptocurrency cl[i].

Time Series Sampling

New samples are constructed that pair sequences of N samples with the subsequent K samples. In this way, a regression model can be fit which predicts K time periods into the future given data from the past M. A helper class which accomplishes this follows.

import numpy as np

class PastSampler:
    '''
    Forms training samples for predicting future values from past value
    '''
    
    def __init__(self, N, K):
        '''
        Predict K future sample using N previous samples
        '''
        self.K = K
        self.N = N

    def transform(self, A, Y = None):
        M = self.N + self.K     #Number of samples per row (sample + target)
        #Matrix of sample indices like: {{1, 2..., M}, {2, 3, ..., M + 1}}
        I = np.arange(M) + np.arange(A.shape[0] - M + 1).reshape(-1, 1)
        B = A[I].reshape(-1, M * A.shape[1], *A.shape[2:])
        ci = self.N * A.shape[1]    #Number of features per sample
        return B[:, :ci], B[:, ci:] #Sample matrix, Target matrix

The above class is applied to the original time sequence data to obtain the desired sample and target matrices.

from PastSampler import PastSampler

#%%Features are channels
C = np.hstack((Di[CN] for Di in D))[:, None, :]
HP = 16                 #Holdout period
A = C[0:-HP]                
SV = A.mean(axis = 0)   #Scale vector
C /= SV                 #Basic scaling of data
#%%Make samples of temporal sequences of pricing data (channel)
NPS, NFS = 256, 16         #Number of past and future samples
ps = PastSampler(NPS, NFS)
B, Y = ps.transform(A)

In the above code, the shapes of \textbf{B} and \textbf{C} are as (Sample Index, Time, Price Data). A holdout period is maintained to access the performance of the network. The number of time units in the period is controlled by HP.

Applying Deep Neural Networks

The TFANN module is used to create an artificial neural network. TFANN can be installed using pip with the following command.

pip install TFANN

A 1D convolution neural network is constructed which transforms the input volume of historical data into predictions. The past NPS samples are transformed into a prediction about the next NFS samples. The C1d option in the network architecture specification indicates 1-dimensional convolution.

#%%Architecture of the neural network
from TFANN import ANNR

NC = B.shape[2]
#2 1-D conv layers with relu followed by 1-d conv output layer
ns = [('C1d', [8, NC, NC * 2], 4), ('AF', 'relu'), 
      ('C1d', [8, NC * 2, NC * 2], 2), ('AF', 'relu'), 
      ('C1d', [8, NC * 2, NC], 2)]
#Create the neural network in TensorFlow
cnnr = ANNR(B[0].shape, ns, batchSize = 32, learnRate = 2e-5, 
            maxIter = 64, reg = 1e-5, tol = 1e-2, verbose = True)
cnnr.fit(B, Y)

The architecture of the CNN is shown below in Figure 1. The top set of parenthesized values indicate the filter dimension while the bottom denote the stride.

CryptoPredCNN

Figure 1: 1D CNN Architecture

More information and the source code for the ANNR class are available on GitHub.

Prediction

Using the above network, the next NFS time steps can be predicted. These predictions can in turn be used for subsequent predictions so that prediction can be made an arbitrary amount into the future. Code to accomplish this follows.

PTS = []                        #Predicted time sequences
P, YH = B[[-1]], Y[[-1]]        #Most recent time sequence
for i in range(HP // NFS):  #Repeat prediction
    P = np.concatenate([P[:, NFS:], YH], axis = 1)
    YH = cnnr.predict(P)
    PTS.append(YH)
PTS = np.hstack(PTS).transpose((1, 0, 2))
A = np.vstack([A, PTS]) #Combine predictions with original data
A = np.squeeze(A) * SV  #Remove unittime dimension and rescale
C = np.squeeze(C) * SV

Using PredictFull, the outputs of intermediate layers in the network can be visualized. Figure 2 shows an input sample as it is transformed by subsequent layers of the network.

PF0

Figure 2: Intermediate Layer Outputs

import matplotlib.pyplot as mpl

nt = 4
PF = cnnr.PredictFull(B[:nt])
for i in range(nt):
    fig, ax = mpl.subplots(1, 4, figsize = (16 / 1.24, 10 / 1.25))
    ax[0].plot(PF[0][i])
    ax[0].set_title('Input')
    ax[1].plot(PF[2][i])
    ax[1].set_title('Layer 1')
    ax[2].plot(PF[4][i])
    ax[2].set_title('Layer 2')
    ax[3].plot(PF[5][i])
    ax[3].set_title('Output')
    fig.text(0.5, 0.06, 'Time', ha='center')
    fig.text(0.06, 0.5, 'Activation', va='center', rotation='vertical')
    mpl.show()

Notice how in subsequent layers the input data is reduced from NPS to NFS time units.

Results

The result of the predictions can be visualized using matplotlib.

CI = list(range(C.shape[0]))
AI = list(range(C.shape[0] + PTS.shape[0] - HP))
NDP = PTS.shape[0] #Number of days predicted
for i, cli in enumerate(cl):
    fig, ax = mpl.subplots(figsize = (16 / 1.5, 10 / 1.5))
    hind = i * len(CN) + CN.index('high')
    ax.plot(CI[-4 * HP:], C[-4 * HP:, hind], label = 'Actual')
    ax.plot(AI[-(NDP + 1):], A[-(NDP + 1):, hind], '--', label = 'Prediction')
    ax.legend(loc = 'upper left')
    ax.set_title(cli + ' (High)')
    ax.set_ylabel('USD')
    ax.set_xlabel('Time')
    ax.axes.xaxis.set_ticklabels([])
    mpl.show()

The resulting plot is shown below in Figure 3.

Figure 3: Cryptocurrency Predictions

The network predicts a dip in the prices of each cryptocurrency followed by a rally. The predicted behavior is similar to Bitcoin’s price over the past few days.

More Predictions

For a user-friendly application that integrates cryptocurrency predictions with market information, please see:

Also, follow RoboInsights on Twitter for daily predictions about Bitcoin (BTC), Ethereum (ETH), Litecoin (LTC), and Monero (XMR):

Advertisements