No-Bullshit Beancount Introduction

Inspired by a Hacker News thread on GnuCash, a piece of software I used extensively for my personal finances, I had a look at beancount. It’s a plaintext accounting software with a CLI and a web interface. However, the beancount docs are maintained as Google Docs and are relatively long. I thought I’d share my experience setting up a beancount file and the basic usage so that it’s easy to dive in. It’s advisable to have a certain degree of familiarity with double-entry bookkeeping. Some of the following examples are plainly stolen from the beancount docs.

Overview

Beancount does plaintext accounting. That means that all is handled in a plaintext file, e.g. leo.beancount.

Accounts

There are five types of accounts:

  • Assets
  • Liabilities
  • Equity
  • Income
  • Expenses

Accounts are structured into parent and child accounts by separating them with colons (:), but every account needs to be the child of one of the five types of root accounts above, e.g. Expenses:Home:Rent or Income:Finance:Interests. Each component starts with an uppercase letter or number and continues with an arbitrary amount of upper/lowercase letters, numbers and dashes.

Commodities

Currencies like EUR are commodities, but you may also declare your own commodities such as stock symbols. Commodities may only include uppercase letters and .-_'.

Strings

Strings are written with double quotes. Splitting a string over multiple lines includes the newline in the string.

Comments

The most important part are comments. In beancount, a comment is declared by a semicolon ;.

Syntax Directives

Here I will give a short overview over the different directives one can use in beancount. This list only includes the simple stuff to get started fast.

Upon parsing a file, directives will be sorted by date internally. This allows you to organize your file any way you like.

Open

Open a new account. Accounts must be opened before being referenced.

1
2018-09-27 open Assets:EUR:Cash

You may provide a currency constraint. All transactions must post to this accountn in the specified currencies.

1
2018-09-27 open Assets:EUR:BankAccount EUR,USD

Close

Close an account. No transactions may post here after this date.

1
2017-02-28 close Assets:EUR:BankAccount

Commodity

Declare a commodity or currency. This is optional but allows you to attach metadata. The metadata attached is completely optional and you may attach any metadata you like. This enables you to filter or aggregate based on metadata values.

1
2
3
4
5
1867-01-01 commodity CAD
  name: "Canadian Dollar" ; arbitrary metadata attachable
  asset-class: "cash"

2002-01-01 commodity EUR ; no metadata attached

Transactions

The core of accounting. A basic transaction looks like this:

1
2
3
2019-08-05 txn "Coffee"
    Assets:EUR:Cash     -1.20 EUR
    Expenses:Coffee     1.20 EUR

Each line below a transaction is called a posting. The sum of all postings must be 0. You may leave out one amount which will automatically get calculated:

1
2
3
2019-08-12 txn "Supermarket"
    Assets:EUR:Cash     -17.85 EUR
    Expenses:Groceries

Instead of the keyword txn, you may use a flag to indicate a transaction. There are to flags available:

1
2
3
4
5
6
7
8
9
; This is a complete transaction
2019-08-13 * "Train to work"
    Assets:EUR:Cash                 -2.50 EUR
    Expenses:Transportation:Train

; This is an incomplete transaction
2019-08-13 ! "Bus to Susan's House"
    Assets:EUR:Bank                 -2.00 EUR
    Expenses:Transportation:Bus

It’s also possible to flag a single posting in a transaction:

1
2
3
2019-07-28 * "Concert Ticket Gorillaz"
    ! Assets:EUR:Bank                 -47.00 EUR
    Expenses:Entertainment

Costs and Prices

Prices are the prices of conversion.

1
2
3
4
2019-08-04 * "ATM Withdrawal Cambodia"
    Expenses:MoneyExchange  5.00 USD @ 0.89 EUR ; Conversion rate
    Assets:USD:Cash         500.00 USD @@ 446.50 EUR ; Total cost
    Assets:EUR:Bank         ; You may leave this empty, sums up to: 5*0.89+446.5=450.95

Prices are good for tracking currencies. We want to forget about the conversion rate after the transaction - it will just be 500 USD going forward. Beancount also allows tracking costs which will result in assets being “held at cost”. You can later identify these specific units by cost when they are removed from the account.

1
2
3
4
5
6
7
8
2018-01-01 * "Buy ETF iShares Global Clean Energy"
    Assets:ETF:A0MW0M   553 A0MW0M {4.4892 EUR}
    Assets:EUR:Bank     -2482.53 EUR

2019-08-06 * "Sell ETF iShares Global Clean Energy"
    Assets:ETF:A0MW0M       -553 {4.4892 EUR} @ 5.41 EUR
    Assets:EUR:Bank         2991.73 EUR
    Income:EUR:Investing    -509.20 EUR

Tagging

Add a tag to a transaction for filtering

1
2
3
2019-04-28 * "Hotel Amsterdam" #amsterdam2018
    Expenses:Travel:Hotel   83.27 EUR
    Assets:EUR:Bank

Balance Assertion

Make sure the balance is correct. The calculation applies at the beginning of the day. Beancount will spit out an error if the balance is not OK.

1
2018-07-26 balance Assets:EUR:Bank 1000000 EUR ; I'm rich, baby

Price

Define the price of a commodity for a given date

1
2019-08-06 price A0MW0M 5.41 EUR

Plugin

Load a plugin

1
plugin "beancount.plugins.implicit_prices"

Option

Set general options

1
2
option "title" "Leo"
option "operating_currency" "EUR"

Include

Include a different file. Allows you to split your accounting into multiple files, e.g. per year transactions.

1
include "accounts.bean"

Commands

If you want to look at an example file, run:

bean-example | less

To check your file, run:

bean-check main.beancount

Where to continue

This is just an overview and a simple reference for myself. There are loads of commands and stuff you can do with beancount.

For deeper dives, you may continue with