What Does the P/E Ratio Tell You About the Market’s Growth Expectations?

by Franklin J. Parker, CFA

I am a big fan of extracting market expectations on various questions. It can give a starting point for your own analysis, giving a sort of “benchmark of knowledge” on a question. Here is what the market thinks, do I agree with that? If I do not agree, why not? Do I have more evidence/insight than the market or should I just adopt that view?

One use-case for this idea is extracting the market’s implied earnings growth rate from a company’s price to earnings ratio. While it isn’t obvious at first, a P/E ratio does carry some information about expected earnings growth. Here’s how.

The Concept

Since equity holders are owners of a company, they are entitled to its profits. Therefore, we can think of current price simply as the sum of all future cashflows discounted back to today:

$$ P = \sum_i^T \frac{E_i(1+g)^i}{(1+d)^i} $$

If we adjust today’s earnings, E, back to $1, that reduces the “price” side of our equation down to the price-to-earnings ratio, so, really

$$ P/E = \sum_i^T \frac{(1+g)^i}{(1+d)^i} $$

where g is the growth rate of earnings, d is the discount rate, and T is the number of years we care about.

What is great about this way of thinking is that we have all of the variables except one: the growth rate of earnings. We know P/E, we know the discount rate (usually it is the yield on the treasury maturing in the year we are discounting for). However, because of the dicounting summation, there is not a closed form solution (at least, I haven’t taken the time to derive it). Which means, we need to use a numerical method to solve for the g that will make the formula match our observed P/E.

Enter python.

Analysis: How Markets Infer a Growth Rate in P/E

After loading the requisite libraries, we will define the equation above as a function in python:

import numpy as np
import pandas as pd # not used, but I always import
from scipy.optimize import minimize
import matplotlib.pyplot as plt

# Define Price Function
#  Inputs the Growth Rate expected, the discount rate, and the time horizon
#  Outputs the expected Price/Earnings ratio
def price_function( growth_rate, discount_rate, time ):
    cashflows = [] # Prime the cashflows variable
    for t in range(1, time+1): # Build discounted cashflows over the time interval
        cashflows.append( (1 + growth_rate)**t / (1 + discount_rate)**t )
    return sum( cashflows )

This is helpful, but it is not a function that we can use our minimizer for, so using this function, let’s build a function that we can minimize:

# Create a function that we can minimize
def solver_function( growth_rate ):
    return ( PE - price_function( growth_rate[0], discount_rate, time ) )**2

Now, we input our known variables. I’ve solved for two time horizons, 10-years to breakeven and 15-years to breakeven, both of which are nested in the for loop:

# Input the known variables
discount_rate = 0.045 # 10-year UST yield. Could use different yields for different time horizons.
time = 15 # initialize the variable
PE = 20 # initialize the variable

# Now we iterate through various P/E ratios to determine what growth rate the 
# market expects
pe = list( range(1,250+1) )
g_15 = []
g_10 = []
for i in range(0, len(pe)):
    PE = pe[i]
    time = 15
    g_15.append( minimize( solver_function, 0.09 ).x[0] )
    time = 10
    g_10.append( minimize( solver_function, 0.09 ).x[0] )

Which gives us two arrays of implied growth rates — one for a 15-year breakeven (g_15), and one for a 10-year breakeven (g_10). Finally, let’s illustrate our results:

# Create visualization
plt.plot( pe, g_15, label = '15-Year Breakeven', color = 'blue' )
plt.plot( pe, g_10, label = '10-Year Breakeven', color = 'black' )
plt.title('Market-Implied Earnings Growth Rate')
plt.xlabel('P/E Ratio')
plt.legend()
plt.grid(True, linestyle = ':', color = 'gray')

As our plot demonstrates, as P/E ratios get above, say, 50 or so, the growth rate required to just break even from earnings gets to be a stretch. At a P/E of 50, we can infer that the market expects a growth rate of 19.4% every year for 15 years, or a growth rate of 34% every year for 10 years! At a P/E of 200, the implied growth rate is 60% for 10 years.

Analysis: How the Discount Rate Affects P/E

We’ve seen how we can derive an implied earnings growth rate from P/E, but what about the discount rate? How does that affect implied growth rates? Again, let’s continue with the code above to find out.

# Analysis of the discount rate
dr = [0.025, 0.045, 0.065] # Test various levels of discount rate
# Create an empty array with pe in rows and discount rates in columns
g_15 = np.empty((len(pe), len(dr)), dtype = float) 
g_10 = np.empty((len(pe), len(dr)), dtype = float)

# Iterate through different discount rates
for j in range(0, len(dr)):
    discount_rate = dr[j]
    # Iterate through pe
    for i in range(0, len(pe)):
        PE = pe[i]
        time = 15
        g_15[i,j] = minimize( solver_function, 0.09 ).x[0]
        time = 10
        g_10[i,j] = minimize( solver_function, 0.09 ).x[0]

And, if we then visualize the results, we find something to talk about.

What we can begin to see in this plot is that as discount rates move higher, growth rates must also move higher to support the same levels of P/E. We can say the same thing a different way: if discount rates move higher, P/E ratios will move lower assuming earnings growth expectations stay the same.

This, by the way, is why bond markets matter to stock markets! US Treasuries are typically the discount rate applied to earnings in coming years. As US Treasury yields rise, stock markets will tend to fall because the discount rate increases. In fact, let’s look at the problem from that perspective:

