A rule-based risk-flagging framework for India's UPI payment ecosystem, built as a Risk/Ops analytics project (Corporate Analyst / Data Analyst, banking context).
A 3-rule, fully explainable risk-flagging framework flags 2.23% of transaction volume and catches 53.6% of high-risk-pattern transactions at 46.6% precision (~24x lift over a 1.93% base rate) — reducing an illustrative 15%-blanket-review baseline by 85.2%.
This project uses two clearly separated data sources:
| Real | Synthetic | |
|---|---|---|
| What | NPCI monthly volume/value trend; NPCI's published Technical Decline (TD) / Business Decline (BD) rate benchmarks | 30,000-row transaction-level dataset, calibrated to the real aggregate rates above |
| Why | Publicly reported by NPCI | No real transaction-level, fraud-labeled UPI dataset is publicly available anywhere (privacy regulation, industry-wide) |
| File(s) | data/real_npci_monthly.csv, data/source_catalog.csv |
data/upi_synthetic_transactions.csv, data/generate_data.py |
| Caveat | Some months are genuinely missing — left blank, not estimated | The is_fraud_flag field is an illustrative heuristic construct, not observed fraud |
Every real statistic used anywhere in this project is traceable via data/source_catalog.csv
(source, metric, period, URL, confidence note).
UPI-Risk-Analytics/
├── data/
│ ├── generate_data.py # synthetic data generator, with built-in validation checks
│ ├── upi_synthetic_transactions.csv
│ ├── real_npci_monthly.csv # real NPCI monthly trend (gaps left honest, not filled)
│ └── source_catalog.csv # provenance for every real statistic used
├── sql/
│ ├── risk_analysis.sql # 11 real analytical queries (not decorative)
│ └── load_and_run.py # loads CSV -> SQLite, runs risk_analysis.sql, prints results
├── notebooks/
│ └── UPI_Risk_Analysis.ipynb # full pipeline: EDA, stats tests, rules, sensitivity, impact
├── dashboard/
│ └── UPI_Risk_Dashboard.html # standalone interactive dashboard (open directly in a browser)
├── reports/
│ ├── UPI_Risk_Analysis_Executive_Summary.docx
│ └── UPI_Risk_Analysis_Resume_and_Interview_Prep.docx
├── images/ # charts exported from the notebook
└── requirements.txt
pip install -r requirements.txt
# 1. Generate the synthetic dataset (includes automatic validation checks)
python data/generate_data.py
# 2. Run the SQL layer (loads CSV into SQLite, runs all 11 queries)
python sql/load_and_run.py
# 3. Execute the notebook end to end
jupyter nbconvert --to notebook --execute --inplace notebooks/UPI_Risk_Analysis.ipynb
# 4. Open the dashboard
open dashboard/UPI_Risk_Dashboard.html # or just double-click it- Every real statistic is sourced in
data/source_catalog.csv— no unattributed numbers. - The synthetic generator validates itself —
generate_data.pyasserts success/TD/BD rates land inside the real NPCI benchmark range, and fails loudly if they don't. - Claims are statistically tested, not asserted. Section 7 of the notebook runs two-proportion z-tests on every headline pattern — including one (evening-hour decline) that did not hold up under testing and is reported as a negative result rather than dropped silently.
- The SQL layer and the notebook (pandas) implement the same rule logic independently and return identical figures (2.23% flagged / 46.6% precision / 53.6% coverage) — see notebook Section 8.
- Threshold sensitivity (Section 9) shows why the p95/p99 cutoffs were chosen, not just that they were.
- Limitations (Section 12) and a "what I'd do with real data" section (Section 13) are included deliberately, because knowing what a project doesn't prove is part of what makes it credible.
This is a portfolio/interview-preparation project. The synthetic dataset and its "fraud" label are
illustrative constructs calibrated to real published aggregate statistics — they are not, and are
not presented as, real transaction or fraud data. See data/generate_data.py docstring and the
notebook's Section 0 disclaimer for full detail.