Fundamental Analysis with edgarfundamentals

Overview

edgarfundamentals provides a simple, ticker-based interface for retrieving fundamental financial ratios directly from SEC EDGAR 10-K filings. No API key or paid subscription is required. All data comes from the SEC’s official XBRL API at data.sec.gov, which is free, stable, and government-maintained.

The package provides four functions:

Setup

Before making any API calls, set your User-Agent string. The SEC requests that automated tools identify themselves so they can contact you if your scripts cause issues with their servers.

library(edgarfundamentals)

options(edgarfundamentals.user_agent = "Jane Smith jane@example.com")

Replace the name and email with your own. You only need to do this once per R session.

Looking Up a CIK

Every EDGAR API call requires a CIK number. get_cik() handles this translation automatically inside the other functions, but you can call it directly if you need the CIK for other purposes.

get_cik("LLY")   # Eli Lilly
get_cik("LMT")   # Lockheed Martin

Fundamentals for a Single Stock

get_fundamentals() returns a named vector of key financial ratios derived from the most recent 10-K filing on or before to_date.

get_fundamentals("LLY", to_date = "2024-12-31")

The returned vector contains:

Name Description
CIK SEC Central Index Key
EPS Diluted Earnings Per Share (USD)
NetIncome Net Income (USD)
Revenue Total Revenue (USD)
ROE Return on Equity (%)
ROA Return on Assets (%)
DE Debt-to-Equity ratio
CurrentRatio Current Assets / Current Liabilities
GrossMargin Gross Profit as a percentage of Revenue (%)
OperatingMargin Operating Income as a percentage of Revenue (%)
NetMargin Net Income as a percentage of Revenue (%)
PE Price-to-Earnings ratio
PB Price-to-Book ratio
DIV Dividend Yield (%)

Because ratios come from annual 10-K filings, they reflect the most recently completed fiscal year – not real-time values. The PE ratio is the only computed value that uses a live market price (via Yahoo Finance), so it will change daily even when the underlying 10-K data does not.

Fundamentals for a Portfolio

get_fundamentals_batch() accepts a vector of tickers and returns a tidy data frame with one row per stock. If retrieval fails for any ticker, that row contains NA values and a message is printed – the batch continues rather than stopping.

healthcare <- c("UNH", "PFE", "MRK", "ABT", "LLY", "CVS", "AMGN")
defense    <- c("LMT", "RTX", "NOC", "GD",  "HII", "LHX", "LDOS")

healthcare.fund <- get_fundamentals_batch(healthcare, to_date = "2024-12-31")
defense.fund    <- get_fundamentals_batch(defense,    to_date = "2024-12-31")

healthcare.fund
defense.fund

Expect retrieval to take approximately 20–30 seconds for 14 stocks. A 0.5 second pause is inserted after each API call to respect the SEC rate limit.

Screening by Fundamental Criteria

Once you have the data frame, standard dplyr operations apply directly.

library(dplyr)

# Growth screen: high EPS stocks relative to peers signal strong earnings
healthcare.fund |> arrange(desc(EPS))

# Value screen: low PE and low PB suggest the market is pricing the stock cheaply
# High dividend yield is also characteristic of value stocks
defense.fund |> filter(PE < 20 & PB < 3) |> select(symbol, PE, PB, DIV, ROE)

# GARP screen (Growth at a Reasonable Price): PE below EPS
# Stocks where the market is not overcharging for growth
healthcare.fund |> filter(EPS > 0 & PE < EPS) |> select(symbol, PE, EPS)

# Profitability screen: strong margins signal pricing power and operational efficiency
bind_rows(healthcare.fund, defense.fund) |>
  filter(GrossMargin > 40 & OperatingMargin > 15) |>
  arrange(desc(NetMargin)) |>
  select(symbol, GrossMargin, OperatingMargin, NetMargin, ROA)

# Liquidity screen: current ratio above 1.5 and low leverage
bind_rows(healthcare.fund, defense.fund) |>
  filter(CurrentRatio > 1.5 & DE < 1) |>
  arrange(desc(CurrentRatio)) |>
  select(symbol, CurrentRatio, DE, ROE)

Checking Filing History

get_filing_history() retrieves recent EDGAR filings for a company, which is useful for verifying data availability or for finding the accession numbers needed to access the full text of specific reports.

# Five most recent annual reports for Lockheed Martin
get_filing_history("LMT", form_type = "10-K", n = 5)

# Four most recent quarterly reports for Eli Lilly
get_filing_history("LLY", form_type = "10-Q", n = 4)

A Note on Data Limitations

EDGAR XBRL data is only as good as what companies report. A small number of companies use non-standard XBRL tag names for common concepts. The package automatically tries fallback tags when the primary tag returns no data, but in rare cases a ratio may still be NA. When this occurs, verifying the company’s most recent 10-K directly on edgar.sec.gov will confirm whether the data exists under a different tag name.

Additionally, EDGAR does not provide market-based ratios directly. Price-to-Book and Dividend Yield both require a current share price, which comes from Yahoo Finance via tidyquant. This means PE, PB, and DIV all reflect today’s price against the most recent annual filing data, which is standard practice for trailing ratios.

mirror server hosted at Truenetwork, Russian Federation.