banking-statements
Deterministic parsing, normalization, and validation of U.S. banking statements across institutions and statement formats.
banking-statements is a typed Python library for turning supported bank
statements into normalized Python domain objects while preserving the source
evidence needed to understand how each result was produced.
The library is designed around isolated statement processors. Each processor owns a known statement format or revision, allowing support for additional banks and statement variants to be added without destabilizing processors already proven against historical statements.
The project emphasizes strict and deterministic behavior. Unsupported statement formats, ambiguous processor matches, malformed recognized data, and unknown statement behavior should fail explicitly rather than being silently ignored or guessed.
Version 0.4.0 expands the package into multi-institution statement support.
Implemented processors now cover Chase credit cards, Chase checking, Chase
home-equity lines of credit, and multiple Wells Fargo consumer and business
statement families. The processors have been developed against private
historical corpora while public tests remain fully synthetic and contain no
private financial data.
The package intentionally focuses on answering:
What did this bank statement say?
It is not a budgeting application, bank API client, accounting system, merchant-categorization engine, personal finance manager, tax engine, database layer, or Beancount-specific importer.
- PyPI: https://pypi.org/project/banking-statements/
- Source: https://github.com/fifoa-labs/banking-statements
- License: MIT
Current Status
Current release:
banking-statements 0.4.0
Supported Python versions:
Python 3.11
Python 3.12
Python 3.13
Python 3.14
Current implemented statement support:
Chase
credit cards
modern statement layouts
historical statement layouts
co-branded statement layouts
observed formats spanning 2019–2026
checking
Chase Total Checking statements
multi-page transaction detail
wrapped transaction descriptions
observed formats spanning 2019–2026
home-equity line of credit
Chase Home Equity Line of Credit statements
advances and initial funding
payments and additional-principal payments
fee assessments and fee payments
finance-charge accrual
funds-applied and funds-reversed allocation behavior
payoff and credit-balance statements
zero-activity statements
observed formats spanning 2020–2022
Wells Fargo
checking
credit card
business checking
business credit card
business line of credit
Current normalized domain includes:
StatementSource
SourceEvidence
StatementPeriod
StatementBalanceSummary
ParsedStatement
AccountType
AccountIdentity
TransactionEvent
TransactionDirection
Current Chase credit-card capabilities include account identity, masked and unmasked account numbers, statement periods, opening and closing balances, purchases, payments, merchant credits, fees, interest charges, balance transfers, My Chase Loan activity, promotional adjustments, reversals, cross-year transaction dates, historical transaction-date references, foreign-currency continuation preservation, and statement reconciliation.
Current Chase checking capabilities include account identity, statement periods, beginning and ending balances, signed transaction-table parsing, multi-page transaction detail, wrapped and continued descriptions, deposits, withdrawals, ACH activity, card payments, transfers, fees, credits, cross-year transaction dates, debit/credit normalization, and statement reconciliation.
Current Chase HELOC capabilities include account identity, statement periods, opening and closing debt balances, advances, initial funding, additional principal payments, regular payments, fee assessments and fee payments, finance-charge accrual, funds-applied and funds-reversed allocation behavior, payoff cycles, negative credit balances, zero-activity cycles, and strict line-of-credit reconciliation. The private Chase HELOC corpus currently validates 28 of 28 monthly statements with zero reconciliation difference.
Current Wells Fargo support includes consumer checking and credit-card statements plus business checking, business credit-card, and business line-of-credit statement families. The Wells Fargo BusinessLine processor uses layout-aware PDF evidence where column position carries transaction direction.
The complete private statement archive currently validates:
1001 / 1001 PASS
reconciliation=PASS
difference=0.00
Quality gates include Ruff, strict mypy, pytest, 100% branch coverage, distribution validation, typed-wheel validation, clean-wheel installation, and full private-archive smoke validation.
Installation
Install from PyPI:
pip install banking-statements
With uv:
uv add banking-statements
Or for development:
git clone https://github.com/fifoa-labs/banking-statements.git
cd banking-statements
uv sync --dev
Basic Usage
The package exposes generic domain primitives plus implemented statement processors for supported Chase and Wells Fargo statement families.
from datetime import date
from decimal import Decimal
from pathlib import Path
from banking_statements import (
StatementPeriod,
StatementSource,
TransactionDirection,
TransactionEvent,
)
source = StatementSource(
path=Path("statement.pdf"),
sha256="example-sha256",
)
period = StatementPeriod(
start=date(2026, 7, 1),
end=date(2026, 7, 31),
)
transaction = TransactionEvent(
date=date(2026, 7, 15),
amount=Decimal("42.17"),
direction=TransactionDirection.DEBIT,
description="Sample purchase",
)
Chase credit-card statements are handled by the stable processor identifier
chase.credit_card.v1.
Chase checking statements are handled by:
chase.checking.v1
Chase home-equity line-of-credit statements are handled by:
chase.heloc.v1
Chase Credit Card Support
0.2.0 includes real Chase credit-card statement support through:
chase.credit_card.v1
The processor supports compatible modern, historical, and co-branded Chase credit-card statement grammars observed in the private development corpus from 2019 through 2026.
The implementation intentionally tolerates only known PDF text-extraction
artifacts demonstrated by real statements. Examples include variants such as Opening/Closing Date versus the extracted form
O\pening/Closing Date, and New BalanceversusN`ew Balance`. Broad fuzzy matching and generic character de-duplication are
intentionally avoided.
Historical statements may expose full account numbers while newer statements
may expose masked numbers. AccountIdentity preserves the display value stated
by the source document and separately exposes the last four digits when
available.
Statement balances
Supported statements expose generic balance checkpoints:
StatementBalanceSummary(
opening_balance=...,
closing_balance=...,
)
These values are parsed as stated by the bank. They are not rewritten to force reconciliation.
Supported activity
The Chase credit-card processor currently normalizes activity including:
purchases
payments
merchant credits
fees
interest charges
balance transfers
My Chase Loan activity
promotional adjustments
reversals
Normalized amounts are positive magnitudes and TransactionDirection carries
the economic direction.
Chase Checking Support
Chase checking statements are handled through:
chase.checking.v1
The processor has been validated against a private chronological corpus of 182 statements spanning observed formats from 2019 through 2026. The complete private corpus currently passes strict parsing and reconciliation:
182 / 182 PASS
reconciliation=PASS
difference=0.00
The parser supports statement identity, reporting periods, beginning and ending balances, transaction-detail tables, multi-page activity sections, wrapped transaction descriptions, and normalized checking transactions.
Checking transaction rows are reconstructed from statement text shaped like:
DATE DESCRIPTION AMOUNT BALANCE
The signed statement amount is interpreted from the checking account's perspective:
positive amount
→ CREDIT
negative amount
→ DEBIT
Normalized TransactionEvent.amount values remain positive magnitudes while
TransactionDirection carries the economic direction.
Chase checking statements can include description continuations on later physical lines. The processor reconstructs those logical rows before economic normalization rather than silently discarding continuation content.
The running-balance column remains parser evidence from the source statement; the normalized public transaction model intentionally stays focused on the transaction date, amount, direction, description, and optional source evidence.
Chase HELOC Support
Chase home-equity line-of-credit statements are handled through:
chase.heloc.v1
The processor has been validated against a private chronological corpus of 28 monthly statements spanning observed formats from 2020 through 2022:
28 / 28 PASS
reconciliation=PASS
difference=0.00
The processor normalizes debt-increasing activity such as initial funding, balance advances, assessed fees, and gross finance charges as debits. Debt-reducing activity such as additional-principal payments, fee payments, and funds actually applied to the account is normalized as credits.
Chase HELOC statements expose payment-allocation rows that are not always independent economic transactions. The processor distinguishes payment allocation detail, funds applied, and funds reversed so statement bookkeeping is not double-counted.
Finance charges are parsed from the statement's finance-charge calculation section rather than inferred from the closing summary alone. This matters when interest accrues and is paid within the same statement cycle, including payoff cycles where the closing summary may report no remaining interest even though finance charges accrued during the period.
The processor also supports zero-activity statements and credit-balance statements where the reported balance is negative.
Reconciliation
Reconciliation is optional and separate from parsing.
from banking_statements.reconciliation import reconcile_statement
result = reconcile_statement(statement)
Reconciliation is account-type aware.
For debt accounts such as credit cards and lines of credit:
opening balance
+ parsed debits
- parsed credits
= expected closing balance
For deposit accounts such as checking and savings:
opening balance
+ parsed credits
- parsed debits
= expected closing balance
The result includes the parsed debit and credit totals, expected closing
balance, difference, and a reconciled boolean. A mismatch does not modify or
reject the parsed statement.
The private archive smoke tooling is stricter by default and treats a reconciliation mismatch as a smoke failure so incomplete or misdirected activity is surfaced during development.
Financial Values
Financial values use Decimal.
from decimal import Decimal
from banking_statements import to_decimal
assert to_decimal("123.45") == Decimal("123.45")
assert to_decimal("$1,234.56") == Decimal("1234.56")
assert to_decimal("(42.17)") == Decimal("-42.17")
Floating-point arithmetic is intentionally avoided for normalized financial values.
Source Evidence
Normalized statement data should remain traceable to the source statement.
from pathlib import Path
from banking_statements import SourceEvidence, StatementSource
source = StatementSource(
path=Path("statement.pdf"),
sha256="example-sha256",
)
evidence = SourceEvidence(
source=source,
page=2,
section="Account Activity",
raw_text="07/15 SAMPLE PURCHASE 42.17",
processor="example.monthly",
sequence=14,
)
Evidence can preserve information such as:
source file identity
page
section
raw text
processor
sequence
This provenance is important for auditing parser behavior and for future reconciliation layers.
Architecture
The implemented processing pipeline is:
PDF
↓
PdfStatementTextReader
↓
page-aware, layout-aware StatementText
↓
institution detection
↓
processor selection
↓
identity and statement-balance parsing
↓
logical transaction rows
↓
focused economic parsing
↓
ParsedStatement
↓
optional reconciliation
The architecture separates document mechanics from normalized financial meaning.
The domain layer should not depend on:
PDF layouts
regular expressions
specific banks
Django
databases
Beancount
application frameworks
Processor Model
Processors represent known statement grammars.
Conceptually:
class StatementProcessor(Protocol):
@property
def name(self) -> str: ...
def match(
self,
text: StatementText,
) -> ProcessorMatch: ...
def parse(
self,
source: StatementSource,
text: StatementText,
) -> ParsedStatement: ...
Processors should be narrow enough that previously proven behavior remains stable as the package grows.
A materially different statement structure should generally receive a new processor rather than turning an existing processor into an increasingly broad universal parser.
Deterministic Processor Selection
ProcessorRegistry requires exactly one compatible processor.
0 matches
→ UnsupportedStatementError
1 match
→ selected
2 or more matches
→ AmbiguousProcessorError
There is intentionally no "first matching processor wins" behavior.
Processor registration order must not silently resolve ambiguous statement formats.
Development Philosophy
The central maintenance rule is:
Proven behavior stays stable.
When a future statement fails, the failure should first be classified.
New institution?
→ add institution detection and processor support
Same institution, materially different statement structure?
→ add a new processor
Same processor, new economic capability?
→ add a focused capability module
Same capability, legitimate new grammar?
→ extend only that capability
Unknown or ambiguous input?
→ fail loudly
The package should grow as a library of proven document grammars rather than as one parser that attempts to understand every possible statement.
Strict Failure Policy
A parser success should mean that the known statement grammar was understood.
The package should not silently discard or guess around:
unknown transaction rows
unknown required sections
ambiguous processor matches
ambiguous amounts
unsupported date grammar
malformed recognized rows
unresolved statement identity
invalid normalized output
Specific failures are preferred over generic parse errors because they make future statement support easier to develop and audit.
Logical Rows
PDF extraction often does not produce one physical line per financial transaction.
Real statements may contain:
wrapped descriptions
continuation lines
multi-line ACH details
fragmented columns
inherited dates
page breaks inside tables
When required, processors should reconstruct logical rows before attempting to normalize economic meaning.
physical extracted lines
↓
logical statement rows
↓
economic normalization
This keeps layout reconstruction separate from transaction interpretation.
Institution Support
Current implemented support:
Chase
credit cards
checking
home-equity line of credit
Wells Fargo
checking
credit card
business checking
business credit card
business line of credit
Chase credit-card and checking processors have been validated against broad private historical corpora covering observed statement formats from 2019 through 2026. Chase HELOC support has been validated against 28 monthly statements covering observed formats from 2020 through 2022.
The complete private development archive currently contains 1001 supported statements, all of which pass extraction, detection, processor selection, parsing, normalization, and strict reconciliation with zero difference.
A bank or statement format is listed as supported only after its processor has been implemented and validated against real statement evidence.
Private Statement Corpus
Real financial statements used during development are maintained outside the repository.
The expected local structure is:
private-data/
└── statements/
├── institution-a/
├── institution-b/
└── ...
private-data/ is excluded from Git.
Real statements, account numbers, transaction histories, names, addresses, and other private financial data must never be committed to the repository or distributed in package artifacts.
Public tests use synthetic statement data.
Statement Inspection
Development includes tooling for inspecting the exact text extracted from a PDF statement.
make inspect-statement \
file="private-data/statements/example/statement.pdf"
Inspect a specific page:
make inspect-statement \
file="private-data/statements/example/statement.pdf" \
page=2
Optionally limit displayed text:
make inspect-statement \
file="private-data/statements/example/statement.pdf" \
head=3000
Parser behavior should be developed against the text actually returned by the package's PDF extraction layer rather than assumptions based only on how a PDF looks visually.
Archive Smoke Testing
Institution processors will be validated against private historical statement archives.
The development workflow is intentionally chronological:
01 PASS
02 PASS
03 PASS
04 FAIL
Development stops at the first failure.
That statement is inspected, the failure is classified, and the smallest correct capability is added.
Then the archive is rerun from the beginning.
Typical usage:
make smoke-archive \
folder="private-data/statements/example"
Limit the run:
make smoke-archive \
folder="private-data/statements/example" \
limit=10
Continue after failures when investigating an archive:
make smoke-archive \
folder="private-data/statements/example" \
continue=1
A strict smoke PASS means:
document extraction succeeded
institution detection succeeded
processor selection succeeded
statement identity parsed
statement period parsed
opening and closing balances parsed
logical activity reconstructed
transactions normalized
debit/credit direction assigned
statement reconciliation succeeded
Run a single statement and print normalized transactions:
uv run python -m scripts.archive_smoke \
private-data/statements/chase/credit-card/example.pdf \
--show-transactions
Allow reconciliation mismatches for investigation without turning them into smoke failures:
uv run python -m scripts.archive_smoke \
private-data/statements/chase/credit-card \
--allow-reconciliation-failures
Development
Install development dependencies:
uv sync --dev
Format:
make format
Check formatting:
make format-check
Lint:
make lint
Type check:
make typecheck
Run tests:
make test
Run tests in parallel:
make test-fast
Run branch coverage:
make coverage
The project maintains:
100% branch coverage
Quality Gates
Run the normal validation suite:
make check
Run the CI-equivalent validation pipeline:
make ci
Before preparing a release:
make release-check
The release check validates:
formatting
linting
mypy
100% branch coverage
distribution build
distribution metadata
typed wheel contents
clean-wheel installation
Build
Build the source distribution and wheel:
make build
Validate distributions:
make check-dist
Inspect wheel contents:
make wheel-contents
Install the built wheel into a clean environment:
make install-wheel
The distributed wheel includes:
banking_statements/py.typed
so type information is available to downstream type checkers.
Dependency Management
The project uses uv.
Synchronize the development environment:
make sync
Refresh the lockfile:
make lock
Upgrade dependencies:
make upgrade
uv.lock is committed so CI and local release validation can use reproducible
locked environments.
After changing package metadata or dependencies, refresh the lockfile before committing when required:
uv lock
Python Support
Supported Python versions:
Python 3.11
Python 3.12
Python 3.13
Python 3.14
CI validates the full supported version matrix.
Typing
banking-statements is a typed package.
The project uses strict mypy checking during development:
make typecheck
The wheel includes the PEP 561 marker:
banking_statements/py.typed
Scope
The package is intentionally narrow.
It aims to provide:
bank statement parsing
statement normalization
source and layout evidence
processor selection
strict statement validation
account-type-aware reconciliation
typed Python domain objects
It does not aim to provide:
online banking access
bank API integrations
budgeting
merchant categorization
tax accounting
bookkeeping rules
ledger rendering
Beancount-specific output
Django integration
database models
REST APIs
background jobs
web interfaces
Those concerns can consume the normalized statement objects produced by this package without becoming responsibilities of the statement parser itself.
Roadmap
Current milestone:
0.4.0
multi-institution statement support
Chase
credit card
checking
home-equity line of credit
Wells Fargo
checking
credit card
business checking
business credit card
business line of credit
account-type-aware reconciliation
layout-aware PDF evidence
strict processor selection
1001 / 1001 private statements PASS
Expected next phases:
additional Chase account families when supported by evidence
additional institutions
additional statement grammars
additional reconciliation capabilities when required by statement evidence
The roadmap is evidence-driven.
Modules and abstractions should be added because real statement formats require them, not because they appear theoretically useful.
Contributing
Contributions are welcome.
Please read CONTRIBUTING.md before submitting changes.
The most important contribution rule is:
New evidence should extend the system at the smallest correct boundary without destabilizing previously proven processors.
Never include real private financial statements or personally identifiable financial information in issues, pull requests, tests, or commits.
Security
Please report security issues according to SECURITY.md.
Do not disclose private financial information or credentials in public security reports.
License
banking-statements is released under the MIT License.
See LICENSE for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file banking_statements-0.4.0.tar.gz.
File metadata
- Download URL: banking_statements-0.4.0.tar.gz
- Upload date:
- Size: 45.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
377d4b8d9e1f5b26f2c69eb21e19e6a3097b4a6d6a17a6c715e5e139ac747809
|
|
| MD5 |
405dc28e9acbd7ade0ef81950a0d0b11
|
|
| BLAKE2b-256 |
97aac18c90f59902e6ca5c371f0dbbf7c29bc58b997ae04e25fb240dbf85526a
|
Provenance
The following attestation bundles were made for banking_statements-0.4.0.tar.gz:
Publisher:
publish.yml on fifoa-labs/banking-statements
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
banking_statements-0.4.0.tar.gz -
Subject digest:
377d4b8d9e1f5b26f2c69eb21e19e6a3097b4a6d6a17a6c715e5e139ac747809 - Sigstore transparency entry: 2481344099
- Sigstore integration time:
-
Permalink:
fifoa-labs/banking-statements@cc59e4f27e1ff2508da050537b0b83000d937efe -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/fifoa-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cc59e4f27e1ff2508da050537b0b83000d937efe -
Trigger Event:
release
-
Statement type:
File details
Details for the file banking_statements-0.4.0-py3-none-any.whl.
File metadata
- Download URL: banking_statements-0.4.0-py3-none-any.whl
- Upload date:
- Size: 87.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d9b0aa4a3574ba8dcf2a7f6c49f208f785b9920ec5fae57d88028d4034eef32
|
|
| MD5 |
5f923abdc3228de24d2fa71c0829605a
|
|
| BLAKE2b-256 |
310121b437324f3975ea66238099ead2e24c28d2795937c68877ca4aace53110
|
Provenance
The following attestation bundles were made for banking_statements-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on fifoa-labs/banking-statements
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
banking_statements-0.4.0-py3-none-any.whl -
Subject digest:
0d9b0aa4a3574ba8dcf2a7f6c49f208f785b9920ec5fae57d88028d4034eef32 - Sigstore transparency entry: 2481344529
- Sigstore integration time:
-
Permalink:
fifoa-labs/banking-statements@cc59e4f27e1ff2508da050537b0b83000d937efe -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/fifoa-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cc59e4f27e1ff2508da050537b0b83000d937efe -
Trigger Event:
release
-
Statement type: