Filing Change Intelligence

Understand what changed between 10-K filings.

Delta reads up to five years of SEC filings and writes the report an analyst would: what changed, when, and why it matters, with every claim traced to verbatim text from all years.

$ python delta.py AAPL --years 5

Analyze a ticker

Available: AAPL · AMZN · GOOGL · META · MSFT · NVDA · TSLA

How it works

A 10-K is 100+ pages, and roughly 95% of it is copied verbatim from the previous year. The signal lives in the 5% that changed, and nobody reads two filings side by side to find it. Delta splits that job in two: deterministic code finds every change, and a language model is allowed only to explain what the code already found.

01

Parse and anchor

Each year's filing is parsed into sections and labelled with an anchor, a stable name that survives the item renumbering and reshuffling filers do between years. Matching anchors give every year a shared coordinate system: Risk Factors in FY2021 is always compared to Risk Factors in FY2025, no matter where either sits in the document.

anchora fixed section name like item1a_risk or income_statement, assigned at parse time and identical across years
02

Align and classify, deterministically

Within each matched section, paragraphs are embedded and paired across years by cosine similarity. The score classifies every pair as unchanged, minor, or major revision; paragraphs with no counterpart become additions or removals. Thresholds are tuned for high recall: over-flag now, discard later, never miss. No language model is involved anywhere in this stage.

embeddinga paragraph converted to a numeric vector, where similar meanings land close together
cosine similaritya 0-to-1 score of how close two vectors point; near 1 means near-identical text
numeric guardre-checks "unchanged" pairs for value-only moves similarity can't see, like revenue going $100M → $489M in otherwise identical prose
03

Interpret, then verify every quote

Only flagged pairs reach the language model, and only to be explained: what kind of change, how much it matters, and short quotes from both years as evidence. Every quote must be a character-for-character substring of the actual filing; fail validation twice and the whole interpretation is excluded. An LLM asked to find changes will hallucinate them; one handed a verified diff and asked why it matters cannot.

materialitythe model's grade per change: boilerplate (date rolls, restyling), notable, or material, what a portfolio manager would want surfaced
04

Compose the report

The surviving interpretations become chapters that follow the 10-K's own structure, written as analyst prose: roughly a 15-minute read, not a change log. Every claim carries a citation into an evidence drawer holding the verbatim text from both years, and every figure in the financial tables comes from XBRL, never from the model.

XBRLthe machine-readable audited figures companies file alongside the prose; the report's numbers are read from it directly
evidence drawerthe expandable list under each chapter holding the exact quote pair behind every citation

Why this matters

The Lazy Prices research (Cohen, Malloy, Nguyen) found that firms whose filings change the most subsequently underperform the market. Analysts don't read diffs. Delta does.

The LLM never finds the diff. It only explains it. Detection is deterministic. Interpretation is generative. Every claim traces to a diff record.

From the same codebase

FinDocQA: the RAG eval harness

Delta is built on a retrieval layer that had to earn trust first. Before any report copy was written, a deterministic evaluation harness measured how chunking, embedding, and reranking choices affect retrieval accuracy over this same SEC filing corpus, against ground truth pulled from XBRL.

448scored answers (56 questions × 8 configs)
28/56best joint score, section-aware chunking
9-10/56joint score, fixed-size chunking

Section-aware chunking (splitting on 10-K item boundaries instead of a fixed token window) roughly triples retrieval accuracy over fixed-size chunking, regardless of embedding model.

The generation model is frozen across all eight configs, so every score change is attributable to a chunking, embedding, or rerank toggle, not the LLM. That is also how the harness caught the "rerank trap": adding a reranker consistently hurt numeric-match accuracy, since it favors MD&A prose that sounds relevant over the terse tables that actually contain the number being asked for.