Importing a bank CSV is mostly a parsing problem. Reconciliation is an inference problem.
I learned this while combining several open-source finance projects into a local-first workflow for tax preparation. The first prototype could map columns from different statements, but the resulting ledger was still not reliable enough for an accountant.
The transfer problem
Suppose a user moves $2,000 from checking to savings. One export contains -2000; another contains +2000. A naive categorizer records an expense and income even though no economic event occurred.
Real exports make the match harder:
- the posting dates may differ by several days
- one account may truncate the memo
- foreign-currency transfers may include a conversion spread
- fees may be separate transactions or included in the amount
- an overlapping export may contain the same row twice
The pipeline I ended up using
- Normalize source columns without discarding the original row.
- Build a stable fingerprint for exact and near-duplicate detection.
- Generate transfer candidates only between accounts the user marks as their own.
- Score candidates using inverted amount, date distance, currency, memo similarity, and nearby fee rows.
- Use one-to-one matching so a transaction cannot explain multiple transfers.
- Auto-accept high-confidence pairs and send the rest to a short review queue.
The confidence boundary matters more than the classifier. Quietly guessing wrong creates clean-looking books with bad numbers. Leaving five uncertain rows visible is much better than hiding them inside hundreds of automatic categorizations.
Privacy boundary
Statement processing runs in the browser or the local desktop app. Raw files and SSNs are not uploaded. The output is a reconciliation package with normalized transactions, detected duplicates, proposed transfer pairs, exceptions, and source references.
The app organizes records for an accountant or a later tax workflow; it does not file the return itself.
I put a working sample-data demo here: https://tax.507643.xyz
The next edge cases I am working through are split transfers, credit-card payments that post over a weekend, and currency conversion where neither side exposes the actual exchange rate.
Top comments (0)