AAYMA
RU
Войти

Граф денег (MoneyGraph)

Локальный AML-инструмент: по выгрузке переводов от 81 seed строит направленный граф, присваивает узлам гипотезы о ролях (координатор, консолидатор…

Доведённый продукт
Команда «doiwannalarp» 8💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: doiwannalarp
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт

Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)

Суть

Локальный AML-инструмент: по выгрузке переводов от 81 seed строит направленный граф, присваивает узлам гипотезы о ролях (координатор, консолидатор, транзит и т.д.), кластеризует и ранжирует узлы для проверки с объяснением.

Как устроено

Пайплайн io → quality → graph → features/temporal → roles → clustering → priority: проверка схемы parquet, структурные, потоковые и временные признаки, применение упорядоченных правил ролей с порогами и полным rule_trace, Louvain на неориентированной проекции, priority_score как взвешенная сумма role_score, перцентилей seed-потока, объёма и betweenness с коэффициентом novelty. Дополнительно маршруты, циклы, устойчивость сети, полнота выгрузки и анализ чувствительности порогов; результаты в CSV и React/Streamlit-интерфейсе с графом окружения узла.

Ключевые решения

  • Louvain
  • betweenness centrality
  • rule-based role assignment
  • percentile-rank priority scoring
  • sensitivity analysis
  • resilience analysis
  • cycle detection

Метрики

Полный пересчёт 20,19 с (лимит 5 мин); 2 248 узлов, 3 119 рёбер, 68 кластеров

Особенности

Аккуратная обработка ограничений выборки: узлы 4-го колена без выгруженных исходящих помечаются как граница выгрузки, а не как конечные получатели; хранится полный rule_trace, включая несработавшие правила; единая точка входа start.py.

Стек

Python · pandas · parquet · React · TypeScript · Vite · Streamlit · Playwright · Vitest · pytest


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-7d988aa1-doiwannalarp.

README

README.md

Граф денег

Локальный инструмент для AML-аналитика: по обезличенной выгрузке исходящих внутрибанковских переводов он строит направленный граф, присваивает каждому узлу проверяемую гипотезу о роли и ранжирует узлы для дальнейшей проверки. Результат отвечает на рабочий вопрос: кого из наблюдаемых клиентов смотреть первым и почему. Роли отражают признаки в ограниченной выборке, а не установленный статус клиента.

Вход: data/nodes.parquet, data/edges.parquet, data/transactions.parquet из предоставленного архива. Выгрузка охватывает 81 исходный gid, 2 248 узлов, 3 119 агрегированных рёбер и 4 840 переводов за июль 2026 года.

Запуск одной командой

Распакуйте архив так, чтобы три parquet-файла находились прямо в data/. Из корня репозитория выполните:

python start.py

Требуется Node.js (для сборки интерфейса) в дополнение к Python. Команда создаёт .venv (если его ещё нет), ставит зависимости из requirements.txt, пересчитывает пайплайн, при первом запуске ставит npm-зависимости и собирает frontend/, затем поднимает интерфейс и API одним процессом на http://127.0.0.1:8080. Повторный запуск быстрее: существующее окружение, node_modules и уже собранный frontend/dist не трогаются.

python start.py — единая точка входа в проект. Если вы добавляете зависимость, новый шаг настройки или меняете способ запуска интерфейса (frontend/, run_fingraph.py, обязательные переменные окружения, обязательные файлы), обновите start.py так, чтобы эта команда по-прежнему поднимала весь проект без ручных шагов, и проверьте это на чистом .venv/node_modules перед пушем.

В поиске введите gid или выберите узел в списке «Приоритет проверки». Карточка узла показывает роль, оценку, наблюдаемые потоки и обоснование. Вкладка «Граф» показывает окружение выбранного узла до двух переходов.

Альтернативный интерфейс (Streamlit)

В репозитории есть второй, независимый интерфейс на Streamlit (app/) поверх тех же выгрузок пайплайна — он не запускается через start.py:

.\.venv\Scripts\python.exe run_pipeline.py
.\.venv\Scripts\streamlit.exe run app/main.py --server.headless true

Пошагово (для проверки, тестов, CI)

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe run_pipeline.py
.\.venv\Scripts\python.exe scripts/check_outputs.py --twice --final
.\.venv\Scripts\python.exe -m pytest -q
npm --prefix frontend install
npm --prefix frontend run build
.\.venv\Scripts\python.exe run_fingraph.py

