library(quantr)
library(dplyr)
library(ggplot2)
library(lubridate)

The goal of this notebook is to demonstrate stock screening using the following eight criteria:

  1. PE ratio < 22
  2. Return on invested capital > 9%
  3. Revenue growth
  4. Net income growth
  5. Shares outstanding decreasing or even
  6. Long term liabilities / 5 year free cash flow < 5
  7. Free cash flow growth
  8. Price to free cash flow < 20

Let's do this for Best Buy, ticker BBY.

ticker <- "WBA"

fin <- yahoo_financials(ticker)
fin_web <- yahoo_financials_web(ticker)
fin_web

PE ratio < 22

For the trailing PE ratio, use current price and the EPS history from yahoo_financials_web():

price <- yahoo_history(ticker, interval = "1d", days = 1, end_date = Sys.time()) %>%
  arrange(desc(date)) %>%
  head(1)

price
pe_ratio <- price$open / mean(fin_web$dilutedEps, na.rm = TRUE)

This gives us a mean PE ratio of r round(mean(pe_ratio), 2).

ggplot() +
  geom_bar(aes(x = fin$endDate, y = fin_web$dilutedEps), stat = "identity", fill = "#33658A", width = as.numeric(days(100))) +
  xlab("date") + ylab("Earnings per share")

Return on invested capital > 9%

Return on invested capital will be calculated here as cash flow divided by equity + debt. Needs to be checked r emo::ji("warning").

roic <- fin$freeCashflow / (fin$longTermDebt + fin$totalStockholderEquity)

ggplot() +
  geom_bar(aes(x = fin$endDate, y = roic), stat = "identity", fill = "#33658A", width = as.numeric(days(100))) +
  xlab("date") + ylab("ROIC")

This gives us a mean ROIC of r round(mean(roic), 2).

Revenue growth

Revenue should be going up, so let's try a linear regression model.

mod <- lm(fin$totalRevenue ~ fin$endDate)
summary(mod)
ggplot(fin) + 
  geom_point(aes(x = endDate, y = totalRevenue)) + 
  geom_abline(slope = coef(mod)[["fin$endDate"]], intercept = coef(mod)[["(Intercept)"]]) +
  scale_y_continuous(expand = c(0.1, 0), limits = c(0, NA))

This gives us an annual growth rate of r round(coef(mod)[["fin$endDate"]] / mean(fin$totalRevenue) * as.numeric(days(365)), 2).

Net income growth

Same procedure as before:

mod <- lm(fin$netIncome ~ fin$endDate)
summary(mod)
ggplot(fin) + 
  geom_point(aes(x = endDate, y = netIncome)) + 
  geom_abline(slope = coef(mod)[["fin$endDate"]], intercept = coef(mod)[["(Intercept)"]]) +
  scale_y_continuous(expand = c(0.1, 0), limits = c(0, NA))

This gives us an annual growth rate of r round(coef(mod)[["fin$endDate"]] / mean(fin$netIncome) * as.numeric(days(365)), 2).

Shares outstanding

Shares outstanding should be decreasing or staying the same.

mod <- lm(fin_web$dilutedAverageShares ~ fin_web$endDate)
summary(mod)
ggplot(fin_web) + 
  geom_point(aes(x = endDate, y = dilutedAverageShares)) + 
  geom_abline(slope = coef(mod)[["fin_web$endDate"]], intercept = coef(mod)[["(Intercept)"]]) +
  scale_y_continuous(expand = c(0.1, 0), limits = c(0, NA))

This gives us an annual growth rate of r round(coef(mod)[["fin_web$endDate"]] / mean(fin_web$dilutedAverageShares) * as.numeric(days(365)), 2).

Long term liabilities / 5 year free cash flow < 5

To do.

Free cash flow growth

mod <- lm(fin$freeCashflow ~ fin$endDate)
summary(mod)
ggplot(fin) + 
  geom_point(aes(x = endDate, y = freeCashflow)) + 
  geom_abline(slope = coef(mod)[["fin$endDate"]], intercept = coef(mod)[["(Intercept)"]]) +
  scale_y_continuous(expand = c(0.1, 0), limits = c(0, NA))

This gives us an annual growth rate of r round(coef(mod)[["fin$endDate"]] / mean(fin$freeCashflow) * as.numeric(days(365)), 2).

Price to free cash flow < 20

pfcf <- price$open / mean(fin$freeCashflow / fin_web$dilutedAverageShares, na.rm = TRUE)

This gives us a price to free cash flow of r round(mean(pfcf), 2).

ggplot() +
  geom_bar(aes(x = fin$endDate, y = fin$freeCashflow / fin_web$dilutedAverageShares), stat = "identity", fill = "#33658A", width = as.numeric(days(100))) +
  xlab("date") + ylab("Free cash flow per share")

Bringing it all together

bind_rows(
  eight_pillars("BBY"),
  eight_pillars("MSFT")
)


pieterprovoost/quantr documentation built on April 16, 2023, 12:50 p.m.