Skip to main content

A lightweight Python package to flatten complex ISO 20022 XML messages into usable data.

Project description

OpenPurse Logo

OpenPurse

The Ultra-Lightweight, Production-Grade Engine for ISO 20022 and SWIFT MT Financial Messaging.

PyPI version License Python Versions Ruff


⚡️ The OpenPurse Mission

Financial messaging is transitioning from archaic, line-based SWIFT MT to deeply nested, namespace-heavy ISO 20022 (MX) XML. Engineers are often caught in the middle, forced to deal with massive XML schemas, fragmented versions, and complex reconciliation logic.

OpenPurse solves this by providing a unified, zero-bloat API that flattens this complexity into clean, structured Python objects.

  • 🚀 Performance-First: Built with C-optimized lxml for lightning-fast parsing even under heavy load.
  • 🛡️ Production-Hardened: Strict validation for IBANs (Modulo-97) and BICs (ISO 9362).
  • 🔌 Schema-Dynamic: Not just one version. OpenPurse dynamically detects and supports 770+ ISO namespaces across Cash Management, Payments Initiation, and Securities.
  • 📦 Zero-Dependency Core: No pandas, no pydantic. Just pure, native Python @dataclasses.

🏗️ Technical Philosophy: The @dataclass Way

Most financial libraries overwhelm you with nested dictionaries or heavy ORM-like objects. OpenPurse uses the Uniform Data Model (UDM) philosophy:

  1. Flattening: We extract only the most critical fields needed for reconciliation and payment processing.
  2. Immutability: Once parsed, messages are stored in lightweight, typed @dataclasses.
  3. Lossless-Ready: While we flatten for convenience, we preserve raw data integrity (e.g., keeping amounts as str to avoid floating-point errors).

🛠️ Feature Deep Dive

1. The Unified Parser

