{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-01-24T13:03:20.590718Z","iopub.execute_input":"2022-01-24T13:03:20.591572Z","iopub.status.idle":"2022-01-24T13:03:20.628282Z","shell.execute_reply.started":"2022-01-24T13:03:20.591398Z","shell.execute_reply":"2022-01-24T13:03:20.627585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# INTRODUCTION\nLes objectifs de la compétition \"G-Research Crypto Forecasting\" étant très compliqués, nous avons simplifié les données \npour nous concentrer seulement sur 1 cryptomonnaie et pour la prédire sur 1 mois.\nLes résultats ne sont pas vraiment représentatifs du programme car nous l'entrainons sur \nseulement 1 mois de jeu de données.\nSi on l'entrainait sur 3 ans, nos prédictions seraient bien plus fiables mais cela prends \nbeaucoup trop de temps.\nNous nous sommes principalement aidées d'un tutoriel ainsi que d'autres codes présents \nsur la plateforme.","metadata":{}},{"cell_type":"markdown","source":"## Import des librairies","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom numpy import array\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\nfrom datetime import datetime, timedelta\nfrom sklearn.preprocessing import MinMaxScaler # pre-traitement des données\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Dropout, LSTM # LSTM = Long Short Term Memory","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:03:20.629562Z","iopub.execute_input":"2022-01-24T13:03:20.629741Z","iopub.status.idle":"2022-01-24T13:03:27.202307Z","shell.execute_reply.started":"2022-01-24T13:03:20.629719Z","shell.execute_reply":"2022-01-24T13:03:27.201599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# I. Pre-processing","metadata":{}},{"cell_type":"markdown","source":"## Import des données","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/g-research-crypto-forecasting/train.csv')\nassets = pd.read_csv('/kaggle/input/g-research-crypto-forecasting/asset_details.csv')","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:03:27.203915Z","iopub.execute_input":"2022-01-24T13:03:27.204319Z","iopub.status.idle":"2022-01-24T13:04:20.172591Z","shell.execute_reply.started":"2022-01-24T13:03:27.204279Z","shell.execute_reply":"2022-01-24T13:04:20.171895Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train.head(10), assets)","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:20.173705Z","iopub.execute_input":"2022-01-24T13:04:20.173993Z","iopub.status.idle":"2022-01-24T13:04:20.205222Z","shell.execute_reply.started":"2022-01-24T13:04:20.173954Z","shell.execute_reply":"2022-01-24T13:04:20.204511Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Processing des données","metadata":{}},{"cell_type":"code","source":"train[\"datetime\"] =  pd.to_datetime(train['timestamp'], unit='s')\n# fusionner les deux df\nprices = pd.merge(train, assets, how=\"outer\", on = [\"Asset_ID\"])\n\nprices.index = prices['timestamp']\nprices = prices.sort_index()\nind = prices.index.unique()","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:20.207163Z","iopub.execute_input":"2022-01-24T13:04:20.207529Z","iopub.status.idle":"2022-01-24T13:04:31.394858Z","shell.execute_reply.started":"2022-01-24T13:04:20.207488Z","shell.execute_reply":"2022-01-24T13:04:31.394096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"prices","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:31.396321Z","iopub.execute_input":"2022-01-24T13:04:31.396595Z","iopub.status.idle":"2022-01-24T13:04:31.420042Z","shell.execute_reply.started":"2022-01-24T13:04:31.396560Z","shell.execute_reply":"2022-01-24T13:04:31.419336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prédire les prix du Bitcoin","metadata":{}},{"cell_type":"code","source":"# selectionner seulement le bitcoin\nbtc = prices[prices[\"Asset_Name\"] == \"Bitcoin\"]\nbtc_price = btc[[\"datetime\", \"Close\"]]\n\n# afficher les prix sur l'ensemble du range de temps (i.e. 01-01-2018 au 21-09-2021)\nplt.figure(figsize=(12,8))\nplt.plot(btc_price[\"Close\"], color=\"green\", label=\"Bitcoin\")\nplt.legend()\nplt.title(\"Prix historiques du Bitcoin $\")\nplt.xlabel('Temps (min)')\nplt.ylabel(\"Price ($)\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:31.421222Z","iopub.execute_input":"2022-01-24T13:04:31.421415Z","iopub.status.idle":"2022-01-24T13:04:35.913783Z","shell.execute_reply.started":"2022-01-24T13:04:31.421392Z","shell.execute_reply":"2022-01-24T13:04:35.913031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"btc_price","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:35.914761Z","iopub.execute_input":"2022-01-24T13:04:35.914975Z","iopub.status.idle":"2022-01-24T13:04:35.927916Z","shell.execute_reply.started":"2022-01-24T13:04:35.914950Z","shell.execute_reply":"2022-01-24T13:04:35.927243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# II. Entrainer le modèle\n\nEntrainement sur le mois de janvier 2018 pour ensuite tester le modèle sur les prix du mois de février. Selectionner 1 mois (i.e. 44 640 minutes) pour entrainer le modèle. Début : 1er janvier 2018 à 00h00 / Fin : 31 janvier 2018 à 23h59","metadata":{}},{"cell_type":"markdown","source":"Nous utilisons par la suite la mémoire longue à court terme (LSTM) qui est un réseau de neurones complexe utilisé dans le domaine du Deep Learning.\nUne unité LSTM commune est composée d'une cellule, d'une porte d'entrée, d'une porte de sortie et d'une porte d'oubli. La cellule se souvient des valeurs sur des intervalles de temps arbitraires et les trois portes régulent le flux d'informations entrant et sortant de la cellule.\nLes réseaux LSTM sont bien adaptés à la classification, au traitement et à la réalisation de prédictions basées sur des données de séries chronologiques, car il peut y avoir des décalages de durée inconnue entre des événements importants dans une série chronologique.\n","metadata":{}},{"cell_type":"code","source":"# filtrer nos données sur 01-2018\ntest_from_date = pd.Timestamp(2018,1,1,0,0) \ntest_to_date = pd.Timestamp(2018,1,31,23,59)\nbtc_price_train = btc_price[btc_price.datetime <= test_to_date] \nprint(len(btc_price_train))\n\n# LSTM utilisant tangente hyperbolique (tanh) qui est une mesure sensible à la magnitude des données, nous devons les normaliser les prix en un vecteur compris entre -1 et 1\nscaler = MinMaxScaler(feature_range=(0,1))\nscaled_data = scaler.fit_transform(btc_price_train[\"Close\"].values.reshape(-1,1))\n\n# definir l'intervalle de temps à prédire : 15 minutes. \n# nous ne pouvons prédire qu'une seule minute dans dans le futur en se servant des 14 dernières minutes.\nprediction_min = 15","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:35.929393Z","iopub.execute_input":"2022-01-24T13:04:35.929883Z","iopub.status.idle":"2022-01-24T13:04:35.949492Z","shell.execute_reply.started":"2022-01-24T13:04:35.929846Z","shell.execute_reply":"2022-01-24T13:04:35.948643Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# comme requis pour les réseaux LSTM, nous devons transformer les données d'entrée en (n_échantillons, pas de temps, n_caractéristiques). \n# dans cet exemple, les n_caractéristiques sont 1 et le pas de temps de 14 (données des dernières minutes utilisées pour l'entraînement).\n\n# créer deux listes vides et les remplir avec les données d'entrainement formatées\nx_train  = [] # liste de données pour entrainer le modèle\ny_train = [] # liste de données que le modèle doit prédire pendant l'entrainement \nfor x in range(prediction_min, len(scaled_data)):\n    x_train.append(scaled_data[x-prediction_min:x, 0])\n    y_train.append(scaled_data[x, 0])\n        \nx_train, y_train = np.array(x_train), np.array(y_train) # tableau numpy\nx_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))\n\n# x_train est de dimension (44624, 15, 1). 44624 car nous regardons les 15 minutes passés (44639 - 15 = 44624).\n# y_train est seulement de dimension (44624,) car le modèle ne prédit qu'une seule valeur chaque minute.\nprint('trainX shape == {}.'.format(x_train.shape))\nprint('trainY shape == {}.'.format(y_train.shape))","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:35.950515Z","iopub.execute_input":"2022-01-24T13:04:35.950723Z","iopub.status.idle":"2022-01-24T13:04:36.019999Z","shell.execute_reply.started":"2022-01-24T13:04:35.950691Z","shell.execute_reply":"2022-01-24T13:04:36.019310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# créer le modèle puis l'entrainer\nmodel = Sequential()\nmodel.add(LSTM(units=50, return_sequences=True, input_shape=(x_train.shape[1], 1)))\nmodel.add(Dropout(0.2)) #Drop-out est utilisé pour contrôler le sur-ajustement pendant l'entraînement\nmodel.add(LSTM(units=50, return_sequences=True))\nmodel.add(Dropout(0.2))\nmodel.add(LSTM(units=50))\nmodel.add(Dropout(0.2))\nmodel.add(Dense(units=1)) # prediction du prix de la prochaine minute\nmodel.compile(optimizer='adam', loss='mean_squared_error')\n# fit le modèle\ntrained = model.fit(x_train, y_train, epochs=15, batch_size=32, validation_split=0.1, verbose=1)","metadata":{"execution":{"iopub.status.busy":"2022-01-24T13:04:36.022053Z","iopub.execute_input":"2022-01-24T13:04:36.022354Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(trained.history['loss'], label='Perte entrainement')\nplt.plot(trained.history['val_loss'], label='Perte validation')\nplt.legend()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# III. Verifier les performances du modèle\n\nPour cela, grâce au modèle, nous allons tester les prédictions sur le mois de fevrier 2018.","metadata":{}},{"cell_type":"code","source":"# filtrer nos données sur 02-2018\ntest_from_date = pd.Timestamp(2018,2,2,0,0) \ntest_to_date = pd.Timestamp(2018,2,28,23,59) \ntest_data = btc_price[(btc_price.datetime >= test_from_date) & (btc_price.datetime <= test_to_date)] \nactual_prices = test_data[\"Close\"].values\ntotal_dataset = pd.concat((btc_price_train[\"Close\"], test_data[\"Close\"]), axis=0)\n\nmodel_inputs = total_dataset[len(total_dataset) - len(test_data) - prediction_min:].values\nmodel_inputs = model_inputs.reshape(-1,1)\nmodel_inputs = scaler.transform(model_inputs)\n\nx_test = []\nfor x in range(prediction_min, len(model_inputs)):\n    x_test.append(model_inputs[x-prediction_min:x, 0])\n    \nx_test = np.array(x_test)\nx_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))\npredicted_prices = model.predict(x_test)\npredicted_prices = scaler.inverse_transform(predicted_prices)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12,8))\nplt.plot(actual_prices, color=\"black\", label=\"Actual bitcoin Price\")\nplt.plot(predicted_prices, color=\"green\", label=\"Predicted bitcoin Price\")\nplt.legend()\nplt.title(\"Bitcoin prediction\")\nplt.xlabel('Temps')\nplt.ylabel(\"Price\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"reel = actual_prices.copy()\npred = np.resize(predicted_prices, predicted_prices.shape[0])        \nnp.corrcoef(reel, pred)[0,1]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Le modèle semble fonctionner parfaitement sur l'ensemble de données d'entraînement et très bien sur l'ensemble de données de test (correlation de 99%). Cependant, nous devons souligner que le modèle prédit une seule observation à l'avance. Cela signifie que pour chaque nouvelle observation qu'il doit prédire, il prend en entrée les 14 minutes précédentes. Dans la vie réelle, cela est un peu inutile puisque nous voulons principalement prédire de nombreuses observations à l'avance.","metadata":{}},{"cell_type":"markdown","source":"# IV. Predisons réellement les 15 prochaines minutes","metadata":{}},{"cell_type":"code","source":"real_data = [model_inputs[len(model_inputs) - 100:len(model_inputs), 0]]\nreal_data = np.array(real_data)\nreal_data = np.reshape(real_data, (real_data.shape[0], real_data.shape[1], 1))\nprediction = model.predict(real_data)\nprediction = scaler.inverse_transform(prediction)\n\nx_input = real_data.copy()\ntemp_input = real_data.copy().reshape(1,-1)\ntemp_input = list(temp_input)\ntemp_input = temp_input[0].tolist()\ntemp_input\n\nlst_output =[]\nn_steps=100\ni=0\nnb_min_pred = 15\n\nwhile(i<nb_min_pred):\n    if(len(temp_input)>100):\n        x_input = np.array(temp_input[1:])\n        x_input = x_input.reshape(-1,1)\n        x_input = x_input.reshape((1, n_steps, 1))\n        yhat = model.predict(x_input, verbose=0)\n        temp_input.extend(yhat[0].tolist())\n        temp_input = temp_input[1:]\n        lst_output.extend(yhat.tolist())\n        i += 1\n    else:\n        x_input = x_input.reshape((1, n_steps, 1))\n        yhat = model.predict(x_input, verbose=0)\n        temp_input.extend(yhat[0].tolist())\n        lst_output.extend(yhat.tolist())\n        i+=1\nprint(\"Done\")\noutput = scaler.inverse_transform(lst_output)\noutput = np.resize(output, output.shape[0])  ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"day_new = np.arange(0, 100)\nday_pred = np.arange(85, 100)\nreel_data = test_data.Close[len(test_data.Close)-100:len(test_data.Close)]\n\nplt.figure(figsize=(12,8))\nplt.plot(day_new, reel_data, color=\"black\", label=\"Prix actuel Bitcoin\")\nplt.plot(day_pred, output, color=\"green\", label=\"Prix prédits Bitcoin\")\nplt.legend()\nplt.title(\"Bitcoin prediction $\")\nplt.xlabel('Temps (min)')\nplt.ylabel(\"Price ($)\")\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# V. Précision de la prédiction (correlation)","metadata":{}},{"cell_type":"code","source":"print(np.corrcoef(test_data.Close[len(test_data.Close)-15:len(test_data.Close)].values,output)[0,1])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Le modèle arrive à prédire les 15 prochaines minutes mais semble ne pas être robuste (correlation de 7% environ). Nous pourrions améliorer la précision du modèle en l'entrainant sur un jeu de données plus grand (ici qu'un seul mois), le fit plus longtemps (ici epochs=15), ou bien créer un modèle prenant en compte les volumes tradés ainsi que la corrélation entre les autres actifs.\n\nIl faut aussi prendre en compte le fait qu'il est très compliqué de prévoir les prix qui sont très volatiles. Il n'est donc pas surprenant d'avaoir une corrélation si faible.","metadata":{}}]}