This is a portfolio project about analytics for a core banking migration. A bank runs two old systems, LEGACY_A and LEGACY_B, and moves all customers to one new platform. The move goes in waves: one country and one source system per wave, with a fixed cutover date for each wave.
The project models one such programme and answers the questions an analyst really gets during it. How many objects are ready to move, and what blocks the rest. Where the data is dirty, and who has to clean it first. Do the numbers in the source and in the target match after the cutover. Which customers lose their IBAN, card or standing order, and how the bank can reach them. Which channel, push or email, makes people update their standing order before the cutover date.
The bank, the customers and the data are not real. The data is generated, but the problems inside it are put there on purpose: dirty records in one legacy system, standing orders that point to closed accounts, an incident on one cutover weekend, and an A/B test where the average result hides two opposite effects. The point of the project is to find these things in the data, not to produce a large number of rows.
The dataset has 10 000 customers and about 1.84 million rows. The period is from September 2024 to August 2026. There are four waves: two in Estonia, one in Latvia and one in Lithuania. The Lithuanian wave is still running, so it still holds objects with the statuses not_started and ready.
The banking part is normal for any bank: customers, accounts, transactions, standing orders and product holdings. Together they describe what the bank has to move.
The migration layer is the core of the project. One table keeps the status of every object that must be moved, its key in the old system, its key in the new one, and the rule that blocks it if it cannot go. A second table keeps the data quality findings, each one linked to a rule and to a customer, with a status: open, fixed or waived. A third table says what each customer loses in the move: a new IBAN, a card reissue, affected standing orders.
The behaviour part shows the result of the move on the customer side: messages sent to customers with the delivery and open time, mobile app events, and support tickets.
There are also three small reference tables (products, waves, data quality rules) and two views. One view compares the source and the target side by side, the other one is a calendar with weekends and public holidays of the three countries.
The CSV files are not in this repository. They are about 160 MB and can be generated again from the code.
- PostgreSQL and SQL — data marts, readiness and data quality queries, source to target reconciliation.
- Python — data generation and statistics: pandas, NumPy, SciPy, Matplotlib, SQLAlchemy.
- Power BI — the dashboard for the migration progress and the customer impact.
- Excel — the exchange format between Python and the report.
sql/analysis.sql is the SQL part. It has six blocks, one for each business question: the size of the portfolio and the migration progress by wave, the readiness of the open wave with its top blockers, the check between status and target key, the data quality queue, the priority of customer segments for cleanup, and the customer impact by source system. Every block ends with a comment that explains how to read the result and where the number can mislead.
ab_test.py is the A/B test for the communication channel. It reads the data from PostgreSQL and goes in three steps. First it checks the randomisation with a chi-square test, because the split in the last wave does not match the plan and the result of that wave cannot be trusted before this is explained. Then it compares the conversion between the groups with a z-test for two proportions, with a confidence interval for the difference. In the end it splits the customers by their mobile app activity before the notice, and this is where the real effect appears. The script also draws the chart used in the report.
docs/FINDINGS.md is the report. It is written for a reader who will not open the code: what was measured, what the numbers mean, what is not proof yet, and what to do next. It ends with a short list of priorities and with an honest section about what is still missing.