One caller to rule them all. Whether your file starts with <?xml or {1:, the OpenPurseParser handles the routing.

import openpurse

# Parses MT103, MT202, pacs.008, camt.053, pain.001, etc.
raw_data = get_incoming_payload()
parser = openpurse.OpenPurseParser(raw_data)
msg = parser.parse()

print(f"Ref: {msg.message_id} | Amt: {msg.amount} {msg.currency}")

The Uniform Data Model (UDM)

Every parsed message returns a PaymentMessage object (or an extended subclass like Pacs008Message) with these standardized attributes:

Attribute Description
message_id Unique ID from Group Header (:20: in MT)
end_to_end_id Original e2e reference (EndToEndId)
amount Transaction value (stored as string for precision)
currency ISO 3-letter currency code (e.g. USD, EUR)
sender_bic / receiver_bic Validated 8/11 char BIC codes
debtor_name / creditor_name Party names
debtor_account / creditor_account IBAN or local account identifiers
uetr SWIFT gpi Unique Transaction Reference

2. Bidirectional Translator

Move between MX (ISO 20022) and MT legacy formats without losing precision.

from openpurse.translator import Translator

# Convert a parsed message object to a legacy MT103 block
mt_payload = Translator.to_mt(msg, mt_type="103")

3. Smart PII Anonymizer

Essential for testing in staging environments with real data. OpenPurse scrubs names and addresses while keeping IBAN checksums valid, so your downstream systems don't reject the files.

from openpurse.anonymizer import Anonymizer

anonymized_xml = Anonymizer().anonymize_xml(raw_xml_bytes)

4. Lifecycle Reconciler

The "Holy Grail" of treasury engineering: linking fragmented messages into a single payment lifecycle (e.g., linking a pain.001 to its pain.002 status report and final camt.054 notification).

from openpurse.reconciler import Reconciler

# Trace the entire life of a payment across your message history
timeline = Reconciler.trace_lifecycle(seed_msg, message_pool)

📊 Capability Matrix

Format Category Supported Message Types
ISO 20022 (MX) Payments pacs.008, pacs.009, pacs.004
Cash Mgmt camt.052, camt.053, camt.054, camt.004
Initiation pain.001, pain.002, pain.008
SWIFT MT Customer MT101, MT103
Financial MT202
Reporting MT900, MT910, MT940, MT942, MT950

🚦 Dual-Tier Validation Engine

OpenPurse doesn't just extract data; it actively defends your downstream systems against corrupted financial payloads using a two-tier validation engine.

Tier 1: Deep Structural Validation

Instantly checks the raw payload syntax before you even attempt to parse it.

  • XML (MX): Automatically evaluates the raw bytes against the precise matching lxml compiled XSD definition from the 770+ ISO schemas in the library.
  • SWIFT MT: Enforces rigorous Regex structural checks ensuring Block 1-5 definitions, proper :tag: spacing, and correct termination syntax are met.
from openpurse.validator import Validator

raw_payload = b"{1:F01BANKUS33XXX0000000000}{2:I103BANKGB22XXXN}{4:\n:20:REF123\n-}"
report = Validator.validate_schema(raw_payload)
if not report.is_valid:
    print(report.errors) # Caught malformed block 4 before parsing

Tier 2: Logical Field Validation

Validates the actual extracted business values of a parsed PaymentMessage.

  • IBAN: Full Modulo-97 checksum execution across all supported country formats.
  • BIC: Strict ISO 9362 8- or 11-character alphanumeric check for Senders/Receivers.
  • UETR: Strict UUIDv4 checking for SWIFT gpi tracking strings.
parsed_msg = parser.parse()
logic_report = Validator.validate(parsed_msg)
if not logic_report.is_valid:
    print(f"Critical Business Error: {logic_report.errors}")

5. High-Performance Streaming Parser

As financial institutions often deal with very large (GB+) batch XML files containing thousands of transactions, loading a full DOM tree is memory-prohibitive. Use the StreamingParser for chunked, memory-efficient processing.

from openpurse.streaming import StreamingParser

# Iterate through transactions in a 5GB file without loading it all into RAM
for msg in StreamingParser("batch_payments.xml").iter_messages():
    process_payment(msg)

6. Database Persistence

OpenPurse includes a built-in SQLAlchemy persistence layer to save financial records directly to your relational database.

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from openpurse.database.repository import MessageRepository

engine = create_engine("sqlite:///payments.db")
Session = sessionmaker(bind=engine)

with Session() as session:
    repo = MessageRepository(session)
    repo.save(parsed_msg) # Persists polymorphic record (pacs.008, pain.001, etc.)

💻 Command Line Interface (CLI)

OpenPurse provides a powerful standalone utility for the terminal.

# Validate a file's structure and data quality
openpurse validate incoming_payment.xml

# Parse and output a JSON summary
openpurse parse mt103_message.txt

# Persist a message directly to a database
openpurse persist message.xml --db-url sqlite:///production.db

🔍 Exporter: Automated API Specs

Generate standard OpenAPI 3.0 / JSON Schema specifications from your OpenPurse models, making it easy to share your financial data structures with other teams or documentation tools.

# Generate openapi.json for your treasury API
python3 scripts/export_schema.py --format json > openapi.json

⚖️ Performance Benchmarks (Qualitative)

OpenPurse is optimized for high-throughput reconciliation pipelines. By leveraging lxml's C-bindings for XML traversal and specialized RegEx engines for MT parsing, OpenPurse can process:

  • Small Messages: ~0.001s / message.
  • Large Statements (CAMT.053/MT940): Typically < 0.05s for statement blocks with 5,000+ entries.

🤝 Contributing

OpenPurse is a community project for financial engineers. We welcome PRs for:

  • New ISO version mappings.
  • New MT block pattern regex improvements.
  • Performance optimizations.
# Setup for development
pip install -e ".[dev]"
pytest tests/

📜 License

OpenPurse is released under the MIT License. Build something great.

Built with ❤️ for modern financial engineering by the OpenPurse team.

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

openpurse-0.1.14.tar.gz (2.4 MB view details)

Uploaded Source

Built Distribution

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

openpurse-0.1.14-py3-none-any.whl (87.8 kB view details)

Uploaded Python 3

File details

Details for the file openpurse-0.1.14.tar.gz.

File metadata

  • Download URL: openpurse-0.1.14.tar.gz
  • Upload date:
  • Size: 2.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for openpurse-0.1.14.tar.gz
Algorithm Hash digest
SHA256 be6f65dcdce56fb8097c4a49ce3bf1e2b3e29cf540c333b0db61ec291f306ac2
MD5 1859145658c0f4982b22ca75e097f333
BLAKE2b-256 b1a59dcf54f8d115589f81d23a4f6ac6b370a89a87dc7c37182fb56da6833cb7

See more details on using hashes here.

File details

Details for the file openpurse-0.1.14-py3-none-any.whl.

File metadata

  • Download URL: openpurse-0.1.14-py3-none-any.whl
  • Upload date:
  • Size: 87.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for openpurse-0.1.14-py3-none-any.whl
Algorithm Hash digest
SHA256 702ce875e91f1e87f9dc2b34a29091089d82b6cd43070adff882339d5ba6789c
MD5 1783664c173791950047a1560b2b7cf3
BLAKE2b-256 a5457786a8632a8efacde2946b877979aafe538a3d19f55fb5dfa0d85eab7d5f

See more details on using hashes here.

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