{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Crypto Buys Prediction using Deep Learning - RNN Model\n\nThis project uses data from [G-Research Crypto forecasting competition](link) to build a deep learning model to predict when to buy a Crypto asset.\n\nThe following are done in this notebook:\n- normalize raw `volume` and `closing price` across different crypto assets so that they can be compared on comparable basis\n- created a collection of sequential data, each containing past 60 periods to predict `buys` in the next period\n- build RNN model to predict BTC `buys` with >80% accuracy","metadata":{}},{"cell_type":"markdown","source":"## Importing Packages","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom datetime import datetime\nfrom sklearn import preprocessing\nfrom collections import deque\nimport random\nimport time\nimport math","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-12-29T07:01:41.294458Z","iopub.execute_input":"2021-12-29T07:01:41.295309Z","iopub.status.idle":"2021-12-29T07:01:42.407733Z","shell.execute_reply.started":"2021-12-29T07:01:41.295250Z","shell.execute_reply":"2021-12-29T07:01:42.406842Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Reading Data from csv","metadata":{}},{"cell_type":"code","source":"data_folder = \"../input/g-research-crypto-forecasting/\"\n!ls $data_folder\n\n# Reading `csv`s into pandas dataframes\ncrypto_df = pd.read_csv(data_folder + 'train.csv')\nasset_details = pd.read_csv(data_folder + 'asset_details.csv')\n\n# Adding Crypto symbols to the dataframes for easy reference\nasset_symbols_map = {\n    0: 'BNB',\n    1: 'BTC',\n    2: 'BCH',\n    3: 'ADA',\n    4: 'DOGE',\n    5: 'EOS.IO',\n    6: 'ETH',\n    7: 'ETC',\n    8: 'IOTA',\n    9: 'LTC',\n    10: 'MKR',\n    11: 'XMR',\n    12: 'XLM',\n    13: 'TRON'\n}\ncrypto_df['Symbol'] = crypto_df.Asset_ID.map(asset_symbols_map)\nasset_details['Symbol'] = asset_details.Asset_ID.map(asset_symbols_map)","metadata":{"execution":{"iopub.status.busy":"2021-12-29T07:01:42.409610Z","iopub.execute_input":"2021-12-29T07:01:42.409859Z","iopub.status.idle":"2021-12-29T07:02:45.165983Z","shell.execute_reply.started":"2021-12-29T07:01:42.409829Z","shell.execute_reply":"2021-12-29T07:02:45.164754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Preprocessing","metadata":{}},{"cell_type":"code","source":"# how many preceeding periods used to predict future prices\nSEQ_LEN = 60\n\n# how many periods forward are we predicting\nFUTURE_PERIOD_PREDICT = 3 \n\n# which crypto to predict\nCRYPTO_TO_PREDICT = \"BTC\"\n\ndef classify(current, future):\n    \"\"\"\n    Compare current vs future crypto price, \n    returns 1 if future price is greater or equal\n    with current price, else returns 0.\n    \"\"\"\n    if future >= current:\n        return 1\n    return 0\n\ndef preprocess(df):\n    \"\"\"\n    Wrapper function to preprocess df for ML input.\n    \n    Args:\n        df: dataframe consisting crypto data (features and target)\n        \n    Returns:\n        x_train, y_train, x_test, y_test: train and test dfs\n            ready for ML modeling.\n    \"\"\"\n    # Normalizing each features\n    df_norm = df.apply(lambda col: scale_normalize(col))\n    \n    # Creating sequential data\n    seq_data = create_sequential_data(df_norm)    \n    \n    # Train test split\n    x_train, y_train, x_test, y_test = train_test_split_seq(seq_data)\n    return x_train, y_train, x_test, y_test\n\ndef scale_normalize(col):\n    \"\"\"\n    Calculates the `pct_change` of a pandas series\n    and normalize the values.\n    \n    Args:\n        col: pandas series of float numbers (crypto prices)\n        \n    Returns:\n        pandas series of processed columns\n    \"\"\"\n    if 'Buy' in col.name:\n        # Skipping preprocessing for `Buy` column\n        # Removing first row to sync with preprocessed columns\n        return col[1:] \n    else:\n        col_processed = col.pct_change()\n        col_processed.dropna(inplace=True)\n        \n        col_processed = preprocessing.scale(col_processed)\n        return col_processed\n    \ndef create_sequential_data(df, sequence_length=SEQ_LEN):\n    \"\"\"\n    Given a `df`, generate a list (sequence) of smaller `dfs`,\n    with each one being a slice of the original `df` of length\n    `sequence_length`\n    \n    Args:\n        df: pandas DataFrame to generate the seq data from\n        sequence_length: the number of rows in each slice\n        \n    Returns:\n        List of numpy arrays containing features and target\n            i.e. [(feature1, target1), (feature2, target2), ..]\n    \"\"\"\n    seq_data = []\n    \n    for row_idx in range(0, len(df)-sequence_length):\n        seq_data.append(\n            [df.iloc[row_idx:row_idx+sequence_length, :-1],\n             df.iloc[row_idx+sequence_length, -1]]\n        )\n    \n    return seq_data\n\ndef train_test_split_seq(seq_data, test_frac=0.1):\n    \"\"\"\n    Custom function to split the sequential data to \n    train and test sets for ML modeling.\n    \n    Args:\n        seq_data: list of np.arrays containing (feature, target)\n        test_frac: proportion of data to assign as test set\n    \"\"\"\n    test_size = math.ceil(len(seq_data)*test_frac)\n    \n    train_seq = seq_data[:-test_size]\n    print('==TRAIN==')\n    print_seq_split_stats(train_seq)\n    x_train = np.array(list(map(lambda seq: seq[0], train_seq)))\n    y_train = np.array(list(map(lambda seq: [seq[1]], train_seq)))\n    \n    print()\n    \n    test_seq = seq_data[-test_size:]\n    print('==TEST==')\n    print_seq_split_stats(test_seq)\n    x_test = np.array(list(map(lambda seq: seq[0], test_seq)))\n    y_test = np.array(list(map(lambda seq: [seq[1]], test_seq)))\n    \n    return x_train, y_train, x_test, y_test\n\ndef print_seq_split_stats(seq_data):\n    \"\"\"\n    Prints the amount of data in the sequence and the distribution\n    of the target split.\n    \"\"\"\n    total = len(seq_data)\n    buys = sum(map(lambda seq: seq[1], seq_data))\n    dont_buys = len(list(filter(lambda seq: seq[1]==0, seq_data)))\n    print(f'Total: {total}')\n    print(f'Buys: {buys} ({round(buys/total*100, 0)}%)')\n    print(f'Dont buys: {dont_buys} ({round(dont_buys/total*100, 0)}%)')\n\n# `crypto_df` consists of rows split by timestamp and crypto asset.\n# For this modeling, want the data to be split by timestamp only so that\n#   each row contains data for each crypto asset that can be used as\n#   `features` for the model.\ndf = crypto_df.head(400000).groupby(['timestamp', 'Symbol']).max().unstack()\ndf = df[['Volume', 'Close']]\ndf.columns = ['_'.join(col).strip() for col in df.columns.values]\ndf.sort_index(inplace=True)\n\n# Creating `target` column\ndf['Future_BTC'] = df['Close_BTC'].shift(-FUTURE_PERIOD_PREDICT)\ndf['Buy_BTC'] = df.apply(lambda row: classify(row.Close_BTC, row.Future_BTC), axis=1)\ndisplay(df[['Close_BTC', 'Future_BTC', 'Buy_BTC']])\n\n# Preprocessing raw data into ML ready inputs\nx_train, y_train, x_test, y_test = preprocess(df)","metadata":{"execution":{"iopub.status.busy":"2021-12-29T07:02:45.168317Z","iopub.execute_input":"2021-12-29T07:02:45.168585Z","iopub.status.idle":"2021-12-29T07:03:03.359933Z","shell.execute_reply.started":"2021-12-29T07:02:45.168556Z","shell.execute_reply":"2021-12-29T07:03:03.359053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Building RNN Model","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Dropout, LSTM, BatchNormalization\nfrom tensorflow.keras.callbacks import TensorBoard, ModelCheckpoint\n\n# Setting parameters\nEPOCHS = 10\nBATCH_SIZE = 64\nNAME = f'{SEQ_LEN}-SEQ-{FUTURE_PERIOD_PREDICT}-PRED-{int(time.time())}'\n\n# Building RNN Model\nmodel = Sequential()\nmodel.add(LSTM(128, input_shape=(x_train.shape[1:]), return_sequences=True))\nmodel.add(Dropout(0.2))\nmodel.add(BatchNormalization())\n\nmodel.add(LSTM(128, input_shape=(x_train.shape[1:]), return_sequences=True))\nmodel.add(Dropout(0.2))\nmodel.add(BatchNormalization())\n\nmodel.add(LSTM(128, input_shape=(x_train.shape[1:])))\nmodel.add(Dropout(0.2))\nmodel.add(BatchNormalization())\n          \nmodel.add(Dense(32, activation='relu'))\nmodel.add(Dropout(0.2))\n\nmodel.add(Dense(2, activation='softmax'))\n\n# Compiling RNN Model\nopt = tf.keras.optimizers.Adam(learning_rate=0.001, decay=1e-6)\nmodel.compile(\n    loss='sparse_categorical_crossentropy',\n    optimizer=opt,\n    metrics=['accuracy']\n)\n\n# Setting up tensorbaord\ntensorboard = TensorBoard(log_dir=f'logs/{NAME}')\n\nfilepath = 'RNN_Final-{epoch:02d}-{accuracy:.3f}'\ncheckpoint = ModelCheckpoint(\n    'models/{}.model'.format(\n        filepath, \n        monitor='val_acc', \n        verbose=1, \n        save_best_only=True, \n        mode='max')\n)\n\nhistory = model.fit(\n    x_train, \n    y_train,\n    batch_size=BATCH_SIZE,\n    epochs=EPOCHS,\n    validation_data=(x_test, y_test),\n    callbacks=[tensorboard, checkpoint]\n)","metadata":{"execution":{"iopub.status.busy":"2021-12-29T07:03:09.305172Z","iopub.execute_input":"2021-12-29T07:03:09.305480Z","iopub.status.idle":"2021-12-29T07:40:01.119410Z","shell.execute_reply.started":"2021-12-29T07:03:09.305446Z","shell.execute_reply":"2021-12-29T07:40:01.118208Z"},"trusted":true},"execution_count":null,"outputs":[]}]}