How to Calculate the Intrinsic Value of a Stock
By Sicong Zhao on The Capital
We have already introduced the definition of intrinsic value in the first tutorial. In summary, the intrinsic value consists of 2 parts:
- Total Dividends in 10 years
- Book Value of Equity Per Share in 10 years
Starting with this definition, we have 2 learning objectives here:
- Calculate intrinsic value in 2 ways
- Discuss the downside and remedy for these 2 methods
Calculate intrinsic value in a simple way
The key to calculating the intrinsic value of a stock is to predict its next 10 year’s performance in terms of dividends and book value change. Let’s firstly use the method Preston Pysh used in his video. It’s under 2 assumptions :
- Next 10-year’s dividends tend to stay the same with previous 10-year’s
- The book value tends to grow at a constant rate, which can be calculated by using the book value of 10 years ago and the current book value.
Let’s load the reformatted financial data (download here) from the previous tutorial, and start the calculation. I will keep using Apple’s stock for demonstration.
(1) Dividends
Under our 1st assumption, the dividends for the next 10 year is constant, and can be calculated by averaging last 10-year’s dividends. We can simply use .mean() function to estimate future dividends.
dvd_avg = aapl['dividend_rate'].mean()
print(dvd_avg)# Output:
# 2.1222222222222222
So, now we can expect Apple stock will give investors $2.12 each year. However, as mentioned in the tutorial by Preston Pysh, $2.12 in 10 years is not equivalent to $2.12 in today. Because we have the national bond which can be considered ‘risk-free’. So, we take the rate of the 10-year national bond and use it as a discount rate, to calculate how much does future money worth today. As indicated in this website, the interest rate for the 10-year national bond is 1.79% today. (The rate is fluctuating frequently, so you might want to check it before calculation.) Now, let’s calculate how much dose 3-year later’s $2.12 worth today.
risk_free_rate = 0.0179
dvd_avg/(1+risk_free_rate)**3# Output:
# 2.0122202476874578
The result means, if we use $2.01 to purchase the 10-year national bond today, it will increase to $2.12 in 3 years due to the interest.
We can repeat this calculation for the next 10 years, but an easier way is to use a for-loop.
dvd_total = 0for i in range(1,11):
dvd_total = dvd_total + dvd_avg/(1+risk_free_rate)**iprint(dvd_total)# Output:
# 19.27420500514451
So, we have an estimation for the next 10-year’s dividends from Apple, it’s 19.27.
Before we go to the next section, you might want to pay attention to these two things. First, range(1,11) function gives the value from 1 to 10, 11 is not included. Second, you might have noticed there are NaN values in our data set, which indicates either missing data or Apple did not issue a dividend in that year. In our computation, Pandas automatically dropped these values. But you should know about the situation, we will come back to this issue in the next method.
Now, let’s move on to book value.
(2) Book Value per Share
As you have noticed, the last value of the date column is ‘TTM’, which means ‘trailing 12 months’, further explanation is here. For convenience, I am going to ignore this row for now and will talk about ways to utilize it later.
Under our second assumption, we can calculate the simplified growth rate of the last 10 years in this way:
book_value = aapl['book_value_per_share']
growth_rate_of_9y= book_value['2019-09']/book_value['2010-09']avg_growth_rate = growth_rate_of_9y**(1/9)print(avg_growth_rate)# Output:
# 1.126189582316940
Here we first calculated the growth rate from 2010 to 2019 by division and then calculated its 9th root to get average growth rate. Attention here, although we have 10-year data, the interval from 2010 to 2019 is 9 years. That’s why we use the 9th root rather than the 10th root.
Then, we can predict future book value by:
bvps_pred = book_value['2019-09']*(growth_rate**10)
print(bvps_pred)# Output:
# 63.26497986577179
(3) The Final Step
So far, we have the 2 components of intrinsic value. By simply adding them up, we can get the intrinsic value of Apple’s stock.
aapl_intrinsic = bvps_pred + dvd_total
print(aapl_intrinsic)# Output:
# 82.5391848709163
Compared with the current price ($312.91), this is way to low. It could be either people for whatever reason overrate Apple at this point, or we missed something in our calculation.
(4) Limitations
Let’s first talk about the downside of the first calculation.
Firstly let’s look at Apple’s dividends in the last 10 years. Pandas is so convenient that you can simply use .plot() function to visualize the data.
aapl['dividend_rate'].plot()
And it gives us this plot:
As you can see, there is a significant upward trend, but we ignore this trend by taking the average, which would lead to an undervalued estimation for future dividends.
Additionally, let’s look at Apple’s book value. We can visualize it in the same way, and it gives the following plot.
The blue line represents the real data, the red line is straight only cross the starting point and endpoint. In the first method, we viewed the book value change as the red line. The downside of this method is that it ignored all the information in the middle.
As a cool data scientist, you would like to try your best to extract any information from your data, and dropping information for nothing is not cool.
A Better Approach — Linear Regression
Linear regression is an approach that enables us to utilize all the information from our existing data point. Fig.3 shows the basic idea of linear regression. The blue dot represents our data, the red line represents our assumption about the data. The purpose of linear regression is to figure out the best assumption (red line) that fits the data to the largest degree.
I am not going to dive into detail since that is not the purpose of this tutorial. But if you are interested, there is an awesome tutorial by StatQuest. But I wish it makes sense to you that linear regression seems to be a better approach for predicting future dividends and book value.
In Python, linear regression really is 3 lines of code, you just need to format the data as the linear regression function requires, and then feed the data into the function. If you do not have prior experience in linear regression, I recommend you to look at this hands-on tutorial from Towards Data Science before going to next section.
Calculate intrinsic value in a better way
Let’s use linear regression to predict future dividends and book value, see if the result differs.
(1) House Keeping
Before start coding, there are some house keeping work.
Firstly, let’s generate a new column ‘year_order’, which basically assigns [1,2,…,10] to our index “2010–09”, “2011–09”… Because the time sting would puzzle linear model. As long as we keep them in the right order, we will be in good shape.
aapl['year_index'] = range(1, len(aapl) + 1)
This is the way to add a new column in Pandas dataframe. If you use .head() function to observe the data, you can see the new column now.
Secondly, let’s add an ‘intercept’ column to free our regression line from having to cross the origin. (If you are confused, go to the tutorial I recommended above, it is discussed in the section “A Little Bit About the Math”)
aapl['intercept'] = 1
Thirdly, import the library for linear regression. We will use statsmodels here, the same as in the recommended tutorial.
import statsmodels.api as sm
Now, we are good to go. Let’s start with book value first this time.
(2) Book Value per Share
Below is how we build the linear model. Simply passing the outcome and predictors, and call .fit() method.
bvps_model = sm.OLS(aapl['book_value_per_share'], aapl[['year_index','intercept']]).fit()
Now let’s predict the book value in 10 years. Since the year order of ‘2019–09’ is 9, the year order of 2029 should be 19.
bvps_pred_lr = bvps_model.predict([19,1])[0]
print(bvps_pred_lr)# Output:
# 41.81709090909092
If the trailing [0] puzzles you, it is only an index. Our model returns an array with a single number, this [0] simply extract that number, which is the prediction of book value in the next 10 years.
As you can see, the book value decreased significantly compared with our first approach’s result. This is due to that the recent decline in book value (after 2017) as shown in the visualization that influenced our model, as a result, made a more ‘conservative’ estimation. This makes more sense to me than simply assuming the company grows at a constant rate, which has been calculated from only 2 data points, for 10 years.
(3) Dividends
Attention, please! As mentioned above, we have NaN value in our dividends column. If we feed any data with NaN value into a linear model, we will break it because this would cause an error in mathematical computation. So we need to drop NaN value before training our model.
aapl_new = aapl.dropna(subset = ['dividend_rate'])
We saved the dataframe in a new variable, since dropping is irreversible manipulation, it is a good habit to maintain the row data.
Now we can build the model and make a prediction.
dvd_model = sm.OLS(aapl_new['dividend_rate'], aapl_new[['year_index','intercept']]).fit()dvd_model.predict([19,1])[0]# Output:
# 5.84583333
The number 5.85 is the estimated dividends in the next 10 years, which is much larger than the mean value used in a simple method. As our previous discussion, this is a logical inference due to the upward trend of historical dividends.
Now let’s use the same approach to compute the total dividend.
dvd_total_lr = 0
current_year_order = 9
for i in range(1,11):
dvd_new = dvd_model.predict([current_year_order + i,1])[0]
dvd_total_lr = dvd_total_lr + dvd_new/(1+risk_free_rate)**i
print(dvd_total_lr)# Output:
# 40.15163616617433
(4) Sum Up
Adding up 2 parts gives us the final estimation of the intrinsic value.
aapl_intrinsic_lr = dvd_total_lr + bvps_pred_lr
print(aapl_intrinsic_lr)# Output:
# 81.96872707526525
The result is very similar to the first result, which is around 82. However, the estimation for each component is hugely different. As for this single case, the result is similar, for another company the result could be very different. In the data science world, due to the nature of uncertainty, you will hardly have the absolute right answer. So, the most important thing is to be considerate and have solid reasoning for the approach you choose. As discussed above, I believe the latter approach would consistently give a better estimation than the first approach.
Below is my code for this tutorial. You can click the ‘Open in Colab’ button, and play around these codes online. Let me know if you have any issues or ideas.
(5) Limitations and Remedy
Although this method seems to be better than the first one, it has its limitations.
Firstly, although linear regression could always give a prediction as you fed the data in a correct format, its confidence level could drop significantly if the data itself does not show a linear relationship. As shown in Fig.4, any linear prediction for data like this can be very wrong.
To resolve this issue is not easy since predicting the future is hard. But at least we could know how confident the model is for its prediction. The output parameter R-squared could be a good indicator, it basically represents to what extent our model could explain the variability among the data. 1 means a perfect fit, 0 or negative means horrible estimation. I would recommend you only use prediction with R-squared larger than 0.8, which is a pretty decent estimation.
Secondly, for young companies, they do not have sufficient financial reports for us to make a prediction. So even if our linear model shows very confident R-squared value, it could due to the small number of the data point. For this issue, my suggestion is that you could always look for companies with a long history, or add any kind of alert/record in your code to keep you informed.
Thirdly, I did not use ‘TTM’ in this tutorial, but it can be informative. The data of TTM comes from quarterly financial reports instead of the annual financial report. While annual reports are more accurate (they are under external audit), the quarterly report gives more up-to-date information. My approach for utilizing this data point could be divided into the following steps: (1) Decide which quarter TTM is, based on the current month and fiscal month of the company. For example, if the company release annually financial in January, and now it’s August, then I expect the TTM data comes from the second quarterly report. (2) Assign year order to TTM, by adding 0.25 (first quarter), 0.5 (second quarter), 0.75 (third quarter). If the last financial report date is within 3 months of the current date, the data in TTM should be the same with the data of its previous row, since the last annual report is the latest report. And that is our case in this tutorial. In this situation, you can simply drop the TTM row because it provides no more additional information.
Summary
This is the last tutorial of this series. So far we have covered how to use Python to calculate the following stock terms:
- Intrinsic Value (future dividends + future book value per share)
- Shares Outstanding
- EPS (Earning Per Share)
- Dividend Rate
- Dividend Yield
- Debt/Equity Ratio
- Book Value Per Share
- ROE (Return on Equity)
- Current Ratio
- P/E (Price to Earning)
- P/BV (Price to Book Value)
I really wish this could help you make a better decision in your investment or give you a flavor of what a data science project looks like.
At last, I want to acknowledge Preston Pysh, Investopedia, StatQuest and Towards Data Science.
Preston Pysh created a fantastic value investing course on YouTube. I would recommend this course to anyone with an interest in investing and finance.
Investopedia is my initial source of financial knowledge. It offers an easy-to-understand explanation for financial terms.
StatQuest Towards Data Science has great tutorials on Machine Learning and Stats.
I benefited a lot from these resources.
Previous tutorial: 2. How to Generate these Popular Stock Terms using Python