When comparing investable assets people often use the compound annual growth rate (CAGR) as a key metric. I was curious so I wanted to know what CAGR a HODLer could expect at various points over the last decade or so and how that changed depending on how long they held it.
I put together a basic python script in a jupyter notebook to show the rolling CAGR for someone who held for various lengths of time. Each point on the graph represents the CAGR on that date for the prior time period. For example on the 4 year chart each point shows the CAGR for the prior 4 year period ending on that date.
Underneath each chart you'll see what percentage of time the CAGR was above and below 25%. Think of this as "pick any N-year period to HODL and the odds that your purchasing power compounded faster than 25% is this."
In case I messed up the calculations, I provided code to generate it yourself at the bottom of this post. I used weekly aggregates to make things simple but you could use arbitrary aggregates.
Bitcoin CAGR
You can see that for any 4 year period the CAGR was greater than 25%.
Comparison to S&P 500
Below is the same 4 year chart for SPY. You can see that at no point did the S&P 500 compound faster than 25%.
Code
Python isn't my native language so please forgive me.
import numpy as np import matplotlib.pyplot as plt def calculate_cagr(start_value, end_value, periods_in_years): return (end_value / start_value) ** (1 / periods_in_years) - 1 def plot_rolling_cagr(num_years, df): """ Plots the rolling CAGR of an asset over a specified number of years. The dataframe should contain a 'vwap' column with the asset's value. This assumes that the data is weekly. Adjust the 'points_per_period' variable if the data is not weekly. """ points_per_period = num_years * 52 # Adjust based on your data's frequency rolling_cagr = [] for i in range(len(df) - points_per_period): start_value = df['vwap'].iloc[i] end_value = df['vwap'].iloc[i + points_per_period] cagr = calculate_cagr(start_value, end_value, num_years) # num years rolling_cagr.append(cagr * 100) # convert to percentage df['rolling_cagr'] = np.nan start_index = df.index[points_per_period] end_index = df.index[len(rolling_cagr) + points_per_period - 1] df.loc[start_index:end_index, 'rolling_cagr'] = rolling_cagr plt.figure(figsize=(14, 7)) plt.plot(df.index, df['rolling_cagr'], label=f"{num_years}-Year Rolling CAGR", color='red', linewidth=1, alpha=0.7) plt.axhline(y=0, color='black', linestyle='-', linewidth=0.8) plt.axhline(y=25, color='blue', linestyle='--', linewidth=0.8) plt.title(f"{num_years}-Year Rolling CAGR") plt.xlabel('Date') plt.ylabel('CAGR (%)') plt.legend() plt.show()