Team Name : Basant
Tech Stack: FastAPI Β· NetworkX Β· Pandas Β· React Β· Cytoscape.js
A production-grade AML (Anti-Money Laundering) graph intelligence engine that detects money mule networks in financial transaction data using purely deterministic, explainable graph algorithms β zero machine learning.
| Pattern | Algorithm | Description |
|---|---|---|
| Circular Fund Routing | Depth-limited DFS | Directed cycles of length 3β5 |
| Smurfing | Sliding-window fan-in/fan-out | β₯10 unique counterparties within 72 h |
| Layered Shell Networks | Shell-DFS | Chains β₯3 through low-activity nodes |
flowchart TD
User(["π€ Analyst / User"])
Upload["π CSV Upload\n(React Frontend)"]
API["β‘ FastAPI\nPOST /analyze"]
Validator["π Validator\nSchema Β· Type Coercion Β· Timestamp Parsing"]
GraphBuilder["πΈοΈ TransactionGraph\nNetworkX DiGraph\nAdjacency Lookups O(n)"]
subgraph Detectors["Detection Layer (Parallel)"]
Cycle["π CycleDetector\nDepth-limited DFS\nCycles len 3β5"]
Smurf["πΈ SmurfDetector\nSliding-window 72h\nFan-in / Fan-out β₯10"]
Shell["π ShellDetector\nShell-DFS\nChains β₯3 low-activity nodes"]
end
FeatureExtractor["π FeatureExtractor\nStructural Β· Flow Β· Temporal"]
RiskScorer["βοΈ RiskScorer\nWeighted Rules\n+ MerchantDampeningRule"]
Aggregator["ποΈ RingAggregator\n+ RingConsolidator"]
JSONOut["π¦ JSON Response"]
subgraph Frontend["React Frontend"]
SummaryCards["π SummaryCards"]
RingTable["π RingTable"]
GraphView["π Cytoscape.js\nGraph Visualisation"]
SuspiciousAcct["π¨ SuspiciousAccounts"]
end
User --> Upload
Upload -->|"HTTP POST multipart/form-data"| API
API --> Validator
Validator -->|"Validated DataFrame"| GraphBuilder
GraphBuilder --> Cycle
GraphBuilder --> Smurf
GraphBuilder --> Shell
Cycle --> FeatureExtractor
Smurf --> FeatureExtractor
Shell --> FeatureExtractor
FeatureExtractor --> RiskScorer
RiskScorer --> Aggregator
Aggregator --> JSONOut
JSONOut -->|"JSON"| SummaryCards
JSONOut -->|"JSON"| RingTable
JSONOut -->|"JSON"| GraphView
JSONOut -->|"JSON"| SuspiciousAcct
style Detectors fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Frontend fill:#1a3a2a,stroke:#22c55e,color:#fff
style Validator fill:#3b1f1f,stroke:#ef4444,color:#fff
style GraphBuilder fill:#2a2a3a,stroke:#a78bfa,color:#fff
style FeatureExtractor fill:#2a2a3a,stroke:#a78bfa,color:#fff
style RiskScorer fill:#2a2a3a,stroke:#a78bfa,color:#fff
style Aggregator fill:#2a2a3a,stroke:#a78bfa,color:#fff
- FastAPI β REST API server
- NetworkX β directed graph construction and analysis
- Pandas β data loading and temporal sliding windows
- NumPy β statistical feature computation
- Uvicorn β ASGI server
- React 18 β UI framework
- Cytoscape.js β interactive graph visualisation
- cytoscape-cola β force-directed layout
- Axios β HTTP client
Algorithm: Depth-limited iterative-deepening DFS
Complexity: O(V Γ d^5) where d = average out-degree (fast for sparse graphs)
For each node v:
DFS(v, path=[v], depth_limit=5)
For each successor u of current node:
If u == v AND len(path) β [3,5]:
β record cycle
Elif u not in path:
β recurse
Normalise: rotate cycle to lex-smallest prefix β deduplicate with set
Parameters: min cycle length = 3, max = 5
Algorithm: O(n log n) two-pointer sliding window
Complexity: O(n log n) β dominated by timestamp sort per account
For each account a:
Sort transactions by timestamp
Two-pointer window of width 72 hours:
Count unique counterparties in window
If count β₯ 10 β SMURFING RING
Thresholds: 72-hour window, β₯10 unique counterparties
Algorithm: Shell-filtered DFS
Complexity: O(V Γ k Γ d^k) where k = max_chain_depth (capped at 8)
Precompute shell_accounts = {a : tx_count(a) β€ 3}
For each non-shell start node s:
DFS through only shell intermediaries
If path length β₯ 3 β record chain
Shell threshold: accounts with total_tx_count β€ 3
No ML. Fully deterministic weighted scoring:
| Signal | Score |
|---|---|
| Cycle length 3 | +40 |
| Cycle length 4 | +35 |
| Cycle length 5 | +30 |
| Smurfing fan-in hub | +45 |
| Smurfing fan-out hub | +40 |
| Smurfing member | +20 |
| Shell intermediary | +25 |
| Shell final beneficiary | +20 |
| High clustering coefficient (>0.5) | +10 |
| High burst score (>0.7) | +15 |
| High uniformity score (>0.8) | +10 |
| High tx velocity (>10/hr) | +10 |
- Scores capped at 100, floored at 0
- Rounded to 2 decimal places
High-volume legitimate merchants (e.g. payment processors) can exhibit fan-out patterns without being money mules. The dampening rule fires when:
distributor_flag = True
AND time_span_hours > 720 # active > 30 days
AND amount_variance > median_variance # diverse transaction sizes
AND unique_receivers > 50 # genuinely broad customer base
Effect: multiply raw score Γ 0.70 (β30% reduction) and add
"merchant_dampening_applied" to detected_patterns for auditability.
| Operation | Complexity |
|---|---|
| Graph construction | O(n) |
| Cycle detection | O(V Γ d^5) |
| Smurfing detection | O(n log n) |
| Shell detection | O(V Γ d^8) with pruning |
| Feature extraction | O(n + E) |
| Risk scoring | O(A) β A = accounts |
| Total | β€ O(n log n) practical |
Designed to handle 10,000 transactions in β€ 30 seconds.
{
"suspicious_accounts": [
{
"account_id": "ACC_F001",
"suspicion_score": 87.50,
"detected_patterns": ["cycle_length_3", "high_velocity"],
"ring_id": "RING_001"
}
],
"fraud_rings": [
{
"ring_id": "RING_001",
"member_accounts": ["ACC_F001", "ACC_F002", "ACC_F003"],
"pattern_type": "cycle",
"risk_score": 87.50
}
],
"summary": {
"total_accounts_analyzed": 500,
"suspicious_accounts_flagged": 15,
"fraud_rings_detected": 4,
"processing_time_seconds": 2.30
}
}- Python 3.11+
- Node.js 18+
# From project root
pip install -r requirements.txt
# Run development server
uvicorn backend.main:app --reload --port 8000Backend available at http://localhost:8000
Swagger UI at http://localhost:8000/docs
cd frontend
npm install
npm startFrontend available at http://localhost:3000
The React dev server proxies /graph-data and /analyze to localhost:8000.
-
Generate or prepare a CSV with columns:
transaction_id, sender_id, receiver_id, amount, timestamp -
Generate the included sample:
python generate_sample.py # β sample_transactions.csv (500 rows with embedded fraud patterns) -
Open the web app, upload the CSV, click Analyse Transactions.
-
Explore:
- Graph: red nodes = high risk, purple border = in a ring
- Click any node to see its scores and patterns
- Fraud Rings table: expand rows to see all member accounts
- Download JSON button: exports the full detection report
| Method | Path | Description |
|---|---|---|
| GET | /health |
Liveness check |
| POST | /analyze |
Full analysis β JSON report |
| POST | /graph-data |
Full analysis + Cytoscape elements |
Both POST endpoints accept multipart/form-data with a file field.
- Push to GitHub
- New Web Service on render.com
- Root directory:
backend - Build:
pip install -r requirements.txt - Start:
uvicorn main:app --host 0.0.0.0 --port $PORT
Option A: Vercel (Recommended for frontend)
- Go to vercel.com
- Import your GitHub repository
- Root directory:
frontend - Env var:
VITE_API_URL=https://mm-detection.onrender.com - Deploy! See VERCEL.md for details.
Option B: Render Static Site
- New Static Site on Render
- Root directory:
frontend - Build:
npm install && npm run build - Publish:
frontend/dist - Env var:
VITE_API_URL=https://mm-detection.onrender.com
No credit card required! See DEPLOYMENT.md or VERCEL.md for detailed instructions.
MM_Detection/
βββ backend/
β βββ main.py # FastAPI app
β βββ requirements.txt
β βββ graph/
β β βββ builder.py # TransactionGraph (DiGraph + lookups)
β βββ detection/
β β βββ cycle_detector.py # DFS cycle detection
β β βββ smurf_detector.py # Sliding-window smurfing
β β βββ shell_detector.py # Layered shell chains
β βββ scoring/
β β βββ feature_extractor.py # Per-account feature computation
β β βββ risk_scorer.py # Deterministic weighted scoring
β βββ utils/
β βββ validator.py # CSV schema validation
β βββ aggregator.py # Ring merging + risk aggregation
βββ frontend/
β βββ package.json
β βββ src/
β βββ App.js
β βββ index.js
β βββ index.css
β βββ components/
β βββ Upload.js # Drag-and-drop CSV upload
β βββ GraphView.js # Cytoscape.js graph
β βββ RingTable.js # Fraud ring summary
β βββ SuspiciousAccounts.js
βββ generate_sample.py # Sample data generator
βββ requirements.txt # Root-level (for Render)
βββ Procfile # Heroku/Railway start command
βββ render.yaml # Render deployment config
βββ README.md
-
Cycle detection depth: capped at 5 hops. Deeper laundering chains are not detected (by design β hackathon constraint). Can be raised via
MAX_CYCLE_LEN. -
Smurfing threshold: 10 unique counterparties. Adjust
FAN_THRESHOLDinsmurf_detector.pyfor different regulatory contexts. -
Shell threshold: β€3 total transactions classifies an account as a shell. May cause false positives for genuinely new accounts.
-
Graph layout: Cytoscape cola layout can be slow for >1,000 nodes. For very large graphs the fallback
coselayout is used. -
No persistence: Results are not stored; each upload is a fresh analysis session.
-
CSV size: Tested up to 10,000 transactions. Beyond this, performance is not guaranteed within 30 seconds.
RIFT 2026 Β· Money Muling Detection Challenge Β· Graph Theory Track
Submission date: February 2026