AAYMA
RU
Войти

Граф денег

Локальный AML-пайплайн по сети переводов: назначает роли узлам, выделяет кластеры, ранжирует приоритет проверки и показывает граф в Streamlit + PyVis.

Рабочий MVP
Команда «TEABEA» 4💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: TEABEA
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Рабочий MVP

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

Суть

Локальный AML-пайплайн по сети переводов: назначает роли узлам, выделяет кластеры, ранжирует приоритет проверки и показывает граф в Streamlit + PyVis.

Как устроено

pipeline.py читает три parquet через pyarrow и строит направленный граф с признаками: число контрагентов, входящий объём, betweenness, seed-соседи, достижимость от seed за 4 шага. Роли назначаются упорядоченными правилами с квантильными порогами, кластеры строит Louvain (seed=42). Приоритет считается как взвешенная сумма: роль×role_score, log-объём, betweenness и seed-соседи. Результаты выгружаются в CSV и JSON-metadata, Streamlit + PyVis показывает интерактивный граф, есть загрузка своих CSV/Parquet.

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

  • Louvain
  • directed betweenness centrality
  • rule-based role assignment
  • quantile thresholds
  • weighted priority score
  • SHA-256 cache

Метрики

Полный прогон parquet→CSV 10.0 и 8.9 с; 2248 узлов, 3119 рёбер, 4840 транзакций, 91 кластер; 25 тестов пройдено

Особенности

Все формулы ролей и приоритета приведены прямо в README. Отдельно перечислены ограничения данных: граница обхода depth=4, порог 5000 KZT, отсутствие ground truth. Есть идеи масштабирования до миллиона узлов (CSR/igraph, Polars).

Стек

Python · pyarrow · pandas · Streamlit · PyVis · React · TypeScript · pytest


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

README

README.md

Граф денег

Обновление: загрузка CSV/Parquet и движущийся граф. Собственные данные рассчитываются отдельно в .user_runs; для них добавлены явные параметры наблюдения в pipeline. Настройки примера хакатона не переносятся автоматически.

Локальный анализ наблюдаемой сети переводов. pipeline.py рассчитывает роли, кластеры и приоритеты; Streamlit + PyVis читает готовые CSV. Старые frontend/backend не участвуют в основном запуске.

Установка Windows

Проверено на Python 3.12.10, Windows x64. Активация PowerShell не требуется:

py -3.12 -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt

В текущем рабочем каталоге используйте созданное проверочное окружение .venv-ui: прежняя .venv ссылается на недоступный Python.

Запуск

.venv\Scripts\python.exe run.py

Откройте http://127.0.0.1:8501. Скрипт пересчитывает parquet, ждёт успешного завершения и запускает Streamlit тем же Python. Ctrl+C останавливает дочерний процесс. Отдельные API/Node-серверы не нужны. Параметры: --data, --out, --edges, --port; пути по умолчанию от корня проекта.

Отдельный пересчёт и просмотр:

.venv\Scripts\python.exe pipeline.py --data data_parquet --out out --edges-export data/edges.csv
.venv\Scripts\python.exe -m streamlit run app.py --server.address 127.0.0.1 --server.port 8501 -- --data out --edges data/edges.csv

Явные относительные пути pipeline CLI разрешаются от текущей папки; run.py передаёт абсолютные.

Работа с интерфейсом

Поиск точного gid и кнопки глобального топа выбирают карточку и окружение, сбрасывая фильтры с уведомлением. Перейдите во вкладку «Граф и карточка» после выбора из топа/кластера. Неизвестный gid не меняет выбранный узел. Один или два шага обходятся в обе стороны, стрелки сохраняют src→dst. Режим всей сети сохраняется при изменении фильтров. Пустой список ролей даёт пустой граф.

