My bitemoji Zach data enthusiast. options trader.

Using a Monte Carlo simulation to determine the Fair Value of a stock

» coding, trading

Introduction

One of the most popular ways to value a company is using a model known as the Discounted Cash Flow Model. This model attempts to find the intrinsic value of a company by projecting its future cash flows and then discounting it. This is typically done using an excel spreadsheet and is relatively simple (just requiring a little bit of math and some manual work to find the financial information of a company). In this article, we’re going to explore how we can easily automate this in Python (both the calculation as well as the pulling of financial data!), adding in a monte carlo simulation to try to get a better range of intrinsic value calculations.

Pulling Financial Information

The first step is to pull financial data. We are using the FundamentalAnalysis package which allows us to access data from FinancialModelingPrep (FMP). In order to use FMP, you need to get a free API key, which you can get here. The first step is really simple - all we’re doing is downloading the Cash Flow statement, the Income Statement, and the Balance Sheet via FMP’s API.

import FundamentalAnalysis as fa 
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import yfinance as yf

ticker = "AMD" #ticker name
api = "384de232a09606f8ff4de3489690dc4c" #api key

#get company profile for beta calculation and marketcap
profile = fa.profile(ticker, api)
quote = fa.quote(ticker, api)

#FMP gives the dates reversed and hence the need for the .iloc in all the data calls
#get the cash flow, income statement, and balance sheet
cf = fa.cash_flow_statement(ticker, api).iloc[:,::-1]
ins = fa.income_statement(ticker, api).iloc[:,::-1]
bs = fa.balance_sheet_statement(ticker, api).iloc[:,::-1]

year = "2019" #set year

FMP returns the statements in descending order which we don’t want. Hence, there’s a need to use the .iloc[:,::-1] to flip it around.

DCF Valuation

There are three main parts to doing a DCF valuation.

1. Project Future Cash Flow.

There are many ways to do this and the most direct would be to just find the mean growth of cash flow and project it forward. However, another way to do it would be to project revenue first. From revenue, we can then project net income, and finally project free cash flow. To do this, we would need revenue/income ratios (i.e. income margin) and also free cash flow/revenue ratios. Being conservative, we then take the lowest positive number of the income margin ratios and free cash flow/revenue ratios.

Below, we first gather the last 5 years of data that we have, and then derive our estimates for revenue change, income margin, and fcf/revenue. As you can see, we use .min() to be conservative. Since we want to do a Monte Carlo simulation, we need both the mean and the standard deviation for our revenue growth. For our later simulation, we will need the risk free rate of return. This is usually assumed to be the returns of the 10-year treasury bonds and we use yfinance to obtain that

dcf_proj = pd.DataFrame({
    "revenue" : ins.loc["revenue"],
    "netincome" : cf.loc["netIncome"],
    "freecash" : cf.loc["freeCashFlow"]
}).transpose()

dcf_proj.loc["incomeMargin"] = dcf_proj.loc["revenue"]/dcf_proj.loc["netincome"]
dcf_proj.loc["FCFr"] = dcf_proj.loc["revenue"]/dcf_proj.loc["freecash"]
rev_change_mean = ins.loc["revenue"].pct_change().mean()
rev_change_stdd = ins.loc["revenue"].pct_change().std()
margin_change = dcf_proj.loc["incomeMargin", dcf_proj.loc["incomeMargin"] > 0].min()
fcf_change = dcf_proj.loc["FCFr", dcf_proj.loc["FCFr"] > 0].min()

tnx = yf.Ticker("^TNX") #use yfinance to get the 10yr treasury bond return
rfr = tnx.info["previousClose"] #use previousclose to estimate rfr

dcf_proj
2015 2016 2017 2018 2019
revenue 3991000000 4272000000 5253000000 6475000000 6731000000
netincome -660000000 -497000000 -33000000 337000000 341000000
freecash -322000000 13000000 -101000000 -129000000 276000000
incomeMargin -6.04697 -8.59557 -159.182 19.2136 19.739
FCFr -12.3944 328.615 -52.0099 -50.1938 24.3877

We continue with our projections of future cash flows below. Using the estimations we got above, we plug these into a 5-period for loop to obtain our forward estimations. We use np.random.normal() with the mean and standard deviation of the revenue change obtained above. The code is then plugged into a parent for loop in order to run the simulation.

startrev = dcf_proj.loc["revenue",year] #get the starting revenue

