Skip to main content

A library to parse MT940 files and returns smart Python collections for statistics and manipulation.

Project description

MT940

CI PyPI Python Downloads Documentation Coverage License

mt940 parses MT940 bank statement files into smart, fully typed Python collections you can iterate, aggregate and serialize. It has no runtime dependencies, ships type information (py.typed), and copes with the quirks of many real-world banks.

import mt940

transactions = mt940.parse('statement.sta')
for transaction in transactions:
    print(transaction.data['date'], transaction.data['amount'])

Why mt940

  • Zero runtime dependencies — pure standard library.
  • Fully typed — ships py.typed; checked under pyright, mypy and pyrefly.
  • Battle-tested — 100% test coverage against fixtures from many banks.
  • Smart models — amounts, balances and dates come back as rich Python objects, not raw strings.
  • JSON-ready — a single encoder serializes a whole statement.
  • Extensible — opt-in tags and pre/post processors for bank-specific formats.
  • Modern Python — supports 3.10 through 3.13.

Installation

pip install mt-940

Using uv:

uv add mt-940

Requires Python 3.10 or newer.

Quick start

parse() accepts a filename, an open file handle, or the raw str/bytes contents, and returns a Transactions collection:

import mt940
import pprint

transactions = mt940.parse('mt940_tests/jejik/abnamro.sta')

# Statement-level data (balances, account, ...) lives on the collection:
pprint.pprint(transactions.data)

# Iterate the individual transactions:
for transaction in transactions:
    print(transaction.data['date'], transaction.data['amount'])
    pprint.pprint(transaction.data)

Each Transaction exposes a data dictionary with the parsed fields. Which fields are present depends on the source bank and the tags in the file.

Usage

Reading balances

Statement-level balances live on the Transactions object's data, not on the individual transactions — this works even for files with no transactions at all:

import mt940

transactions = mt940.parse('statement.sta')
print(transactions.data['final_opening_balance'])
print(transactions.data['final_closing_balance'])
print(transactions.data['available_balance'])

Set opening / closing balance on each transaction

import mt940
import pprint

mt940.tags.BalanceBase.scope = mt940.models.Transaction

# The currency has to be set manually when moving the BalanceBase scope to
# Transaction.
transactions = mt940.models.Transactions(processors=dict(
    pre_statement=[mt940.processors.add_currency_pre_processor('EUR')],
))

with open('mt940_tests/jejik/abnamro.sta') as fh:
    transactions.parse(fh.read())

for transaction in transactions:
    pprint.pprint(transaction.data)

Multiple statements in one file

A single parse() merges everything into one Transactions and keeps only the last block's statement-level data (e.g. balances). For files that concatenate several statements (including balance-only blocks), use parse_statements(), which splits on :20: boundaries and returns one Transactions per statement, each with its own balances:

import mt940

# src may be a filename, a file handle or the raw data, just like parse()
for statement in mt940.parse_statements('statements.sta'):
    print(statement.data['final_opening_balance'])
    print(statement.data['final_closing_balance'])

Serializing to JSON

import json
import mt940

transactions = mt940.parse('statement.sta')
print(json.dumps(transactions, indent=4, cls=mt940.JSONEncoder))

Transaction grouping (opt-in)

By default a new transaction is started only on the :61: statement tag. Some banks delimit transactions differently — for example by repeating the :20: transaction reference per block. Because changing the default grouping would break existing users, this behaviour is opt-in: pass transaction_boundary (an iterable of tag slugs) to start a new transaction on those tags too.

import mt940

# Each `:20:` (transaction_reference_number) starts its own transaction:
transactions = mt940.parse(
    'statement.sta', transaction_boundary={'transaction_reference_number'}
)

The same option is accepted by mt940.models.Transactions(transaction_boundary=...).

Banks with longer reference fields (opt-in)

Some banks (e.g. GLS / Atruvia) put a customer reference longer than the SWIFT 16-character cap on the :61: line, followed by the // bank reference. Relaxing the default would change how other banks (e.g. Rabobank) split same-line data, so this is handled by an opt-in StatementGLS tag:

import mt940

gls = mt940.tags.StatementGLS()
transactions = mt940.parse('statement.sta', tags={gls.id: gls})

(Longer supplementary details — issue #117, e.g. Wise — are handled by the default parser and need no opt-in.)

Statements from the Dutch bank ASN (opt-in)

Tag 61 in ASN statements does not follow the SWIFT specification, so the opt-in StatementASNB tag is used:

import mt940
import pprint

tag = mt940.tags.StatementASNB()
transactions = mt940.models.Transactions(tags={tag.id: tag})

with open('mt940_tests/ASNB/mt940.txt') as fh:
    transactions.parse(fh.read())

pprint.pprint(transactions.data, sort_dicts=False)

Supported tags

Tag Meaning
:13: Date/time the report was created
:20: Transaction reference number
:21: Related reference
:25: Account identification
:28C: Statement number / sequence number
:34F: Floor limit for debit and credit
:60F: / :60M: (Final / intermediate) opening balance
:61: Statement line (a transaction)
:62F: / :62M: (Final / intermediate) closing balance
:64: Available balance
:65: Forward available balance
:86: Transaction details
:90C: / :90D: Number and sum of credit / debit entries
:NS: Bank-specific non-SWIFT extensions

Contributing

Help is greatly appreciated. Please clone the develop branch and run tox before opening a pull request; CI checks linting (ruff), type-checking (pyright, mypy, pyrefly), the test suite (100% coverage required), and the documentation build.

git clone --branch develop https://github.com/WoLpH/mt940.git
cd mt940
uv sync
uv run tox          # run the full matrix
uv run tox -e py312 # or a single environment

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mt_940-5.0.0.tar.gz (26.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mt_940-5.0.0-py3-none-any.whl (29.1 kB view details)

Uploaded Python 3

File details

Details for the file mt_940-5.0.0.tar.gz.

File metadata

  • Download URL: mt_940-5.0.0.tar.gz
  • Upload date:
  • Size: 26.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mt_940-5.0.0.tar.gz
Algorithm Hash digest
SHA256 82d914988837a95bae21e772c74fb925b716735723f718c3ddd93cf3ad2b0f3a
MD5 b52ddcfba11a887e7da8f558f8217af1
BLAKE2b-256 aa6439abc6f3967663760943afd13e46908cf422299e784cb012127031987140

See more details on using hashes here.

Provenance

The following attestation bundles were made for mt_940-5.0.0.tar.gz:

Publisher: publish.yml on wolph/mt940

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mt_940-5.0.0-py3-none-any.whl.

File metadata

  • Download URL: mt_940-5.0.0-py3-none-any.whl
  • Upload date:
  • Size: 29.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mt_940-5.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 731ad50dd74bd9b4c74c7baab62dead534e30867ef00217841060dd7a7e3201a
MD5 03e8b01bb3e8c9792d8c6862abbf070b
BLAKE2b-256 4e9cb9f58c0cc2058476c75d5106fbf0b76b894346038abdbbb12d8fd4ec129f

See more details on using hashes here.

Provenance

The following attestation bundles were made for mt_940-5.0.0-py3-none-any.whl:

Publisher: publish.yml on wolph/mt940

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page