INTRODUCTION: THE AGGREGATION PROBLEM — When One Transaction Becomes Two
In Lessons 5.1 and 5.2, we defined the canonical JSON schemas for Account, Balance, and Transaction resources. We learned to transform ISO 20022 XML into OBIE/CDR/FDX compliant JSON. We designed the Unified Schema Bridge to serve multiple jurisdictions with zero code duplication. However, a fundamental problem remains: the data arrives from multiple banks.
A PSU (the customer) typically holds accounts at multiple banks: a personal current account with Bank A, a savings account with Bank B, and a credit card with Bank C. A TPP (e.g., a personal finance management app) aggregates all these accounts to provide a consolidated financial picture. The TPP issues separate API calls to Bank A, Bank B, and Bank C, receiving three distinct JSON responses. The TPP’s backend must then merge these datasets into a single, unified view.
This is the Account Aggregation problem. It seems trivial—simply concatenate the account lists. However, the devil lies in the transaction deduplication. A single real-world transaction (e.g., a direct debit from Bank A to Bank C) appears in both Bank A’s statement (as a debit) and Bank C’s statement (as a credit). If the TPP simply sums all transactions across banks without deduplicating, the PSU’s “net spend” will be double-counted, rendering the financial analysis useless.
This lesson tackles the mathematical and algorithmic challenges of deduplication. We define the Deduplication Problem as a record linkage task: given two transaction records (from different banks, with different identifiers, timestamps, and descriptions), determine whether they represent the same economic event. We will implement deterministic matching (exact matches on TransactionId if the banks share a common reference) and probabilistic matching (fuzzy matching on Amount, BookingDateTime, and TransactionInformation). We will derive the Levenshtein distance for string similarity, the Jaro-Winkler distance for merchant name matching, and the blocking strategy to reduce the computational complexity from O(N^2) to O(N log N).
We will quantify the accuracy of these algorithms using the F1-score (Precision vs. Recall) and prove that a hybrid deterministic-probabilistic approach achieves a precision of 99.9% and a recall of 98.5%. We will also calculate the end-to-end latency of deduplication: a streaming Kafka pipeline that can deduplicate 10,000 transactions per second with a p95 latency of 35ms.
LEARNING OBJECTIVES
-
Formalize the Deduplication Problem—defining the record linkage framework
M(D_A, D_B) → {Match, Non-Match}for two transaction datasetsD_AandD_B, and deriving the hypothesis testing framework: the Fellegi-Sunter model for probabilistic record linkage. -
Implement Deterministic Matching—identifying the cases where transactions share a global identifier (e.g., ISO 20022
EndToEndIdor SWIFTUETR), and proving that exact matching on these fields yields zero false positives (Precision = 100%) but may miss matches due to missing fields. -
Design Probabilistic Matching with Fuzzy Text—deriving the Levenshtein Distance (edit distance)
d(s1, s2)for merchant descriptions and transaction references, and the Jaro-Winkler Distancesim(s1, s2)for short strings (e.g., merchant names), and calculating the similarity thresholdθthat maximizes the F1-score. -
Deconstruct the Blocking Strategy—proving that naive pairwise comparison is
O(N^2)and computationally infeasible for large datasets, and designing a blocking key based onAmount(rounded to nearest integer) andBookingDate(YYYY-MM-DD) to reduce the comparison space toO(N log N). -
Quantify Deduplication Latency—measuring the end-to-end latency of a streaming deduplication pipeline (Kafka → Stateful Processor → Redis Cache) and proving that the p95 latency is under 35ms for 10,000 TPS (transactions per second).
-
Evaluate the Accuracy Trade-off—deriving the Precision-Recall curve for the hybrid model (deterministic + probabilistic), calculating the F1-score at the optimal threshold, and proving that the model achieves Precision ≥ 99.9% and Recall ≥ 98.5% for typical banking datasets.
PART 1: THE FORMAL FRAMEWORK OF RECORD LINKAGE
1.1 The Deduplication Problem as a Binary Classification
Let T_A be the set of transactions from Bank A, and T_B be the set from Bank B. The TPP’s goal is to construct a unified set U such that:
U = (T_A ∪ T_B) \ D
where D is the set of duplicate transactions (one from Bank A, one from Bank B). Each transaction in T_A has a list of attributes: (TransactionId, Amount, BookingDate, Description, RefNumber). The deduplication algorithm applies a matching rule R that takes two records r1 ∈ T_A and r2 ∈ T_B and outputs {Match, Non-Match}.
The Fellegi-Sunter Model (1969): This is the foundational probabilistic record linkage model. For a pair of records, we compare a set of fields f1...fk. Each comparison yields a similarity score. The total score is the sum of weighted log-likelihoods:
Score(r1, r2) = Σ_{i=1}^{k} w_i * log( P(Comparison_i | Match) / P(Comparison_i | Non-Match) )
where w_i are weights (e.g., 2.0 for high-importance fields like Amount). If Score > θ (a threshold), the pair is declared a match.
The Bayesian Interpretation: Score is proportional to the posterior probability P(Match | Comparisons). The threshold θ is chosen to minimize the expected cost of false positives vs. false negatives.
1.2 The Four Types of Comparison Outcomes
| Outcome | Definition | Example |
|---|---|---|
| True Positive (TP) | System correctly identifies a duplicate pair. | Both banks’ transactions are merged. |
| True Negative (TN) | System correctly identifies a non-match. | Distinct transactions are kept separate. |
| False Positive (FP) | System incorrectly merges two different transactions. | Leads to double-counting the amount. |
| False Negative (FN) | System fails to merge a duplicate pair. | Leads to overcounting expenses. |
The Cost Matrix:
-
Cost(FP): A false positive means the TPP under-reports the PSU’s spending (since it merges two different transactions). The PSU may be undercharged. This is extremely serious.
-
Cost(FN): A false negative means the TPP over-reports the PSU’s spending (since it counts a duplicate). This is less serious but can cause user confusion.
The optimal threshold θ is chosen to ensure that P(FP) is as close to zero as possible. We set θ such that Precision = TP / (TP + FP) ≥ 99.9%.
PART 2: DETERMINISTIC MATCHING — Exact Identifiers
2.1 The Global Identifier (EndToEndId)
ISO 20022 mandates that each payment transaction has an EndToEndId field (pain.001) and that this ID may be echoed in the camt.053 account report. If the TPP initiates a payment with an EndToEndId = "PAY-2026-08-03-001", and the PSU’s statement from Bank B shows a transaction with AcctSvcrRef containing the same EndToEndId, we have a perfect match.
Algorithm:
if r1.EndToEndId == r2.EndToEndId:
return Match (100% confidence)
Precision: 100%. There is zero chance of a false positive because the identifier is unique.
Recall: Low. Only ~10-15% of transactions carry an EndToEndId that is preserved across both banks.
2.2 The Bank Reference (AcctSvcrRef)
The bank’s internal reference number (AcctSvcrRef in ISO 20022) is often printed on statements. If Bank A and Bank B share the same clearing network (e.g., SEPA), the AcctSvcrRef may be the same.
Algorithm:
if r1.AcctSvcrRef == r2.AcctSvcrRef:
return Match (95% confidence)
Precision: 95%. Occasional false positives if the same reference number is reused across different clearing cycles.
PART 3: PROBABILISTIC MATCHING — Fuzzy Text and Temporal Alignment
3.1 The Levenshtein Distance for Descriptions
For transactions that lack a global identifier, we rely on fuzzy matching of the Description (or TransactionInformation) field. Let s1 and s2 be two transaction descriptions. The Levenshtein distance d(s1, s2) is the minimum number of single-character edits (insertions, deletions, or substitutions) required to transform s1 into s2.
Example:
-
s1 = "AMAZON UK",s2 = "AMAZON.CO.UK" -
d = 4(insert.,.,o,.).
We normalize the distance by the maximum length: Sim_Levenshtein = 1 - d / max(|s1|, |s2|).
A threshold of Sim_Levenshtein ≥ 0.85 indicates a likely match.
3.2 The Jaro-Winkler Distance for Merchant Names
The Levenshtein distance is good for longer strings, but for short strings (e.g., “ASDA” vs. “ASDA LTD”), the Jaro-Winkler distance is more accurate. It gives higher weight to prefix matches.
Jaro Similarity: Sim_j = (1/3) * (m/|s1| + m/|s2| + (m-t)/m), where m is the number of matching characters, and t is the number of transpositions.
Winkler Adjustment: If the first l characters match (typically l=4), the similarity is boosted by 0.1 * l * (1 - Sim_j).
Threshold: Sim_Jaro-Winkler ≥ 0.92 yields a match.
3.3 The Temporal Alignment (Amount + Date)
If two transactions have exactly the same amount and the booking date is within ±2 days (to account for weekends), they are highly likely to match.
if abs(Amount1 - Amount2) < 0.01 and |Date1 - Date2| ≤ 2 days:
candidate_score += 0.5
3.4 The Combined Score
The final similarity score is a weighted sum:
Total_Score = 0.4 * Sim_Levenshtein + 0.3 * Sim_JaroWinkler + 0.2 * Amount_Matches + 0.1 * Date_Matches
If Total_Score ≥ 0.75, we declare a match.
PART 4: BLOCKING STRATEGY — Reducing O(N²) to O(N log N)
A naive pairwise comparison of all transactions across two banks is O(|T_A| * |T_B|). For a large bank with 1 million transactions per day, this is 10^12 comparisons—impossible.
We use Blocking to reduce the comparison space. The block key is the integer part of the amount (e.g., floor(Amount)) and the booking date (YYYY-MM-DD). Transactions with different amounts cannot be duplicates (a £100 transaction cannot match a £50 transaction).
Algorithm:
-
For each transaction in
T_A, compute the keyK = (Date, floor(Amount)). -
Group transactions by
K. -
For each transaction in
T_B, compute its keyK'. -
If
K == K', compare the transactions using the Probabilistic Matcher (Part 3). -
If the key does not match, skip the comparison entirely.
Complexity:
If the number of transactions per block is small (approximately 10-20), the total number of comparisons is Σ_{blocks} n_block_A * n_block_B. If the blocks are evenly distributed, this is approximately N * (N / M) where M is the number of blocks. This effectively reduces the complexity to O(N * b) where b is the average block size (≈ 10). Hence, we achieve a 10,000x speedup.
Latency Measurement:
For a streaming pipeline (Kafka Streams), we process transactions in windows of 1 second. With 10,000 transactions per second, the number of blocks is approximately 5,000 (for 10 different integer amounts and 500 days). The average block size is 2. The deduplication latency is:
-
Block Assignment: 0.1ms.
-
Pairwise Comparison: 2 transactions × 0.2ms (fuzzy string matching) = 0.4ms.
-
Total: 0.5ms per transaction.
The end-to-end pipeline (Kafka → Processor → Redis) adds ~35ms (p95).
PART 5: ACCURACY EVALUATION — Precision vs. Recall
We evaluate the deduplication algorithm on a labelled test dataset (10,000 transactions, of which 2,500 are duplicate pairs).
| Metric | Value |
|---|---|
| True Positives (TP) | 2,450 |
| False Positives (FP) | 3 |
| True Negatives (TN) | 7,497 |
| False Negatives (FN) | 50 |
Precision = TP / (TP + FP) = 2450 / 2453 = 99.87% Recall = TP / (TP + FN) = 2450 / 2500 = 98.0% F1-Score = 2 * (P * R) / (P + R) = 2 * (0.9987 * 0.98) / (0.9987 + 0.98) = 98.92%
The algorithm achieves a 99.9% Precision (only 3 false positives out of 2,453 matches) and a 98.0% Recall (50 duplicates missed). This is highly acceptable for personal finance management.
CLOSING — THE AGGREGATION PIPELINE
The aggregation pipeline is now complete. The TPP fetches data from multiple banks, normalizes the schemas (Lesson 5.2), and runs the deduplication pipeline (Lesson 5.3). The result is a unified, deduplicated view of the PSU’s finances.
Operational Risk: If the deduplication algorithm has a low Precision (high false positives), the PSU will see their account balance as artificially low (because transactions are merged incorrectly). This erodes trust. Therefore, we deliberately set the threshold θ high to prioritise Precision over Recall.
Transition to Lesson 5.4: With the transactions consolidated, we must revisit the Account-Request endpoint. Lesson 5.4 addresses the duplicate consent problem: when a TPP retries a request due to a timeout, the ASPSP must deduplicate the POST /account-requests itself. We will implement an idempotency key for account requests, store the consent status (AWAITING → AUTHORISED → REVOKED), and use Redis atomic locks to prevent duplicate consent creation.