Replies: 5 comments
-
|
Hi @dgunning Any thoughts on this? Is there any way to identify the stock splits or any means by doing so? Thank you! |
Beta Was this translation helpful? Give feedback.
-
|
Hi @amcamc92! Great question - stock split detection is definitely an important piece of the EPS normalization puzzle. Let me walk you through what edgartools provides and show you some concrete approaches. Good News: EdgarTools Has Built-In Stock Split DetectionEdgarTools includes stock split detection capabilities in the 1. Detecting Stock Splits from XBRL FactsThe most reliable method is using the XBRL concept from edgar import Company
from edgar.ttm import detect_splits
# Get company facts
company = Company("NVDA")
facts = company.facts._facts
# Detect all stock splits
splits = detect_splits(facts)
for split in splits:
print(f"Split Date: {split['date']}, Ratio: {split['ratio']}:1")Output for NVIDIA: The
2. Finding Related 8-K Filings (Item 5.03)Item 5.03 ("Amendments to Articles of Incorporation or Bylaws; Change in Fiscal Year") is the standard disclosure for stock splits. You can find these filings: from edgar import Company
company = Company("NVDA")
eight_ks = company.get_filings(form='8-K')
print("8-Ks with Item 5.03 (often stock splits):")
for filing in eight_ks:
try:
eight_k = filing.obj()
if '5.03' in eight_k.items:
print(f" {filing.filing_date}: Items {eight_k.items}")
except Exception:
passOutput: Note: Item 5.03 covers all amendments to articles of incorporation, not just stock splits, so you'll still need to check the content or cross-reference with XBRL facts. 3. Complete EPS Normalization WorkflowHere's how to implement your exact logic ( from edgar import Company
from edgar.ttm import detect_splits
company = Company("NVDA")
facts = company.facts._facts
# Step 1: Detect all stock splits
splits = detect_splits(facts)
# Step 2: Get EPS facts
eps_facts = [f for f in facts
if 'earningspershare' in f.concept.lower()
and 'diluted' in f.concept.lower()
and f.numeric_value is not None]
# Step 3: Apply normalization logic
def normalize_eps_for_splits(fact, splits):
"""Normalize EPS for stock splits that occurred after the filing date"""
if not fact.filing_date:
return fact.numeric_value
cumulative_ratio = 1.0
for split in splits:
# Only adjust if filing was BEFORE the split date
if fact.filing_date < split['date']:
cumulative_ratio *= split['ratio']
return fact.numeric_value / cumulative_ratio if cumulative_ratio > 1.0 else fact.numeric_value
# Step 4: Process your EPS data
for fact in eps_facts:
original = fact.numeric_value
normalized = normalize_eps_for_splits(fact, splits)
if original != normalized:
print(f"Period {fact.period_end}, Filed {fact.filing_date}:")
print(f" Original: ${original:.2f}, Normalized: ${normalized:.2f}")Example Output: 4. Extracting Effective DatesThe
If you need the exact announcement date vs effective date, you can:
# Cross-reference split dates with 8-K filings
for split in splits:
split_date = split['date']
# Find 8-K filings near the split date
nearby_8ks = [f for f in company.get_filings(form='8-K')
if abs((f.filing_date - split_date).days) < 30]
for filing in nearby_8ks:
eight_k = filing.obj()
if '5.03' in eight_k.items:
print(f"Split {split_date}: Announced in 8-K on {filing.filing_date}")Built-In Split Adjustment (Bonus)EdgarTools also has a built-in from edgar import Company
from edgar.ttm import detect_splits, apply_split_adjustments
company = Company("AAPL")
facts = company.facts._facts
# Detect splits
splits = detect_splits(facts)
# Apply adjustments to ALL facts (EPS, share counts, etc.)
adjusted_facts = apply_split_adjustments(facts, splits)This function:
Notes and Limitations
Complete Working ExampleHere's a complete script you can adapt for your pipeline: from edgar import Company
from edgar.ttm import detect_splits
from datetime import datetime
def build_normalized_eps_series(ticker):
"""Build a normalized EPS series for a company, adjusted for stock splits"""
company = Company(ticker)
facts = company.facts._facts
# Detect stock splits
splits = detect_splits(facts)
print(f"\n{ticker} Stock Splits Detected:")
for split in splits:
print(f" {split['date']}: {split['ratio']}:1")
# Get EPS facts
eps_facts = [
f for f in facts
if 'earningspershare' in f.concept.lower()
and 'diluted' in f.concept.lower()
and f.numeric_value is not None
and f.filing_date is not None
]
# Sort by period
eps_facts.sort(key=lambda f: f.period_end)
# Normalize for splits
normalized_series = []
for fact in eps_facts:
cumulative_ratio = 1.0
for split in splits:
if fact.filing_date < split['date']:
cumulative_ratio *= split['ratio']
normalized_value = fact.numeric_value / cumulative_ratio if cumulative_ratio > 1.0 else fact.numeric_value
normalized_series.append({
'period': fact.period_end,
'filing_date': fact.filing_date,
'original_eps': fact.numeric_value,
'normalized_eps': normalized_value,
'split_adjusted': cumulative_ratio > 1.0,
'cumulative_ratio': cumulative_ratio
})
return normalized_series
# Use it
eps_data = build_normalized_eps_series("NVDA")
# Show the results
print("\nNormalized EPS Series:")
for row in eps_data[-10:]: # Last 10 periods
print(f"{row['period']}: ${row['normalized_eps']:.2f} "
f"({'adjusted' if row['split_adjusted'] else 'original'})")This gives you a fully SEC-sourced, split-adjusted EPS series without relying on external APIs like Yahoo Finance. Let me know if you have questions about any of this or need help adapting it to your specific use case! |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for the breakdown, @dgunning. I was expecting to get something like this, or at least similar to the most recent splits:
I understand why the earlier splits (2000–2007) are missing, as they pre-date the SEC's XBRL requirements. However, I noticed an issue with the 2021 and 2024 detections:
Why does the tool prioritize the filing date over the effective date in these cases? I’d appreciate your insights on whether this can be improved or if I should look into a hybrid approach for historical splits. Thank you! |
Beta Was this translation helpful? Give feedback.
-
|
Great catch @amcamc92 — you're right that the dates were off. The issue was that I've pushed a fix that prioritizes 8-K instant facts when selecting the split date. An 8-K instant fact's The priority order is now:
This should give you more accurate split dates for companies like AAPL and NVDA. The fix will be in the next release — update and give it a try: from edgar import Company
from edgar.ttm import detect_splits
company = Company("AAPL")
facts = company.facts._facts
splits = detect_splits(facts)
for split in splits:
print(f"Split Date: {split['date']}, Ratio: {split['ratio']]:1")One caveat: for pre-XBRL splits (before ~2009), the data simply isn't available in structured form from the SEC. For those, an external data source would still be needed. Let me know if the dates look better now! |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for your response @dgunning ! However, the investigation shows conflicting split dates for both AAON, TSLA and AAPL: AAON Case:
TSLA Case:
AAPL Case:
Analysis: yfinance generally uses the Ex-Dividend Date (or Ex-Split Date), which is when the stock actually starts trading at the new price. This is the correct date for adjusting price history. However, edgartools derives splits from XBRL facts, which often record the Record Date or the Filing Date of the period where the split occurred. For splits, there is often a lag between Record/Announcement and Ex-Date. Is there a way to extract from XBRL structured data the Ex-Split Date? Thanks! |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
Hi everyone,
I am building a financial analysis pipeline using
edgartoolsand I'm currently working on normalizing historical Earnings Per Share (EPS) data.The Challenge
As we know, SEC filings report EPS based on the share count at the time of the filing (or restated in recent filings). To have a consistent historical series, I need to adjust EPS values for stock splits, but only for data points extracted from filings published BEFORE the split date.
Data extracted from filings published after a split is already adjusted/restated by the company, so double-adjusting must be avoided.
My Logic
For every EPS fact, I want to apply this check:
if filing_date < split_date: apply_adjustment(value, ratio)Questions for the Community:
1.Detection: Is there a reliable way to programmatically detect a stock split event (Date and Ratio) using
edgartools?us-gaap:StockholdersEquityNoteStockSplit...)Effective Date: How can I accurately extract the "effective date" or "record date" of the split directl from the filing metadata or XBRL facts?
Best Practices: Has anyone implemented a similar normalization pipeline using only SEC data? I am tryin to avoid relying on external APIs (like Yahoo Finance) to keep the data source consistent within the SEC ecosystem.
I would love to hear how you handle stock splits and if there are specific features in the library that could simplify this "point-in-time" adjustment.
Thanks in advance for your insights!
Beta Was this translation helpful? Give feedback.
All reactions