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.1.0 establishes the package foundation: typed statement and
transaction domain objects, source-evidence models, page-aware text models,
financial Decimal normalization, processor contracts, and deterministic
processor selection.
No specific bank statement format is claimed as supported in 0.1.0.
Institution-specific processors will be added only after they have been
developed and validated against real statement evidence.
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
banking-statements is currently in early development.
The 0.1.0 release provides the generic architecture required to build strict,
institution-specific bank statement processors without coupling the package to
a single bank or statement layout.
Current foundation:
banking-statements 0.1.0
Python
3.11
3.12
3.13
3.14
Domain
StatementSource
SourceEvidence
StatementPeriod
ParsedStatement
TransactionEvent
TransactionDirection
Financial values
Decimal-based normalization
Text
StatementPage
StatementText
StatementTextReader
Processors
ProcessorMatch
StatementProcessor
ProcessorRegistry
Validation
deterministic processor selection
explicit unsupported-statement failure
explicit ambiguous-processor failure
Quality
Ruff
mypy strict
pytest
100% branch coverage
Institution-specific statement support will be documented only after a processor has been validated against a meaningful private historical corpus.
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 initial public package exposes generic domain and processor primitives.
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",
)
The package does not yet expose a default bank-specific parser in 0.1.0.
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 intended processing pipeline is:
PDF
↓
page-aware text extraction
↓
institution detection
↓
processor selection
↓
statement structure
↓
logical transaction rows
↓
focused economic parsing
↓
normalized domain objects
↓
strict validation
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
banking-statements is intended to support multiple U.S. banking institutions.
Future processor families may include institutions such as:
Bank of America
Chase
U.S. Bank
Wells Fargo
Capital One
other U.S. banks
This list represents intended package scope, not current support.
A bank or statement format should be listed as supported only after its processor has been implemented and validated against real statements.
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 future smoke-test PASS should mean:
document extraction succeeded
institution detection succeeded
processor selection succeeded
required statement structure was understood
recognized financial rows were accounted for
normalized output validated
unsupported activity was not silently discarded
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 evidence
processor selection
strict statement validation
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
The next development phases are expected to be:
0.1.x
generic package foundation
page-aware PDF extraction
institution detection
statement inspection tooling
private archive smoke tooling
first institution milestone
first real bank detector signature
first statement processor
statement identity parsing
statement-period parsing
logical transaction rows
normalized transactions
real archive validation
later milestones
additional statement revisions
additional institutions
statement-level validation
possible reconciliation capabilities
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.1.0.tar.gz.
File metadata
- Download URL: banking_statements-0.1.0.tar.gz
- Upload date:
- Size: 10.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
15bd79c14d883a54fbc567c2df80f95e343ecee7d6b02788bd0f41994eea50bb
|
|
| MD5 |
d17c8f2359a2330217f166c7df4fcae6
|
|
| BLAKE2b-256 |
d4375cdccdb42119d339912d88decb33982737d1ffb28c4aecb18df30b34367a
|
Provenance
The following attestation bundles were made for banking_statements-0.1.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.1.0.tar.gz -
Subject digest:
15bd79c14d883a54fbc567c2df80f95e343ecee7d6b02788bd0f41994eea50bb - Sigstore transparency entry: 2461870814
- Sigstore integration time:
-
Permalink:
fifoa-labs/banking-statements@ce73f941152e97b4c8034786051059156d4b8ab7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/fifoa-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ce73f941152e97b4c8034786051059156d4b8ab7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file banking_statements-0.1.0-py3-none-any.whl.
File metadata
- Download URL: banking_statements-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.9 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 |
7325e14113472a14ce7c5abe617c8500cbe68bb75d9495aadaa797ff57d914c4
|
|
| MD5 |
7432ee35e7cef455e596ddaf825b68bb
|
|
| BLAKE2b-256 |
1604425d79262cb7b672e3d41e8a28ed3b8eba8f8da4be34d9f3248b64c9a80b
|
Provenance
The following attestation bundles were made for banking_statements-0.1.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.1.0-py3-none-any.whl -
Subject digest:
7325e14113472a14ce7c5abe617c8500cbe68bb75d9495aadaa797ff57d914c4 - Sigstore transparency entry: 2461870958
- Sigstore integration time:
-
Permalink:
fifoa-labs/banking-statements@ce73f941152e97b4c8034786051059156d4b8ab7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/fifoa-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ce73f941152e97b4c8034786051059156d4b8ab7 -
Trigger Event:
release
-
Statement type: