Pull to refresh

Генератор больших графов транзакций с паттернами преступной деятельности

Reading time 5 min
Views 5.2K

Доброго времени суток.


Network

Пару лет назад перед нашей командой (compliance в швейцарском банке) встала очень интересная задача — нужно было сгенерировать большой граф транзакций между клиентами, компаниями и банкоматами, добавить в этот граф паттерны, похожие на паттерны отмывания денег и другой преступной деятельности, а также добавить минимальную информацию об узлах этого графа — имена, адреса, время, и т.д. Разумеется, все данные должны былии быть сгенерированны с нуля, без использования существующих данных клиентов.

Для решения данной задачи был написан генератор, которым мне бы хотелось с вами поделиться. Под катом вы найдете историю, объясняющую зачем нам это было нужно, и описание работы генератора. Для нетерпеливых — вот тут лежит код. Буду рад, если кому-нибудь будет полезен наш опыт.


Зачем мы занялись такой ерундой?


Наша команда решила поучаствовать спонсорами на хакатоне LauzHack

. Одним из условий участия в формате спонсора было предоставление реальной бизнес задачи для участников. Как раз в то время у нас был очень интересный проект, связанный с автоматизацией поиска финансовых преступлений и отмывания денег среди транзакций наших клиентов, и мы, не долго думая, решили предложить эту же задачу участникам хакатона.

По очевидным причинам мы не могли использовать реальные данные, по-этому пришлось их создавать. Чтобы задача была максимально приближена к реальности, мы посмотрели на статистику реальных данных и пытались, как могли, приблизить сгенерированные данные к реальным распределениям, а так же не скупились на количество и сложность данных — нам не нужно было решение, работающие на графе из 100 узлов и 200 соединений, мы искали решение, способное обрабатывать графы размером в миллионы узлов и миллиарды соединений, и учитывающие всю доступную информацию об узлах и соединениях.


Что у нас получилось


А получился у нас вполне себе быстрый (с поправкой на количество данных), интересный и конфигурируемый генератор! Давайте разбираться детально


Типы данных


Мы хотим иметь граф финансовых транзакций, соответственно возможные участники этого графа это:


  • Клиент — можно сказать, аккаунт абстрактного клиента банка. Описывается именем, имэилом, возрастом, рабочей деятельность, политическими взглядами, национальностью, образованием и адресом проживания
  • Компания — бизнес сущность в финансовой системе. Определяется типом компании, названием и страной.
  • Банкомат — грубо говоря, точки выхода денег из подконтрольного нам графа. Определены географическими координатами.
  • Транзакция — Факт передачи денег от одного узла графа к другому. Определены начальным и конечным узлом, суммой, валютой и временем.

Для создания этих данных мы используем Mimesis, отличная библиотека для создания фейковых данных.


Создание графа: базовые сущности


Для начала надо создать все базовые сущности — клиентов, компании и банкоматы. Скрипт принимает количество клиентов, которое требуется создать, и на основе этого высчитывает количество компаний и банкоматов. По нашим данным, количество компаний, имеющих какое-либо крупное количество транзакций с клиентами, равно приблизительно 2.5% от количества клиентов, а количество банкоматов — 0.05% от количества клиентов. Эти значения очень обобщены и неконфигурируемы (зашиты в коде генератора).


Вся информация сохраняется в .csv файлы. Запись в эти файлы происходит пакетно, по k строк за раз. Это значение настраивается аргументами скрипта. Также три типа узлов генерируются паралельно.


Создание графа: соединения между сущностями


После создания базовых сущностей мы начинаем соединять их между собой. На этом этапе мы еще не генирируем сами транзакции, а просто сам факт наличия связи между узлами. Сделано это для ускорения процесса генерации всего графа и работает примерно следующим образом: если два узла соединены, то мы генерируем какое-то количество транзакций между ними, раскиданное по времени. Если не соединены, но транзакций между данными узлами не существует.