Для проверки секретов на машине с Git Bash:

& 'C:\Program Files\Git\bin\bash.exe' scripts/secret_scan.sh

Пайплайн полностью локален и не требует API-ключа. Необязательный текстовый ассистент в интерфейсе появляется только при наличии OPENAI_API_KEY в окружении или в локальном, игнорируемом Git файле .env.

Демо-сценарий (5 минут)

  1. python start.py на чистой машине — интерфейс и API поднимаются одним процессом на http://127.0.0.1:8080, без ручных шагов.

  2. «Обзор» — KPI и распределение ролей за один взгляд: 2 248 узлов, 3 119 рёбер, 4 840 транзакций, 365,9 млн KZT оборота.

  3. «Граф» — список «Приоритет проверки» слева, интерактивный граф окружения справа. Разбор трёх произвольных узлов (можно взять любой другой gid — карточка справа показывает роль, оценку, наблюдаемые потоки и обоснование для каждого):

    gidРольЧто показать
    100000008165763100координаторСходятся средства нескольких seed через несколько ветвей.
    100000008477350100консолидация11+ плательщиков; в графе виден узел с этим gid и его окружение до двух переходов.
    100000005767403100граница выгрузки4-е колено, исходящие не выгружены — карточка явно говорит, что роль не определима, а не выдаёт это за конечного получателя.
  4. «Кластеры» — 68 кластеров с гипотезой о назначении каждого, приоритетные gid по кластеру.

  5. «Аналитика» и «Данные» — сводные показатели набора и выгрузки для скачивания.

Второй интерфейс на Streamlit (app/, см. ниже) дополнительно показывает полный rule_trace (все правила, включая несработавшие) и раскладку графа по коленам — полезно для более глубокой проверки конкретного узла.

Как получается результат

io проверяет схему parquet, quality сверяет размеры и суммы, graph строит направленный граф с учётом изолированных seed. Затем features и temporal считают структурные, потоковые и временные признаки; roles применяет правила; clustering выделяет сообщества Louvain на явно суммированной неориентированной проекции; priority создаёт ранжирование и текстовое объяснение. Проверенный полный пересчёт предоставленного набора, включая чувствительность, занял 20,19 с, что ниже лимита 5 минут из задания. Расчёт детерминирован при seed=42.

Правила проверяются в порядке таблицы. Первая прошедшая роль становится основной, остальные успешные сохраняются как вторичные. rule_trace хранит все проверки, включая неуспешные. Пороговые значения ниже взяты из moneygraph/thresholds.py; детали вычисления приведены в методике.

РольКритерий основной роли
coordinator — кандидат в координаторыДостижимость от ≥3 seed, ≥3 питающие ветви и прирост схождения ≥1 одновременно.
consolidator — признаки консолидации≥4 разных наблюдаемых плательщика.
distributor — признаки веерного распределенияИсходящие наблюдаемы и есть ≥6 разных получателей.
transit — признаки транзитаИсходящие наблюдаемы, узел не seed, входящий поток положителен, out_sum / in_sum от 0,7 до 1,3, входящая и исходящая степени ≤3.
terminal — кандидат в конечные получателиИсходящие наблюдаемы, узел не seed и не на границе выгрузки, входящий поток ≥50 000 KZT, out_sum / in_sum ≤0,3 либо исходящих нет, доля поздних поступлений <0,5.
boundary — граница выгрузкиГлубина 4, исходящих в выгрузке нет, ни одно предыдущее правило не прошло.
peripheral — периферияНи одно предыдущее правило не прошло.

role_score лежит в диапазоне 0–1 и показывает выраженность условий выбранной роли. Для ранга используется формула:

priority_score = novelty × (
    0.35 × role_weight × role_score
  + 0.30 × pct(seed_flow_in)
  + 0.20 × pct(max(in_sum, out_sum))
  + 0.15 × pct(betweenness)
)

pct — процентный ранг среди узлов со средним рангом при равенстве. novelty равна 0,7 для исходных seed и 1,0 для остальных. Веса ролей: coordinator 1,0; consolidator 0,9; distributor 0,75; transit и terminal 0,6; boundary 0,4; peripheral 0,1. why называет два крупнейших слагаемых и рекомендуемое действие.

Выходные файлы

