Table of contents

Open Table of contents

Introduction

Every transaction I make - every bus ride, every coffee, every gift - goes into one plain text file. No banking app, no aggregator, no spreadsheet. Just a text file and Ledger, a command-line double-entry accounting tool.

If presentations can be code, accounting definitely can.

Note:All names, banks, tickers and amounts in this post are made up. The structure is exactly how I run mine; the numbers are not.

Purpose

  • Understand plain text accounting- double-entry in one minute, and why a text file beats an app.
  • Structure a journal- the account tree, multi-currency handling (USD, EUR, gold, silver, stocks, crypto) and the file conventions I settled on.
  • Query it- the aliases and commands I actually run, including a few that answer suspiciously specific questions.

Prerequisites

  • A terminal.
  • ledgerinstalled:- apt install ledger, or- nix shell nixpkgs#ledger- I am a NixOS person, after all.
  • The discipline to spend two minutes a day logging. This is the hard part; the tool is the easy part.

Why a Text File and Not an App

  • Want your full financial history on someone else’s server, categorized by their ML model?
  • Banking app: yes, by default.
  • Ledger: it is a file on your disk.- grepis the ML model.

  • Want version control for your money?

  • Ledger:- git add finances.ledger && git commit -m "July"

  • Want gold grams, EUR, stocks and crypto in the same report as your main currency?

  • Excel: prepare the VLOOKUP of your nightmares.
  • Ledger: built in. One flag converts everything.

  • Want to know how much one specific doner place has cost you since 2025?

  • Ledger: one command, milliseconds.

And it is free, works offline, and the file will still open in 30 years.

Double-Entry in One Minute

Every transaction moves value between at least two accounts, and the postings must sum to zero. All accounts live under five roots. The comment header at the top of my file is the whole theory:

; Assets - Bank Accounts, Wallet, Investments, Loans credited ; Income - Paychecks, Gifts received, Dividends, Interest ; Expense - Groceries, Taxes, Gifts given, Donations ; Liabilities - Mortgage, Credit cards, Student loans, Loans owed ; Equity - Untrackable errors or Opening Balances
One sign convention to internalize: Income is negative. Money flows from Income into Assets, so a salary posting is -3000 USD on the Income side. It feels wrong for a day and then it clicks.

The Journal

A routine day in the file looks like this:

2026-07-10 Assets:Cash:Bank:WeeWeeBank:Checking Expenses:Groceries:PurPurMarket 7.12 USD Expenses:Transport:Transit:Bus 0.60 USD Expenses:Food:Restaurants:Doner 9.80 USD
The conventions I converged on:

  • One transaction per day per funding account.The account without an amount is- elided- Ledger balances it automatically (here:- -17.52 USDleaves Checking). No payee line for routine days; I name transactions only when they are worth remembering (“Buy gold”, “Pay Day”).
  • A subaccount per venue and per person.- Expenses:Food:Cafes:<CafeName>,- Expenses:Gifts:Monetary:<Person>,- Expenses:Transport:Taxi. The tree grows organically - a new place is just a new line - and “how much did I ever spend there” becomes free.
  • Day one is anagainst- Opening Balancestransaction- Equity:OpeningBalances, with whatever numbers you remember. Precision comes later; starting matters more.

A payday - income negative in the wild, with the day’s expenses and cashback netted into the same transaction:

2026-07-02 Pay Day Assets:Cash:Bank:WeeWeeBank:Checking Income:Salary:Employer -3000.00 USD Expenses:Groceries:PurPurMarket 17.61 USD Income:Cashback:Bank:WeeWeeBank -5.00 USD
Debts are assets. Lending moves money from the bank into Assets:Receivable:<Person>, repayment moves it back, and if it is never coming back - reclassify and accept reality:

