Analyze Companies using Financial Ratios

Tidy Finance Webinar Series

Christoph Scheuch

Recap of last webinars

So far only used stock return data


Today: introduction to company financial statements

Financial statements are key source of information

  • Standardized way for investors, creditors, analysts to assess companies
  • Companies are legally required to file financial statements
  • Public companies required to have independent audits of statements
  • US Security Exchange Commission requires quarterly & annual filings

Financial ratios help understand companies

  • Comparison across companies (benchmarking)
  • Comparison across periods for specific company (trend analysis)
  • Firm quality in portfolio selection (e.g., ratio-based filters)
  • Factor models in asset pricing (e.g., Fama-French factors, Q-factors)
  • Capital structure research & risk management

Outline of this webinar

  1. Illustrations of financial statements
  2. Examples of financial statements
  3. Download financial data in R
  4. Calculate financial ratios
  5. Compare company ratios to peers
  6. Analyze company ratios over time
  7. Firm characteristics in Fama-French

Balance sheets

Assets breakdown

Liabilities breakdown

Equity breakdown

Example balance sheet from Microsoft in 2023

Use fmpapi package to access financial statements

Download balance sheet data

library(fmpapi)

get_balance_sheet_statements("MSFT", period = "annual", limit = 5)
# A tibble: 5 × 54
  date       symbol reported_currency cik        filling_date accepted_date
  <date>     <chr>  <chr>             <chr>      <date>       <date>       
1 2024-06-30 MSFT   USD               0000789019 2024-07-30   2024-07-30   
2 2023-06-30 MSFT   USD               0000789019 2023-07-27   2023-07-27   
3 2022-06-30 MSFT   USD               0000789019 2022-07-28   2022-07-28   
4 2021-06-30 MSFT   USD               0000789019 2021-07-29   2021-07-29   
5 2020-06-30 MSFT   USD               0000789019 2020-07-30   2020-07-30   
# ℹ 48 more variables: calendar_year <int>, period <chr>,
#   cash_and_cash_equivalents <dbl>, short_term_investments <dbl>,
#   cash_and_short_term_investments <dbl>, net_receivables <dbl>,
#   inventory <dbl>, other_current_assets <dbl>, total_current_assets <dbl>,
#   property_plant_equipment_net <dbl>, goodwill <dbl>,
#   intangible_assets <dbl>, goodwill_and_intangible_assets <dbl>,
#   long_term_investments <dbl>, tax_assets <dbl>, …

Income statements

Example income statements of Microsoft 2023

Download income statements data

get_income_statements("MSFT", period = "annual", limit = 5)
# A tibble: 5 × 38
  date       symbol reported_currency cik        filling_date accepted_date
  <date>     <chr>  <chr>             <chr>      <date>       <date>       
1 2024-06-30 MSFT   USD               0000789019 2024-07-30   2024-07-30   
2 2023-06-30 MSFT   USD               0000789019 2023-07-27   2023-07-27   
3 2022-06-30 MSFT   USD               0000789019 2022-07-28   2022-07-28   
4 2021-06-30 MSFT   USD               0000789019 2021-07-29   2021-07-29   
5 2020-06-30 MSFT   USD               0000789019 2020-07-30   2020-07-30   
# ℹ 32 more variables: calendar_year <int>, period <chr>, revenue <dbl>,
#   cost_of_revenue <dbl>, gross_profit <dbl>, gross_profit_ratio <dbl>,
#   research_and_development_expenses <dbl>,
#   general_and_administrative_expenses <dbl>,
#   selling_and_marketing_expenses <dbl>,
#   selling_general_and_administrative_expenses <dbl>, other_expenses <int>,
#   operating_expenses <dbl>, cost_and_expenses <dbl>, interest_income <dbl>, …

Cash flow statements

Example cash flow statements of Microsoft 2023

Download cash flow statements data

get_cash_flow_statements("MSFT", period = "annual", limit = 5)
# A tibble: 5 × 40
  date       symbol reported_currency cik        filling_date accepted_date
  <date>     <chr>  <chr>             <chr>      <date>       <date>       
