The article explores the application of Facebook Prophet, a machine learning model, for intraday cryptocurrency trading, specifically Bitcoin, and evaluates its performance through backtesting against baseline strategies.
Abstract
The author of the article has conducted an experiment to assess the viability of using Facebook Prophet for day trading Bitcoin. This time-series model is applied to hourly price data, a departure from the more common daily data frequency. The experiment involves retrieving intraday Bitcoin price data via the EOD HD financial API service, preprocessing the data, and then training and predicting price movements with Facebook Prophet. The author adjusts the model to accommodate hourly data, simulates predictions over time, and establishes trading positions based on the model's predictions. These positions are then compared to baseline strategies of buy and hold and random trading during the backtesting process. The results indicate that the Prophet model could yield a 16% return over approximately 300 hours, outperforming the baseline strategies. However, the author acknowledges the need for further testing with different parameters to ensure the robustness of the strategy before real-world implementation.
Opinions
The author has previously tested various machine learning strategies for crypto trading, including classification models, time-series models, and sentiment analysis, with mixed results.
The author believes that hourly data frequency provides more opportunities than daily data due to the 24/7 nature of the cryptocurrency market.
Facebook Prophet is praised for its ease of use and implementation in time-series forecasting.
The author suggests that the initial results of using Facebook Prophet for intraday trading are promising but cautions against overoptimism without extensive parameter testing.
The author encourages readers to code along with the provided functions and to support content like theirs by signing up for a Medium Membership.
The author discloses a financial interest in the EOD HD API service through an affiliate link.
The article concludes with the author's intention to continue refining the trading strategy and exploring different scenarios before considering its use in real-time trading.
What Happens if you Day Trade Crypto with Facebook Prophet?
Results of backtesting a Bitcoin intraday trading strategy using Facebook’s machine learning model
In the past, I’ve experimented with various forms of machine learning models in order to develop a successful cryptocurrency trading strategy. These various strategies would utilize classification models, time-series models, and sentiment analysis. The results of these strategies would range from mediocre to “good but requires more testing”. I’ve never settled on a specific strategy and hope to one day move on from the testing phase to “actually implementing it with real money” phase.
In all of these different strategies, I have yet to change the frequency of the historical data tested. The frequency of data I have always used and backtested with was the daily historical price data. That frequency for price data is not bad at all but it is limited, especially with the around the clock trading availability of the cryptocurrency market.
So in order to continue my testing, I’ll need to know what it is like to day trade crypto with machine learning. Specifically, I’ll be using a time series machine learning model that I have found to be easy to use and implement — Facebook Prophet. Feel free to code along with me or just continue reading to see the final outcome.
from eod import EodHistoricalData
import pandas as pd
from datetime import datetime, timedelta
import calendar
from tqdm import tqdm
import numpy as np
import random
import plotly.express as px
from prophet import Prophet
If you’re familiar with data science, then you may recognize many of these libraries.
Crypto Intraday Price Data
Next, I’ll have to grab the intraday price data for a selected cryptocurrency. To do so, I utilized a financial API service called EOD HD, which allows me to retrieve the historical price data of select cryptocurrencies at various time intervals. Disclosure: I earn a small commission from any purchases made through the link above.
# Importing and assigning the api keywithopen("../../eodHistoricalData-API.txt", "r") as f:
api_key = f.read()
# EOD Historical Data client
client = EodHistoricalData(api_key)
With my provided API key, I can now retrieve the hourly price data from assorted cryptocurrencies. For this project, I chose to focus on the most famous of crypto of them all — Bitcoin.
Function for Intraday Crypto Price Data
For this function, I’ll be grabbing the hourly price data. However, there are other options for shorter time intervals. At times during the API call, I found there to be a slight time delay. The API does have a separate package that provides live data but for the purpose of this project it was not necessary.
Next, I retrieved the hourly data from a specific timestamp dictated by the provided parameters within the function. After grabbing the data and performing some basic formatting and handling of missing data, I moved on to applying a moving average to the dataset. This is done in order to smooth out any outliers that may appear within the data. Lastly, the DataFrame is then renamed to be compatible with Facebook Prophet.
Training and Predicting with Facebook Prophet
I’ve written other articles detailing the process of training and predicting with Facebook Prophet on daily data. If you choose to learn more about that specific process then click here. However, in this project, I had to make adjustments to the function in order to compensate for the hourly data:
There are only two adjustments that needed to be made in order to handle intraday or hourly data. One was switching all the seasonality options to False. The other one was designating the frequency parameter to hourly with “H” within the .make_future_dataframe() function. Finally, this function returns the dates and predictions based on the provided DF.
Predictions Over Time
The next step would be to make predictions over a set period of time. To do so, I created a function that implements the train and predict function in a loop to simulate these predictions over time.
This function returns all the predictions made over the set period of time. For example, if the forecast_hours parameter is set to 5, then each date will have the predicted price of the fifth hour in the future. The function only trains on a moving window of the most recent hourly data in the ‘for’ loop, then makes a price forecast of 5 hours. I then shift these predictions back so that they will align with the “current” values. This is done to simulate what predictions could look like every hour if it were ran in realtime.
Now with the predictions and their respective “current” values set in a dataframe, I can move on to establishing the trading positions…
Trading Positions
The function above establishes the basic trading strategy used during the backtest. Here, if the “open” price of Bitcoin is less than the predicted lower bound price, then the returned position is a “Buy” or “1” and vice-versa. If it is neither then it basically means the position is a “0” or do nothing. These positions will make up the Prophet positions for the backtest.
Baseline Positions
The next thing I need to do is establish the baseline positions for the backtest. These positions will be used to compare to the Prophet positions in order to see if there are any significant improvements with using Prophet over them. These baseline trading strategy positions will be Buying and Holding and Random positions:
# Adding sentiment positions to the forecast DFpositions = pred_df
# Random positions
positions['random_positions'] = random.choices(
[1,0,-1], k=len(positions)
)
Performing the Backtest
With the positions ready to go, the final thing to do is to get the hour-by-hour returns. This is done by using the logarithmic returns on the ‘open’ prices:
With the log returns set, all I have left to do is multiply the returns with the positions DataFrame from before:
# The positions to backtest (shifted ahead by 1 to prevent lookahead bias)bt_positions = positions[[
'buy_hold',
'random_positions',
'fbp_positions'
]].shift(1)
# The returns during the backtest
returns = bt_positions.multiply(
log_returns['open'],
axis=0
)
# Inversing the log returns to get daily portfolio balance
performance = returns.cumsum().apply(
np.exp
).dropna().fillna(
method='ffill'
)
The backtest is complete! The results of which are stored within the performance variable. However, in order to quickly run different parameters for the backtest, I’ll need to consolidate all of these functions into one which will also include a visualization of the backtest after it has been completed.
Final Backtest Function
The above function consolidates the previous functions and will retrieve data, run predictions, establish trading positions, run the backtest, then return the final numbers and visualization.
Based on these results, it looks like Prophet would’ve performed very well in the previous 300 hours. It resulted in a return of 16% in about 300 hours! However, this could be a fluke and may require further testing of different parameters to really assess the robustness of this strategy.
With the final function I created, I can attempt other parameters such as increasing the backtesting length, increasing the training amount, or alter the moving average. Or even with the strategy itself, I can decide different positions based on the predictions. There are many different scenarios that should be tested before this strategy could be used in a real-time trade. But, as of now, the results are much more promising than I would have expected.