Stopping when Robert Shiller tells you to
Nov 08 2022
“If these trends continue… aaay!”
– Disco Stu
Obligatory disclaimer: This is not financial advice. The information presented here does not constitute any form of advice or recommendation on my part. Seek independent financial advice on your own.
Anyways
Let’s say you are investing in an index fund tracking the S&P 500.
Each month you invest some amount, let’s say $USD 500: you buy as many S&P 500 units as this can afford you.
You do this no matter what the price is. This simple strategy is called cost averaging and it follows this idea that you can’t time the market. What is more important is
“time in the market”, not “timing the market”, the saying goes.
But, what if you start thinking that maybe things are getting a bit dumb. Maybe
you see some 49 year old CEO posting memes
or maybe you hear people comparing the stock market and astrology unironically (I love her, by the way).
Instead of following the cost averaging strategy, every month you could ask “Is
the market expensive at the moment?” and then, if it is, save that money for a
while, until the market gets reasonable again. We will start with some data.
S&P 500 Data
We will be looking at the historic value of the S&P 500, which is a stock market
index that tracks the performance of 500 large companies listed on stock
exchanges in the United States. It is a good proxy for the US stock market, and
several investment services offer ETFs that track it.
Here’s a logarithmic graph showing its value from 1920 to 2021:
In gray there are some recessions/interesting events.
We will first evaluate cost-averaging. For this, we will run some simulations.
If you are not interested in looking at the code you can jump here.
Simulating Cost Averaging
Let’s simulate the simple cost averaging strategy.
We will write a function that applies this strategy to a period of time,
which should contain monthly S&P Value data. The next function returns two
things:
- the total number of acquired units by the end of the investment horizon
- the total amount of deposits made in exchange for those units
deposit_per_month = 500
def cost_average_monthly_strategy(monthly_data):
total_acquired_units = (deposit_per_month / monthly_data["Value"]).sum()
total_deposits = deposit_per_month * len(monthly_data)
return total_acquired_units, total_deposits
Here deposit_per_month / monthly_data["Value"]
is a vector, indicating the number of S&P units
acquired each month. The total sum would represent the number of units acquired by the end of the
investment horizon.
Now, we could iteratively apply this function starting at different months to evaluate the final
annualized rate of return. We will parametrize this by different arguments:
- the total number of investment years: that’s how long we will be acquiring
S&P units. At the end of this period we will evaluate our performance.
- an initial date at which we start doing this.
- the S&P historic data, including the unit value: This is a 2 dimensional array
, mapping months to S&P values.
- finally, some investment strategy: initially we will be using cost averaging.
"""
Given an strategy and some data simulates an investing for
a time horizon of `inv_years` and an initial date of `date_start`.
Returns:
- the annualized rate of return
- the investment end date (the date at which the units are redeemed)
"""
def simulate_investing_for_period(inv_years, date_start, data, investment_strategy):
inv_end_date = date_start + relativedelta(years=inv_years)
investment_range = data[(date_start <= data["Date"]) & (data["Date"] <= inv_end_date)]
total_units, total_deposits = investment_strategy(investment_range)
final_units_value = total_units * investment_range["Value"].iat[-1]
inv_return = ( final_units_value / total_deposits ) ** (1 / inv_years) - 1
return inv_return, inv_end_date
"""
Simulates an investing given an horizon of `inv_years`,
an `investment_strategy` function (e.g. `cost_average_monthly_strategy`),
and some `data` used to calculate the dates and passed to the strategy.
Returns an array with `Return` and `RedemptionDate` columns, which indicates
the annualized rate of return of the strategy when the redeeming the units at
that date.
"""
def simulate_investment(inv_years, investment_strategy, data):
up_to_date = max(data["Date"]) - relativedelta(years=inv_years)
inv_dates = data[data["Date"] <= up_to_date]["Date"]
inv_sim = inv_dates.map(lambda date_start:
simulate_investing_for_period(
inv_years,
date_start,
data,
investment_strategy
)
).tolist()
inv_sim = pd.DataFrame(inv_sim, columns=['Return','RedemptionDate'])
return inv_sim
And that’s a good start. With simulate_investment
we can simulate an investment of
x
number of years, using some investment strategy and based on some historic data.
This parametrization will be useful when comparing different strategies and time horizons.
Cost Averaging Evaluated
Let’s see the annualized return of Cost averaging $500 USD per month with different time horizons.
These graphs show the annualized investment return of cost averaging the S&P 500 for the last
x
years, from 1920 to February 2021:
These graphs don’t show anything overly interesting. A few things to note:
10 year investment horizons can show high returns (up to %10) but also some years present loses. Instead, with longer investements the maximum return doesn’t reach 10% but they are consistently positive.
Related to the previous point: the longer the investment horizon, the likelier it is to get a higher return. For example comparing the 10 years horizon returns versus the 20 years, during the 2010s decade: 10 years returns between 2% and 4%, while the 20 years returns sit more consistently at around 4%.
CAPE and Excess CAPE Yield
Let’s come back to our original question. Instead of following that simple strategy, would it be worth
considering if the market is expensive?
Fortunately, a smart guy came up with a way to tell if a market is overpriced or not. This is
Robert Schiller’s Cyclically Adjusted Price to Earnings Ratio, CAPE for short.
It’s the Price to Earnings ratio, adjusted to business cycles (averaged over the last 10 years) and taking into
consideration inflation.
You can read a better description of the CAPE ratio in this article.
Here’s a summary:
The way it works is that you take the average of the last ten years of earnings, adjust them for inflation, and divide the current index price by that adjusted earnings. This makes it so that the current price is divided by the average earnings over the latest business cycle rather than just one recent year of bad or good earnings.
You can even download a monthly updated version of Shiller’s CAPE ratio from his own website.
Let’s play with that data using Python, pandas, numpy, and plotly.
Here’s a graph showing the S&P value, the CAPE ratio:
Some things to note:
Just before two stock market crashes the CAPE ratio reached very high levels. This was true for the
1929 stock market crash and for the DotCom bubble aftermath, but not for the Global Financial Crisis.
I suppose that the Global Financial Crisis, which was caused by a housing market bubble, didn’t raise
the CAPE ratio. Once the crisis consequences leaked into the stock market, you can see the CAPE ratio
plummet, but there was no previous peak. In essence, the CAPE ratio was not a good predictor for the
Global Financial Crisis, but it was for the DotCom bubble or the 1929 stock market crash.
Another thing to note, is that, as of February 2021 the CAPE ratio sits at around 34
, a bit higher
than the peak of the Great Depression.
Identifying when the market is overvalued
Our definition of an overvalued market is going to be a simple one: when the
CAPE ratio is above the 90% historic percentile.
When would have we considered the market overvalued in the past? Let’s plot this:
Let’s define a strategy that takes advantage of the CAPE ratio. If it’s above
the 95%th percentile we don’t acquire units during that period of time and
instead save it. Once the CAPE ratio falls below the 90%th percentile we use the
saved money to buy units again.
Note: I’m using the yearly data here, because doing it monthly was very slow.
Anyways, let’s get to the results. Here we compare the 2 strategies with
different investment horizons:
This seems to suggest that this kind of strategy might be worth pursuing if you
investment horizon is very short, but not much if it’s longer. This whole
analysis is not very scientific or rigurous, but it seems to confirm some of the
usual investment advice you hear.