<jupyter_start><jupyter_text>Getting dependencies ready<jupyter_code>%tensorflow_version 2.x
import json
import requests
from keras.models import Sequential
from keras.layers import Activation, Dense, Dropout, LSTM
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
%matplotlib inline<jupyter_output><empty_output><jupyter_text>We are using **cryptocompare API** to get the *BTC OHLCV* data for our LSTM model The response we get form the API is in json format. so we load only the *BTCUSD* last **1000 day** OHLCV data from the API.```pythonres = requests.get(endpoint + '?fsym=BTC&tsym=USD&limit=1000') fsym = BTC means bitcoin cryptocurrency limit = 1000 means last 1000 day data tsym = USD means USD pair of BTC``` And convert the **json data** into pandas data-frame with time as index```pythonhist = pd.DataFrame(json.loads(res.content)['Data'])hist = hist.set_index('time')```<jupyter_code>endpoint = 'https://min-api.cryptocompare.com/data/histoday'
res = requests.get(endpoint + '?fsym=BTC&tsym=USD&limit=1000')
hist = pd.DataFrame(json.loads(res.content)['Data'])
hist = hist.set_index('time')
hist.index = pd.to_datetime(hist.index, unit='s')
target_col = 'close'

hist.head(10)<jupyter_output><empty_output><jupyter_text>Dropping the **conversionType & conversionSymbol** columns from the dataframe Setting columns as axis and u can see the change in the above and below  dataframes and can tell which one is more cleaner & enough for the **LSTM model**<jupyter_code>hist.drop(["conversionType", "conversionSymbol"], axis='columns', inplace=True)
hist.head(10)<jupyter_output><empty_output><jupyter_text>**Splitting of dataset into** : 1.   Training dataset2.   Testing dataset  **train** - training dataset  **test** - testing dataset "***test_size = 0.2***" means **20%** of the dataset is for testing purpose  and rest **80%** is for training the LSTM model. Here, we wrote the function to split the dataset into training and testing datset with 2 parameters:1.   **dataframe** to split 2.   **test_size** (with default value of 0.2) Here is the function definition :```pythondef train_test_split(df, test_size=0.2):```this function returns the dataset as **train_data (initial 80%)** & **test_data (20%)**```javascriptreturn train_data, test_data```<jupyter_code>def train_test_split(df, test_size=0.2):
    split_row = len(df) - int(test_size * len(df))
    train_data = df.iloc[:split_row]
    test_data = df.iloc[split_row:]
    return train_data, test_data

# calling the function and storing the splitted dataset
# into train and test variables 
train, test = train_test_split(hist, test_size=0.2)

train
test<jupyter_output><empty_output><jupyter_text>Function to plot the line chart for **BTCUSD** :- differentiating the training and testing data for our LSTM model - on daily timeframe squeezed as per the weekly timeframe chart<jupyter_code>def line_plot(line1, line2, label1=None, label2=None, title='', lw=2):
    fig, ax = plt.subplots(1, figsize=(13, 7))
    ax.plot(line1, label=label1, linewidth=lw)
    ax.plot(line2, label=label2, linewidth=lw)
    ax.set_ylabel('USD', fontsize=14)
    ax.set_xlabel('Daily time frame', fontsize=14)
    ax.set_title(title, fontsize=16)
    ax.legend(loc='best', fontsize=16)


line_plot(train[target_col], test[target_col], 'training', 'test', title='BTCUSD',lw =1)<jupyter_output><empty_output><jupyter_text>**IMPLEMENTATION OF LSTM MODEL**  LSTMs are nothing but a stack of neural networks composed of linear layers composed of weights and biases, just like any other standard neural network. The weights are constantly updated. **Why LSTM when we have RNN?**A sentence or phrase only holds meaning when every word in it is associated with its previous word and the next one. LSTM, short for Long Short Term Memory, as opposed to RNN, extends it by creating both short-term and long-term memory components to efficiently study and learn sequential data. Hence, it’s great for Machine Translation, Speech Recognition, time-series analysis, etc.<jupyter_code>def normalise_zero_base(df):
    return df / df.iloc[0] - 1
def extract_window_data(df, window_len=5, zero_base=True):
    window_data = []
    for idx in range(len(df) - window_len):
        tmp = df[idx: (idx + window_len)].copy()
        if zero_base:
            tmp = normalise_zero_base(tmp)
        window_data.append(tmp.values)
    return np.array(window_data)

def prepare_data(df, target_col, window_len=10, zero_base=True, test_size=0.2):
    train_data, test_data = train_test_split(df, test_size=test_size)
    X_train = extract_window_data(train_data, window_len, zero_base)
    X_test = extract_window_data(test_data, window_len, zero_base)
    y_train = train_data[target_col][window_len:].values
    y_test = test_data[target_col][window_len:].values
    if zero_base:
        y_train = y_train / train_data[target_col][:-window_len].values - 1
        y_test = y_test / test_data[target_col][:-window_len].values - 1

    return train_data, test_data, X_train, X_test, y_train, y_test<jupyter_output><empty_output><jupyter_text>**Sequential**Sequential groups a linear stack of layers into a tf.keras.model.Sequential provides training and inference features on this model What is a Neural Network Activation Function?An Activation Function decides whether a neuron should be activated or not. This means that it will decide whether the neuron’s input to the network is important or not in the process of prediction using simpler mathematical operations.  **Linear Activation Function**The linear activation function is also known as Identity Function where the activation is proportional to the input.Mathematically it can be represented as :f(x) = x  However, a linear activation function has a major problem :- All layers of the neural network will collapse into one if a linear activation function is used. No matter the number of layers in the neural network, the last layer will still be a linear function of the first layer. So, essentially, a linear activation function turns the neural network into just one layer.We are using 4 LSTM layers in our model.<jupyter_code>def build_lstm_model(input_data, output_size, neurons=100, activ_func='linear',
                     dropout=0.2, loss='mse', optimizer='adam'):
    model = Sequential()
    model.add(LSTM(neurons, input_shape=(
        input_data.shape[1], input_data.shape[2])))
    model.add(Dropout(dropout))
    model.add(Dense(units=output_size))
    model.add(Activation(activ_func))

    model.compile(loss=loss, optimizer=optimizer)
    return model<jupyter_output><empty_output><jupyter_text>**Training and interference features**<jupyter_code>np.random.seed(42)