Вероятность наличия связи между двумя узлами настраивается через аргументы, стандартные значения указаны ниже.


Возможные типы соединений:


  • Клиент -> Клиент (p = 0.4%)
  • Клиент -> Компания (p = 1%)
  • Клиент -> Банкомат (p = 3%)
  • Компания -> Клиент (p = 0.5%)

Как и узлы, все типы соединений генерируются паралельно и записываются в свои файлы пакетно.


Создания графа: транзакции


Имея узлы графа и соединения между ними, попадающие под желаемое распределение, мы можем начать генерировать транзакции. Процесс достаточно простой сам по себе, но распаралелить его достаточно сложно. Поэтому на этом этапе есть только два независимых потока — транзакции, исходящие от клиента, и транзакции, исходящии от компании.


Ничего особо интересного на этом этапе не происходит: скрипт пробегает по списку соединений и для каждого соединения генерирует случайное количество транзакций. Пишется это все точно также — в .csv файлы по пакетам.


Создания графа: паттерны


А вот тут есть интересные моменты. Типы паттернов поведения, которые мы хотели получить в конечном графе:


  • Flow — большая сумма уходит из одного узла к m другим, каждый из этих m узлов переправляет деньги следующему уровню из n узлов, и так далее, пока последний уровень не отправляет все деньги одному получателю.
  • Circular — сумма денег идет по кругу и возвращается к источнику.
  • Time — определенная сумма денег переходит от отдого узла к другому с какой-то фиксированной частотой.

Давайте рассмотрим каждый из этих паттернов подробней:


Flow


Для начала выбирается количество уровней, через которые деньги должны будут пройти. В нашей реализации это случайное число между 2 и 6, не конфигурируемо и зашито в коде. Далее выбираются два узла графа — отправитель и получатель. Также выбирается случайная сумма, которую отправитель отправит получателю (по учень хитрой формуле 50000 * random() + 50000 * random()).


Каждый участник этой сети берет какую то плату за свои услуги. В нашей реализации максимальная цена за пропуск денег через сеть будет 10% от суммы, передаваемой отправителем.


Сгенерированные транзакции имеют сдвиг по времени относительного транзакций предыдущего уровня сети — то есть деньги сначала приходят на уровень n-1, а только потом уходят на уровень n. Задержки выбираются случайно в пределах 4-5 дней. Также сгенерированные транзакции имеют псевдо-случайные суммы (ограниченные изначальной суммой и с учетом платы каждому узлу)


Circular


Генерируется по схожему принципу, что и Flow, только вместо разных отправителя и получателя и нескольких уровней в данном паттерне деньги идут по кругу и возвращаются в изначальному узел. Все промежуточные узлы берут плату, как и в случае с Flow, и транзакции также имеют сдвиг по времени.


Time


Самый простой паттерн. Определенная сумма отправляется от отправителя получателю случайное количество раз (от 5 до 50, не настраивается) с псевдо-случайными сдвигами во времени.


Все новые транзакции точно так же пишутся в .csv файлы пакетами.


Рандомизация графа и сбор всех транзакций в один файл


На данном этапе у нас есть несколько .csv файлов:


  • 3 файла с узлами (клиенты, компании и банкоматы)
  • 4 файла с транзакциями: один для обычных транзакций и 3 содержащих паттерны.

Дополнительный скрипт смешивает транзакции паттернов вместе с обычными транзакциями, чтобы не было возможности увидеть паттерны в графе по порядку записи транзакций в файле.


И что со всем этим делать?


В конце концов у нас есть 4 красивых файла с узлами графа и транзакциями между ними. Можно импортировать в Neo4J, можно раздавать через REST, да всё что душе угодно можно с ними делать.


А что касается нас — мы получили очень положительный фидбек со стороны участников хакатона, и несколько очень интересных решений по поиску паттернов в массивных графах.

Tags:
Hubs:
+9
Comments 9
Comments Comments 9

Articles