Over the past six months, I’ve tracked my money with My previous attempts to pick up real accounting tools
floundered. Hosted tools are privacy nightmares, and my stint with GnuCash didn’t last. But after stumbling on Dmitry Astapov’s “Full-fledged
It should be easy to work towards eventual consistency. […] I should
be able to [add financial records] bit by little bit, leaving things
half-done, and picking them up later with little (mental) effort. – Dmitry Astapov, Full-Fledged Hledger I’ve cobbled together a system based on these principles: Transactions move money between accounts: This transaction shows that on Jan 1, 2024, money moved from
The sum of each transaction should be $0. Money comes from somewhere,
and the same amount goes somewhere else—double-entry accounting. This is
powerful technology—it makes mistakes impossible to ignore. Journal files are text files containing one or more
transactions: Rules files transform CSVs into journal files via
regex matching. Here’s a CSV from my bank: And here’s a With these two files ( Reports are interesting ways to view transactions
between accounts. There are registers, balance sheets, and income statements: At the beginning of September, I spent But a good chunk is going to the default expense account,
Then, I can add some more rules to my
Now, I can reprocess my data to get a better picture of my
spending: For the Amazon.com purchase, I lumped it into the
This is the power of working bit-by-bit—the data guides you to the
next, deeper rabbit hole. Why am I doing this? For years, I maintained a monthly spreadsheet of
account balances. I had a balance sheet. But I still had questions. Before diving into accounting software, these were my goals: Non-goals—these are the parts I never cared
about: I spend about an hour a month checking in on my money Which frees me
to spend time making fancy charts—an activity I perversely
enjoy. Here’s my setup: Process: The I include all the journal files in the Here’s the If I find anything amiss (e.g., if my balances are different than
what the bank tells me), I look at Simple, plain text accounting made simple. And if I ever want to dig deeper, while reading a blog post from Jonathan Dowland↩︎ Note, this is covered by full-fledged
hledger – Investements↩︎ Also covered in full-fledged
hledger – Tax returns↩︎
hledger
—a plain text
double-entry accounting system written in Haskell. It’s been
surprisingly painless.hledger
” wiki1, it
clicked—eventually consistent accounting. Instead of
modeling your money all at once, take it one hacking session at a
time.
Principles of my system
Learn
hledger
in five minuteshledger
concepts are heady, but its use is simple. I
divide the core concepts into two categories:
hledger
cares about:
hledger
moves money
between accounts.
2024-01-01 Payday
income:work $-100.00
assets:checking $100.00
income:work
into assets:checking
—Payday.2024-01-01 Payday
income:work $-100.00
assets:checking $100.00
2024-01-02 QUANSHENG UVK5
assets:checking $-29.34
expenses:fun:radio $29.34
Transaction Date,Description,Category,Type,Amount,Memo
09/01/2024,DEPOSIT Paycheck,Payment,Payment,1000.00,
09/04/2024,PizzaPals Pizza,Food & Drink,Sale,-42.31,
09/03/2024,Amazon.com*XXXXXXXXY,Shopping,Sale,-35.56,
09/03/2024,OBSIDIAN.MD,Shopping,Sale,-10.00,
09/02/2024,Amazon web services,Personal,Sale,-17.89,
checking.rules
to transform that CSV into a
journal file so I can use it with hledger
:# checking.rules
# --------------
# Map CSV fields → hledger fields[0]
fields date,description,category,type,amount,memo,_
# `account1`: the account for the whole CSV.[1]
account1 assets:checking
account2 expenses:unknown
skip 1
date-format %m/%d/%Y
currency $
if %type Payment
account2 income:unknown
if %category Food & Drink
account2 expenses:food:dining
# [0]: <https://hledger.org/hledger.html#field-names>
# [1]: <https://hledger.org/hledger.html#account-field>
checking.rules
and
2024-09_checking.csv
), I can make the CSV into a
journal:$ > 2024-09_checking.journal \
hledger print \
--rules-file checking.rules \
-f 2024-09_checking.csv
$ head 2024-09_checking.journal
2024-09-01 DEPOSIT Paycheck
assets:checking $1000.00
income:unknown $-1000.00
2024-09-02 Amazon web services
assets:checking $-17.89
expenses:unknown $17.89
$ hledger incomestatement \
--depth=2 \
--file=2024-09_bank.journal
Revenues:
$1000.00 income:unknown
-----------------------
$1000.00
Expenses:
$42.31 expenses:food
$63.45 expenses:unknown
-----------------------
$105.76
-----------------------
Net: $894.24
$105.76
and made
$1000
, leaving me with $894.24
.expenses:unknown
. I can use the
hleger aregister
to see what those transactions are:$ hledger areg expenses:unknown \
--file=2024-09_checking.journal \
-O csv | \
csvcut -c description,change | \
csvlook
| description | change |
| ------------------------ | ------ |
| OBSIDIAN.MD | 10.00 |
| Amazon web services | 17.89 |
| Amazon.com*XXXXXXXXY | 35.56 |
l
checking.rules
:if OBSIDIAN.MD
account2 expenses:personal:subscriptions
if Amazon web services
account2 expenses:personal:web:hosting
if Amazon.com
account2 expenses:personal:shopping:amazon
$ > 2024-09_bank.journal \
hledger print \
--rules-file bank.rules \
-f 2024-09_bank.csv
$ hledger bal expenses \
--depth=3 \
--percent \
-f 2024-09_checking2.journal
30.0 % expenses:food:dining
33.6 % expenses:personal:shopping
9.5 % expenses:personal:subscriptions
16.9 % expenses:personal:web
--------------------
100.0 %
expenses:personal:shopping
account. But I could dig
deeper—download my
order history from Amazon and categorize that spending.Goals and non-goals
hledger
can track all these things. My setup is flexible
enough to support them someday. But that’s unimportant to me right
now.Monthly maintenance
$ tree ~/Documents/ledger
.
├── export
│ ├── 2024-balance-sheet.txt
│ └── 2024-income-statement.txt
├── import
│ ├── in
│ │ ├── amazon
│ │ │ └── order-history.csv
│ │ ├── credit
│ │ │ ├── 2024-01-01_2024-02-01.csv
│ │ │ ├── ...
│ │ │ └── 2024-10-01_2024-11-01.csv
│ │ └── debit
│ │ ├── 2024-01-01_2024-02-01.csv
│ │ ├── ...
│ │ └── 2024-10-01_2024-11-01.csv
│ └── journal
│ ├── amazon
│ │ └── order-history.journal
│ ├── credit
│ │ ├── 2024-01-01_2024-02-01.journal
│ │ ├── ...
│ │ └── 2024-10-01_2024-11-01.journal
│ └── debit
│ ├── 2024-01-01_2024-02-01.journal
│ ├── ...
│ └── 2024-10-01_2024-11-01.journal
├── rules
│ ├── amazon
│ │ └── journal.rules
│ ├── credit
│ │ └── journal.rules
│ ├── debit
│ │ └── journal.rules
│ └── common.rules
├── 2024.journal
├── Makefile
└── README
import/in/<account>/<dates>.csv
make
git diff
; if it looks
good, git add . && git commit -m "💸"
otherwise
review hledger areg
to see details.Makefile
generates everything under
import/journal
:
export
folder2024.journal
with
the line: include ./import/journal/*/*.journal
Makefile
:SHELL := /bin/bash
RAW_CSV = $(wildcard import/in/**/*.csv)
JOURNALS = $(foreach file,$(RAW_CSV),$(subst /in/,/journal/,$(patsubst %.csv,%.journal,$(file))))
.PHONY: all
all: $(JOURNALS)
hledger is -f 2024.journal > export/2024-income-statement.txt
hledger bs -f 2024.journal > export/2024-balance-sheet.txt
.PHONY clean
clean:
rm -rf import/journal/**/*.journal
import/journal/%.journal: import/in/%.csv
@echo "Processing csv $< to $@"
@echo "---"
@mkdir -p $(shell dirname $@)
@hledger print --rules-file rules/$(shell basename $$(dirname $<))/journal.rules -f "$<" > "$@"
hleger areg
. I may tweak
my rules or my CSVs and then I run
make clean && make
and try again.hledger
’s docs have more to
teach. But for now, the balance of effort vs. reward is perfect.
Posted