О проекте
Команда: Simplex
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Локальный MVP AML-анализа: из трёх parquet получаются объяснимые роли всех узлов, Louvain-кластеры, топ приоритетов с разложением вкладов и автономный HTML-дашборд; есть анализ чувствительности и сценарии удаления узлов.
Как устроено
Пайплайн проверяет входные данные (типы, уникальность, согласованность транзакций с рёбрами, SHA-256), считает степени, обороты, betweenness на ориентированном графе по числу переходов, достижимость от seed и временные признаки (FIFO-совместимость за 1–2 дня, синхронные плательщики). Роли назначаются правилами с порогами из config.json; coordinator требует путей от ≥2 seed и соседей в ≥2 кластерах. Priority — взвешенная сумма лог-насыщенных на P95 признаков. Louvain запускается по каждой слабосвязной компоненте с весом log1p(сумма). sensitivity.py варьирует пороги и веса и проверяет устойчивость топ-20.
Ключевые решения
- Louvain
- betweenness centrality
- seed reachability
- temporal FIFO patterns
- sensitivity analysis
- log-saturation P95 scoring
- top-N removal scenario
Метрики
Минимальное пересечение с базовым топ-20 при вариациях порогов и весов — 18 из 20
Особенности
Анализ чувствительности ролей и топа к порогам и весам. Сценарий достижимости от seed при исключении топ-1/3/5. Хранение gid строками из-за точности JS. Генератор синтетических данных для проверки без датасета. Методологическая аккуратность: не использовать суммы как расстояние в betweenness.
Стек
Python · NetworkX · pandas · PyArrow · HTML/JS · unittest · GitHub Actions
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-bb09aaa3-simplex.
README
README.mdMoneyGraph · HackAlem AI
Локальный MVP для трека «Граф денег: восстановление финансовой структуры организованной группы по транзакционной сети».
Три Parquet-файла превращаются в объяснимые роли всех участников, кластеры, список приоритетов и интерактивную карту. Основной вопрос: кого проверить первым и какие наблюдения это обосновывают?
Это инструмент поддержки аналитика. Роли — гипотезы, role_score — сила формальных признаков, priority_score — приоритет проверки. Ни один скор не является вероятностью виновности. Реальные остатки на счетах и происхождение конкретных денег по этой выгрузке не восстанавливаются.
Быстрый старт
Нужен Python 3.12. Рабочая директория во всех командах — корень проекта.
Windows / PowerShell
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Распакуйте архив организаторов data (1).zip в корень проекта. Должно получиться:
data/
nodes.parquet
edges.parquet
transactions.parquet
Один запуск от сырых данных до всех результатов и интерфейса:
.\.venv\Scripts\python.exe run.py --data data --out results --serve --open
Откроется http://127.0.0.1:8765/dashboard.html.
Остановка сервера: Ctrl+C.
Активация окружения не требуется; менять PowerShell ExecutionPolicy не нужно.
macOS / Linux
python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python run.py --data data --out results --serve --open
Для расчёта без сервера:
python run.py --data data --out results
После него results/dashboard.html можно открыть локально. Данные, стили и код включены в HTML; CDN и интернет для работы интерфейса не требуются.
Интернет нужен только при первой установке библиотек.
Для полностью офлайн-машины можно заранее подготовить wheelhouse:
python -m pip download -r requirements.txt -d wheelhouse
python -m pip install --no-index --find-links wheelhouse -r requirements.txt
Проверка без закрытого датасета
python scripts/make_demo.py --out data-demo
python run.py --data data-demo --out results --serve
Генератор создаёт полностью вымышленные узлы и переводы, включая изолированный seed, четвёртое колено, веер и транзит. Это данные для демонстрации и тестирования, а не замена датасету хакатона.
Что готово
| Требование | Реализация |
|---|---|
| Воспроизводимый пайплайн < 5 минут | run.py, закреплённые версии, проверка входа и выхода, SHA-256 входных файлов |
| Роль у каждого узла | Все nodes.gid, включая изолированные; шесть ролей обязательного словаря |
| Формальные объяснения | Пороги в config.json, методика ниже и в docs/METHODOLOGY.md; числовое evidence ≤ 200 символов |
| Кластеры | Louvain по отдельным компонентам, включая singleton-кластеры изолятов |
| Топ ≥ 20 | По умолчанию топ-50, отдельное объяснение вкладов факторов |
| Визуализация | Стрелки, роли/кластеры, поиск точного gid, карточки, масштабирование и фильтры |
| Учёт обрыва | Узлы depth=4 с нулевым выходом не получают terminal; положительные признаки входа сохраняются |
| Временные паттерны | FIFO-совместимость за 1–2 дня, синхронные плательщики, доля пикового дня |
| Структурный сценарий | Достижимость от seed при исключении топ-1/3/5 |
| Чувствительность выводов | Отдельный отчёт по изменению порогов ролей и весов приоритета |
| Воспроизводимость | Локальные unittest-тесты; CI-конфигурация для Windows/Linux включена в .github/workflows/ci.yml |
В текущем MVP нет внешнего LLM, GPU, платных API и обучения с размеченными ролями. Используются объяснимые правила и кластеризация без учителя.
Нельзя заявлять accuracy: истинные роли не размечены. AI/LLM-ассистент может быть подключён отдельным слоем поверх рассчитанного графа, не заменяя формальные правила ролей и приоритета.
Выходные файлы
| Файл | Содержимое |
|---|---|
nodes_roles.csv | gid, role, role_score, cluster_id, priority_score, evidence, далее наблюдаемые метрики |
clusters.csv | cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis |
top_nodes.csv | rank, gid, role, priority_score, why; минимум 20 при наличии ≥20 узлов |
dashboard.html | Полностью автономный интерактивный интерфейс |
graph.json | Данные интерфейса, альтернативные роли, временные ряды, сценарии |
audit.json | Объёмы, проверки, ограничения, распределение ролей, время расчёта |
manifest.json | Параметры, версии Python/библиотек, хеши входных Parquet |
sensitivity.json | Дополнительный отчёт устойчивости, создаётся отдельной командой |
CSV — UTF-8 с BOM, разделитель — запятая.
gid в CSV хранится точной десятичной записью int64. При импорте в Excel выберите для gid тип «Текст»: Excel может округлять длинные числа.
В JSON и браузере gid представлены строками, поскольку эти значения могут выходить за точный целочисленный диапазон JavaScript.
top_gids — до пяти gid через ;, по убыванию приоритета.
Внутренний оборот кластера считает каждое направленное ребро один раз. Сумма внутренних оборотов может быть меньше общего оборота из-за межкластерных переводов.
Объяснение произвольного узла
Проверить произвольный узел без браузера:
python run.py --out results --explain <полный_gid>
Проверка чувствительности
После подготовки данных:
Windows / PowerShell
.\.venv\Scripts\python.exe scripts\sensitivity.py --data data --out results\sensitivity.json
macOS / Linux
python scripts/sensitivity.py --data data --out results/sensitivity.json
Анализ чувствительности:
- изменяет пороги числа плательщиков и получателей на ±1;
- изменяет каждый вес приоритета на ±20%;
- нормирует итоговую сумму весов;
- сравнивает изменения ролей;
- проверяет устойчивость состава топ-20.
В sensitivity.json сохраняются gid с изменившейся ролью и узлы, вошедшие или выбывшие из топа.
Это проверка зависимости результата от настроек, а не измерение accuracy или вероятности виновности.
На предоставленном датасете при проверенных вариациях минимальное пересечение с базовым топ-20 составило 18 из 20 узлов. Этот результат относится только к реализованным сценариям чувствительности и не является гарантией устойчивости при любых параметрах.
Формальные правила ролей
I, O — суммы наблюдаемого входа/выхода.
din, dout — число разных контрагентов.
R = O / I, только при I > 0.
Для seed и обрезанных узлов R не участвует в назначении транзита/стока.
| Роль | Условие допуска по умолчанию |
|---|---|
consolidator | din ≥ 3 |
distributor | dout ≥ 10 |
transit | Не seed, не обрыв, I>0, dout>0, 0.8 ≤ R ≤ 1.2 |
terminal | Не seed, не обрыв, I>0, R ≤ 0.1, есть вход до последних двух дней и нет входа в последние два дня |
coordinator | Не обрыв; din≥2, dout≥2; направленные пути от ≥2 разных seed; соседи в ≥2 внешних кластерах; положительное посредничество ≥P90 положительных значений |
peripheral | Ни одно из правил не выполнено; означает недостаток признаков, а не безопасность клиента |
Допущенные роли получают собственный скор.
coordinator имеет приоритет как более специфический структурный паттерн; иначе выбирается максимальный скор.
Альтернативные роли сохраняются и видны в карточке. Формулы и tie-break описаны в методике.
Координатор в интерфейсе назван «кандидат-мост»: центральность сама по себе не доказывает управление группой.
terminal отображается как «гипотеза стока». Даже внутри глубины 4 невозможно доказать, что деньги остались на счёте: отсутствуют остатки, межбанк и следующий период.
Приоритет проверки
priority = 0.30 × S(seed_reach)
+ 0.25 × S(in_degree)
+ 0.15 × S(max(in_kzt, out_kzt))
+ 0.15 × S(betweenness)
+ 0.10 × S(out_degree)
+ 0.05 × temporal_share
S(x) = min(1, log(1+x) / log(1+P95 положительных x))
Если положительных значений нет, S=0.
Временной вклад равен нулю для seed и обрыва.
Метрики считаются по всей сети, а не только по крупнейшей компоненте.
Сортировка: score по убыванию, затем точный числовой gid по возрастанию.
Насыщение на P95 может давать одинаковые scores; такой порядок не означает доказанное превосходство одного узла над другим.
Веса — документированная эвристика MVP. Они не обучены на скрытом «правильном ответе». Оценивать нужно обоснованность и устойчивость, а не заявлять неподтверждённую точность.
Кластеры и направление денег
Исходный граф всегда ориентированный.
Для Louvain строится отдельная неориентированная проекция:
- суммы A→B и B→A складываются;
- вес —
log1p(сумма); - каждая слабосвязная компонента кластеризуется отдельно;
- изоляты получают отдельные кластеры;
- случайное зерно — 42;
- ID кластеров канонизируются по размеру и минимальному gid;
- самопетли не усиливают Louvain.
Число кластеров не задаётся заранее.
«8 сообществ с несколькими seed» из ТЗ — ориентир организаторов, а не требование получить ровно восемь кластеров.
Кластер — структурная гипотеза, а не доказанное сообщество соучастников.
Посредничество считается на ориентированном графе по числу переходов.
Суммы денег нельзя использовать как расстояние в betweenness_centrality: в таком случае крупный перевод ошибочно стал бы длинным путём.
Данные и ограничения
Период по умолчанию — 01–31 июля 2026.
Датасет содержит только внутрибанковские исходящие цепочки до четырёх колен и переводы от 5 000 KZT.
Персональных атрибутов нет; приложение их не придумывает и не обогащает внешними источниками.
При чтении проверяются:
- типы;
- уникальность gid/пар;
- диапазоны глубины;
- принадлежность концов рёбер списку узлов;
- даты;
- положительные суммы;
- порог сбора;
- совпадение сумм и числа операций между транзакциями и агрегированными рёбрами.
Совпадающие строки транзакций сохраняются: без transaction_id нельзя считать их дублями.
Особенности фактического датасета
- 2 248 узлов;
- 3 119 направленных рёбер;
- 4 840 транзакций;
- 81 seed;
- 16 компонент с рёбрами + 19 изолированных seed = 35 компонент полного графа;
- 444 узла обрезаны на четвёртом колене;
- 31 seed не имеет исходящих переводов;
- у 354 узлов с положительным наблюдаемым входом выход больше входа;
- ещё у 23 узлов есть выход при нулевом наблюдаемом входе;
- 97 полностью совпадающих строк транзакций сохранены, агрегаты сходятся;
- оборот по рёбрам — 365 890 012.01 KZT.
Оборот — это сумма по наблюдаемым рёбрам, а не объём уникальных денег и не оценка преступного дохода.
Фактические исходные данные и рассчитанные результаты остаются локальными и исключены из Git.
Датасет предоставлен организаторами для хакатона; исходники проекта не расширяют права на его распространение.
dashboard.html содержит встроенные данные, поэтому с ним следует обращаться как с аналитической выгрузкой.
Архитектура
flowchart LR
A[3 Parquet] --> B[Проверка схемы и агрегатов]
B --> C[Ориентированный граф со всеми узлами]
C --> D[Структурные метрики и seed-пути]
C --> E[Louvain по компонентам]
B --> F[Временные признаки 1–2 дня]
D --> G[Правила ролей и вклад в приоритет]
E --> G
F --> G
G --> H[3 CSV и аудит]
G --> I[Автономный HTML-интерфейс]
G --> J[Анализ чувствительности]
Отдельная схема для демонстрации: docs/architecture.svg.
Структура проекта
run.py CLI, объяснение узла, локальный HTTP-сервер
config.json пороги, период, зерно, размер топ-листа
moneygraph/data.py проверка и нормализация входа
moneygraph/temporal.py временное FIFO-сопоставление
moneygraph/analysis.py граф, роли, кластеры, приоритеты, сценарии
moneygraph/pipeline.py контракты, экспорт, аудит, встраивание данных
moneygraph/web/dashboard.html интерактивный интерфейс без внешних зависимостей
scripts/make_demo.py вымышленный датасет для тестирования
scripts/check_submission.py проверка сдачи на датасете хакатона
scripts/sensitivity.py анализ чувствительности порогов и весов
tests/test_core.py тесты критических свойств
docs/ методика, план MVP, демонстрация, схема
Проверки
Unit tests
python -m unittest discover -s tests -v
Полный расчёт
python run.py --data data --out results
Проверка сдачи
python scripts/check_submission.py --data data --out results
Тесты проверяют:
- обрыв на четвёртом колене;
- особенности seed;
- период наблюдения;
- отсутствие двойного расходования входа;
- неизвестные/округлённые gid;
- несовпадение агрегатов;
- сохранение изолятов;
- точность ID в JSON;
- повторяемость CSV;
- независимость результата от порядка строк;
- корректность sensitivity-отчёта и сравнения топов.
CI-конфигурация для Windows и Linux находится в .github/workflows/ci.yml.
Локальные итоги проверки реальных файлов описаны в docs/VALIDATION.md.
До 1 млн узлов
Текущая реализация рассчитана на объём хакатона; обещания пяти минут для миллиона узлов нет.
При росте нужны:
- Агрегация Parquet через DuckDB/Polars с чтением по колонкам и партициями по датам.
igraph/CSR вместо Python-объектов NetworkX; Leiden/Louvain по компонентам.- Приближённое посредничество по выборке источников вместо полного Brandes.
- Ограниченные обходы от seed, пакетная обработка и битовые множества для достижимости.
- Временные признаки в отсортированном потоке по gid/дате и инкрементальный пересчёт затронутых узлов.
- Серверная выдача подграфов с пагинацией вместо загрузки миллиона узлов в один HTML.
- Версионирование расчётов, контроль доступа и журнал действий аналитиков перед эксплуатацией.
Следующие шаги команды
Практический план MVP и разделение работ: docs/MVP_PLAN.md.
Пятиминутная защита: docs/DEMO.md.
Ближайшие улучшения после рабочего MVP:
- AI-ассистент аналитика поверх рассчитанного графа;
- анализ устойчивости кластеров;
- временные повторяющиеся маршруты;
- проверяемые запросы на естественном языке.
Новые функции не должны заменять формальные правила ролей и приоритета. AI-слой должен использовать рассчитанные метрики и связи как источник фактов и формулировать результаты как гипотезы для аналитической проверки.
Авторы коммитов
История коммитов
- 2026-09-23 17:39Update README documentationAnzy737
- 2026-09-23 16:39Fix direct sensitivity script executionAnzy737
- 2026-09-23 15:53Add sensitivity analysis for roles and priority rankingesenkhannurali-ux
- 2026-09-23 15:26Improve README for submissionAnzy737
- 2026-09-23 14:51Add MoneyGraph MVPAnzy737
Структура репозитория (22 файлов)
.github/workflows/ci.yml .gitignore README.md config.json data/.gitkeep docs/DEMO.md docs/METHODOLOGY.md docs/MVP_PLAN.md docs/VALIDATION.md docs/architecture.svg moneygraph/__init__.py moneygraph/analysis.py moneygraph/data.py moneygraph/pipeline.py moneygraph/temporal.py moneygraph/web/dashboard.html requirements.txt run.py scripts/check_submission.py scripts/make_demo.py scripts/sensitivity.py tests/test_core.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.