ФайлСодержание
outputs/nodes_roles.csvОсновная роль, сила признаков, кластер, приоритет и краткое свидетельство для каждого из 2 248 gid.
outputs/clusters.csvРазмер, число seed, внутренний объём переводов, ключевые gid и гипотеза о каждом кластере.
outputs/top_nodes.csvТоп-30 узлов с рангом и объяснением why.
outputs/run_meta.jsonВремена этапов, размеры данных, распределение ролей и проверки качества.
outputs/node_features.parquetПолные признаки и rule_trace для карточек узлов; создаётся локально и не хранится в Git.
outputs/graph_edges.parquetАгрегированные направленные рёбра для интерфейса; создаётся локально и не хранится в Git.
outputs/resilience.csvРазмер компонент после удаления узлов по разным стратегиям.
outputs/completeness.csvУзлы с censored или inflow_incomplete независимо от роли: запрос исходящих для границы, входящих для неполного входа, обоих направлений при сочетании флагов. Интерфейс показывает и выгружает весь список.
outputs/routes.csvСовместимые по датам двухшаговые цепочки через транзитный узел, отдельные однодневные кандидаты и структурные циклы длины 2–4.
outputs/sensitivity.csvАнализ изменения порогов; автоматически рассчитывается после основных выгрузок. Ошибка этого этапа не прерывает основной расчёт; при отсутствии файла вкладка «Анализ» показывает пояснение.
outputs/docs/DATA_NOTES.mdПроверки качества конкретного запуска. При использовании --out отчёт сохраняется внутри указанного каталога; тесты не перезаписывают docs/DATA_NOTES.md в репозитории.

Ограничения данных и трактовка

Граф построен только по исходящим переводам от 81 seed на четыре перехода. У 444 узлов глубины 4 исходящие не выгружались; отсутствие ребра у них нельзя считать признаком конечного получателя. Такие узлы получают метку границы, если не прошли более раннее структурное правило. Входящий поток по всей сети является нижней оценкой: переводы из-за пределов обхода отсутствуют, особенно у seed. В текущей проверке данных 377 узлов имеют наблюдаемый исходящий поток выше входящего; это признак неполноты выборки, а не ошибка баланса.

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

fast_through_share — доля исходящей суммы, отправленной в дни наблюдаемых поступлений или в следующие два дня. Входящие и исходящие суммы не сопоставляются: показатель отражает близость активности во времени, а не долю конкретных поступлений, переданных дальше. Значение 100% не означает, что весь исходящий поток профинансирован наблюдаемыми недавними поступлениями.

В routes.csv тип transit_chain означает наличие пар дат с интервалом 1–2 дня; same_day_candidate — совпадение в один день без известного порядка. n_occurrences считает различные пары (дата входа, дата выхода) для данного пути и типа; несколько переводов в один день сначала суммируются. days_span — минимальный интервал среди этих пар. min_leg_kzt — максимум меньшей из двух дневных сумм по совместимым парам; суммы разных пар не складываются, поскольку пары могут использовать общие переводы. Это не трассировка происхождения средств. Для structural_cycle поле min_leg_kzt содержит минимальную месячную сумму ребра, а days_span и n_occurrences пусты: наличие цикла в агрегированном графе не подтверждает движение денег по нему во времени.

Проверка чувствительности

Анализ изменяет каждый градуируемый порог до 75% и 125% исходного значения и сравнивает роли и состав топ-20. На полном расчёте из сырых признаков проверено 26 сценариев. Только уменьшение CONS_PAYERS_MIN до 75% дало сходство топ-20 ниже 0,7: изменились 106 ролей, коэффициент Жаккара равен 0,667. Порог консолидации стоит рассматривать как чувствительный при ручной проверке кандидатов. Пайплайн вызывает moneygraph.extras.sensitivity.run на признаках до округления и сохраняет результат в sensitivity.csv выбранного выходного каталога.

Проверка охватывает числовые параметры с окончаниями _MIN и _STRONG; границы отношений, ограничения степени, доля поздних поступлений и веса приоритета в эти 26 сценариев не входят. Результат характеризует устойчивость к перечисленным изменениям, а не точность определения ролей.

Масштабирование до миллиона узлов

