Bei der Aufdeckung von Geldwäsche und Sanktionsumgehungen stoßen relationale JOINs schnell an ihre Performance-Grenzen. Unser neuer Blogbeitrag zeigt, wie Graph-Datenbanken wie Neo4j komplexe Multi-Hop-Beziehungen in konstanter Zeit analysieren.
Inklusive praxisnahem Python-Beispiel („Operation Silicon Shield“) zum Herunterladen und Ausprobieren!
Sanktionsverstöße, Geldwäsche und Finanzbetrug aufspüren mit Neo4j
Den vollständigen Artikel inkl. der Sourcen finden Sie auf github Andrey Bulezyuk
Warum Graphen? Das Problem mit relationalen Daten
Betrug, Geldwäsche und Sanktionsumgehung haben eines gemeinsam: Sie verstecken sich in den Verbindungen zwischen Entitäten, nicht in den Entitäten selbst. Eine einzelne Transaktion zwischen zwei Firmen mag harmlos wirken — erst wenn man drei Schritte zurücktritt und das gesamte Netzwerk betrachtet, wird die Struktur sichtbar.
Genau hier trennen sich Graph-Datenbanken wie Neo4j fundamental von klassischen Ansätzen.
Neo4j vs. SQL vs. NoSQL — die wesentlichen Unterschiede
Relationale Datenbanken (PostgreSQL, MySQL) speichern Daten in starren Tabellen und verbinden sie über JOINs — effizient für direkte Abfragen, aber exponentiell langsamer bei tief verschachtelten Beziehungen über mehrere Ebenen. Wer in SQL herausfinden will, ob Firma A über drei Zwischenstellen mit einer sanktionierten Person verbunden ist, so wird er bei großen Datensätzen große Query Ausführungsdauer in Kauf nehmen müssen. Bei Ad-Hoc analytischen Abfragen, ist die Latenz akzeptabel. Braucht man aber eine real-time analytische Fähigkeit, so wird SQL schnell an eigene Grenzen stößen.
NoSQL-Datenbanken (MongoDB, Cassandra) lösen das Skalierungsproblem bei unstrukturierten oder hoch-volumigen Daten, sind aber nicht für relationale Traversierungen optimiert — eine Multi-Hop-Pfadsuche ist kein natives Paradigma dieser Systeme.
Neo4j hingegen speichert Knoten (Personen, Firmen, Konten) und Kanten (Transaktionen, Eigentümerschaft, Verbindungen) als native Graphstrukturen im Speicher. Ein Beziehungssprung — ob zwei oder zwanzig Ebenen tief — kostet konstante Zeit, weil die Verbindungen physikalisch als Pointer im Graphen existieren, nicht als JOINs, die zur Laufzeit berechnet werden. Das macht Neo4j für Financial Crime Analysis, Know-Your-Customer (KYC) und Sanctions Screening zur überlegenen Wahl.
Sanktionsverstöße, Geldwäsche & Finanzbetrug — Definitionen und reale Muster
Sanktionsumgehung: Wenn Dual-Use-Güter über Umwege reisen
Sanktionen sind wirtschaftliche oder rechtliche Beschränkungen, die Staaten oder internationale Organisationen gegen Länder, Unternehmen oder Personen verhängen. 2022 belegten die EU, die USA, Großbritannien und andere Staaten Russland mit umfassenden Exportrestriktionen — darunter ein striktes Verbot für sogenannte Dual-Use-Güter: Technologie, die sowohl zivil als auch militärisch genutzt werden kann. Dazu gehören FPGA-Chips, Trägheitssensoren, bestimmte Halbleiter und Drohnenkomponenten.
Investigative Berichte haben gezeigt, wie dieses Verbot systematisch umgangen wird. Das Muster ist immer ähnlich: Ein EU-Lieferant verkauft Bauteile an eine unauffällig klingende Firma in Estland, Lettland oder Deutschland. Diese Firma tritt nach außen als lokales Handelsunternehmen auf, ist aber im Eigentum sanktionierter Staatsangehöriger oder von sanktionierten Strukturen — oft über Briefkastenfirmen auf Zypern oder in anderen Offshore-Jurisdiktionen. Die Güter landen letztlich im saktionierten Gebiet.
Technisch definiert handelt es sich bei Sanktionsumgehung um das Vorhandensein eines gerichteten Pfades im Transaktionsgraphen von einer nicht-sanktionierten Quellentität zu einer sanktionierten Zielentität, wobei mindestens eine Zwischenentität als Verschleierungsschicht fungiert: (LegalSupplier)-[:TRANSFERRED_TO*1..N]->(SanctionedTarget). Je mehr Hops, desto schwerer die manuelle Erkennung — und desto mächtiger die Graph-Traversierung. Mehr Hops (Zwischen-Entitäten) auch mögliche Datenlücken, da Jurisdikationen unkooperativ agieren.
Geldwäsche: Der dreiaktige Kreislauf
Geldwäsche verfolgt das Ziel, kriminell erlangtes Geld in den legalen Finanzkreislauf einzuschleusen. Die Financial Action Task Force (FATF) beschreibt drei klassische Phasen: Placement (Einspeisung von Bargeld), Layering (Verschleierung durch komplexe Transaktionsketten) und Integration (Rückführung als scheinbar legale Einnahmen).
Das Hauptwerkzeug dabei sind Offshore-Strukturen: Eine Person einem Hochrisikoland gründet oder kauft Briefkastenfirmen auf Zypern, in den British Virgin Islands oder auf den Cayman Islands — Jurisdiktionen mit geringer Transparenzpflicht gegenüber Behörden. Diese Shells überweisen sich gegenseitig Gelder als „Beratungshonorare", „Lizenzgebühren" oder „Kapitaleinlagen", ohne zugrundeliegende wirtschaftliche Substanz. Das Layering hinterlässt eine scheinbar legitime Papierspur, die für manuelle Prüfung nahezu undurchdringlich ist. Kleinere Akteure agieren dagegen mit physischen Geschäften wie Autowäsche/Casinos oder fiktive Digital-Güter mit geringeren Besteuerung.
Technisch ist eine Geldwäschestruktur ein gerichteter Teilgraph mit zyklischen oder vielschichtigen Transferpfaden, bei dem Knotenattribute wie country auf bekannte Offshore-Jurisdiktionen hinweisen und die wirtschaftlich Berechtigten (Ultimate Beneficial Owner, UBO) — via OWNS-Kanten — risikobehaftete Personen sind, die keine direkten Verbindungen zu sanktionierten Entitäten auf der Oberfläche aufweisen, aber durch Graph-Traversierung auffindbar werden. Für kleinere Akteure dagegen ist eine detailiertere Analyse der Bilanz des Unternehmens sowie der granularen Transaktionen der Bankkonten des Unternehmens.
Offshore-Leaks: Die offene Datenbasis
Das International Consortium of Investigative Journalists (ICIJ) veröffentlicht seit den Panama Papers 2016 strukturierte Daten zu Offshore-Firmen aus Leaks wie den Pandora Papers, FinCEN Files und OpenCorporates. Die ICIJ Offshore Leaks Database ist öffentlich zugänglich und enthält Millionen von Knoten und Kanten — genau das richtige Format für eine Graphdatenbank. Ergänzend bietet OpenSanctions.org eine maschinenlesbare, täglich aktualisierte Sanktionsliste.
Das konkrete Beispiel: Operation „Silicon Shield"
Disclaimer: Alle folgenden Firmen- und Personennamen sind vollständig fiktiv und dienen ausschließlich der technischen Veranschaulichung. Sie bilden jedoch bekannte, reale Muster aus investigativen Berichten nach.
Der Sachverhalt
Stellen wir uns folgendes Szenario vor: Der deutsche Halbleiterhändler MicroSemi Europe AG verkauft FPGA-Chips — Hochleistungsprozessoren, die in der militärischen Signalelektronik eingesetzt werden — an die in Frankfurt registrierte Alpha Circuit GmbH. Diese Firma erscheint im Handelsregister unauffällig, ihr tatsächlicher Eigentümer ist jedoch der russische Staatsbürger Dmitri Volkov, der sie über eine zypriotische Holding kontrolliert. Alpha Circuit reicht die Chips über eine weitere estnische Briefkastenfirma (Nordic Bridge Trading Ltd) weiter, wo sie letztlich bei Vostok Tech LLC in Moskau landen — einem Unternehmen auf der EU-Sanktionsliste.
Parallel dazu läuft eine Geldwäschestruktur: Die ebenfalls von sanktionierten Personen kontrollierte Kyra Investments Ltd auf Zypern empfängt Millionenbeträge aus Alpha Circuit als angebliche „Beratungshonorare" und transferiert sie anschließend direkt an die sanktionierte Person Alexei Petrov.
Wie wir mit Neo4j vorgehen
Das Ziel ist, diesen Ring mit Cypher-Queries schrittweise aufzudecken:
- Query 1 sucht nach direkten Transfers zu sanktionierten Entitäten — low-hanging fruits.
- Query 2 analysiert die Eigentümerschaftsstrukturen: Welche EU-Firmen werden von russischen Personen kontrolliert?
- Query 3 verbindet beide Dimensionen: Sanktionierte Personen, die über eigene Shells Geld fließen lassen.
- Query 4 findet alle vollständigen Lieferkettenpfade vom legitimen Lieferanten bis zum sanktionierten Endempfänger — das ist der endgültige forensische Beweis für indirekte Sanktionsumgehung.
Das Schöne an Graph-Traversierung: Für jeden dieser Schritte schreiben wir in Cypher einen einzigen, lesbaren Musterabgleich — ohne Subqueries, ohne Tabellen-JOINs, ohne explizite Schleifen.
Hands-on: Neo4j mit Python und JupyterLab
Voraussetzungen
# Neo4j lokal starten (z.B. via Docker)
docker run \
--name neo4j-sanctions \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/password \
neo4j:5.18
# Python-Abhängigkeiten installieren
mkdir neo4j-sanctions && cd neo4j-sanctions
uv init
uv venv
source ./venv/bin/activate
uv add neo4j pandas matplotlib networkx yfiles_jupyter_graphs_for_neo4j
Notebook-Zelle 1 — Imports & Datenbankverbindung
# ─── Imports ─────────────────────────────────────────────────────────────────
from neo4j import GraphDatabase
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.patheffects as pe
from yfiles_jupyter_graphs_for_neo4j import Neo4jGraphWidget
# ─── Verbindungsparameter ─────────────────────────────────────────────────────
NEO4J_URI = "neo4j://localhost:7687"
NEO4J_USER = "neo4j"
NEO4J_PASSWORD = "password" # ← euer lokales Passwort hier eintragen
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))
visualize_cypher = Neo4jGraphWidget(driver)
# Verbindungstest
with driver.session() as session:
result = session.run("RETURN 'Neo4j connected ✓' AS status")
print(result.single()["status"])
Der offizielle Neo4j Python Driver kommuniziert über das Bolt-Protokoll mit der Datenbank. GraphDatabase.driver() erzeugt einen thread-sicheren Connection-Pool — geeignet für Produktivbetrieb und Batch-Analysen gleichermaßen. Neo4jGraphWidget ist das interaktive Visualisierungs-Widget, das wir später für die Kartenansicht nutzen.
Notebook-Zelle 2 — Schema & Dataimport
Wir legen zunächst einen eindeutigen Index auf die Entitäts-IDs an — das verhindert Duplikate bei wiederholtem Import und beschleunigt Lookups erheblich. Dann importieren wir unsere fiktiven Datensätze per MERGE-Statement (idempotent, kein Duplikat bei erneutem Ausführen):
# ─── Graph leeren (Achtung: löscht ALLE Knoten & Kanten!) ────────────────────
with driver.session() as session:
session.run("MATCH (n) DETACH DELETE n")
print("Graph geleert.")
# ─── Eindeutige Constraints / Indizes anlegen ─────────────────────────────────
constraints = [
"CREATE CONSTRAINT IF NOT EXISTS FOR (e:Entity) REQUIRE e.id IS UNIQUE",
"CREATE INDEX IF NOT EXISTS FOR (e:Entity) ON (e.country)",
"CREATE INDEX IF NOT EXISTS FOR (e:Entity) ON (e.sanctioned)",
]
with driver.session() as session:
for c in constraints:
session.run(c)
print("Constraints & Indizes angelegt.")
# ─── Fiktives Datensample ─────────────────────────────────────────────────────
ALL_NODES = [
# Sanktionierte Entitäten (Russland)
{"id": "SE001", "name": "Vostok Tech LLC", "country": "Russia", "type": "Company", "sanctioned": True, "location_x": 37.6176, "location_y": 55.7558},
{"id": "SE002", "name": "Alexei Petrov", "country": "Russia", "type": "Person", "sanctioned": True, "location_x": 37.6176, "location_y": 55.7558},
{"id": "SE003", "name": "Meridian Arms Group", "country": "Russia", "type": "Company", "sanctioned": True, "location_x": 37.6176, "location_y": 55.7558},
# Shell-/Briefkastenfirmen (EU)
{"id": "SH001", "name": "Nordic Bridge Trading Ltd", "country": "Estonia", "type": "Company", "sanctioned": False, "location_x": 25.7482, "location_y": 58.5953},
{"id": "SH002", "name": "Alpha Circuit GmbH", "country": "Germany", "type": "Company", "sanctioned": False, "location_x": 10.4515, "location_y": 51.1657},
{"id": "SH003", "name": "Baltica Trade OÜ", "country": "Estonia", "type": "Company", "sanctioned": False, "location_x": 25.7482, "location_y": 58.5953},
{"id": "SH004", "name": "Nexus Components SARL", "country": "France", "type": "Company", "sanctioned": False, "location_x": 2.2137, "location_y": 46.2276},
{"id": "SH005", "name": "Kyra Investments Ltd", "country": "Cyprus", "type": "Company", "sanctioned": False, "location_x": 33.4299, "location_y": 35.1264},
# Wirtschaftlich Berechtigte (verdeckt)
{"id": "BO001", "name": "Dmitri Volkov", "country": "Russia", "type": "Person", "sanctioned": False, "location_x": 37.6176, "location_y": 55.7558},
{"id": "BO002", "name": "Irina Sorokina", "country": "Russia", "type": "Person", "sanctioned": False, "location_x": 37.6176, "location_y": 55.7558},
# Legitime EU-Lieferanten
{"id": "SUP001", "name": "MicroSemi Europe AG", "country": "Germany", "type": "Company", "sanctioned": False, "location_x": 10.4515, "location_y": 51.1657},
{"id": "SUP002", "name": "DronePartsHub GmbH", "country": "Austria", "type": "Company", "sanctioned": False, "location_x": 14.5501, "location_y": 47.5162},
{"id": "SUP003", "name": "AdvancedChips B.V.", "country": "Netherlands", "type": "Company", "sanctioned": False, "location_x": 5.2913, "location_y": 52.1326},
]
TRANSACTIONS = [
{"id": "TX001", "from": "SH001", "to": "SE001", "amount": 480000, "currency": "EUR", "date": "2024-03-15", "goods": "FPGA-Chips (Dual-Use)"},
{"id": "TX002", "from": "SH002", "to": "SH001", "amount": 510000, "currency": "EUR", "date": "2024-02-20", "goods": "FPGA-Chips (Dual-Use)"},
{"id": "TX003", "from": "SUP001","to": "SH002", "amount": 490000, "currency": "EUR", "date": "2024-01-18", "goods": "FPGA-Chips (Dual-Use)"},
{"id": "TX004", "from": "SH003", "to": "SE003", "amount": 270000, "currency": "EUR", "date": "2024-04-02", "goods": "Drohnen-Komponenten"},
{"id": "TX005", "from": "SH004", "to": "SH003", "amount": 285000, "currency": "EUR", "date": "2024-03-10", "goods": "Drohnen-Komponenten"},
{"id": "TX006", "from": "SUP002","to": "SH004", "amount": 260000, "currency": "EUR", "date": "2024-02-28", "goods": "Drohnen-Komponenten"},
{"id": "TX007", "from": "SH005", "to": "SE002", "amount": 1200000, "currency": "EUR", "date": "2024-05-01", "goods": "Überweisung (Tarnung Gewinne)"},
{"id": "TX008", "from": "SH002", "to": "SH005", "amount": 1100000, "currency": "EUR", "date": "2024-04-10", "goods": "Beratungshonorar"},
]
OWNERSHIPS = [
{"owner": "BO001", "company": "SH001", "share_pct": 100},
{"owner": "BO001", "company": "SH002", "share_pct": 60},
{"owner": "BO002", "company": "SH002", "share_pct": 40},
{"owner": "BO002", "company": "SH003", "share_pct": 100},
{"owner": "SE002", "company": "SH005", "share_pct": 100}, # ⚠ Risiko
{"owner": "SE002", "company": "SH004", "share_pct": 51},
]
ASSOCIATIONS = [
{"person": "SE002", "company": "SE001", "role": "Director"},
{"person": "BO001", "company": "SE001", "role": "Geschäftspartner"},
{"person": "BO002", "company": "SE003", "role": "Geschäftspartner"},
]
# ─── Cypher: Knoten & Kanten importieren ───────────────────────────────────────
with driver.session() as session:
for node in ALL_NODES:
label = "Person" if node["type"] == "Person" else "Company"
cypher = f"""
MERGE (e:Entity:{label} {{id: $id}})
SET e.name = $name,
e.country = $country,
e.sanctioned = $sanctioned,
e.location_x = $location_x,
e.location_y = $location_y
"""
session.run(cypher, **node)
for tx in TRANSACTIONS:
session.run("""
MATCH (a:Entity {id: $from_id}), (b:Entity {id: $to_id})
MERGE (a)-[r:TRANSFERRED_TO {id: $id}]->(b)
SET r.amount = $amount,
r.currency = $currency,
r.date = $date,
r.goods = $goods
""", from_id=tx["from"], to_id=tx["to"], **{k:v for k,v in tx.items() if k not in ("from","to")})
for o in OWNERSHIPS:
session.run("""
MATCH (p:Entity {id: $owner}), (c:Entity {id: $company})
MERGE (p)-[r:OWNS]->(c)
SET r.share_pct = $share_pct
""", **o)
for a in ASSOCIATIONS:
session.run("""
MATCH (p:Entity {id: $person}), (c:Entity {id: $company})
MERGE (p)-[r:ASSOCIATED_WITH]->(c)
SET r.role = $role
""", **a)
print("\n✅ Daten erfolgreich in Neo4j importiert.")
Der MERGE-Befehl ist in Cypher das Äquivalent zu INSERT OR IGNORE — sicher für wiederholten Aufruf. Das Graph-Schema ergibt sich organisch aus den Daten; kein Schema-Migration-Skript wie in SQL nötig. Die location_x/location_y-Koordinaten (Längen- und Breitengrad) werden später für die Kartenvisualisierung benötigt.
Zwischenschritt: Simple Visualisierung mit yFiles (Neo4jGraphWidget)
visualize_cypher.show_cypher("MATCH p=()-[]-() RETURN p LIMIT 50", layout="orthogonal")
Visualisierung zeigt den extrahierten Graphen direkt im Jupyter Notebook
Notebook-Zelle 3 — Query 1: Direkte Transaktionen zu sanktionierten Entitäten
# ─── Cypher Abfrage für direkte Transfers ─────────────────────────────────────
QUERY_1 = """
MATCH (sender:Entity)-[tx:TRANSFERRED_TO]->(target:Entity {sanctioned: true})
RETURN
sender.name AS Absender,
sender.country AS Absender_Land,
tx.amount AS Betrag_EUR,
tx.goods AS Güter,
tx.date AS Datum,
target.name AS Empfänger
ORDER BY tx.amount DESC
"""
with driver.session() as session:
rows = session.run(QUERY_1).data()
df_q1 = pd.DataFrame(rows)
display(df_q1)
Ergebnis:
| Absender | Absender_Land | Betrag_EUR | Güter | Datum | Empfänger |
|---|---|---|---|---|---|
| Kyra Investments Ltd | Cyprus | 1.200.000 € | Überweisung (Tarnung Gewinne) | 2024-05-01 | Alexei Petrov |
| Nordic Bridge Trading Ltd | Estonia | 480.000 € | FPGA-Chips (Dual-Use) | 2024-03-15 | Vostok Tech LLC |
| Baltica Trade OÜ | Estonia | 270.000 € | Drohnen-Komponenten | 2024-04-02 | Meridian Arms Group |
Drei direkte Treffer — aber die Absender selbst erscheinen unverdächtig. Ohne Eigentümerschaftsdaten ist nicht erkennbar, dass hinter Kyra Investments und Nordic Bridge dieselben russischen Akteure stecken.
Notebook-Zelle 4 — Query 2: Shell-Firmen mit russischen Eigentümern
QUERY_2 = """
MATCH (owner:Entity {country: 'Russia'})-[o:OWNS]->(shell:Entity)
WHERE shell.country <> 'Russia'
RETURN
owner.name AS Eigentümer,
CASE owner.sanctioned WHEN true THEN '⚠ SANKTIONIERT' ELSE 'nicht sanktioniert' END AS Status,
o.share_pct AS Anteil_Pct,
shell.name AS Shell_Firma,
shell.country AS Shell_Land
ORDER BY owner.sanctioned DESC, o.share_pct DESC
"""
Ergebnis:
| Eigentümer | Status | Anteil_Pct | Shell_Firma | Shell_Land |
|---|---|---|---|---|
| Alexei Petrov | ⚠ SANKTIONIERT | 100 | Kyra Investments Ltd | Cyprus |
| Alexei Petrov | ⚠ SANKTIONIERT | 51 | Nexus Components SARL | France |
| Dmitri Volkov | nicht sanktioniert | 100 | Nordic Bridge Trading Ltd | Estonia |
| Irina Sorokina | nicht sanktioniert | 100 | Baltica Trade OÜ | Estonia |
| Dmitri Volkov | nicht sanktioniert | 60 | Alpha Circuit GmbH | Germany |
| Irina Sorokina | nicht sanktioniert | 40 | Alpha Circuit GmbH | Germany |
Sechs EU-registrierte Firmen unter russischer Kontrolle — darunter die entscheidende Verbindung: Alexei Petrov (sanktioniert) hält 100 % an Kyra Investments Ltd (Zypern) und 51 % an Nexus Components SARL (Frankreich). Das ist die OWNS-Kante, die jeden Compliance-Alarm auslösen müsste.
Notebook-Zelle 5 — Query 3: Sanktionierte Person → Shell → Geldfluss
QUERY_3 = """
MATCH (sp:Person {sanctioned: true})
-[:OWNS]->(shell:Entity)
-[tx:TRANSFERRED_TO]->(target:Entity)
RETURN
sp.name AS Sanktionierte_Person,
shell.name AS Shell_Firma,
shell.country AS Shell_Land,
tx.amount AS Betrag_EUR,
tx.goods AS Güter,
target.name AS Empfänger,
CASE target.sanctioned WHEN true THEN '⚠ SANKTIONIERT' ELSE 'unverdächtig' END AS Empfänger_Status
ORDER BY tx.amount DESC
"""
Ergebnis:
| Sanktionierte_Person | Shell_Firma | Shell_Land | Betrag_EUR | Güter | Empfänger | Empfänger_Status |
|---|---|---|---|---|---|---|
| Alexei Petrov | Kyra Investments Ltd | Cyprus | 1.200.000 € | Überweisung (Tarnung Gewinne) | Alexei Petrov | ⚠ SANKTIONIERT |
| Alexei Petrov | Nexus Components SARL | France | 285.000 € | Drohnen-Komponenten | Baltica Trade OÜ | unverdächtig |
Diese Query verbindet zwei Kantentypen in einem Atemzug: OWNS und TRANSFERRED_TO. In SQL wären das drei Tabellen-JOINs. In Cypher ist es ein natürliches Muster, das der tatsächlichen Struktur der Realität entspricht. Der erste Treffer ist besonders brisant: Alexei Petrov überweist sich über seine eigene Offshore-Shell letztlich selbst Geld — ein klassisches Geldwäsche-Schema.
Notebook-Zelle 6 — Query 4: Vollständige Lieferkettenpfade
QUERY_4 = """
MATCH path = (supplier:Entity)-[:TRANSFERRED_TO*1..4]->(target:Entity {sanctioned: true})
WHERE NOT supplier.sanctioned
AND NOT EXISTS {
MATCH (x:Entity)-[:TRANSFERRED_TO]->(supplier)
WHERE NOT x.sanctioned = false
}
WITH path,
[n IN nodes(path) | n.name + ' [' + n.country + ']'] AS Stationen,
reduce(total = 0, r IN relationships(path) | total + r.amount) AS Gesamtbetrag,
length(path) AS Hops
RETURN Stationen, Hops, Gesamtbetrag
ORDER BY Gesamtbetrag DESC
"""
Ergebnis:
| Pfad | Hops | Gesamtbetrag |
|---|---|---|
| MicroSemi Europe AG [Germany] → Alpha Circuit GmbH [Germany] → Kyra Investments Ltd [Cyprus] → Alexei Petrov [Russia] | 3 | 2.790.000 € |
| Alpha Circuit GmbH [Germany] → Kyra Investments Ltd [Cyprus] → Alexei Petrov [Russia] | 2 | 2.300.000 € |
| MicroSemi Europe AG [Germany] → Alpha Circuit GmbH [Germany] → Nordic Bridge Trading Ltd [Estonia] → Vostok Tech LLC [Russia] | 3 | 1.480.000 € |
| Kyra Investments Ltd [Cyprus] → Alexei Petrov [Russia] | 1 | 1.200.000 € |
| Alpha Circuit GmbH [Germany] → Nordic Bridge Trading Ltd [Estonia] → Vostok Tech LLC [Russia] | 2 | 990.000 € |
| DronePartsHub GmbH [Austria] → Nexus Components SARL [France] → Baltica Trade OÜ [Estonia] → Meridian Arms Group [Russia] | 3 | 815.000 € |
| Nexus Components SARL [France] → Baltica Trade OÜ [Estonia] → Meridian Arms Group [Russia] | 2 | 555.000 € |
| Nordic Bridge Trading Ltd [Estonia] → Vostok Tech LLC [Russia] | 1 | 480.000 € |
| Baltica Trade OÜ [Estonia] → Meridian Arms Group [Russia] | 1 | 270.000 € |
Das Endergebnis: Neun Lieferkettenpfade werden aufgedeckt. Der gravierendste: MicroSemi Europe AG [Germany] → Alpha Circuit GmbH [Germany] → Kyra Investments Ltd [Cyprus] → Alexei Petrov [Russia] mit einem Gesamtvolumen von 2.790.000 €. Genau dieser Pfad wäre in einer normalen Transaktionsdatenbank unsichtbar — drei separate Lieferketten ohne offensichtliche Verbindung auf Tabellenebene.
Das [:TRANSFERRED_TO*1..4] in Cypher ist die elegante Syntax für „bis zu vier Hops" — Neo4j's variable-length path queries traversieren den Graphen intern ohne explizite Rekursion.
Notebook-Zelle 7 — Geographische Kartenvisualisierung
Mit yfiles_jupyter_graphs_for_neo4j lassen sich die Pfade direkt auf einer interaktiven Weltkarte darstellen. Die location_x/location_y-Koordinaten aus dem Dataimport werden als geographische Positionen verwendet — Knoten erscheinen an ihrem tatsächlichen Standort.
# Mappings & Widget aufrufen
visualize_cypher.add_node_configuration("*", coordinate=coordinate_mapping)
visualize_cypher.show_cypher(QUERY_4_MAP, layout="map")
Die geografische Visualisierung mappt die Handelsrouten über Landesgrenzen hinweg.
Notebook-Zelle 8 — Netzwerkgraph (NetworkX)
# Code zur Generierung des Matplotlib-Netzwerk-Graphen ausführen
fig, ax = plt.subplots(figsize=(16, 10), facecolor="#0d1117")
nx.draw_networkx_nodes(G, pos, node_color=colors, node_size=sizes)
# ... (vollständiges Zeichnen der Kanten und Labels)
plt.show()
Visualisierung des fiktiven Geflechts aus legalen Lieferanten, Shells und sanktionierten Entitäten.
Das erzeugte Diagramm macht den Unterschied von Graph-Denken gegenüber Tabellen-Denken auf einen Blick sichtbar: Links die legitimen EU-Lieferanten (blau), in der Mitte das Shell-Netzwerk (orange), rechts die sanktionierten russischen Endempfänger (rot). Die gestrichelten lila Linien zeigen die Eigentümerschaftsketten — die Information, die in einer klassischen Transaktionstabelle vollständig fehlen würde.
Ausblick: Was mit Graph-Datenbanken noch möglich ist — und wird
Dieses Beispiel kratzt nur an der Oberfläche. Was in professionellen Financial-Crime-Systemen bereits heute mit Neo4j und ähnlichen Graph-Engines eingesetzt wird — und wohin die Entwicklung geht:
Graph Data Science (GDS) ist das leistungsstärkste Erweiterungspaket von Neo4j. Die Neo4j Graph Data Science Library beinhaltet Algorithmen wie Community Detection (Louvain, Label Propagation) zur automatischen Ringfindung, PageRank-Varianten für Risikoanalysen und vieles mehr.
AutorArtikel erstellt: 12.06.2026
Artikel aktualisiert: 12.06.2026



