We’ll frame our problem as follows. We have historical price data for Bitcoin, which includes the following predictors for each day (where we have daily time steps):

Opening price
High price
Low price
Volume traded
Our goal is to take some sequence of the above four values (say, for 100 previous days), and predict the target variable (Bitcoin’s price) for the next 50 days into the future. Consequently, we need a way to feed in these multiple values at each time step to our LSTM using Google Colab with our csv file located in goodle drive with the csv link in https://drive.google.com/file/d/1J_0IgW_Ef6acETKT5r87J2aaX8Iy-OuS/view?usp=share_link , and to produce a singular output representing the prediction at the next time step in return. In this way, we construct a multivariate LSTM.  then we clean the data by removing adjusted close from the csv:



df = pd.read_csv('BTC-USD.csv', index_col = 'Date', parse_dates=True)
df.drop(columns=['Adj Close'], inplace=True)
df.head(5)


from now on you will refeer to historical prices csv as: https://drive.google.com/file/d/1J_0IgW_Ef6acETKT5r87J2aaX8Iy-OuS/view?usp=share_link.








At the bare minimum, your exploratory data analysis should consist of plotting the target variable of interest.  Let’s plot the Bitcoin price over time to see what we’re actually trying to predict.





plt.plot(df.Close)
plt.xlabel("Time")
plt.ylabel("Price (USD)")
plt.title("Bitcoin price over time")
plt.savefig("initial_plot.png", dpi=250)
plt.show();



We want a realistic emulation of what would happen in the real-world. That is, we want a few years of historical data, and train an LSTM to predict what will happen to the price of Bitcoin in the next few months.

Setting inputs and outputs
Recall that our predictors will consist of all the columns except our target, closing price. Note that we want to use an sklearn preprocessor below, which requires reshaping the array if it consists of a single feature, as our target does. Hence, for the target y, we have to call values, which removes the axes labels and will allow us to reshape the array.

X, y = df.drop(columns=['Close']), df.Close.values
X.shape, y.shape

>>> ((5438, 4), (5438,))

-----
We’ll use standardisation for our training features X by removing the mean and scaling to unit variance. Standardisation helps the deep learning model to learn by ensuring that parameters can exist in the same multi-dimensional space; it wouldn’t make much sense to have the weights have to change their size simply because all the variables have different scales. For our target y, we will scale and translate each feature individually to between 0 and 1. This transformation is often used as an alternative to zero mean, unit variance scaling:



from sklearn.preprocessing import StandardScaler, MinMaxScaler
mm = MinMaxScaler()
ss = StandardScaler()

X_trans = ss.fit_transform(X)
y_trans = mm.fit_transform(y.reshape(-1, 1))


---

We want to feed in 100 samples, up to the current day, and predict the next 50 time step values. To do this, we need a special function to ensure that the corresponding indices of X and y represent this structure. Examine this function carefully, but essentially it just boils down to getting 100 samples from X, then looking at the 50 next indices in y, and patching these together. Note that because of this we'll throw out the first 50 values of y. :



# split a multivariate sequence past, future samples (X and y)
def split_sequences(input_sequences, output_sequence, n_steps_in, n_steps_out):
    X, y = list(), list() # instantiate X and y
    for i in range(len(input_sequences)):
        # find the end of the input, output sequence
        end_ix = i + n_steps_in
        out_end_ix = end_ix + n_steps_out - 1
        # check if we are beyond the dataset
        if out_end_ix > len(input_sequences): break
        # gather input and output of the pattern
        seq_x, seq_y = input_sequences[i:end_ix], output_sequence[end_ix-1:out_end_ix, -1]
        X.append(seq_x), y.append(seq_y)
    return np.array(X), np.array(y)

X_ss, y_mm = split_sequences(X_trans, y_trans, 100, 50)
print(X_ss.shape, y_mm.shape)

>>> (2529, 100, 4) (2529, 50)


------


we wanted to predict the data a several months into the future. Thus, we’ll use a training data size of 95%, with 5% left for the remaining data that we’re going to predict. This gives us a training set size of 2763 days, or about seven and a half years. We will predict 145 days into the future, which is almost 5 months:

total_samples = len(X)
train_test_cutoff = round(0.90 * total_samples)

X_train = X_ss[:-150]
X_test = X_ss[-150:]

y_train = y_mm[:-150]
y_test = y_mm[-150:] 

print("Training Shape:", X_train.shape, y_train.shape)
print("Testing Shape:", X_test.shape, y_test.shape) 

>>> Training Shape: (2379, 100, 4) (2379, 50)
Testing Shape: (150, 100, 4) (150, 50)


-------




input tensor to be forward propagated has to be can facilitate automatic back propagation (through backward()) without being wrapped in a variable:

# convert to pytorch tensors
X_train_tensors = Variable(torch.Tensor(X_train))
X_test_tensors = Variable(torch.Tensor(X_test))

y_train_tensors = Variable(torch.Tensor(y_train))
y_test_tensors = Variable(torch.Tensor(y_test))


--------