I recently read the book Coffee can investing by CIO of Marcellus. I highly recommend this book if you want a simple formula on how to identify and invest in stocks which give consistent 20% CAGR. But in my opinion and my personal experience identifying and buying the stock is just half the battle, sticking with it through thick and thin is much more difficult and I must admit I’m still learning on this front. Patience in investing pays off.
Let us see if we can back our hypothesis with data.
(Even the book talks about patience, there is a separate chapter for it. Below analysis is inspired by that chapter)
from datetime import date, datetime
import calendar
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns; sns.set_theme()
import jugaad_data.nse as nse
Download NIFTY historical data using jugaad-data.
nifty = nse.index_df(symbol="NIFTY 50", from_date=date(1990, 8,1), to_date=datetime.now().date())
As you can see the data received is newest first, we need to sort oldest first and reset the indices.
nifty.sort_values('HistoricalDate', inplace=True)
nifty.reset_index(drop=True, inplace=True)
print(nifty.head())
Index Name INDEX_NAME HistoricalDate OPEN HIGH LOW CLOSE
0 Nifty 50 Nifty 50 1990-08-03 NaN NaN NaN 337.29
1 Nifty 50 Nifty 50 1990-08-07 NaN NaN NaN 331.45
2 Nifty 50 Nifty 50 1990-08-08 NaN NaN NaN 322.11
3 Nifty 50 Nifty 50 1990-08-09 NaN NaN NaN 325.69
4 Nifty 50 Nifty 50 1990-08-10 NaN NaN NaN 333.35
nifty.set_index('HistoricalDate', inplace=True)
Note on how we calculate returns to keep things simple
We will calculate returns on weekly rolling basis
We will use 1527 one year periods from Aug 1990 till today. eg. 5 Aug 1990 to 4 Aug 1991, 12 Aug 1990 to 11 Aug 1991 so on and so forth
An year consists of 52 trading weeks
An year will have approximately 52 weeks, This will create some gaps over longer period of times but it does not matter for our purpose.
We will also calcuate CAGR and not the absolute returns
t is the holding period here
# Sampling NIFTY on every first day of the week
nifty_sampled = nifty.resample("W").first()
holding_period = [1, 5, 10, 15]
for i in holding_period:
nifty_sampled['{}Y CAGR'.format(i)] = (np.power(nifty_sampled['CLOSE']/nifty_sampled['CLOSE'].shift(52*i), 1/i) - 1)*100
Now let’s look at the returns over various holding periods
nifty_returns = nifty_sampled[['{}Y CAGR'.format(i) for i in holding_period]]
nifty_returns.mean().plot(grid=True, title="Mean CAGR for different holding periods")
print(nifty_returns.mean())
1Y CAGR 16.250648
5Y CAGR 11.146642
10Y CAGR 11.377821
15Y CAGR 12.665201
dtype: float64
Mean CAGR values in isolation are misleading
ax = nifty_returns.boxplot(grid=True)
fig = ax.get_figure()
fig.suptitle("Boxplot for CARG over different holdig periods")
Text(0.5, 0.98, 'Boxplot for CARG over different holdig periods')
Range of returns over 1 year period is wild
(Minimum and maximum returns might change slightly from the book over longer periods because of our simple approach of taking 52 weeks returns)
std_data = nifty_returns.std()
std_data.plot.bar(title="Standard deviation of CAGR over different holding periods")
print(std_data)
1Y CAGR 33.204075
5Y CAGR 9.481012
10Y CAGR 4.858627
15Y CAGR 2.231804
dtype: float64
In world of investment standard deviation of returns is the key indicator of risk. As you can see the standard deviation reduces drastically with holding period.