The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
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:
yf_get_financials().Tickers$valuation_measures.ggplot2.The functional helper yf_get_financials() retrieves tidy
financial statements across single or multiple tickers. Statements are
available at "annual" (default) or "quarterly"
frequencies.
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
#> <chr> <date> <chr> <dbl> <dbl> <dbl>
#> 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 88136000000Cash flow statements provide visibility into actual cash generated from operations and free cash flow after capital expenditures:
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
#> <chr> <date> <dbl> <dbl> <dbl>
#> 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 74071000000Balance sheets expose liquidity and solvency positions including working capital, cash reserves, and debt levels:
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)By joining the statement tables, we can compute standardized performance indicators:
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_fundamentalsValuation multiples allow comparing market pricing against underlying
fundamentals. Using the Tickers class, we retrieve
quarterly valuation measures across the peer group:
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
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 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.6We combine the latest operational margins with current valuation multiples to create a screening scorecard:
# 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)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)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")Below is the complete, self-contained fundamental screening script:
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)In this guide, you learned how to:
yf_get_financials().Tickers$valuation_measures.frequency = "quarterly" to spot inflection points in
revenue and margin trends earlier in the fiscal year.Ticker$technical_insights.yf_download_prices() to chart stock price performance
against earnings and fundamental milestones.vignette("cookbook", package = "yahoofinancer") for 15
additional recipes spanning portfolios, currency conversion, and
technical analysis.These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.