1 2024-06-30 MSFT   USD               0000789019 2024-07-30   2024-07-30   
2 2023-06-30 MSFT   USD               0000789019 2023-07-27   2023-07-27   
3 2022-06-30 MSFT   USD               0000789019 2022-07-28   2022-07-28   
4 2021-06-30 MSFT   USD               0000789019 2021-07-29   2021-07-29   
5 2020-06-30 MSFT   USD               0000789019 2020-07-30   2020-07-30   
# ℹ 34 more variables: calendar_year <int>, period <chr>, net_income <dbl>,
#   depreciation_and_amortization <dbl>, deferred_income_tax <dbl>,
#   stock_based_compensation <dbl>, change_in_working_capital <dbl>,
#   accounts_receivables <dbl>, inventory <int>, accounts_payables <dbl>,
#   other_working_capital <dbl>, other_non_cash_items <dbl>,
#   net_cash_provided_by_operating_activities <dbl>,
#   investments_in_property_plant_and_equipment <dbl>, …

Download & store financial statements

library(tidyverse)
library(tidyfinance)

constituents <- download_data_constituents("Dow Jones Industrial Average") |> 
  pull(symbol)

financial_statements <- list(
  balance_sheet_statements = get_balance_sheet_statements,
  income_statements = get_income_statements,
  cash_flow_statements = get_cash_flow_statements
) |> 
  map(\(x) map_df(constituents, x))

Calculate liquidity ratios

  • Current Ratio: Current Assets / Total Assets
  • Quick Ratio: (Current Assets - Liabilities) / Current Liabilities
  • Cash Ratio: Cash and Cash Equivalents / Current Liabilities
selected_symbols <- c("MSFT", "AAPL", "AMZN")

balance_sheets_statements <- financial_statements$balance_sheet_statements |> 
  mutate(
    current_ratio = total_current_assets / total_assets,
    quick_ratio = (total_current_assets - total_liabilities) / total_current_liabilities,
    cash_ratio = cash_and_cash_equivalents / total_current_liabilities,
    label = if_else(symbol %in% selected_symbols, symbol, NA),
  )

Comparing liquidity ratios

selected_colors <- c("#B23B9A", "#9AB23B", "#3B9AB2", "lightgrey")

fig_liquidity_ratios <- balance_sheets_statements |> 
  filter(calendar_year == 2023 & !is.na(label)) |> 
  select(symbol, contains("ratio")) |> 
  pivot_longer(-symbol) |> 
  mutate(name = str_to_title(str_replace_all(name, "_", " "))) |> 
  ggplot(aes(x = value, y = name, fill = symbol)) +
  geom_col(position = "dodge") +
  scale_x_continuous(labels = scales::percent) + 
  scale_fill_manual(values = selected_colors) +
  labs(x = NULL, y = NULL, fill = NULL,
       title = "Liquidity Ratios for Selected Stocks from the Dow Jones Industrial Average for 2023")

Calculating leverage ratios

  • Debt-to-Equity = Total Debt / Total Equity
  • Debt-to-Asset = Total Debt / Total Assets
  • Interest Coverage = EBIT / Interest Expense
balance_sheets_statements <- balance_sheets_statements |> 
  mutate(
    debt_to_equity = total_debt / total_equity,
    debt_to_asset = total_debt / total_assets
  )

income_statements <- financial_statements$income_statements |> 
  mutate(
    interest_coverage = operating_income / interest_expense,
    label = if_else(symbol %in% selected_symbols, symbol, NA),
  )

Debt-to-assets over time

fig_debt_to_asset <- balance_sheets_statements |> 
  filter(symbol %in% selected_symbols) |> 
  ggplot(aes(x = calendar_year, y = debt_to_asset,
             color = symbol)) +
  geom_line(linewidth = 1) +
  scale_y_continuous(labels = scales::percent) +
  scale_color_manual(values = selected_colors) +
  labs(x = NULL, y = NULL, color = NULL,
       title = "Debt-to-Asset Ratio of selected Stocks Between 2020 and 2024") 

