Have you ever wondered how much your business is worth, or what a reasonable stock price for your favourite company should be? This calculator will help you land a rough estimate based on the discounted free cash flow (DCF) method. Read more about the DCF method here .
Calculator Parameters
Report type
!
Quarterly
Annual
Please enter the financial data in chronological order from left to right in the table below. Provide full numbers, be aware of reports given in thousands or millions. Calculation will be valid in the currency of the numbers you input. After entering the numbers you can adjust the parameters above.
Estimated present company value:
Estimated present fair value per share:
Your assumptions were:
import pyscript
from js import document
from pyodide.ffi import create_proxy
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
def calculate(e):
# Reset any potential error messages
document.getElementById("errorMessageShEquity").textContent = ""
document.getElementById("errorMessageCheck").textContent = ""
# Read inputs
numcols = document.getElementById("numColumns").value
safety_margin = document.getElementById("safetyMargin").value
safety_factor = 1 - (float(safety_margin) / 100)
shAmountEnd = document.getElementById("shAmountEnd").value
shAmountEnd = int(shAmountEnd)
shEquity = document.getElementById("shEquity").value
shEquity = float(shEquity)
assets = document.getElementById("assets").value
assets = float(assets)
liabilities = document.getElementById("liabilities").value
liabilities = float(liabilities)
revenue = np.array([])
x = np.array([])
for i in range (1, int(numcols)+1):
revenue = np.append(revenue,np.array([float(document.getElementById("cellj1k"+str(i)).value)]))
x = np.append(x,np.array([i]))
# Find best fit growth rate
# Start by scaling numbers down
downscaled = revenue / 10**len(str(revenue[0]))
# Apply curve fit function
popt, pcov = curve_fit(lambda x,b,m: b*m**x,x,downscaled,maxfev = 1000)
growth_rate = document.getElementById("growthRate").value
growth_rate = float(growth_rate)/100 + 1
# Find current estimated revenue
est_curr_rev = popt[0]*growth_rate**x[-1] * 10**len(str(revenue[0]))
# Find revenue estimate array
rev_est_array = []
# Project future revenues and free cash flow for a time period equal to input period.
x_cont = np.arange(x[-1]+1,x[-1]+1+len(x))
projected_revenue = popt[0]*growth_rate**x_cont * 10**len(str(revenue[0]))
# Find free cash flow margin
fcf_margin = document.getElementById("fcfMargin").value
fcf_margin = float(fcf_margin)/100
# Read perpetual growth rate
perpetual_growth_rate = float(document.getElementById("perpetualGrowthRate").value)
perpetual_growth_rate = perpetual_growth_rate/100
# Read required return
required_return = float(document.getElementById("requiredReturn").value)
required_return = required_return/100
# Set terminal value horizon
h = int(numcols)
# Calculate discounted terminal value
discounted_TV = (projected_revenue[-1]*fcf_margin*(1+perpetual_growth_rate) / (required_return - perpetual_growth_rate))/((1+required_return)**h)
# Calculate discounted cash flows
discounted_FCF = 0
for j in range(0,h):
discounted_FCF = discounted_FCF + (projected_revenue[j]*fcf_margin)/((1+required_return)**(j+1))
# Get shareholder's equity from either assets and liabilities or entered value
if shEquity == 0 and assets == 0 and liabilities == 0:
document.getElementById("errorMessageShEquity").textContent = "Missing data: Please enter most recent values for total stockholder's equity, or total assets and total liabilities to factor it into the valuation estimate."
document.getElementById("errorMessageShEquity").style.color = "red"
# Calculate intrinsic value
intrinsic_value = discounted_FCF + discounted_TV + shEquity
intrinsic_value_per_share = intrinsic_value/shAmountEnd
# Calculate dilution/contraction
shAmount_change = document.getElementById("shAmountChange").value
shAmount_change = float(shAmount_change)/100 + 1
# Calculate adjusted intrinsic value
adj_intrinsic_value = intrinsic_value/shAmount_change
adj_intrinsic_value_per_share = adj_intrinsic_value/shAmountEnd
# Check for number of errors in the parameter inputs
error_message_ids = ["errorMessage", "errorMessage2", "errorMessage3", "errorMessageShEquity"]
error_message_sum = 0
for j in range(0,len(error_message_ids)):
if document.getElementById(error_message_ids[j]).textContent != "":
error_message_sum += 1
if error_message_sum > 0:
document.getElementById("errorMessageCheck").textContent = str(error_message_sum) + " warning(s) found, see detailed error messages."
document.getElementById("errorMessageCheck").style.color = "red"
# Print results
Element("result").write(str(round(adj_intrinsic_value*safety_factor,3)))
Element("result2").write(str(round(adj_intrinsic_value_per_share*safety_factor,3)))
Element("assumption1").write(str(round((growth_rate-1)*100,4)) + "% yearly growth over the next " + str(numcols) + " years.")
Element("assumption2").write("Free cash flow margin of " + str(round(fcf_margin*100,1)) + "%")
Element("assumption3").write("Perpetual growth rate of " + str(round(perpetual_growth_rate*100,1)) + "%")
Element("assumption4").write("Required Return of " + str(round(required_return*100,1)) + "%")
if (shAmount_change-1) < 0:
Element("assumption5").write("Amount of shares will decrease by " + str(round((shAmount_change-1)*100,4)) + "%")
else:
Element("assumption5").write("Amount of shares will increase by " + str(round((shAmount_change-1)*100,1)) + "%")
calculate_proxy = create_proxy(calculate)
calculate_button = document.getElementById("Calculatebtn")
calculate_button.addEventListener("click", calculate_proxy)
# Add a function to calculate growth rate and update input field
def updateParameters(e):
# Read single inputs and create arrays
numcols = document.getElementById("numColumns").value
revenue = np.array([])
income = np.array([])
opCashFlow = np.array([])
capEx = np.array([])
FCF = np.array([])
x = np.array([])
# Populate arrays
for i in range (1, int(numcols)+1):
revenue = np.append(revenue,np.array([float(document.getElementById("cellj1k"+str(i)).value)]))
income = np.append(income,np.array([float(document.getElementById("cellj2k"+str(i)).value)]))
opCashFlow = np.append(opCashFlow,np.array([float(document.getElementById("cellj3k"+str(i)).value)]))
capEx = np.append(capEx,np.array([float(document.getElementById("cellj4k"+str(i)).value)]))
FCF = np.append(FCF,np.array([float(document.getElementById("cellj5k"+str(i)).value)]))
x = np.append(x,np.array([i]))
# Calculate growth rate
downscaled = revenue / 10**len(str(revenue[0]))
popt, pcov = curve_fit(lambda x,b,m: b*m**x,x,downscaled,maxfev = 1000)
growth_rate = round((popt[1] - 1)*100,4)
# Calculate fcf margin
# If no fcf is entered, use opCashFlow and capEx,
if (FCF == 1).all():
fcf_margin = ((np.sum(opCashFlow)-np.sum(capEx))/np.sum(revenue))*100
document.getElementById("errorMessage3").textContent = "";
# or if no opCashFlow or capEx is entered, use net result,
if (opCashFlow == 1).all() or (capEx == 1).all():
fcf_margin = (np.sum(income)/np.sum(revenue))*100
document.getElementById("errorMessage3").textContent = "";
# or if no income is entered, complain about lacking data,
if (income == 1).all():
document.getElementById("errorMessage3").textContent = "Missing data: Please enter free cash flow, or operating cash flow and capital expenditures, or net income to estimate free cash flow margin.";
document.getElementById("errorMessage3").style.color = "red";
# else, calculate fcf margin from fcf entries.
else:
fcf_margin = (np.sum(FCF)/np.sum(revenue))*100
document.getElementById("errorMessage3").textContent = "";
if fcf_margin/100 > 1:
document.getElementById("errorMessage3").textContent = "Free cash flow margin cannot be more than 100%. Please check your numbers.";
document.getElementById("errorMessage3").style.color = "red";
# Calculate perpetual growth rate
if growth_rate > 20:
perpetual_growth_rate = 3.0
else:
perpetual_growth_rate = growth_rate*3/20
# Update parameter fields
document.getElementById("growthRate").value = growth_rate
document.getElementById("fcfMargin").value = round(fcf_margin,4)
document.getElementById("perpetualGrowthRate").value = round(perpetual_growth_rate,4)
# Add event listener to table to call the function when a value is entered
numberTable_proxy = create_proxy(updateParameters)
numberTable = document.getElementById("numberTable")
numberTable.addEventListener("input", numberTable_proxy)
def calculateShEquity(e):
assets = document.getElementById("assets").value
assets = float(assets)
liabilities = document.getElementById("liabilities").value
liabilities = float(liabilities)
if liabilities < 0:
liabilities = -liabilities
# Calculate shareholder's equity
shareholders_equity = assets - liabilities
# Update parameter fields
document.getElementById("shEquity").value = shareholders_equity
# Add event listener to assets and liabilities
assets_proxy = create_proxy(calculateShEquity)
assets = document.getElementById("assets")
assets.addEventListener("input", assets_proxy)
liabilities_proxy = create_proxy(calculateShEquity)
liabilities = document.getElementById("liabilities")
liabilities.addEventListener("input", liabilities_proxy)
def calculateShAmountChange(e):
shAmountStart = document.getElementById("shAmountStart").value
shAmountStart = int(shAmountStart)
shAmountEnd = document.getElementById("shAmountEnd").value
shAmountEnd = int(shAmountEnd)
# Calculate change in shares
if shAmountStart == 0:
print("Missing data: Please enter the amount of shares at the beginning of the period.")
else:
shAmount_change = round(((shAmountEnd/shAmountStart)-1)*100,4)
# Update parameter fields
document.getElementById("shAmountChange").value = shAmount_change
# Add event listener to share amount start
shAmountStart_proxy = create_proxy(calculateShAmountChange)
shAmountStart = document.getElementById("shAmountStart")
shAmountStart.addEventListener("input", shAmountStart_proxy)
# Add event listener to share amount start
shAmountEnd_proxy = create_proxy(calculateShAmountChange)
shAmountEnd = document.getElementById("shAmountEnd")
shAmountEnd.addEventListener("input", shAmountEnd_proxy)