2026-03-01 Assets:Receivable:Friend 100 USD Assets:Cash:Bank:WeeWeeBank:Checking 2026-04-01 Assets:Cash:Bank:WeeWeeBank:Checking 100 USD Assets:Receivable:Friend 2026-06-30 Clearing Receivables Assets:Receivable:Friend -50 USD Expenses:Gifts:Monetary:Friend 50 USD
The receivable balance is always the honest answer to “who owes me what”.

More Than One Currency

In Ledger everything is a commodity: USD is one, EUR is one, a gram of gold is one, a share of ACME is one. Buying an asset records the total cost with @@:

2026-01-05 Buy gold and silver Assets:Cash:Bank:WeeWeeBank:Checking Assets:Investments:PreciousMetals:Gold 5 GOLD @@ 545 USD Assets:Investments:PreciousMetals:Silver 20 SILVER @@ 26 USD Expenses:Fees:MetalPremium 21 USD
Note the premium split into its own expense - the bank’s margin over spot is a fee, not an investment. Stocks and crypto work the same way:

2026-01-06 Assets:Cash:Bank:WeeWeeBank:Checking Assets:Investments:Stocks:Broker 2 ACME @@ 505 USD Assets:Crypto:Exchange 0.5 COIN @@ 60 USD
Even exchanging cash is just a commodity purchase:

2026-01-10 Assets:Cash:Pocket:Checking 100 EUR @@ 108 USD Assets:Cash:Bank:WeeWeeBank:Checking
Market prices are P directives that I update by hand every month or two:

P 2026-07-01 EUR 1.08 USD P 2026-07-01 GOLD 109 USD P 2026-07-01 SILVER 1.30 USD P 2026-07-01 ACME 260 USD
Plain ledger bal Assets then reports each commodity separately; add -X USD and the whole portfolio collapses into one USD number at the latest known prices.

Where Did the Cash Go

Pocket cash never reconciles. Instead of pretending it does, I book the difference honestly:

Expenses:FX:Loss 2.05 USD ; idk whereThose entries accumulate into the real, measured cost of carrying physical cash. Income:FX:Gain exists too, for the rarer opposite case of finding money on your own shelf.

Daily Workflow

Aliases, because I run these constantly:

alias lg='ledger' alias lgb='ledger b' alias lgba='ledger b Assets' alias lgbe='ledger b Expenses' alias lgbi='ledger b Income'
At the end of the day I append one transaction from the bank app history and one from memory for cash. To lower the friction I pre-create empty date lines for the week ahead:

2026-07-25 2026-07-26 2026-07-27
They parse fine, and an empty date staring at you is a surprisingly effective nag to fill it.

The Queries I Actually Run

```

net worth, per commodity

lgba -f finances.ledger

net worth, one number

lgba -f finances.ledger -X USD

liquid money only - boolean filters on account names

lgba -f finances.ledger -X USD and not gold and not crypto and not saving

this month's burn and income

lgbe -f finances.ledger --begin "2026-07-01"
lgbi -f finances.ledger --begin "2026-07-01"

monthly expense totals across the whole history

lg -f finances.ledger reg ^Expenses -X USD -M --collapse

lifetime damage in one supermarket chain

lgb -f finances.ledger PurPurMarket
`` The query language is just account regexes plusand/or/not` plus date windows. That is enough to answer almost anything.

What the File Knows About You

After a while, the fun part is asking questions you never planned to ask:

```

most frequent expense accounts - where does life actually happen

lg -f finances.ledger reg ^Expenses -F "%(account)\n" | sort | uniq -c | sort -rn | head

how many distinct restaurants have I tried

lg -f finances.ledger accounts Expenses:Food:Restaurants | wc -l

savings rate in one line

lg -f finances.ledger bal ^Income ^Expenses -X USD --depth 1

average cost of a taxi ride vs a bus ride - you already know, but now in numbers

lg -f finances.ledger bal Expenses:Transport:Taxi -X USD
lg -f finances.ledger reg Expenses:Transport:Taxi | wc -l
```
I will keep my own numbers to myself, but I promise some of them surprised me.

