Required Python Packages and Files
For this analysis, we use CAMT files. These XML files are used by banks to report transactions. Most European banks will provide this export starting in 2025.
To practice this analysis yourself, you can download this CAMT file with dummy data. More information on the CAMT.053 format and its specifications can be found on this external page.
Additionally, we use Python and the following Python packages:
pandas
for data manipulationxml.etree.ElementTree
for parsing XML files (built into Python)
If you haven't installed Pandas yet, you can do so with the following command:
pip install pandas
Step 1: Define a List of High-Risk Countries
To detect payments to high-risk countries, we first define a list based on the FATF list.
# List of high-risk countries (example based on the FATF list)
high_risk_country_codes = {"IR", "KP", "SY", "SD", "PK", "YE", "MM", "CU", "AF", "ZM"}
Step 2: Read and Process the CAMT.053 XML File
A CAMT.053 file contains all transactions for a bank account. We extract relevant fields such as date, amount, debtor, creditor, and IBAN.
import xml.etree.ElementTree as ET
import pandas as pd
# Function to parse a CAMT.053 XML file
def parse_camt053(file_path):
tree = ET.parse(file_path) # Parse the XML file
root = tree.getroot() # Get the root element
ns = {'ns': 'urn:iso:std:iso:20022:tech:xsd:camt.053.001.02'} # Define XML namespace
transactions = []
for entry in root.findall('.//ns:Ntry', ns):
try:
amount = float(entry.find('ns:Amt', ns).text) if entry.find('ns:Amt', ns) is not None else 0.0
except ValueError:
amount = 0.0 # Fallback for conversion errors
currency = entry.find('ns:Amt', ns).attrib.get('Ccy', 'N/A') if entry.find('ns:Amt', ns) is not None else 'N/A'
date = entry.find('.//ns:BookgDt/ns:Dt', ns).text if entry.find('.//ns:BookgDt/ns:Dt', ns) is not None else '-'
debtor = entry.find('.//ns:Dbtr/ns:Nm', ns).text if entry.find('.//ns:Dbtr/ns:Nm', ns) is not None else '-'
debtor_iban = entry.find('.//ns:DbtrAcct/ns:Id/ns:IBAN', ns).text if entry.find('.//ns:DbtrAcct/ns:Id/ns:IBAN', ns) is not None else '-'
creditor = entry.find('.//ns:Cdtr/ns:Nm', ns).text if entry.find('.//ns:Cdtr/ns:Nm', ns) is not None else '-'
creditor_iban_element = entry.find('.//ns:CdtrAcct/ns:Id/ns:IBAN', ns)
creditor_iban = creditor_iban_element.text if creditor_iban_element is not None else '-'
# Store extracted transaction details in a list
transactions.append({
"Date": date,
"Debtor": debtor,
"Debtor IBAN": debtor_iban,
"Creditor": creditor,
"Creditor IBAN": creditor_iban,
"Amount": amount,
"Currency": currency
})
return pd.DataFrame(transactions) # Convert to a Pandas DataFrame
# Load and parse the CAMT.053 file
file_path = "camt053_dummy.xml"
df = parse_camt053(file_path)
print(df.head()) # Display the first few rows
Step 3: Extract Country Codes from IBAN
The first two letters of an IBAN represent the account holder’s country code. This helps us determine whether a payment was sent to a high-risk country.
# Function to extract the country from IBAN
def get_country_from_iban(iban):
return iban[:2] # The first two characters of IBAN are the country code
# Add country codes to dataframe
df["Creditor Country Code"] = df["Creditor IBAN"].apply(get_country_from_iban)
df["Debtor Country Code"] = df["Debtor IBAN"].apply(get_country_from_iban)
Step 4: Filter Payments to High-Risk Countries
Now we filter transactions to countries on the FATF list.
# Filter payments to high-risk countries
df_high_risk = df[df["Creditor Country Code"].isin(high_risk_country_codes) | df["Debtor Country Code"].isin(high_risk_country_codes)]
Step 5: Analyze Frequency and Amounts
An overview of the number of transactions and total amounts sent to high-risk countries.
# Group by country and calculate totals
df_analysis = df_high_risk.groupby(["Creditor Country Code", "Debtor Country Code"]).agg(
Transaction_Count=("Amount", "count"),
Total_Amount=("Amount", "sum")
).reset_index()
Conclusion
With this script, you can determine whether payments have been made to high-risk countries. Can you apply this to non-dummy data?