quackiso
ISO-20022-Finanznachrichten (camt, pacs, pain) mit SQL abfragen
Maintainer: tempoloss
Installation und Laden
INSTALL quackiso FROM community;LOAD quackiso;Beispiel
-- Bank statements as rows: camt.053, camt.054 and camt.052. A glob is-- parsed in parallel, one worker per file.SELECT booking_date, amount, currency, credit_debit, counterparty_nameFROM read_iso20022('statements/*.xml', threads := 8)ORDER BY booking_date;
-- What is in the folder, before choosing a reader: one row per file with-- the message type, the reader that covers it, and the record count.SELECT family, reader, count(*) AS files, sum(records) AS recordsFROM sniff_iso20022('inbox/**/*.xml')GROUP BY family, reader;
-- Interbank credit transfers (pacs.008, the ISO 20022 MT103).SELECT uetr, amount, currency, debtor_name, creditor_agent_bicFROM read_pacs008('pacs008.xml');
-- Credit transfer initiation (pain.001). The payer lives on the <PmtInf>-- group and is carried down to every transaction in it.SELECT payment_info_id, debtor_name, creditor_name, amount, requested_execution_dateFROM read_pain001('pain001.xml');
-- Direct debits (pain.008): the creditor pulls, the mandate makes it legal.SELECT mandate_id, sequence_type, debtor_name, amountFROM read_pain008('pain008.xml');
-- Payment returns (pacs.004): what came back, beside what was settled.-- A return with charges deducted is amount < original_amount.SELECT return_id, amount, original_amount, return_reason_code, original_debtor_name, original_creditor_nameFROM read_pacs004('pacs004.xml');
-- Payment status reports (pain.002). A status is stated per batch, per-- payment group and per transaction, and status_level says which a row is.SELECT status_level, status, reason_code, original_end_to_end_id, amountFROM read_pain002('pain002.xml')WHERE status_level = 'TRANSACTION';
-- Amounts are DECIMAL(38,5), so totals are exact.SELECT currency, SUM(amount) AS totalFROM read_iso20022('statements/*.xml')WHERE credit_debit = 'DBIT'GROUP BY currency;Über quackiso
quackiso liest ISO-20022-Finanznachrichten direkt in DuckDB-Tabellen. Kein Python-Vorverarbeitungsschritt, kein Glue-Code je Schema: richten Sie eine Tabellenfunktion auf Bank-XML und erhalten Sie Transaktionen als Zeilen.
Vierzehn Funktionen: dreizehn Reader, die den Zahlungslebenszyklus von Ende zu Ende in beide Richtungen abdecken, und ein Sniffer, der Dateien an sie weiterleitet.
read_iso20022(path)- camt.053-Auszüge, camt.054-Benachrichtigungen und camt.052-Berichte; eine Zeile pro gebuchtem Eintrag.read_pacs008(path)/read_pacs009(path)- Kunden- und Interbanken-Überweisungen (ISO-20022-MT103 und MT202/MT202COV); in der COV-Form tragen dieunderlying_*-Spalten die Kundenüberweisung, die das Cover settlet.read_pain001(path)/read_pain008(path)- Initiierung von Überweisung und Lastschrift; die zahlende bzw. einziehende Seite liegt auf der<PmtInf>- Gruppe und wird heruntergetragen, Lastschriften tragen das Mandat.read_pacs003(path)- die Interbanken-Strecke einer Lastschrifteinziehung.read_pain002(path)/read_pacs002(path)- Zahlungsstatusberichte, kunden- und interbankenseitig; eine Zeile pro Statusangabe, auf welcher Ebene die Bank sie auch angegeben hat, weil ein Batch angenommen oder abgelehnt werden kann, ohne dass eine einzelne Transaktion detailliert wird.read_pacs004(path)/read_pacs007(path)- Rückgaben und Stornierungen: zurückkommendes settled Geld, vom Empfänger oder vom Absender zurückgeholt; der zurückgegebene Betrag steht neben dem Original, sodass Teilrückgaben sichtbar sind.read_camt056(path)/read_camt055(path)/read_camt029(path)- Stornierungsanfragen (interbanken- und kundenseitig) und die Auflösung, die sie beantwortet; eine Stornierung des gesamten Batches ohne Transaktionen ist trotzdem eine Zeile.sniff_iso20022(path)- Inventar vor dem Lesen: eine Zeile pro Datei mit erkanntem Nachrichtentyp, Familie, Wire-Level-Datensatzanzahl und dem zuständigen Reader. Die Identität kommt aus dem Namensraum, den epochenbedingten Containernamen oder der Envelope-Bindung; Inhaltsprobleme landen in einererror-Spalte statt den Scan abzubrechen, sodass ein Ordner gemischter Downloads eine Tabelle ist und kein Fehlschlag.
Alle Ausnahme- und Status-Reader legen die ursprünglichen Referenzen offen (UETR, End-to-End-ID, ursprüngliche Nachrichten-ID), sodass eine Zahlung über ihren gesamten Lebenszyklus verbunden werden kann: Initiierung, Settlement, Status, Stornierung, Auflösung, Rückgabe.
Beträge sind DECIMAL(38,5), niemals DOUBLE. Werte werden vom
Wire-String direkt in eine skalierte Ganzzahl umgewandelt und berühren nie einen Float, sodass SUM
exakt ist; ISO 20022 erlaubt 18 signifikante Stellen mit bis zu 5 Nachkommastellen,
die ein 64-Bit-DECIMAL(18,5) nicht fassen kann. Ein Betrag, der nicht
exakt darstellbar ist, löst einen Fehler aus, statt zu NULL zu werden und
still aus einer Summe zu fallen.
Datumsangaben sind typisiert statt als Text belassen; Offsets werden nach UTC normalisiert, und
sowohl die <Dt>- als auch die <DtTm>-Hüllen werden gelesen.
Das Parsen ist ein Streaming-Durchlauf über XML-Ereignisse, sodass das Maximum ein Ausgabe-Batch
plus dem größten einzelnen XML-Teilbaum ist und nicht die Datei: ein 1,7-GB-Auszug
mit drei Millionen Einträgen parst in 1,23 MiB Live-Heap und etwa 2 MB
Resident, gemessen mit cargo test membound, und fügt einer laufenden
DuckDB 7,8 MiB hinzu. Ein Glob wird parallel geparst, ein Worker pro Datei – XML hat keine
sicheren Splittpunkte, daher wird ein einzelnes Dokument nie geteilt – mit
threads := n zum Festlegen des Pools; gemessen 6,9× bei 8 Dateien à 35 MB.
Eine Datei des falschen Nachrichtentyps schlägt laut fehl, statt eine leere
Tabelle zurückzugeben. Die Transaktionselementnamen kollidieren zwischen Familien (camt.056 und
pacs.004 sagen beide TxInf, pacs.008 und pain.001 beide CdtTrfTxInf),
daher ist die Identität der eigene Container der Nachricht und Zeilen entstehen nur
darin.
Getestet gegen rund 260 echte Nachrichten aus mehr als einem Dutzend Quellen – darunter Goldman Sachs US/UK/EU und Wire, SIX Interbank, CBPR+, ProgressSoft, Nivaes, Prowide, OpenBankProject, Mbanq, Handelsbanken, issettled, prog-nov, salesking und Dolibarr – über dreizehn Nachrichtenfamilien und jede Epoche ihres Vokabulars: umbenannte Reason-Blöcke, umbenannte Container, namensraum-präfixierte Teilbäume, auf Gruppenebene heruntergetragene Felder und Partei- seiten, die sich in einer Rückgabe umkehren. Jedes Verhalten, das wie ein Sonderfall wirkt, stammt aus einer dieser Dateien.
Pfade sind lokale Dateien oder Globs; jede Zeile speichert ihre source_file. Entfernte
URIs und XSD-Validierung fehlen bewusst; die Begründung steht
in docs/adr/.
Hinzugefügte Funktionen
| function_name | function_type | description | comment | examples |
|---|---|---|---|---|
| read_camt029 | table | NULL | NULL | |
| read_camt055 | table | NULL | NULL | |
| read_camt056 | table | NULL | NULL | |
| read_iso20022 | table | NULL | NULL | |
| read_pacs002 | table | NULL | NULL | |
| read_pacs003 | table | NULL | NULL | |
| read_pacs004 | table | NULL | NULL | |
| read_pacs007 | table | NULL | NULL | |
| read_pacs008 | table | NULL | NULL | |
| read_pacs009 | table | NULL | NULL | |
| read_pain001 | table | NULL | NULL | |
| read_pain002 | table | NULL | NULL | |
| read_pain008 | table | NULL | NULL | |
| sniff_iso20022 | table | NULL | NULL |
Überladene Funktionen
Diese Erweiterung fügt keine Funktionsüberladungen hinzu.
Hinzugefügte Typen
Diese Erweiterung fügt keine Typen hinzu.
Hinzugefügte Einstellungen
Diese Erweiterung fügt keine Einstellungen hinzu.