Tips

1. LLM

Once an LLM has the Ledger manual and your file, it writes any query you can describe in words, finds patterns you did not think to look for, and catches inconsistencies. This is the same trick as with Presenterm: plain text formats and LLMs are a natural pair.

2. Guard the Account Names

A typo silently creates a new account - Expenses:Grocery next to Expenses:Groceries will split your data without a single error. Run ledger accounts once in a while and read the list; renames are one sed away.

3. Start Stupid, Split Later

Do not design the perfect taxonomy upfront. Log everything to Expenses:Food and split into subaccounts the day you have a question that needs the split. The file is text; restructuring it is editing, not migration.

4. Edit in Neovim

The vim-ledger plugin adds syntax highlighting, folding, account completion and amount alignment for .ledger files, which makes a growing journal far easier to read and keep consistent.

Conclusion

One text file under git, two minutes a day. In return, any question about my money is one command away - in a format no vendor can take away or discontinue.

References

  • Ledger CLI and the full manual
  • plaintextaccounting.org - the wider ecosystem
  • Alternatives in the same family: hledger, beancount

Appendix: A Sample Journal

Everything from this post in one small, valid file. Save it as finances.ledger and start querying:

; Assets - Bank Accounts, Wallet, Investments, Loans credited ; Income - Paychecks, Gifts received, Dividends, Interest ; Expense - Groceries, Taxes, Gifts given, Donations ; Liabilities - Mortgage, Credit cards, Student loans, Loans owed ; Equity - Untrackable errors or Opening Balances P 2026-01-01 EUR 1.08 USD P 2026-01-01 GOLD 105 USD P 2026-01-01 SILVER 1.20 USD P 2026-01-01 ACME 250 USD 2026-01-01 Opening Balances Assets:Cash:Bank:WeeWeeBank:Checking 1500 USD Assets:Cash:Bank:WeeWeeBank:Saving 2000 USD Assets:Cash:Pocket:Checking 120 USD Assets:Cash:Pocket:Checking 50 EUR Liabilities:StudentLoans:University -4500 USD Equity:OpeningBalances 2026-01-02 Pay Day Assets:Cash:Bank:WeeWeeBank:Checking Income:Salary:Employer -3000.00 USD Expenses:Groceries:PurPurMarket 17.61 USD Income:Cashback:Bank:WeeWeeBank -5.00 USD 2026-01-03 Assets:Cash:Bank:WeeWeeBank:Checking Expenses:Food:Cafes:Coffee 3.20 USD Expenses:Transport:Transit:Bus 0.60 USD Expenses:Transport:Transit:Bus 0.60 USD Expenses:Subscriptions:Music 11.99 USD 2026-01-04 Assets:Cash:Pocket:Checking Expenses:Food:Restaurants:Doner 9.80 USD Expenses:FX:Loss 2.05 USD ; idk where 2026-01-05 Buy gold and silver Assets:Cash:Bank:WeeWeeBank:Checking Assets:Investments:PreciousMetals:Gold 5 GOLD @@ 545 USD Assets:Investments:PreciousMetals:Silver 20 SILVER @@ 26 USD Expenses:Fees:MetalPremium 21 USD 2026-01-06 Assets:Cash:Bank:WeeWeeBank:Checking Assets:Investments:Stocks:Broker 2 ACME @@ 505 USD Assets:Crypto:Exchange 0.5 COIN @@ 60 USD 2026-01-07 Assets:Receivable:Friend 100 USD Assets:Cash:Bank:WeeWeeBank:Checking 2026-01-10 Assets:Cash:Pocket:Checking 100 EUR @@ 108 USD Assets:Cash:Bank:WeeWeeBank:Checking 2026-01-15 Assets:Cash:Bank:WeeWeeBank:Checking 100 USD Assets:Receivable:Friend 2026-01-16 2026-01-17 2026-01-18