A lightweight Python package to flatten complex ISO 20022 XML messages into usable data.
Project description
OpenPurse
The Ultra-Lightweight, Production-Grade Engine for ISO 20022 and SWIFT MT Financial Messaging.
⚡️ 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
lxmlfor 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, nopydantic. 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:
- Flattening: We extract only the most critical fields needed for reconciliation and payment processing.
- Immutability: Once parsed, messages are stored in lightweight, typed
@dataclasses. - Lossless-Ready: While we flatten for convenience, we preserve raw data integrity (e.g., keeping amounts as
strto 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
lxmlcompiled 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
Release history Release notifications | RSS feed
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be6f65dcdce56fb8097c4a49ce3bf1e2b3e29cf540c333b0db61ec291f306ac2
|
|
| MD5 |
1859145658c0f4982b22ca75e097f333
|
|
| BLAKE2b-256 |
b1a59dcf54f8d115589f81d23a4f6ac6b370a89a87dc7c37182fb56da6833cb7
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
702ce875e91f1e87f9dc2b34a29091089d82b6cd43070adff882339d5ba6789c
|
|
| MD5 |
1783664c173791950047a1560b2b7cf3
|
|
| BLAKE2b-256 |
a5457786a8632a8efacde2946b877979aafe538a3d19f55fb5dfa0d85eab7d5f
|