Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CashLens

A cash-flow engine for checking accounts. It categorizes transactions, detects recurring income and bills, projects each account's daily balance 14 days forward, and warns several days before the balance is projected to go low or overdraft. A simulation then checks how many overdrafts a suggested transfer from a linked reserve account would have prevented.

It is the decision logic behind the low-balance early-warning features some banks ship with checking accounts, rebuilt on synthetic data. Every account comes from a seeded generator in cashlens/gen/; there is no real bank data or bank API in the repo.

Pipeline

categorize transactions -> project daily balance -> warn before overdraft -> suggest a reserve transfer

Categorization is TF-IDF over merchant strings plus amount and day-of-month into a logistic regression, the only learned component. Recurring detection groups history by normalized merchant key and keeps streams that repeat on a regular interval with near-constant amounts; variable spend falls through to a flat discretionary estimate. Projection is rule-based on top of that.

The warning flags when the projected minimum over the next 14 days drops under a $50 buffer, while the avoidance simulation scores against actual $0 crossings. Flagging at the buffer rather than at zero leaves room to act.

The code follows the same order: cashlens/gen, categorize, forecast, warn, and api (FastAPI over a SQLite ledger).

Results

Run on 2026-08-05 with seed 42: 60 accounts, 11,712 transactions, 180 days. The personas skew thin-buffer on purpose (41 of 60 accounts dip below zero at some point). Full tables in RESULTS.md, raw JSON in results/.

Categorization on 20 held-out accounts (split by account, so no test account's transactions are seen in training): 93.92% accuracy, 0.9537 macro-F1. Stable-name recurring classes all score F1 1.00; error sits in other (0.778) and groceries (0.878) because big-box retailers and cafes deliberately appear under two ground-truth labels.

Recurrence detection: precision 0.980, recall 1.000. Balance projection over 89,040 held-out case-days: MAE $348.78, median absolute error $188.36.

Warning, day-level over 6,360 account/as-of decisions: precision 0.7659, recall 0.8431. Event-level, all 32 overdraft episodes in the evaluation window were flagged in advance (median lead 14.0 days, mean 12.22).

Avoidance simulation, baseline of no warnings versus warn-and-transfer:

baseline overdrafts avoided missed too late reserve too small
32 8 (25.0%) 0 0 24

The 25% needs the breakdown next to it: the warning caught every overdraft at least a day early, and what limited avoidance was that in 24 of 32 episodes the reserve could not cover the full shortfall.

GET /alerts, which re-projects all 60 accounts per request, measures p50 25.05 ms and p95 29.03 ms over real HTTP.

Setup

python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

python -m scripts.run_all            # full pipeline -> results/*.json
python -m scripts.measure_latency    # boots uvicorn, times /alerts and /categorize
pytest -q --cov=cashlens             # 24 tests, 96% coverage
uvicorn cashlens.api.app:app --reload

Everything is seeded (SEED = 42 in cashlens/config.py); re-running the pipeline regenerates identical JSON.

Testing

tests/test_split_no_leakage.py proves the train and test account sets are disjoint. tests/test_warning_catches_dips.py builds an account whose rent lands before its paycheck and checks the flag fires ahead of the dip (and not on a healthy account). The generator, recurring detector, and every endpoint have their own tests.

Caveats

The numbers reflect the simulator, not production transactions, which are messier (typos, MCC codes, refunds, pending versus posted). Only 32 overdraft episodes land in the evaluation window, so 32/32 is a small-sample result. Median lead equals the horizon because rent-driven dips get flagged the moment they enter the 14-day window. Grace periods, incoming-deposit logic, payment execution, and any UI are not modeled.

About

Personal cash-flow engine: transaction categorization (93.9% acc, 0.95 macro-F1), daily balance projection, and low-balance/overdraft early warning (median 14-day lead) with an avoidance sim. Python, FastAPI, scikit-learn.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages