LY

Finance Tracking with Beancount

finance

Aug 15, 2026

Finance Tracking with Beancount

I've been tracking personal finances in beancount for a while now, but the tooling around it has evolved significantly. The ledger file lives in a private git repo, and I've built a set of scripts and automation around it that make the whole system practical for daily use.

1. The ledger

Everything lives in ~/finance/accounts.beancount — a plain-text double-entry ledger. Operating currency is AED, with CAD positions tracked separately for a Canadian brokerage. The file follows beancount's syntax: account declarations at the top, then chronological transactions. Each transaction has a date, payee, narration, and at least two postings that sum to zero.

2. Account structure

The chart of accounts covers: checking accounts (AED and CAD), credit cards, brokerage accounts (AED equities, CAD positions in three funds), a family member's managed portfolio, and expense categories broken down by type — groceries, dining, transport, subscriptions, services, and so on. Income tracks salary and investment returns separately.

3. Vim integration

Since I edit the ledger in Vim, I set up key mappings and abbreviations to speed up entry. ,b opens the ledger, ,B runs bean-check for syntax validation, ,r runs the report script. Abbreviations expand common accounts: expg becomes Expenses:Food:Groceries, ac becomes Assets:Bank:Checking, lcc becomes Liabilities:Credit:Card. These are defined in .vimrc and make manual entry fast enough that I don't need a separate input tool.

4. Reporting scripts

Two main scripts handle reporting. ~/bin/networth is the original Python script that parses the ledger and outputs a detailed breakdown — assets by account, liabilities, net position, and month-over-month change. ~/bin/networth-report is a newer script that generates markdown reports for automated delivery. It combines net worth and budget data into a single document.

The budget section compares actual spending against caps defined in the script. Categories with caps: groceries (3,000), dining (3,000), services (2,000), subscriptions, gas, personal care. When spending exceeds a cap, the report flags it. The total budget target is around 19,000 AED/month.

5. Automated monthly reports

Three cron jobs generate reports: on the 1st (previous month final), the 15th (mid-month check), and the last day (month-end snapshot). Each runs the report script and sends the output to Telegram. This means I get a financial summary delivered three times a month without doing anything.

6. Statement reconciliation

When bank statements come in, I diff them against the ledger to find discrepancies. This has caught missing transactions — a gas station charge here, an interest charge there. The process is manual but fast: export the statement, compare line by line, add anything missing. Beancount's bean-check validates the result.

7. Multi-currency handling

The AED ledger is straightforward, but the CAD positions add complexity. Beancount handles multiple currencies natively — each posting specifies its currency, and the price directive sets exchange rates for net worth calculations. The CAD brokerage holds positions in three funds, each tracked as a separate commodity. Monthly investment targets are defined separately and checked against actual contributions.

8. Git versioning

The finance directory is its own git repo, pushed to a private remote. Every edit gets committed — the gitpush function in .bashrc handles journal, finance, and dotfiles in one command. This gives full history of every transaction ever added, modified, or corrected. If I fat-finger an amount, git diff shows exactly what changed.

9. What works well

Plain text means the ledger is greppable, diffable, and version-controlled. No vendor lock-in, no subscription, no cloud dependency. The Vim shortcuts make entry as fast as any GUI. Automated reports mean I actually look at the numbers regularly instead of ignoring them. The git history provides a complete audit trail.

10. What's not included

I don't use fava (the web UI for beancount) — the terminal workflow is sufficient for now. I don't have automated bank import — transactions are entered manually, which is actually a feature since it forces me to review each one. I don't track individual stock lots for tax purposes since the jurisdictions I operate in don't have capital gains tax on securities.

← all posts