Tidy Finance Webinar Series
Company valuation helps determine the economic value of a business for purposes like investment, mergers, acquisitions or financial reporting
Common valuation methods:
Forecasted free cash flows
Continuation / terminal value
Discount rate
pak::pak("tidy-finance/r-fmpapi")
Cash that a company generates after accounting for outflows to support operatings & maintain capital assets
\[\begin{align*} \text{FCF} &= \text{EBIT} \\ &\quad + \text{Depreciation & Amortization} \\ &\quad - \text{Taxes} \\ &\quad + \Delta \text{Working Capital} \\ &\quad - \text{CAPEX} \end{align*}\]
Alternative ways to define free cash flow: investopedia.com
dcf_data <- income_statements |>
mutate(ebit = net_income + income_tax_expense - interest_expense - interest_income) |>
select(year = calendar_year,
ebit, revenue, depreciation_and_amortization, taxes = income_tax_expense) |>
left_join(
cash_flow_statements |>
select(year = calendar_year,
delta_working_capital = change_in_working_capital,
capex = capital_expenditure), join_by(year)
) |>
mutate(fcf = ebit + depreciation_and_amortization - taxes + delta_working_capital - capex) |>
arrange(year)
# A tibble: 5 × 8
year ebit revenue depreciation_and_amo…¹ taxes delta_working_capital
<int> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2020 4.78e10 1.43e11 12300000000 8.76e 9 2148000000
2 2021 6.66e10 1.68e11 10900000000 9.83e 9 -936000000
3 2022 7.96e10 1.98e11 14460000000 1.10e10 446000000
4 2023 8.43e10 2.12e11 13861000000 1.70e10 -2388000000
5 2024 1.02e11 2.45e11 22287000000 1.97e10 1824000000
# ℹ abbreviated name: ¹depreciation_and_amortization
# ℹ 2 more variables: capex <dbl>, fcf <dbl>
Typical financial analyst approach:
dcf_data <- dcf_data |>
mutate(
revenue_growth = revenue / lag(revenue) - 1,
operating_margin = ebit / revenue,
da_margin = depreciation_and_amortization / revenue,
taxes_to_revenue = taxes / revenue,
delta_working_capital_to_revenue = delta_working_capital / revenue,
capex_to_revenue = capex / revenue
)
fig_financial_ratios <- dcf_data |>
pivot_longer(cols = c(operating_margin:capex_to_revenue)) |>
ggplot(aes(x = year, y = value, color = name)) +
geom_line() +
scale_x_continuous(breaks = scales::pretty_breaks()) +
scale_y_continuous(labels = scales::percent) +
labs(x = NULL, y = NULL, color = NULL,
title = "Financial ratios of Microsoft between 2020 and 2024")
dcf_data_forecast_ratios <- tribble(
~year, ~operating_margin, ~da_margin, ~taxes_to_revenue, ~delta_working_capital_to_revenue, ~capex_to_revenue,
2025, 0.41, 0.09, 0.08, 0.001, -0.2,
2026, 0.42, 0.09, 0.07, 0.001, -0.22,
2027, 0.43, 0.09, 0.06, 0.001, -0.2,
2028, 0.44, 0.09, 0.06, 0.001, -0.18,
2029, 0.45, 0.09, 0.06, 0.001, -0.16
) |>
mutate(type = "Forecast")
dcf_data <- dcf_data |>
mutate(type = "Realized") |>
bind_rows(dcf_data_forecast_ratios)
fig_financial_ratios_forecast <- dcf_data |>
pivot_longer(cols = c(operating_margin:capex_to_revenue)) |>
ggplot(aes(x = year, y = value, color = name, linetype = rev(type))) +
geom_line() +
scale_x_continuous(breaks = scales::pretty_breaks()) +
scale_y_continuous(labels = scales::percent) +
labs(x = NULL, y = NULL, color = NULL, linetype = NULL,
title = "Financial ratios and their forecasts of Microsoft between 2020 and 2024")
Alternative: look up consensus analyst forecasts (typically proprietary)
gdp_growth <- tibble(
year = 2020:2029,
gdp_growth = c(-0.02163, 0.06055, 0.02512, 0.02887, 0.02765, 0.02153, 0.02028, 0.02120, 0.02122, 0.02122)
)
dcf_data <- dcf_data |>
left_join(gdp_growth, join_by(year))
revenue_growth_model <- dcf_data |>
lm(revenue_growth ~ gdp_growth, data = _) |>
coefficients()
dcf_data <- dcf_data |>
mutate(
revenue_growth_modeled = revenue_growth_model[1] + revenue_growth_model[2] * gdp_growth,
revenue_growth = if_else(type == "Forecast", revenue_growth_modeled, revenue_growth)
)
fig_growth <- dcf_data |>
filter(year >= 2021) |>
pivot_longer(cols = c(revenue_growth, gdp_growth)) |>
ggplot(aes(x = year, y = value, color = name, linetype = rev(type))) +
geom_line() +
scale_x_continuous(breaks = scales::pretty_breaks()) +
scale_y_continuous(labels = scales::percent) +
labs(x = NULL, y = NULL, color = NULL, linetype = NULL,
title = "GDP growth and Microsoft revenue growth and their forecasts between 2020 and 2024")
dcf_data$revenue_growth[1] <- 0
dcf_data$revenue <- dcf_data$revenue[1] * cumprod(1 + dcf_data$revenue_growth)
dcf_data <- dcf_data |>
mutate(
ebit = operating_margin * revenue,
depreciation_and_amortization = da_margin * revenue,
taxes = taxes_to_revenue * revenue,
delta_working_capital = delta_working_capital_to_revenue * revenue,
capex = capex_to_revenue * revenue,
fcf = ebit + depreciation_and_amortization - taxes + delta_working_capital - capex
)
fig_fcf <- dcf_data |>
ggplot(aes(x = year, y = fcf / 1e9)) +
geom_col(aes(fill = type)) +
scale_x_continuous(breaks = scales::pretty_breaks()) +
scale_y_continuous(labels = scales::comma) +
labs(x = NULL, y = "Free Cash Flow (in B USD)", fill = NULL,
title = "Actual and predicted free cash flow for Microsoft from 2020 to 2029")
Perpetuity growth model: cash flows grow at a constant rate indefinitely
\[TV_{T} = \frac{{FCF_{T+1}}}{{r - g}}\]
Alternative: estimate continuation value based on multiple of EBITDA (exit multiple approach)
WACC represents the average rate of return required by all investors (equity & debt holders)
\[WACC = \frac{E}{D+E} \cdot r^E + \frac{D}{D+E} \cdot r^D \cdot (1 - \tau)\]
Useful data source: Aswath Damodaran
library(readxl)
file <- tempfile(fileext = "xls")
url <- "https://pages.stern.nyu.edu/~adamodar/pc/datasets/wacc.xls"
download.file(url, file)
wacc_raw <- read_xls(file, sheet = 2, skip = 18)
unlink(file)
wacc <- wacc_raw |>
filter(`Industry Name` == "Computer Services") |>
pull(`Cost of Capital`)
wacc
[1] 0.07417564
\[ \text{Total DCF Value} = \sum_{t=1}^{\text{T}} \frac{\text{FCF}_t}{(1 + \text{WACC})^t} + \frac{\text{TV}_{T}}{(1 + \text{WACC})^{\text{T}}} \]
forecasted_years <- 5
compute_dcf <- function(wacc, growth_rate, years = 5) {
free_cash_flow <- dcf_data$fcf
last_fcf <- tail(free_cash_flow, 1)
terminal_value <- compute_terminal_value(last_fcf, growth_rate, wacc)
present_value_fcf <- free_cash_flow / (1 + wacc)^(1:years)
present_value_tv <- terminal_value / (1 + wacc)^years
total_dcf_value <- sum(present_value_fcf) + present_value_tv
total_dcf_value
}
compute_dcf(wacc, 0.03) / 1e9
[1] 6083.605
DCF is full of assumptions:
Check how sensitive valuations are due to different assumptions
wacc_range <- seq(0.06, 0.08, by = 0.01)
growth_rate_range <- seq(0.02, 0.04, by = 0.01)
sensitivity <- expand_grid(
wacc = wacc_range,
growth_rate = growth_rate_range
) |>
mutate(value = pmap_dbl(list(wacc, growth_rate), compute_dcf))
fig_sensitivity <- sensitivity |>
mutate(value = round(value / 1e9, 0)) |>
ggplot(aes(x = wacc, y = growth_rate, fill = value)) +
geom_tile() +
geom_text(aes(label = value), color = "white") +
scale_x_continuous(labels = scales::percent) +
scale_y_continuous(labels = scales::percent) +
labs(
title = "DCF Sensitivity Analysis",
x = "WACC",
y = "Perpetual Growth Rate",
fill = "Company Value"
)
DCF model provides an estimate for value of operations
\[\text{Equity Value} = \text{DCF Value} + \text{Non-Operating Assets} - \text{Value of Debt}\]
Find all slides & recordings on talks.tidy-finance.org