# Analyze how discount rates affect P/E ratios
dr = np.linspace(0.001, 0.100, num = 100) # Various discount rates
gr = [0.10, 0.15, 0.20, 0.25] # Various Growth rates
pe = np.empty( (len(dr), len(gr)) ) # Empty array with rows as dr and columns as gr

for g in range(0, len(gr)):
    for d in range(0, len(dr)):
        pe[d, g] = price_function( gr[g], dr[d], 10 )

Visualizing the results:

And here we see that P/E rates move downward in a subexponential way as discount rates increase, and vice versa — assuming that expected earnings growth remains the same (which is often not the case).

P/E Ratios are Speaking to You

What is important about this idea, and slicing it in different ways, is that P/E ratios are telling you something about what the market expects. From there, we can ask ourselves, is this reasonable? Do I agree with this expectation?

Isolating each angle can help us understand the magnitude of the affect of a particular variable. For example, moving from a P/E ratio of 200 to 150 only implies a decrease from 60% growth to about 55% growth. However, moving from 200x earnings to 150x earnings means a price drop of 25%. This is an exponential affect: a lowering of growth by 8% means a lowering of price by 25%.

For more extreme scenarios, we might seriously question whether the market is overestimating (or understimating) what might happen. I’m not aware of a company that averaged 60% compounding earnings growth over a 10-year period, but that is what a P/E ratio of 200 implies! And, as we just mentioned, there is a lot of price risk in a small adjustment of expectations.

Of course, there are other things going on, too. Movement in discount rates implies a change in P/E ratios, all else equal. Of course, all else is rarely equal, so think about all the angles.

All that said, I’ve included the full code below so you can play with your own expectations and see how their changes might affect price multiples in the company you care about.

But, P/E ratios are speaking to you. You can hear them if you know how to listen.

Full Code

import numpy as np
import pandas as pd
from scipy.optimize import minimize
import matplotlib.pyplot as plt

# Define Price Function
#  Inputs the Growth Rate expected, the discount rate, and the time horizon
#  Outputs the expected Price/Earnings ratio
def price_function( growth_rate, discount_rate, time ):
    cashflows = [] # Prime the cashflows variable
    for t in range(1, time+1): # Build discounted cashflows over the time interval
        cashflows.append( (1 + growth_rate)**t / (1 + discount_rate)**t )
    return sum( cashflows )

# Create a function that we can minimize
def solver_function( growth_rate ):
    return ( PE - price_function( growth_rate[0], discount_rate, time ) )**2

# Input the known variables
discount_rate = 0.045
time = 15
PE = 20

# Now we iterate through various P/E ratios to determine what growth rate the 
# market expects
pe = list( range(1,250+1) )
g_15 = []
g_10 = []
for i in range(0, len(pe)):
    PE = pe[i]
    time = 15
    g_15.append( minimize( solver_function, 0.09 ).x[0] )
    time = 10
    g_10.append( minimize( solver_function, 0.09 ).x[0] )
 
# Create visualization

# Analysis of the discount rate
dr = [0.025, 0.045, 0.065] # Test various levels of discount rate
# Create an empty array with pe in rows and discount rates in columns
g_15 = np.empty((len(pe), len(dr)), dtype = float) 
g_10 = np.empty((len(pe), len(dr)), dtype = float)

# Iterate through different discount rates
for j in range(0, len(dr)):
    discount_rate = dr[j]
    # Iterate through pe
    for i in range(0, len(pe)):
        PE = pe[i]
        time = 15
        g_15[i,j] = minimize( solver_function, 0.09 ).x[0]
        time = 10
        g_10[i,j] = minimize( solver_function, 0.09 ).x[0]

# Create Visualization

# Analyze how discount rates affect P/E ratios
dr = np.linspace(0.001, 0.100, num = 100) # Various discount rates
gr = [0.10, 0.15, 0.20, 0.25] # Various Growth rates
pe = np.empty( (len(dr), len(gr)) ) # Empty array with rows as dr and columns as gr

for g in range(0, len(gr)):
    for d in range(0, len(dr)):
        pe[d, g] = price_function( gr[g], dr[d], 10 )

# Create Visualization

This document is a general communication being provided for informational purposes only. It is educational in nature and not designed to be taken as advice or a recommendation for any specific investment product, strategy, plan feature or other purpose in any jurisdiction, nor is it a commitment from Directional Advisors to participate in any of the transactions mentioned herein. Any examples used are generic, hypothetical and for illustration purposes only. This material does not contain sufficient information to support an investment decision and it should not be relied upon by you in evaluating the merits of investing in any securities or products. In addition, users should make an independent assessment of the legal, regulatory, tax, credit, and accounting implications and determine, together with their own financial professionals, if any investment mentioned herein is believed to be appropriate to their personal goals. Investors should ensure that they obtain all available relevant information before making any investment. Any forecasts, figures, opinions or investment techniques and strategies set out are for information purposes only, based on certain assumptions and current market conditions and are subject to change without prior notice. All information presented herein is considered to be accurate at the time of production, but no warranty of accuracy is given and no liability in respect of any error or omission is accepted. It should be noted that investment involves risks, the value of investments and the income from them may fluctuate in accordance with market conditions and taxation agreements and investors may not get back the full amount invested. Both past performance and yields are not reliable indicators of current and future results.

Discover more from

Subscribe now to keep reading and get access to the full archive.

Continue reading

Exit mobile version
%%footer%%