Skip to content

Extracting Opening and Closing Balances From a Multi-Page Bank Statement

9 min read · updated August 11, 2026

A bank statement is one of the few documents that will tell you when your extraction of it is wrong. It does that through a single identity, and the value of the identity is not that it passes — it is what the failure looks like when it does not.

The identity that makes a statement checkable

Over the statement period, the closing balance equals the opening balance plus every credit minus every debit. Nothing else on the page is verifiable without an external source, and this one is verifiable from the page alone:

opening              1_482.11
+ sum(credits)       6_310.00
- sum(debits)        5_927.44
= computed_closing   1_864.67
printed_closing      1_864.67
residual                 0.00

There are three further checks worth running, each catching something the first does not. Most statements print a summary box on the first page — opening balance, total credits, total debits, closing balance — which is an independent statement of the same four numbers laid out differently, so extracting it separately and comparing gives you two readings of one fact for the cost of one more crop. Statements with a running balance column let you check every row: the balance after row n equals the balance after row n-1 plus that row’s signed amount, which localises an error to a single line rather than to the document. And where a running balance is printed at the foot of each page and again at the head of the next, the two must match, which localises to a page boundary.

Run all of them. They cost nothing, they are pure arithmetic over numbers you already have, and each fails on a different class of mistake.

Which balance is which

The identity only holds if you extracted the right two balances, and a statement offers several that look interchangeable and are not.

  • Opening / beginning / previous balance — the balance at the start of the statement period. On a statement that begins mid-month this is not the balance on the first of the month, and the period dates printed in the header are what define it.
  • Closing / ending / new balance — the balance at the end of the period, after interest and fees, which most banks post on the final day. An extractor that takes the last running-balance value in the transaction table can miss a fee posted below the table.
  • Available balance — the ledger balance minus holds and uncleared items. It is a snapshot at print time, it is not the closing balance, and it does not satisfy the identity. It appears on statements from banks whose statement is generated from an online view.
  • Credit card statement balance — on a card, the sign convention inverts. Purchases increase what you owe, payments reduce it, and “new balance” is a liability. The identity still holds; the labels for credit and debit have swapped meaning relative to a deposit account.

Store the period start and end dates alongside the balances and assert that consecutive statements chain: this statement’s opening balance equals the previous statement’s closing balance, and its period starts the day after the previous one ended. A gap in that chain is a statement you never received, which is invisible from any single document.

Signs, parentheses and CR/DR

More extraction bugs live in sign conventions than in digit recognition. Statements express a negative amount in at least five ways: a leading minus, a trailing minus, parentheses around the number, a CR or DR suffix, or by position — separate debit and credit columns where every printed value is unsigned and the column carries the meaning.

The positional case is the dangerous one, because a table extractor that merges two narrow adjacent columns produces a perfectly plausible list of positive amounts with no indication that half of them should be negative. It is also the case a vision model handles worst, since the only evidence is horizontal position relative to a header several rows above. Ask for the column each value came from, not just the value.

Locale conventions compound this. A statement using 1.234,56 for one thousand two hundred and thirty-four and a half will be parsed as 1.23456 or 1234.56 or 1.234 depending on which naive rule you applied, and a date written 03/04/2026 is March in one convention and April in another. Neither is recoverable from the number alone; both are recoverable from the statement header, which names the bank and usually the country. Extract locale first, then parse.

How a page goes missing

The reason this page exists is that missing pages are common and silent. A statement PDF arrives with marketing inserts between transaction pages. A scan of a posted statement drops a sheet that stuck to the one above it. A page consisting entirely of check images gets skipped by a pipeline that filters pages without extractable text. A rescan puts the pages in a different order. And an extractor with a page cap silently processes the first ten pages of a fourteen-page statement.

Two cheap defences. Statements print “Page 3 of 7” in a header or footer — extract both numbers from every page, check the set of page numbers is complete and the total agrees, and you catch most of these before doing any arithmetic. Then let the balance identity catch the rest, because a dropped page produces a residual equal to that page’s net movement, which is almost never zero. The general mechanics of getting pages and reading order out of a PDF are covered in PDF parsing.

Reading the residual

When the identity fails, the residual is diagnostic. This is the part worth building into the pipeline, because it turns “the statement did not balance” into a specific instruction.

  • Residual equals an extracted transaction amount— a row was dropped or duplicated. Search the extracted rows for that amount; if it appears once, it was counted twice or the duplicate is missing, and the running balance column will say which.
  • Residual is exactly twice an amount — a sign error on that row. A debit parsed as a credit moves the computed closing by two times its value, which is the single most recognisable signature in this list.
  • Residual equals a page’s net movement — a whole page is missing. Compare against the page-boundary running balances to find which.
  • Residual is small and under a currency unit — rounding, or interest posted but not itemised. Worth a tolerance, but log it rather than absorbing it silently; a persistent few cents is a real bug.
  • Residual is a digit-place multiple — 9, 90, 900, or 0.9 — a single transposed or misread digit. A residual divisible by 9 is the classic signature of two digits swapped within one number, and it tells you the magnitude of the field to re-read.

None of this requires a model. It is arithmetic run over the extraction output, it is deterministic, and it converts a vague quality problem into a targeted re-read of one row or one page. When it cannot be resolved, the statement goes to review with the residual and its classification attached, which is a far more actionable queue item than a low confidence score.