На графе порядка 1 млн узлов точная посредническая центральность и полный spring_layout станут дорогими. Агрегацию переводов следует выполнять по секциям в DuckDB или Spark; достижимость от seed считать пакетным либо инкрементальным обходом; сообщества — алгоритмом Leiden/igraph; центральность — приближённо на фиксированной выборке. Для интерфейса нужен серверный выбор подграфа вокруг gid и заранее сохранённые координаты, а не передача всего графа в браузер. Эти изменения описывают путь масштабирования и не требуются для текущей локальной выгрузки.

Потенциал развития

За рамками обязательных выгрузок пайплайн уже считает четыре дополнительных анализа, которые отличают решение от базового «роль + приоритет»: устойчивость сети к изъятию топ-узлов (resilience.csv), явный список того, каких данных не хватает, с готовым текстом запроса (completeness.csv), совместимые по датам маршруты и циклы без ложной трассировки конкретных средств (routes.csv), и чувствительность правил к порогам с коэффициентом Жаккара по топ-20 (sensitivity.csv). Необязательный AI-ассистент (moneygraph/extras/llm.py) отвечает только на основе уже вычисленных признаков графа — не обращается к сырым данным и не может сообщить то, что не прошло через role/evidence, поэтому его ответы наследуют те же гарантии осторожности формулировок.

Дальнейшие направления: детекция аномалий (дробление сумм ниже порога выгрузки, профили узлов относительно распределения по своему колену); ML-калибровка порогов на размеченной аналитиками выборке вместо фиксированных персентилей; потоковый пересчёт при регулярной выгрузке вместо разового батча; серверный выбор подграфа для масштабирования интерфейса (см. раздел о миллионе узлов ниже).

Схема

flowchart LR
    A["data/*.parquet<br/>(2248 узлов, 3119 рёбер, 4840 переводов)"] --> B["io + quality<br/>(схема, объёмы, DATA_NOTES)"]
    B --> C["graph.build_graph<br/>(DiGraph, суммарная неориентированная проекция)"]
    C --> D["features<br/>(структура + множества seed + время)"]
    D --> E["roles<br/>(правила + rule_trace)"]
    D --> F["clustering<br/>(Louvain на взвешенной проекции)"]
    E --> G["priority<br/>(оценка + обоснование + действие)"]
    F --> G
    G --> H["nodes_roles.csv<br/>clusters.csv<br/>top_nodes.csv"]
    H --> I["Streamlit UI<br/>(сеть, карточка, приоритеты, анализ)"]
    H --> J["extras<br/>(resilience, completeness, routes, sensitivity)"]

Статическая версия: схема решения.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
33
Авторы
4
Файлы
86
Ветки
5

Авторы коммитов

yerassyl007 20Elect07 7bbaton 4Olzhas Malik 2

История коммитов

  • 2026-09-23 17:59test: cover local adapter validation and int64 GID preservationOlzhas Malik
  • 2026-09-23 17:59test: verify MoneyGraph branding and filtered CSV totalsOlzhas Malik
  • 2026-09-23 17:50[dev1] refresh run_meta.json after mergeyerassyl007
  • 2026-09-23 17:50Merge remote-tracking branch 'origin/dev2/analytics'yerassyl007
  • 2026-09-23 17:47[dev1] wire start.py to the React UI (run_fingraph.py) as the primary entry pointyerassyl007
  • 2026-09-23 17:44[dev2] fix Windows startup and expose verified investigation evidenceElect07
  • 2026-09-23 17:39[dev1] merge dev2/analytics: new React frontend (frontend/) + run_fingraph.pyyerassyl007
  • 2026-09-23 17:31Merge remote-tracking branch 'origin/dev2/analytics'yerassyl007
