Personal Finance¶
TL;DR
A personal finance system built from scratch: bank CSV imports, deterministic transaction categorization, envelope budget tracking, and a monthly reporting dashboard โ all running locally. The data never leaves my machine. Stack: Python, PostgreSQL 16, scikit-learn, Apache ECharts, MkDocs Material.
I tracked my finances for years on paper, then in a spreadsheet. After looking at a bunch of personal finance apps that cost money and didn't fit how I actually think about budgeting, I decided to build my own.
It pulls in transactions from my bank and credit card accounts and conforms them into my budget universe, physicalized in a database. Flexible structure, intramonth tracking, and the ability to refactor anything backed by my own data.
How It Works¶
graph TB
A["Bank CSVs"] --> B["Ingest & Stage"]
B --> C["Categorize"]
C --> D["Clean Data"]
D --> E["Envelope Tracking"]
D --> F["Report Generation"]
E --> G["Dashboard"]
F --> G
Under the Hood¶
The pipeline starts with CSV exports from my bank accounts. A Python ingest layer stages them into PostgreSQL, then a categorization pass matches each transaction to a budget category โ mostly deterministic rules built up over time, with a TF-IDF-assisted CLI for edge cases that don't pattern-match cleanly. The result is clean, categorized data in my own schema, which I can query and refactor on my own terms.
From there, the system tracks 13 spending envelopes and savings pools, and generates a monthly budget vs. actual report with a 6-month trend view. Everything ships as static JSON that the dashboard reads on load โ same architecture as the SF6 tool, different problem domain.
The next step is pulling in retirement account data to close the loop between spending, saving, and long-term planning.
Stack: PostgreSQL 16 ยท Python ยท psycopg2 ยท scikit-learn ยท Apache ECharts ยท MkDocs Material
Dashboard access
The live dashboard contains real financial data and is password protected. The writeup above is open. To request access, reach out via the Contact page.