Все узлы круглые. Цвет — роль, размер — priority_score (10–34 px), золотая обводка — seed, пунктирная — boundary. Доступны движение/пауза, плавное перестроение и показ всей сети. Полная сеть останавливается после стабилизации или через 6 секунд; малые подграфы затухают естественно. Открытие подробностей ставит физику на паузу. Полные gid в pandas/JSON/PyVis — строки. Клик по графу открывает подробности внутри iframe; карточка Streamlit выбирается поиском и топом.

Фильтры меняют видимость, но не выгрузки. Кэш учитывает SHA-256 файлов, кнопка обновления перечитывает результаты. Отсутствующие поля останавливают экран с точной командой пересчёта. vis-network встроен из установленного PyVis, внешних script/link нет; телеметрия Streamlit отключена. Браузерная проверка при отключённой внешней сети не выполнена.

Файлы данных

Вход: data_parquet/nodes.parquet (gid, depth, is_seed), edges.parquet (src, dst, sum_kzt, n_tx, depth), transactions.parquet (src, dst, date, sum_kzt). Основной reader — pyarrow. Дополнительный CSV-вход требует полного набора и корректных типов.

ВыходПоля
out/nodes_roles.csvgid, role, role_score, cluster_id, priority_score, evidence
out/clusters.csvcluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis
out/top_nodes.csvrank, gid, role, priority_score, why; топ-40
out/node_metrics_full.csvПолные метрики, explanation и четыре вклада приоритета
out/run_metadata.jsonСтатус, размеры, период, пороги, метод, время, предупреждения
data/edges.csvНаправленные рёбра для интерфейса

CSV сохраняют десятичные gid без округления. Данные и архив предназначены для передачи организаторам хакатона, наружу не публикуются.

Правила ролей и формулы

Ниже приведены параметры примера хакатона. Для загрузок глубина берётся из явно указанного профиля; при неизвестной глубине terminal отключён, достижимость seed считается без искусственного ограничения 4. Порог суммы и охват не предполагаются автоматически.

Порядок: изолят → coordinator → consolidator → distributor → transit → terminal → peripheral. При пересечении выбирается первое правило; дополнительные сигналы записаны в explanation. Role_score — эвристическая поддержка гипотезы, не вероятность виновности.

I/O — число входящих/исходящих контрагентов; V — вход KZT; B — направленная невзвешенная betweenness; S — прямые seed-соседи; R — число seed, достигающих узла по направлению за ≤4 шага. C=max(5,floor(q90 положительных I)), D=max(15,floor(q90 положительных O)), Q=q95 положительных B. В текущем расчёте C=5, D=15, Q=0.0013361804007553844.

РольУсловиеrole_score
coordinatorS≥2 и B≥Q, Q существуетmin(1, 0.5·min(S/4,1)+0.5·B/Q)
consolidatorI≥C, V>00.55·min(I/(2C),1)+0.45·min(R/4,1); boundary: максимум 0.65
distributorO≥D0.55·min(O/(2D),1)+0.45·min(out_tx/100,1)
transitне seed/boundary, вход/выход >0, p=out_kzt/in_kzt в [0.8,1.2]min(1, 1−abs(p−1)/0.4)
terminalне seed, depth<4, V>0, O=0min(1, 0.55+0.45·min(V/max(Vmax,1),1))
peripheralостальные; изоляты обрабатываются первыми0.15; для изолята 0.20

Счёт округляется до 6 знаков. Нулевой вход означает отсутствующий pass-through («н/д»).

priority = 0.40·weight(role)·role_score
         + 0.25·minmax(log1p(in_kzt))
         + 0.20·minmax(betweenness)
         + 0.15·minmax(min(n_seed_neighbors,5))

Веса: coordinator=1, consolidator=0.9, distributor=0.75, transit=0.35, terminal=0.25, peripheral=0.10. Minmax по всем узлам; при постоянном признаке 0. Равные приоритеты сортируются по gid. Louvain: seed=42; встречные веса суммируются в неориентированную пару. Внутренний оборот считается по исходным направленным рёбрам один раз.

Подтверждённые результаты

Два полных прогона parquet→CSV: 10.007 и 8.891 секунды, включая старт Python и публикацию. Три обязательных CSV побайтно совпали. 2248 узлов, 3119 рёбер, 4840 транзакций, 91 кластер. Роли: consolidator 51, transit 67, distributor 26, terminal 1071, coordinator 7, peripheral 1026.

25 тестов прошли: 6 pipeline, 5 UI, 3 launcher, 11 загрузок/пересчёта. Обновлённый сервер отвечает HTTP health 200. Визуальная проверка 1280/1440/1920 px, перетаскивание и скачивание через браузер не подтверждены: браузерный инструмент недоступен. Новые проверки: upload-and-motion.md. Предыдущая сдача: verification.md, измерения и SHA-256, версии пакетов.

.venv\Scripts\python.exe -m pip install -r requirements-dev.txt
.venv\Scripts\python.exe scripts/verify_release.py
.venv\Scripts\python.exe -m pytest tests/test_pipeline.py tests/test_ui.py tests/test_run.py tests/test_uploads.py -q -p no:cacheprovider
.venv\Scripts\python.exe scripts/package_release.py

Архив предыдущей сдачи: release/graf-deneg-submission.zip. Для включения последних изменений пересоберите его через scripts/package_release.py. Папки пользовательских загрузок не включаются.

Архитектура

flowchart LR
  P[Три parquet] --> V[Валидация pyarrow]
  V --> G[Направленный граф и признаки]
  G --> R[Louvain и роли]
  R --> S[Приоритет и объяснения]
  S --> C[CSV и metadata]
  C --> U[Streamlit и PyVis]

Архитектура, SVG для слайда, демо на 5 минут.

Ограничения и масштабирование

Следующие числа и ограничения относятся только к примеру хакатона; для своих данных используйте профиль загрузки.

Depth=4: 444 boundary не доказывают конец потока. Вход seed неполон; 19 изолятов не имеют наблюдаемых переводов. Только внутрибанковские переводы от 5000 KZT: дробление ниже порога невидимо. Нет ground truth и клиентских атрибутов. Наблюдаемые суммы — не полный баланс; сумма по рёбрам может повторно учитывать те же деньги. Даты дневные, совпадение сумм не доказывает транзит тех же средств.

Для миллиона узлов: компактный/компилируемый граф (CSR/igraph), колоночные агрегаты Arrow/Polars, приближённые центральности, пакетная кластеризация, выдача подграфов/агрегированных слоёв. Глобальные PageRank/betweenness нельзя точно обновлять только у ближайших соседей без влияния на остальную сеть.

Резервные исходники

Старый pqmini в _kit — ограниченный Linux fallback с системной libzstd, не основной Windows-reader. В запуск и архив он не входит; его декодер не сравнивался с pyarrow. reference/viewer_original.html в архиве сохранён как резерв: он использует CDN и не является автономным интерфейсом сдачи.

Звёзды
⭐ 0
Форки
0
Язык
HTML
Последний push
в прошлом месяце
Коммиты
11
Авторы
2
Файлы
111
Ветки
1

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

Narbol Zhorabek 6Tima 5

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

  • 2026-09-23 17:41Preserve publishing safeguards when integrating remote changesNarbol Zhorabek
  • 2026-09-23 17:39endNarbol Zhorabek
  • 2026-09-23 17:39Complete data uploads and interactive graph UINarbol Zhorabek
  • 2026-09-23 16:53bbbTima
  • 2026-09-23 15:59backeNarbol Zhorabek
  • 2026-09-23 15:59FrontTima
  • 2026-09-23 15:36Complete money graph integrationNarbol Zhorabek
  • 2026-09-23 15:14froTima
Все коммиты (11)
  • 2026-09-23 14:58bacNarbol Zhorabek
  • 2026-09-23 11:51не істегеніңTima
  • 2026-09-23 11:09testTima
Структура репозитория (111 файлов)
.gitignore
.streamlit/config.toml
README.md
_kit/money-graph-final-kit/00_START.md
_kit/money-graph-final-kit/01_BACKEND.md
_kit/money-graph-final-kit/02_FRONTEND.md
_kit/money-graph-final-kit/03_FINISH.md
_kit/money-graph-final-kit/04_CONTRACT.md
_kit/money-graph-final-kit/AUDIT.md
_kit/money-graph-final-kit/README.md
_kit/money-graph-final-kit/SKILL.md
_kit/money-graph-final-kit/app.py
_kit/money-graph-final-kit/audit_original.json
_kit/money-graph-final-kit/convert.py
_kit/money-graph-final-kit/data/edges.csv
_kit/money-graph-final-kit/data/nodes.csv
_kit/money-graph-final-kit/data/transactions.csv
_kit/money-graph-final-kit/data_parquet/README_dataset.md
_kit/money-graph-final-kit/data_parquet/edges.parquet
_kit/money-graph-final-kit/data_parquet/nodes.parquet
_kit/money-graph-final-kit/data_parquet/transactions.parquet
_kit/money-graph-final-kit/out/clusters.csv
_kit/money-graph-final-kit/out/node_metrics_full.csv
_kit/money-graph-final-kit/out/nodes_roles.csv
_kit/money-graph-final-kit/out/top_nodes.csv
_kit/money-graph-final-kit/pipeline.py
_kit/money-graph-final-kit/pqmini/__init__.py
_kit/money-graph-final-kit/pqmini/pqread.py
_kit/money-graph-final-kit/pqmini/thrift_compact.py
_kit/money-graph-final-kit/pqmini/zstd_ctypes.py
_kit/money-graph-final-kit/requirements-ui.txt
_kit/money-graph-final-kit/requirements.txt
_kit/money-graph-final-kit/source/task.docx
_kit/money-graph-final-kit/source/task.txt
_kit/money-graph-final-kit/viewer_original.html
app.py
backend/README.md
backend/app/clusters.py
backend/app/exports.py
backend/app/features.py
backend/app/graph.py
backend/app/load_validate.py
backend/app/main.py
backend/app/pipeline.py
backend/app/roles.py
backend/app/schemas.py
backend/tests/test_core.py
data/edges.csv
data/edges.parquet
data/edges_local.csv
data/nodes.parquet
data/transactions.parquet
data_parquet/edges.parquet
data_parquet/nodes.parquet
data_parquet/transactions.parquet
docs/architecture.md
docs/architecture.svg
docs/demo.md
docs/tested-environment.txt
docs/upload-and-motion.md
docs/verification.json
docs/verification.md
frontend/.gitignore
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/src/App.tsx
frontend/src/api/client.ts
frontend/src/api/demoData.ts
frontend/src/api/types.ts
frontend/src/components/NetworkGraph.tsx
frontend/src/main.tsx
frontend/src/styles.css
frontend/src/styles/app.css
frontend/src/vite-env.d.ts
frontend/tsconfig.app.json
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/vite.config.d.ts
frontend/vite.config.js
frontend/vite.config.ts
out_local/clusters.csv
out_local/node_metrics_full.csv
out_local/nodes_roles.csv
out_local/run_metadata.json
out_local/top_nodes.csv
pipeline.py
pytest.ini
requirements-core.txt
requirements-dev.txt
requirements-lock.txt
requirements-ui.txt
requirements.txt
run.py
scripts/check_launcher.py
scripts/package_release.py
scripts/smoke_archive.py
scripts/verify_release.py
scripts/write_release_docs.py
tests/test_pipeline.py
tests/test_run.py
tests/test_ui.py
tests/test_uploads.py
ui/__init__.py
ui/data.py
ui/graph.html
ui/graph.py
ui/model.py
ui/source.py
ui/style.css
ui/uploads.py

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

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

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