Debt-to-asset in the cross-section

fig_debt_to_asset_cross_section <-  balance_sheets_statements |> 
  filter(calendar_year == 2023) |> 
  ggplot(aes( x = debt_to_asset,
              y = fct_reorder(symbol, debt_to_asset), 
             fill = label)) +
  geom_col() +
  scale_x_continuous(labels = scales::percent) +
  scale_fill_manual(values = selected_colors) +
  labs(x = NULL, y = NULL, color = NULL,
       title = "Debt-to-Asset Ratio of Dow Jones Industrial Average Constituents in 2023")

Debt-to-asset vs interest coverage

fig_debt_to_asset_interest_coverage <- income_statements |> 
  filter(calendar_year == "2023") |> 
  select(symbol, interest_coverage, calendar_year) |> 
  left_join(
    balance_sheets_statements,
    join_by(symbol, calendar_year)
  ) |> 
  ggplot(aes(x = debt_to_asset, y = interest_coverage, color = label)) +
  geom_point(size = 2) +
  ggrepel::geom_label_repel(aes(label = label), seed = 42, box.padding = 0.75) +
  scale_x_continuous(labels = scales::percent) +
  scale_y_continuous(labels = scales::percent) +
  scale_color_manual(values = selected_colors) +
  labs(x = "Debt-to-Asset", y = "Interest Coverage",
       title = "Debt-to-Asset Ratio vs Interest Coverage for Dow Jones Industrial Average Constituents") 

Calculate efficiency ratios

  • Asset Turnover = Revenue / Total Assets
  • Inventory Turnover = COGS / Inventory
  • Receivables Turnover = Revenue / Accounts Receivable
combined_statements <- balance_sheets_statements |> 
  select(symbol, calendar_year, label, current_ratio, quick_ratio, cash_ratio,
         debt_to_equity, debt_to_asset, total_assets, total_equity) |> 
  left_join(
    income_statements |> 
      select(symbol, calendar_year, interest_coverage, revenue, cost_of_revenue,
             selling_general_and_administrative_expenses, interest_expense,
             gross_profit, net_income),
    join_by(symbol, calendar_year)
  ) |> 
  left_join(
    financial_statements$cash_flow_statements |> 
      select(symbol, calendar_year, inventory, accounts_receivables),
     join_by(symbol, calendar_year)
  )

combined_statements <- combined_statements |> 
  mutate(
    asset_turnover = revenue / total_assets,
    inventory_turnover = cost_of_revenue / inventory,
    receivables_turnover = revenue / accounts_receivables
  )

Calculate profitability ratios

  • Gross Margin = Gross Profit / Revenue
  • Profit Margin = Net Income / Revenue
  • After-Tax ROE = Net Income / Total Equity
combined_statements <- combined_statements |> 
  mutate(
    gross_margin = gross_profit / revenue,
    profit_margin = net_income / revenue,
    after_tax_roe = net_income / total_equity
  )

Gross margin over time

fig_gross_margin <- combined_statements |> 
  filter(symbol %in% selected_symbols) |> 
  ggplot(aes(x = calendar_year, y = gross_margin, color = symbol)) +
  geom_line() +
  scale_y_continuous(labels = scales::percent) + 
  scale_color_manual(values = selected_colors) +
  labs(x = NULL, y = NULL, color = NULL,
       title = "Gross Margins for Selected Stocks Between 2019 and 2023")

Profit margin vs gross margin

fig_gross_margin_profit_margin <- combined_statements |> 
  filter(calendar_year == "2023") |> 
  ggplot(aes(x = gross_margin, y = profit_margin, color = label)) +
  geom_point(size = 2) +
  ggrepel::geom_label_repel(aes(label = label), seed = 42, box.padding = 0.75) +
  scale_color_manual(values = selected_colors) +
  scale_x_continuous(labels = scales::percent) +
  scale_y_continuous(labels = scales::percent) + 
  labs(x = "Gross Margin", y = "Profit Margin") +
  labs(title = "Gross Margin vs Profit Margin for Dow Jones Industrial Average Constituents for 2023")

