--- title: "Fundamental Analysis and Stock Screening" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Fundamental Analysis and Stock Screening} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ## Overview Fundamental analysis evaluates a company's financial health, operational profitability, balance sheet strength, and market valuation multiples. In this guide, you will build an end-to-end fundamental analysis and stock screening workflow using `yahoofinancer`: 1. **Extract Financial Statements**: Download income statements, balance sheets, and cash flow statements across multiple peer companies using `yf_get_financials()`. 2. **Compute Core Ratios**: Calculate profitability margins (operating margin, net profit margin) and cash generation metrics (free cash flow conversion). 3. **Inspect Valuation Multiples**: Retrieve quarterly valuation ratios (P/E, forward P/E, PEG, EV/EBITDA, P/B) with `Tickers$valuation_measures`. 4. **Build a Fundamental Screener**: Combine statement metrics and valuation multiples into a tidy scorecard to filter and rank candidates. 5. **Visualize Peer Comparisons**: Compare financial performance and valuation multiples using `ggplot2`. ### Required Packages ```{r load-libraries, message=FALSE, warning=FALSE} # Install required packages if needed: # install.packages(c("yahoofinancer", "dplyr", "tidyr", "ggplot2", "scales")) library(yahoofinancer) library(dplyr) library(tidyr) library(ggplot2) library(scales) ``` --- ## 1. Downloading Financial Statements The functional helper `yf_get_financials()` retrieves tidy financial statements across single or multiple tickers. Statements are available at `"annual"` (default) or `"quarterly"` frequencies. ### Income Statement ```{r fetch-income} peer_tickers <- c("AAPL", "MSFT", "GOOG") income_df <- yf_get_financials( tickers = peer_tickers, statement_type = "income", frequency = "annual" ) income_df |> select(symbol, date, period_type, total_revenue, operating_income, net_income) |> head(n = 6) #> # A tibble: 6 x 6 #> symbol date period_type total_revenue operating_income net_income #> #> 1 AAPL 2021-09-25 12M 365817000000 108949000000 94680000000 #> 2 AAPL 2022-09-24 12M 394328000000 119437000000 99803000000 #> 3 AAPL 2023-09-30 12M 383285000000 114301000000 96995000000 #> 4 MSFT 2022-06-30 12M 198270000000 83383000000 72738000000 #> 5 MSFT 2023-06-30 12M 211915000000 88523000000 72361000000 #> 6 MSFT 2024-06-30 12M 245122000000 109433000000 88136000000 ``` ### Cash Flow Statement Cash flow statements provide visibility into actual cash generated from operations and free cash flow after capital expenditures: ```{r fetch-cashflow} cashflow_df <- yf_get_financials( tickers = peer_tickers, statement_type = "cash-flow", frequency = "annual" ) cashflow_df |> select(symbol, date, operating_cash_flow, capital_expenditure, free_cash_flow) |> head(n = 6) #> # A tibble: 6 x 5 #> symbol date operating_cash_flow capital_expenditure free_cash_flow #> #> 1 AAPL 2021-09-25 104038000000 -11085000000 92953000000 #> 2 AAPL 2022-09-24 122151000000 -10708000000 111443000000 #> 3 AAPL 2023-09-30 110543000000 -10959000000 99584000000 #> 4 MSFT 2022-06-30 89035000000 -23886000000 65149000000 #> 5 MSFT 2023-06-30 87582000000 -28107000000 59475000000 #> 6 MSFT 2024-06-30 118548000000 -44477000000 74071000000 ``` ### Balance Sheet Balance sheets expose liquidity and solvency positions including working capital, cash reserves, and debt levels: ```{r fetch-balancesheet} balance_df <- yf_get_financials( tickers = peer_tickers, statement_type = "balance-sheet", frequency = "annual" ) balance_df |> select(symbol, date, total_assets, total_stockholder_equity, working_capital, total_debt) |> head(n = 6) ``` --- ## 2. Computing Profitability & Solvency Ratios By joining the statement tables, we can compute standardized performance indicators: * **Operating Margin**: $\text{Operating Income} / \text{Total Revenue}$ * **Net Margin**: $\text{Net Income} / \text{Total Revenue}$ * **Free Cash Flow Conversion**: $\text{Free Cash Flow} / \text{Operating Cash Flow}$ ```{r compute-ratios} fundamentals <- income_df |> left_join(cashflow_df, by = c("symbol", "date", "period_type")) |> left_join(balance_df, by = c("symbol", "date", "period_type")) |> group_by(symbol) |> arrange(date, .by_group = TRUE) |> mutate( operating_margin = operating_income / total_revenue, net_margin = net_income / total_revenue, fcf_conversion = free_cash_flow / operating_cash_flow, debt_to_equity = total_debt / total_stockholder_equity ) |> ungroup() # View the most recent period for each peer latest_fundamentals <- fundamentals |> group_by(symbol) |> slice_tail(n = 1) |> ungroup() |> select(symbol, date, total_revenue, operating_margin, net_margin, fcf_conversion, debt_to_equity) latest_fundamentals ``` --- ## 3. Valuation Measures Across Peers Valuation multiples allow comparing market pricing against underlying fundamentals. Using the `Tickers` class, we retrieve quarterly valuation measures across the peer group: ```{r fetch-valuation} stocks <- Tickers$new(peer_tickers) val_measures <- stocks$valuation_measures val_measures |> group_by(symbol) |> slice_tail(n = 1) |> ungroup() |> select(symbol, date, market_cap, pe_ratio, forward_pe_ratio, peg_ratio, enterprise_value_ebitda_ratio) #> # A tibble: 3 x 7 #> symbol date market_cap pe_ratio forward_pe_ratio peg_ratio enterprise_value_ebitda_ratio #> #> 1 AAPL 2024-06-30 3.45e12 33.2 28.5 2.10 24.2 #> 2 GOOG 2024-06-30 2.25e12 25.4 21.8 1.45 17.8 #> 3 MSFT 2024-06-30 3.30e12 36.1 30.2 2.35 25.6 ``` --- ## 4. Multi-Factor Stock Screening Scorecard We combine the latest operational margins with current valuation multiples to create a screening scorecard: ```{r create-screener} # Extract latest valuation metrics latest_valuation <- val_measures |> group_by(symbol) |> slice_tail(n = 1) |> ungroup() |> select(symbol, pe_ratio, forward_pe_ratio, peg_ratio, enterprise_value_ebitda_ratio) # Combine fundamentals and valuation screener_table <- latest_fundamentals |> left_join(latest_valuation, by = "symbol") |> mutate( # Screening criteria flags pass_margin = operating_margin >= 0.25, pass_fcf = fcf_conversion >= 0.70, pass_peg = peg_ratio < 2.5, pass_valuation = forward_pe_ratio < 35, screen_passed = pass_margin & pass_fcf & pass_peg & pass_valuation ) screener_table |> select(symbol, operating_margin, fcf_conversion, forward_pe_ratio, peg_ratio, screen_passed) ``` --- ## 5. Visualizing Peer Comparisons ### Operating Margin Trends ```{r plot-margin-trends} ggplot(fundamentals, aes(x = date, y = operating_margin, color = symbol)) + geom_line(linewidth = 1.1) + geom_point(size = 2.5) + scale_y_continuous(labels = percent_format(accuracy = 1)) + scale_x_date(date_labels = "%Y", date_breaks = "1 year") + labs( title = "Annual Operating Margin Comparison", subtitle = "Operating income as a percentage of total revenue", x = "Fiscal Year", y = "Operating Margin", color = "Company", caption = "Source: Yahoo Finance via yahoofinancer" ) + theme_minimal(base_size = 12) ``` ### Valuation vs. Operating Profitability ```{r plot-val-vs-margin} ggplot(screener_table, aes(x = operating_margin, y = enterprise_value_ebitda_ratio, label = symbol)) + geom_point(aes(color = symbol), size = 4) + geom_text(vjust = -1, fontface = "bold") + scale_x_continuous(labels = percent_format(accuracy = 1)) + labs( title = "EV / EBITDA Multiple vs. Operating Margin", subtitle = "Evaluating valuation premium relative to operating efficiency", x = "Operating Margin", y = "Enterprise Value / EBITDA Multiple", caption = "Source: Yahoo Finance via yahoofinancer" ) + theme_minimal(base_size = 12) + theme(legend.position = "none") ``` --- ## 6. Minimal Reproducible Example Below is the complete, self-contained fundamental screening script: ```{r full-workflow} library(yahoofinancer) library(dplyr) library(ggplot2) library(scales) # 1. Define peer universe tickers <- c("AAPL", "MSFT", "GOOG") # 2. Fetch income statement and cash flow statements income_df <- yf_get_financials(tickers, statement_type = "income", frequency = "annual") cashflow_df <- yf_get_financials(tickers, statement_type = "cash-flow", frequency = "annual") # 3. Retrieve quarterly valuation measures stocks <- Tickers$new(tickers) val_measures <- stocks$valuation_measures # 4. Join and calculate key metrics latest_income <- income_df |> group_by(symbol) |> slice_tail(n = 1) |> ungroup() |> mutate(operating_margin = operating_income / total_revenue) |> select(symbol, date, total_revenue, operating_margin) latest_cf <- cashflow_df |> group_by(symbol) |> slice_tail(n = 1) |> ungroup() |> mutate(fcf_conversion = free_cash_flow / operating_cash_flow) |> select(symbol, fcf_conversion) latest_val <- val_measures |> group_by(symbol) |> slice_tail(n = 1) |> ungroup() |> select(symbol, pe_ratio, forward_pe_ratio, peg_ratio, enterprise_value_ebitda_ratio) screener_summary <- latest_income |> left_join(latest_cf, by = "symbol") |> left_join(latest_val, by = "symbol") # 5. Display screener table print(screener_summary) ``` --- ## 7. Summary In this guide, you learned how to: 1. **Retrieve Structured Statements**: Access income, balance sheet, and cash flow data with `yf_get_financials()`. 2. **Compute Standardized Metrics**: Derive operating margins, net margins, and free cash flow conversion. 3. **Extract Valuation Multiples**: Query multi-stock valuation ratios with `Tickers$valuation_measures`. 4. **Screen & Compare Stocks**: Build custom screening rules combining profitability and market valuation. --- ## 8. Going Further * **Quarterly Trend Analysis**: Switch to `frequency = "quarterly"` to spot inflection points in revenue and margin trends earlier in the fiscal year. * **Combine with Technicals**: Pair fundamental screening with technical indicator snapshots via `Ticker$technical_insights`. * **Historical Price Context**: Use `yf_download_prices()` to chart stock price performance against earnings and fundamental milestones. * **More recipes**: See `vignette("cookbook", package = "yahoofinancer")` for 15 additional recipes spanning portfolios, currency conversion, and technical analysis.