Brokerage Statement Extraction: Positions, Transactions, and Income to Structured Data
Brokerage statements feed portfolio aggregation, performance reporting, tax preparation, and lending verification — yet they resist automation, because the industry standardized what a statement must say, not what it must look like. This guide covers what's in them, why they're hard to parse, how to design an extraction schema, and how to validate the output.
What's in a brokerage statement — and why no two look alike
Under FINRA Rule 2231, carrying broker-dealers ("general securities members") must send account statements to customers at least quarterly for any account with a security position, money balance, or activity during the period. The rule specifies required content: a description of securities positions (in practice, statements also show market values per position), money balances, and account activity — purchases, sales, dividend and interest credits, fees, and transfers — along with a legend telling customers how to report inaccuracies.
Crucially, the rule prescribes content, not layout — aside from a few front-of-statement placement requirements (firm identity and contact information, SIPC membership, opening and closing balances), no layout is prescribed. With layout left open, custodians design their own statements — section ordering, table structure, terminology, and footnoting conventions vary widely, often even between account types at the same firm. That is the core engineering problem: a parser tuned to one custodian's template tends to break on the next, so a durable pipeline has to work from document structure rather than fixed positions on the page.
Why brokerage statements are hard to parse
A typical statement is a multi-section document — account summary, positions, account activity, income detail, fees, disclosures — where each section carries a different table shape. Several structural patterns recur across custodians' statements, and each defeats naive parsing in its own way:
-
Tables that span page breaks. A positions table may start midway down one page and continue across two more, sometimes with repeated headers, sometimes without. A parser that treats each page independently splits one holding list into disconnected fragments.
-
Footnote qualifiers on values. Market values are frequently marked with asterisks or superscripts pointing to footnotes — an estimated price, an unpriced security, a pending trade. Dropping the qualifier silently changes the meaning of the number.
-
Parenthesized negatives. Fees, withdrawals, and losses often appear as
(1,250.00)rather than with a minus sign. Text-level extraction that misses the parentheses flips the sign on every debit. -
Totals interleaved with line items. Subtotal and section-total rows sit inside the same tables as line items. If a total row is extracted as a position or transaction, downstream sums double-count.
Consider a typical positions section: an asset-class subheading, a dozen holdings rows, a subtotal, another subheading, more rows, then a portfolio total — continued onto the next page. Flat OCR loses the column alignment that distinguishes quantity from price from market value; layout-naive parsers merge subtotal rows into the holdings list. Getting this right requires a parser that preserves table structure — rows, columns, spans, hierarchy — across page boundaries.
Where traditional approaches fall short
Template and zonal OCR keys extraction on fixed page coordinates — but because Rule 2231 prescribes content rather than layout, there is no stable template to key on across custodians, and every statement redesign quietly invalidates the zones. Flat OCR text dumps fail in the opposite direction: the words survive but the table does not, and a positions list without its row and column structure stops being data. Rule-based extraction layered on either tends to become a per-custodian maintenance exercise, re-tuned for each new firm and each redesign. The job requires structure-preserving, template-free parsing that survives layout variance.
What to extract: designing the schema
Because Rule 2231 fixes minimum content categories for FINRA-member carrying firms — and bank and trust-company custodians sit outside FINRA's rule, though their statements carry similar content in practice — a single well-designed schema generally covers statements across custodians even though the layouts differ. A practical shape has five parts:
| Schema section | Shape | Typical fields |
|---|---|---|
| Account metadata | Single object | Account number, account type, statement period start/end, custodian name |
| Positions | Array | Security description, symbol or identifier, quantity, price, market value, asset class |
| Transactions | Array | Date, transaction type, security, quantity, amount |
| Income summary | Single object | Dividends, interest, other income for the period (and year-to-date where shown) |
| Fees | Array or object | Fee description, amount |
Two design principles matter more than the exact field list:
-
Model line items as repeating structures. Positions and transactions are repeated rows of unknown count, so define them as repeating structures and extract them with Deep Extract rather than as a fixed set of numbered fields. The extraction returns one object per row, however many rows the statement contains.
-
Keep a citation on every value. Financial data feeds decisions and filings, so every extracted number should point back to its exact location in the source document. Citations attach bounding-box references to each field, which makes both human review and automated audit tractable.
Describe fields the way the document does — "market value as of the statement period end date," not just "value" — and note sign conventions directly in field descriptions.
Building it with Reducto
The pipeline is the pattern Reducto's brokerage-statement cookbook walks through end to end on brokerage statements themselves: upload, parse with table structure preserved, then run Extract against your schema for structured JSON with citations.
In practice:
-
Parse the statement. Reducto's parser maintains row/column structure through page breaks and keeps footnote markers associated with their values, so the positions and activity tables arrive intact.
-
Extract with your schema. Deep Extract pulls every position and transaction row; the account metadata and income summary come back as single objects.
-
Review with citations. Each value carries a reference to its source region, so a reviewer can click from an extracted market value to the exact spot on the page.
-
Deploy. Deploy the working configuration as a stable pipeline endpoint and send statements to it programmatically, or run the whole flow without code in Studio — see the Studio quickstart.
For backfiles of historical statements, use batch processing: batch jobs queue with a 12-hour completion guarantee and a 20% discount on parsing credits.
Validating accuracy: reconciliation is the test suite
Brokerage statements come with their own answer key: the totals printed on the document. Use them.
-
Positions reconciliation. Sum the extracted market values per asset class and compare against the printed subtotals and the portfolio total. A mismatch means a missed row, a captured total row, or a sign error.
-
Activity reconciliation. Opening balance plus extracted deposits, withdrawals, income, and fees should equal the closing balance shown on the statement — the same transaction-level reconciliation Reducto's bank-statement reconciliation cookbook walks through on bank statements.
-
Income cross-check. Individual dividend and interest transactions should sum to the income summary figures.
These checks are also exactly what Reducto's Deep Extract builds into extraction itself: an agentic loop that verifies and corrects its results against the source document — catching missed rows, misaligned table cells, and inconsistencies — until a quality threshold is met, so much of the reconciliation burden is carried inside the extraction step rather than left entirely to downstream code.
Statements that reconcile cleanly flow straight through; those that don't are routed to human review, where bounding-box citations let the reviewer jump directly to the source region for any suspect value. For systematic ground-truth testing across vendors, see the evaluation bakeoff guide.
Compliance and deployment
Statement processing means handling account numbers, balances, and holdings. Reducto is SOC 2 Type II certified; zero data retention is available on Growth-tier plans and above; and for broker-dealers with stricter requirements, VPC and on-premise deployment keep documents inside your own environment. See enterprise readiness and reducto.ai/industries/finance for details.
FAQ
Can I extract cost basis and realized gain/loss tables?
Yes. They are repeated tabular structures like positions and transactions: define them as arrays with fields for acquisition date, cost basis, proceeds, and gain or loss, and extract them alongside the core sections.
How are consolidated or multi-account statements handled?
Custodians often deliver one PDF covering several accounts. Use Split to separate it into per-account documents first, then run the extraction pipeline on each, so positions and transactions never bleed across account boundaries. For very large consolidated files, the agentic Deep Split mode (4 credits/page) handles bundles running to thousands of pages.
Do I need to write code to set this up?
No. Studio provides a no-code path: upload sample statements, define the schema, test extraction and citations interactively, and deploy the pipeline. The same configuration is then callable via API when you're ready to integrate. If you primarily use coding agents, Reducto also offers an MCP and CLI to help you get started easily.
Will one schema work across custodians?
Generally yes — FINRA Rule 2231 fixes minimum content categories for FINRA-member carrying firms, and while bank and trust-company custodians sit outside FINRA's rule, their statements carry similar content in practice. Expect to refine field descriptions as you meet new custodians' terminology, but the schema shape carries across. Reducto is one-shot, meaning it can process documents you've never seen before and does not use a template.