Hello Dear Readers! Thank you for being part of my community! 🙂
Most people understand crypto arbitrage as the buying and selling of cryptocurrency on different exchanges or platforms in order to take advantage of price differences. This strategy has been lucrative however, now everybody knows about it and it is no longer viable. But, that is not the only way to create an arbitrage strategy within crypto.
We may try to employ an old arbitrage strategy widely used in stock trading. Specifically, looking to capture the spread between two assets, in the crypto case I think It makes sense to start with Bitcoin and Ethereum. But before we employ any strategy or build a software program to automate execution and create a trading bot, we have to do one of the most crucial parts of the work, which is strategy research and development! In this article, we will explore the idea of using the correlation between Bitcoin and Ethereum in combination with a simple technical indicator and explore if there are viable opportunities to screen for in the year 2023.
Before getting started, you’ll need the following :
Let’s load the packages we would need for the whole project.
import matplotlib.pyplot as plt
import yfinance as yf
import pandas as pd
import numpy as np
import pandas_ta
df = yf.download(tickers=['BTC-USD', 'ETH-USD'], start='2019-01-01', end='2023-01-01')['Adj Close']
df.columns = ['btc_close_price', 'eth_close_price']
df['btc_log_ret'] = (np.log(df['btc_close_price']) - np.log(df['btc_close_price'].shift(1)))
df['eth_log_ret'] = (np.log(df['eth_close_price']) - np.log(df['eth_close_price'].shift(1)))
df
Here we first download the prices of bitcoin and ethereum from yahoo finance and select the Adj Close
column. Then we rename the columns and calculate log returns.
df['roll_corr'] = df['btc_log_ret'].rolling(30).corr(df['eth_log_ret'])
df['roll_corr_chg'] = df['roll_corr'].diff()
df['btc_rsi'] = pandas_ta.rsi(close=df['btc_close_price'], length=20)
df['roll_corr'].plot(figsize=(16,4))
plt.title('30-Day Rolling Correlation Looks More Stable After Mid 2021')
plt.show()
After calculating the 30-day rolling correlation, there is an interesting observation, looks like the correlation is really high throughout the whole period, however, there are big drops here or there. But, the correlation is stabilizing after the middle of 2021.
My idea for a signal here is the following:
When we have those 3 conditions met we will generate a signal. Let’s do it!
3. Create An Entry Signal From The Available Information And Columns We Estimated.
df['entry_signal'] = df.apply(lambda x: 1 if (x['roll_corr']<.8) & (x['roll_corr_chg']>.05) & (x['btc_rsi']>45) else 0, axis=1)
signals_df = df[df['entry_signal']==1]
signals_df
After visualizing all the rows where we have a signal, we can see that they are quite spread. However, there is a small overlap between two specific dates(because we know we are going to use a 7-day look forwardholding period, which means that we need to have at least 7 days between each signal).
This overlap is between 17th May 2019 and 19th May 2019… hmm, what should be our approach here?
Let’s follow the SSOS (Superior State Of Mind) approach and just drop the observation for 19th May 2019. 😁
df['btc_fut_7d_ret'] = df['btc_close_price'].pct_change(7).shift(-7)
df['eth_fut_7d_ret'] = df['eth_close_price'].pct_change(7).shift(-7)
print(f"BTC 7day: {round((signals_df['btc_fut_7d_ret']+1).cumprod()[-1], 3)}")
print(f"ETH 7day: {round((signals_df['eth_fut_7d_ret']+1).cumprod()[-1], 3)}")
In this step, we are calculating the 7-day percent change and then we shift it backward with 7 days, this way we would have it on the same row we want it. Then we are calculating the cumulative return if on each signal we would have gone all-in in the coin and held it for the next 7 days. Looks like that strategy would have made good returns for either of the coins. However! BTC return is 96.7% and ETH return is 146.4%.
💥💥 Here, on the basis of the evidence so far, knowing that I may have a flawed assumption, I would conclude that: On the days of the signal we may construct an Arbitrage Strategy, where we Long Ethereum and Short Bitcoin with the same amount of cash! 💥💥
dates_list = (signals_df.index+pd.DateOffset(1)).astype(str).tolist()
dates_list.remove('2019-05-18')
dates_list
In this step, first we create a new dataframe signals_df
only for the signals. Then we Move the index of the dataframe with 1 day into the future, so we can use it to select start and end dates for the holding periods. Because the signal would be available to us at the end of a given day, however, the first return would be generated at the end of the next day. Then we create the list of signal dates and remove the overlapping index.
portfolio_df = pd.DataFrame()
for start_date in dates_list:
end_date = (pd.to_datetime(start_date)+pd.DateOffset(7)).strftime('%Y-%m-%d')
temp_df = df[start_date:end_date][['btc_log_ret', 'eth_log_ret']]
portfolio_df = pd.concat([portfolio_df, temp_df])
portfolio_df['portfolio_return'] = portfolio_df['btc_log_ret']*-1 + portfolio_df['eth_log_ret']
df = df.merge(portfolio_df['portfolio_return'], right_index=True, left_index=True, how='left')
df['portfolio_return'].plot(figsize=(16,4))
plt.title('Portfolio Returns When Have Been Invested', fontsize=20)
plt.show()
In this step, we first create an empty dataframe, then for each date in our signal dates list, we estimate the end date to be exactly 7 days in the future. Then, we select from the dataframe we have df
the exact period of the position between the start_date
and end_date
, the we select both log return columns for bitcoin and ethereum. After we have selected that data in the temp_df
we just concat it into the portfolio_df
. After we have the data selected and sliced and concated together we can move on and calculate the daily portfolio return, which would be bitcoin return multiplied by -1 and ethereum return as it is. (Because we are short bitcoin and long ethereum). Finally, we just merge portfolio_df
to our df
with a left join, with the goal to have only values where we have a return and elsewhere NaN
, so only the 7 holding days after the signal.
np.exp(np.log1p(df['portfolio_return']).cumsum()).plot(figsize=(16,4))
plt.title('Cumulative Return Of Arbitrage Strategy Portfolio', fontsize=20)
plt.show()
In this article, we explored how to re-create and backtest an arbitrage trading strategy based on the Rolling Correlation between Bitcoin and Ethereum.
Our analysis showed that the portfolio of shorting Bitcoin and longing Ethereum with an equal amount of cash yields positive and significant returns over the span of the given study. This suggests that it may be worth considering further research on the potential investment strategy based on this analysis. However, an apparent issue is that the Rolling Correlation itself is stabilizing after the middle of 2021 and the conditions have not been met over the whole of 2022. This may suggest that the strategy is currently out of fashion, however, it may return back in 2023, so keep we may want to keep an eye on it!