window_len = 5
test_size = 0.2 
zero_base = True
lstm_neurons = 100 
epochs = 20
batch_size = 32
loss = 'mse'
dropout = 0.2
optimizer = 'adam'

train, test, X_train, X_test, y_train, y_test = prepare_data(
    hist, target_col, window_len=window_len, zero_base=zero_base, test_size=test_size)
model = build_lstm_model(
    X_train, output_size=1, neurons=lstm_neurons, dropout=dropout, loss=loss,
    optimizer=optimizer)
history = model.fit(
    X_train, y_train, validation_data=(X_test, y_test), epochs=epochs, batch_size=batch_size, verbose=1, shuffle=True)<jupyter_output>Epoch 1/20
25/25 [==============================] - 3s 48ms/step - loss: 0.0059 - val_loss: 0.0025
Epoch 2/20
25/25 [==============================] - 0s 16ms/step - loss: 0.0041 - val_loss: 0.0025
Epoch 3/20
25/25 [==============================] - 0s 17ms/step - loss: 0.0033 - val_loss: 0.0020
Epoch 4/20
25/25 [==============================] - 0s 14ms/step - loss: 0.0031 - val_loss: 0.0026
Epoch 5/20
25/25 [==============================] - 0s 13ms/step - loss: 0.0034 - val_loss: 0.0018
Epoch 6/20
25/25 [==============================] - 1s 22ms/step - loss: 0.0029 - val_loss: 0.0019
Epoch 7/20
25/25 [==============================] - 0s 17ms/step - loss: 0.0028 - val_loss: 0.0017
Epoch 8/20
25/25 [==============================] - 0s 15ms/step - loss: 0.0025 - val_loss: 0.0019
Epoch 9/20
25/25 [==============================] - 0s 15ms/step - loss: 0.0024 - val_loss: 0.0016
Epoch 10/20
25/25 [==============================] - 0s 16ms/step - loss: 0.0026 - val_loss: 0.0015
Epoch 11/[...]<jupyter_text>Line chart of **Epochs vs MSE**- An epoch is a term used in machine learning which inicates the number of passes of the entire training dataset the machine learning algo/model has been trained with.- In our case it is 20,which we come up with no luck but hit and trial only.<jupyter_code>plt.plot(history.history['loss'], 'r', linewidth=2, label='Train loss')
plt.plot(history.history['val_loss'], 'g',
         linewidth=2, label='Validation loss')
plt.title('LSTM ( Epochs vs MSE )')
plt.xlabel('Epochs')
plt.ylabel('MSE')
plt.show()<jupyter_output><empty_output><jupyter_text>Mean Absolute Error<jupyter_code>targets = test[target_col][window_len:]
preds = model.predict(X_test).squeeze()
mean_absolute_error(preds, y_test)<jupyter_output><empty_output><jupyter_text>Mean Squared Error<jupyter_code>MSE = mean_squared_error(preds, y_test)
MSE<jupyter_output><empty_output><jupyter_text>**R2 score** is used to evaluate the performance of a regression-based machine learning model. Also known as the ***coefficient of determination***. It works by measuring the amount of variance in the predictions explained by the dataset. In other fields, the standards for a good R-Squared reading can be much higher, such as **0.9 or above**.  In finance, an R-Squared **above 0.7** would generally be seen as showing a high level of correlation whereas a measure **below 0.4** would show a low correlation. R2 score using plain python :```pythonprint ("total sum of squares", y)print ("ẗotal sum of residuals ", g)print ("r2 calculated", 1 - (g / y)) where g is the sum of the differences between the observed values and the  predicted ones. (ytest[i] – preds[i]) **2. y is each observed value y[i]  minus the average of observed values np.mean(ytest).``` We can ofcourse let scikit-learn to do this with its S2_score() method:```pythonprint("R2 score : %.2f" % r2_score(ytest,preds))```which we have implemented in our LSTM model<jupyter_code>R2 = r2_score(y_test, preds)
R2<jupyter_output><empty_output><jupyter_text>FINAL COMPARISON**Comparison of actual vs predicted price action of BTC of last 200 days (testing data)**.<jupyter_code>preds = test[target_col].values[:-window_len] * (preds + 1)
preds = pd.Series(index=targets.index, data=preds)
line_plot(targets, preds, 'actual', 'prediction', lw=2)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1339252336448598, 'fraction_numerical': 0.031214953271028037, 'mean_word_length': 5.328208160851567, 'pattern_counts': {'":': 0, '<': 52, '<?xml version=': 0, '>': 52, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '117016', 'license': 'MIT', 'n_tokens_mistral': 3687, 'n_tokens_neox': 3350, 'n_words': 1089, 'path': 'BTC_LSTM_MODEL.ipynb', 'repo_name': 'jeetintyagi/BTC-ETH-price-prediction', 'size': '10700'}