Все коммиты (33)
  • 2026-09-23 17:30chore: rename UI branding to MoneyGraphbbaton
  • 2026-09-23 17:29feat: add local FinGraph analyst interface and data adapterbbaton
  • 2026-09-23 17:07[dev1] add demo script and potential-development section to READMEyerassyl007
  • 2026-09-23 17:02[dev1] add python start.py as the single entry point for the whole projectyerassyl007
  • 2026-09-23 16:53[dev1] merge dev3/ui: honest temporal-route wording, completeness reworkyerassyl007
  • 2026-09-23 16:47[dev1] redesign network view as a layered diagram, not a force-directed blobyerassyl007
  • 2026-09-23 16:36[dev3] fix completeness requests and temporal route evidenceElect07
  • 2026-09-23 16:30[dev1] fix unreadable Сеть/Кластеры network viewsyerassyl007
  • 2026-09-23 16:04[dev1] fix why-text role duplication and KZT metric truncationyerassyl007
  • 2026-09-23 16:01[dev1] merge dev2/analytics and dev3/ui bug fixes into mainyerassyl007
  • 2026-09-23 15:51[dev3] scope run notes and integrate sensitivityElect07
  • 2026-09-23 15:46[dev2] fix evidence and cluster explanations; preserve calibration notesbbaton
  • 2026-09-23 15:38[dev1] fix app boot crash and wire up missing sensitivity extrayerassyl007
  • 2026-09-23 15:23[dev1] refresh run_meta.json after merging dev3/uiyerassyl007
  • 2026-09-23 15:18[dev3] show selected gid in sidebarElect07
  • 2026-09-23 15:15[dev3] document verified setup and analysisElect07
  • 2026-09-23 14:54[dev3] focus completeness view on boundary requestsElect07
  • 2026-09-23 14:48[dev3] add analyst dashboard and graph queriesElect07
  • 2026-09-23 14:43[dev1] refresh outputs after merging dev2/analyticsyerassyl007
  • 2026-09-23 14:41Merge remote-tracking branch 'origin/dev2/analytics'yerassyl007
  • 2026-09-23 14:38[dev2] implement calibrated analytics and verificationbbaton
  • 2026-09-23 14:26[dev1] remove CLAUDE.md and AGENTS.md from repo, gitignore bothyerassyl007
  • 2026-09-23 14:23[dev1] refresh run_meta.json from latest pipeline runyerassyl007
  • 2026-09-23 14:20[dev1] fix secret_scan.sh self-match false positiveyerassyl007
  • 2026-09-23 14:18[dev1] build MoneyGraph pipeline skeleton end to endyerassyl007
Структура репозитория (86 файлов)
.env.example
.gitignore
README.md
app/__init__.py
app/data.py
app/main.py
app/render_diagram.py
app/views/__init__.py
app/views/analysis.py
app/views/clusters.py
app/views/network.py
app/views/node_card.py
app/views/overview.py
app/views/top.py
docs/DATA_NOTES.md
docs/METHODOLOGY.md
docs/solution_diagram.md
docs/solution_diagram.png
frontend/README.md
frontend/components.json
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/playwright.config.ts
frontend/src/app/context.tsx
frontend/src/components/graph/GraphCanvas.tsx
frontend/src/components/layout/Shell.tsx
frontend/src/components/shared/ActivityChart.tsx
frontend/src/components/shared/NodeInspector.tsx
frontend/src/components/shared/index.tsx
frontend/src/components/ui/button.tsx
frontend/src/lib/data.ts
frontend/src/main.tsx
frontend/src/pages/ClientsPage.tsx
frontend/src/pages/GraphPage.tsx
frontend/src/pages/OtherPages.tsx
frontend/src/pages/TransactionsPage.tsx
frontend/src/services/api.ts
frontend/src/styles.css
frontend/src/types/index.ts
frontend/tests/browser/app.spec.ts
frontend/tests/data.test.ts
frontend/tsconfig.json
frontend/vite.config.ts
frontend/vitest.config.ts
moneygraph/__init__.py
moneygraph/clustering.py
moneygraph/config.py
moneygraph/evidence.py
moneygraph/extras/__init__.py
moneygraph/extras/completeness.py
moneygraph/extras/llm.py
moneygraph/extras/queries.py
moneygraph/extras/resilience.py
moneygraph/extras/routes.py
moneygraph/extras/sensitivity.py
moneygraph/features.py
moneygraph/graph.py
moneygraph/io.py
moneygraph/pipeline.py
moneygraph/priority.py
moneygraph/quality.py
moneygraph/roles.py
moneygraph/schemas.py
moneygraph/temporal.py
moneygraph/thresholds.py
outputs/clusters.csv
outputs/completeness.csv
outputs/nodes_roles.csv
outputs/resilience.csv
outputs/routes.csv
outputs/run_meta.json
outputs/sensitivity.csv
outputs/top_nodes.csv
requirements.txt
run_fingraph.py
run_pipeline.py
scripts/check_outputs.py
scripts/secret_scan.sh
start.py
tests/fixtures.py
tests/test_extras.py
tests/test_pipeline_contract.py
tests/test_priority.py
tests/test_roles.py
tests/test_sensitivity.py
Ветки (5)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.