Ranking companies in different categories

financial_ratios <- combined_statements |> 
  filter(calendar_year == 2023) |> 
  select(symbol, 
         contains(c("ratio", "margin", "roe", "_to_", "turnover", "interest_coverage"))) |> 
  pivot_longer(cols = -symbol) |> 
  mutate(
    type = case_when(
      name %in% c("current_ratio", "quick_ratio", "cash_ratio") ~ "Liquidity Ratios",
      name %in% c("debt_to_equity", "debt_to_asset", "interest_coverage") ~ "Leverage Ratios",
      name %in% c("asset_turnover", "inventory_turnover", "receivables_turnover") ~ "Efficiency Ratios",
      name %in% c("gross_margin", "profit_margin", "after_tax_roe") ~ "Profitability Ratios"
    )
  ) 

fig_ranks <- financial_ratios |> 
  group_by(type, name) |> 
  arrange(desc(value)) |> 
  mutate(rank = row_number()) |> 
  group_by(symbol, type) |> 
  summarize(rank = mean(rank), 
            .groups = "drop") |> 
  filter(symbol %in% selected_symbols) |> 
  ggplot(aes(x = rank, y = type, color = symbol)) +
  geom_point(shape = 17, size = 4) +
  scale_color_manual(values = selected_colors) + 
  labs(x = "Average rank", y = NULL, color = NULL,
       title = "Average Rank Among Dow Jones Industrial Average for Selected Stocks") +
  coord_cartesian(xlim = c(1, 30))

Financial ratios in Fama-French

  • Size = log(Market Cap)
  • Book-to-Market = Market Cap / Book Equity
  • Profitability = (Revenue - COGS - SGA - Interest Expense) / Book Equity
  • Investment: Total Assets / Lagged Total Assets - 1
market_cap <- constituents |> 
  map_df(~ get_market_cap_historical(., "2023-12-29", "2023-12-29")) 

combined_statements_ff <- combined_statements |> 
  filter(calendar_year == 2023) |> 
  left_join(market_cap, join_by(symbol)) |> 
  left_join(
    balance_sheets_statements |> 
      filter(calendar_year == 2022) |> 
      select(symbol, total_assets_lag = total_assets), 
    join_by(symbol)
  ) |> 
  mutate(
    size = log(market_cap),
    book_to_market = market_cap / total_equity,
    operating_profitability = (revenue - cost_of_revenue - selling_general_and_administrative_expenses - interest_expense) / total_equity,
    investment = total_assets / total_assets_lag
  )

Rank in Fama-French variables

fig_rank_ff <- combined_statements_ff |> 
  select(symbol, Size = size, 
         `Book-to-Market` = book_to_market, 
         `Profitability` = operating_profitability,
         Investment = investment) |> 
  pivot_longer(-symbol) |> 
  group_by(name) |> 
  arrange(desc(value)) |> 
  mutate(rank = row_number()) |> 
  ungroup() |> 
  filter(symbol %in% selected_symbols) |> 
  ggplot(aes(x = rank, y = name, color = symbol)) +
  geom_point(shape = 17, size = 4) +
  scale_color_manual(values = selected_colors) + 
  labs(x = "Rank", y = NULL, color = NULL,
       title = "Rank in Fama-French Variables for Selected Stocks from the Dow Jones Industrial Average") +
  coord_cartesian(xlim = c(1, 30))

Recap & key takeaways

  • Financial statements provide standardized, legally required insights into a company’s financial position
  • Ratios allow benchmarking & trend analysis across liquidity, leverage, efficiency & profitability dimensions
  • fmpapi enables easy access to financial data for ratio calculations & peer comparisons

Next webinar: Value Companies using Discounted Cash Flow Analysis