Financial data engineering pipelines processing SEC EDGAR facts frequently suffer from a silent, destructive bug: quarter-over-quarter metrics suddenly spike or double without warning. When querying SEC EDGAR Company Facts for interim filings (Form 10-Q), naive parsers often assume that every reported fact under a fiscal period tag such as fp: "Q2" or fp: "Q3" represents three months of economic activity.
This assumption is incorrect. Under U.S. GAAP and SEC reporting regulations, interim financial statements blend discrete quarterly measurements with cumulative year-to-date (YTD) measurements. In this guide, we break down the mechanical difference between discrete and cumulative XBRL durations, demonstrate how this silently corrupts financial data, and provide a verified Python implementation to normalize quarterly facts accurately.
The Architectural Failure: How Duration Ambiguity Breaks Pipelines
When an automated pipeline extracts financial facts from the SEC EDGAR API endpoint (/api/xbrl/companyfacts/CIK{cik}.json), each reported metric in the units array contains metadata fields including form, fy (fiscal year), fp (fiscal period), start, end, and val.
Two distinct reporting behaviors create data engineering traps:
Dual Records in the Income Statement: For Q2 and Q3 Form 10-Q filings, companies are required to disclose both the discrete 3-month figures and the cumulative 6-month or 9-month YTD figures. For concepts such as
RevenuesorOperatingIncomeLoss, filtering byfy: 2026andfp: "Q2"returns two separate records for the same filing accession number. A parser that selects the first record or the maximum value inadvertently grabs the 6-month cumulative figure, instantly corrupting quarterly trend lines.Absence of Discrete Facts in Cash Flow Statements: Under ASC 230 and Regulation S-X Rule 10-01, the Statement of Cash Flows in Form 10-Q is presented only on a cumulative year-to-date basis. There is no discrete 3-month cash flow fact reported for Q2 or Q3. A query searching for discrete Q2
NetCashProvidedByUsedInOperatingActivitiesreturns zero results—or worse, matches the 6-month cumulative fact and treats it as a single quarter's cash generation.
Dissecting Real Filings: Qualcomm FY26 Disclosures
To see the magnitude of this distortion, consider Qualcomm Incorporated (CIK 0000804328) across its fiscal 2026 filings.
1. Revenue: Conflicting Records in Form 10-Q Q2
In Qualcomm's Q2 FY26 Form 10-Q (Accession 0000804328-26-000061), the SEC facts endpoint lists two distinct USD values under us-gaap:Revenues:
-
start: 2025-12-29, end: 2026-03-29(90 days): $10,599,000,000 (Discrete Q2) -
start: 2025-09-29, end: 2026-03-29(181 days): $22,851,000,000 (Cumulative 6-Month YTD)
If a parser filters by fp == 'Q2' without evaluating the duration span (end - start), selecting the YTD record overstates Q2 revenue by 115.6%.
2. Operating Cash Flow: Deriving What Is Not Directly Filed
In the same fiscal year, Qualcomm's reported operating cash flow facts are strictly cumulative:
- Q1 10-Q (90 days, YTD): $4,965,000,000
- Q2 10-Q (181 days, YTD): $7,414,000,000
- Q3 10-Q (272 days, YTD): $8,405,000,000
- Full Year 10-K (363 days, YTD): $11,145,000,000
To determine discrete cash generation for Q2, Q3, and Q4, the pipeline must derive the discrete delta:
$$\text{Discrete Cash Flow}{Q_n} = \text{YTD Cash Flow}{Q_n} - \text{YTD Cash Flow}{Q{n-1}}$$
Applying this normalization yields:
- Discrete Q1: $4,965,000,000
- Discrete Q2: $7,414,000,000 - $4,965,000,000 = $2,449,000,000
- Discrete Q3: $8,405,000,000 - $7,414,000,000 = $991,000,000
- Discrete Q4: $11,145,000,000 - $8,405,000,000 = $2,740,000,000
Treating the Q3 cumulative fact ($8.405B) as quarterly cash flow overstates Qualcomm's actual third-quarter cash generation ($991M) by 748%.
Implementing the Normalization Pipeline in Python
The following complete script fetches SEC EDGAR company facts, inspects the duration in days, extracts discrete revenue records, and derives discrete cash flows across quarterly transitions.
import datetime import gzip import json import urllib.request from typing import Any, Dict, List, Optional def fetch_company_facts(cik: str, user_agent: str) -> Dict[str, Any]: """Fetch structured XBRL facts from SEC EDGAR API.""" cik_padded = cik.strip().zfill(10) url = f"https://data.sec.gov/api/xbrl/companyfacts/CIK{cik_padded}.json" req = urllib.request.Request( url, headers={"User-Agent": user_agent, "Accept-Encoding": "gzip, deflate"}, ) with urllib.request.urlopen(req, timeout=15) as resp: raw = resp.read() if ( resp.info().get("Content-Encoding") == "gzip" or raw[:2] == b"\x1f\x8b" ): raw = gzip.decompress(raw) return json.loads(raw.decode("utf-8")) def get_duration_days(start_str: str, end_str: str) -> int: """Calculate the number of calendar days in a reporting period.""" start = datetime.date.fromisoformat(start_str) end = datetime.date.fromisoformat(end_str) return (end - start).days def extract_discrete_revenue( facts: Dict[str, Any], fiscal_year: int ) -> Dict[str, float]: """Extract discrete quarterly revenues by selecting ~90-day intervals.""" units = ( facts.get("facts", {}) .get("us-gaap", {}) .get("Revenues", {}) .get("units", {}) .get("USD", []) ) quarterly_rev: Dict[str, float] = {} for record in units: if record.get("fy") != fiscal_year or record.get("form") != "10-Q": continue fp = record.get("fp") if not fp or fp not in ("Q1", "Q2", "Q3"): continue days = get_duration_days(record["start"], record["end"]) # A discrete quarter spans between 70 and 105 days if 70 <= days <= 105: quarterly_rev[fp] = float(record["val"]) return quarterly_rev def derive_discrete_cash_flows( facts: Dict[str, Any], fiscal_year: int ) -> Dict[str, float]: """Derive discrete quarterly cash flows by computing YTD deltas.""" units = ( facts.get("facts", {}) .get("us-gaap", {}) .get("NetCashProvidedByUsedInOperatingActivities", {}) .get("units", {}) .get("USD", []) ) ytd_records: Dict[str, float] = {} for record in units: if record.get("fy") != fiscal_year: continue form = record.get("form") fp = record.get("fp") if form == "10-Q" and fp in ("Q1", "Q2", "Q3"): ytd_records[fp] = float(record["val"]) elif form == "10-K" and fp == "FY": ytd_records["FY"] = float(record["val"]) discrete: Dict[str, float] = {} if "Q1" in ytd_records: discrete["Q1"] = ytd_records["Q1"] if "Q2" in ytd_records and "Q1" in ytd_records: discrete["Q2"] = ytd_records["Q2"] - ytd_records["Q1"] if "Q3" in ytd_records and "Q2" in ytd_records: discrete["Q3"] = ytd_records["Q3"] - ytd_records["Q2"] if "FY" in ytd_records and "Q3" in ytd_records: discrete["Q4"] = ytd_records["FY"] - ytd_records["Q3"] return discrete def main() -> None: cik = "0000804328" # Qualcomm Incorporated ua = "DataEngineeringPipeline/1.0 (dev@example.com)" fy = 2026 print(f"Fetching disclosures for CIK {cik}...") facts = fetch_company_facts(cik, ua) revs = extract_discrete_revenue(facts, fy) print(f"\n--- Discrete Quarterly Revenues (FY{fy}) ---") for q, val in sorted(revs.items()): print(f" {q}: ${val:,.0f}") cf = derive_discrete_cash_flows(facts, fy) print(f"\n--- Derived Discrete Operating Cash Flows (FY{fy}) ---") for q, val in sorted(cf.items()): print(f" {q}: ${val:,.0f}") total_cf = sum(cf.values()) print(f" Sum of Quarters: ${total_cf:,.0f}") if __name__ == "__main__": main() Critical Engineering Tradeoffs and Production Realities
When implementing duration normalization in enterprise pipelines, account for three structural edge cases:
- Restatements Across Accession Numbers: In subsequent filings, companies may reclassify prior quarterly results. When deriving deltas (such as
Q2 YTD - Q1 YTD), prioritize YTD figures filed within the same reporting sequence or compare the most recent restated baseline. Mixing a restated Q2 YTD figure with an unrevised Q1 filing can introduce artificial reconciliation variances. - 52-53 Week Fiscal Calendars: Retailers and technology companies operating on non-calendar fiscal years have varying quarter lengths (e.g., 13 weeks vs 14 weeks). Hardcoding exact 90-day intervals causes parser misses; use a duration tolerance window of 70 to 105 days to identify discrete quarters reliably.
- Negative Deltas in Cash Flows: A derived discrete cash flow figure can be negative even if the cumulative YTD cash flow remains positive. Pipeline validations should never reject negative quarterly cash flows as anomalies, as capital-intensive quarters routinely consume cash after strong earlier periods.
Conclusion
Reliable financial data engineering requires moving beyond basic field matching to understand regulatory reporting structures. By evaluating calendar duration days and programmatically deriving discrete interim deltas, automated pipelines prevent severe metric inflation and maintain institutional-grade financial data.
This post is part of the **SEC Filing Data Engineering* series on DEV. For the previous installment on reconciling operating cash flows and tax adjustments, see Parsing SEC EDGAR Facts: Reconciling Cash Flow with Python.*