allFVs = [] #create a list to store FV values

for p in range(10000): #run the simulation 10000 times
    #creating projections for a 5 year dcf model
    Fiveyrev = []
    Fiveyinc = []
    Fiveyfcf = []
    for i in range(5):
        startrev = startrev*(1+np.random.normal(loc=rev_change_mean, scale=rev_change_stdd))
        neti = startrev*(margin_change/100)
        fcf = neti*(fcf_change/100)
        Fiveyrev.append(startrev)
        Fiveyinc.append(neti)
        Fiveyfcf.append(fcf)

2. Discount Projected Cash Flow

We now have our projected cash flows but need to discount them to find the Present Value (PV). This is because $100 today will not have the same value as $100 in the future. Hence, we need to discount the future value of the cash (i.e. reduce its value to what it would be worth today). Again, there are many ways to do this but one way to get the discount rate is to use the Weighted Average Cost of Capital (WACC).

image

The equation seems really scary at first but its really basic math. Essentially, we find the cost of the firm’s debt and the cost of the firm’s equity and then weigh them (hence the word “weighted”). If you want to go into greater depth as to how this is done, this video is particularly helpful. The code is basically a translation of the equation. We find the past 5 years of Wacc in order to calculate the standard deviation.

beta = profile.loc["beta"] #get beta of stock from stock profile
mktCap = quote.loc["marketCap"] #market cap of stock
totdebt = bs.loc["totalDebt",year] #total debt
sharesOut = quote.loc["sharesOutstanding"]
pg = 0.025 #perpetural growth

wacklist = []
for i,d in enumerate(["2015","2016","2017","2018","2019"]):
    Rd = ins.loc["interestExpense", d]/totdebt*(1-(ins.loc["incomeTaxExpense",d]/ins.loc["incomeBeforeTax",d]))
    Re = rfr + (beta*(10 - rfr))
    wacc = float(((Rd)*(totdebt/(mktCap + totdebt)) + (Re)*(1-(totdebt/(mktCap + totdebt))))/100)
    wacklist.append(Wacc)

wacc_stdd = np.std(wacklist)

We also need to find “Terminal Value” which is the value of the company beyond the 5 years that we are projecting. This requires an estimation of the perpetual growth rate of the company. We typically use 2.5% because this is lower than the growth rate of the economy (and it cannot be higher than the economy unless we think that one day this company will be LARGER than the entire economy)

For the discount value, we also use np.random.normal(), setting the mean as the current WACC and the standard deviation as the std dev. of the last 5 years of WACC.

# continuing the for loop...

    DV = np.random.normal(loc=wacc, scale=wacc_stdd)

    TV = float(((Fiveyfcf[-1]*(1+pg))/(DV - pg))) #Obtain Terminal value
    Fiveyfcf.append(TV)

After obtaining the Terminal Value and calculating the Discount Value, we now have to apply the discount to all the projected cash flows. The discount equation is as follow: image

We go through the list of projected cash flows and apply the discount formula to them

    Fiveyfcfdisc = []

    for i,d in enumerate(Fiveyfcf): #apply discount formula to all projected cash flows
        disc = float(Fiveyfcf[i]) * (1+DV)**(1+i)
        Fiveyfcfdisc.append(disc)

3. Calculating Net Present Value and Fair Value

And… we’re almost done! All that’s left to do is calculate the Net Present Value by summing up the discounted cash flows. And then dividing by the total number of shares issued by the company. We then append the calculated Fair value to a list so that we can keep track of all the calculated FVs in our simulation.

    FV = round(float(float(sum(Fiveyfcfdisc))/sharesOut),2) #divide NPV by shares

    allFVs.append(FV) #add to the list

With that, we have our simulated output of Fair Values! We can then work with these outputs by, for instance, plotting them in a histogram and determining the median Fair Value.

import matplotlib.pyplot as plt

plt.hist(allFVs, density=True, color="darkblue")
plt.title("Monte Carlo Plot of Fair Value")
plt.xlabel("Fair Value")
plt.ylabel("Count")

print("Median Fair Value: {}".format(np.median(allFVs)))
Median Fair Value: 15.17

output_15_1

Conclusion

Thanks for reading! This was a simple guide to how one can use a Monte Carlo simulation in order to ascertain the range of Fair Value when performing stock valuation using a Discounted Cash Flow model.