ledger comes with a built in budget system, but I wanted to get an “envelope”, or “YNAB” style budget working. While this was easy to do in theory, in practice it proved more difficult. The theory is pretty simple; enevelope budgeting merely requires you to create some new accounts to keep track of each “envelope” of money. But in practice, it requires a huge amount of duplicate data-entry, because even when using ledger’s automatic transactions, because each month’s budget is mostly the same but not necessarily exactly the same.The following document describes how I managed to get something working, with nice reporting, using org-mode.
In theory, the automatic transaction feature of ledger can easily be
used to manage an envelope budget. The basic idea is that you will
place all your money in different Budgeted:
accounts. For instance,
if you earn $1000/mo and want to place half of that money in your food
budget and the other half in your bar budget, you would use:
2014/01/01 Income Assets:Checking $1000 Income -$1000 Budgeted:Expenses:Food $500 Budgeted:Expenses:Bar $500 Budget -$1000
The Budget
account is to ensure things balance; it should be equal
to your income.
Now, we will use automated transactions to deplete your budget as you
move money from various accounts to your Expenses
accounts. Here is
an automated transaction that will work for all your expenses in 2014
and afterwards:
= expr (date>=[2014] and account =~ /^Expenses:/) Budgeted:$account -1 Budget 1
Now, given expense transactions that look like:
2014/01/02 Foo Assets:Checking -$100 Expenses:Food $100
this will add, for every posting that starts with Expenses:
, a
posting to Budgeted:Expenses
and to Budget
. This will result in
the equivalent of having entered:
2014/01/02 Foo Assets:Checking -$100 Expenses:Foo $100 Budgeted:Expenses:Foo -$100 Budget $100
The automated transaction saves us a great deal of repetetive work.
Now, this solution will work. But the trickiest part, I found, was in adjusting your budget as needed. For instance, I found that I needed a base budget for things like food and gas, but that other items might suddenly stop. For instance, you might cancel your cable, or take up a new hobby. You can copy and paste your base budget, but then when you go back to retroactively change your food budget (as you will probably need to, to adjust to the fact that you are spending more or less than anticipated), you need to go back and change all those budget entries.
This is where org-mode, and particularly babel’s noweb features come in. But first, let’s look at how we can use org-mode to generate some useful reports. Here is how I generate last month’s expenses:
**** Last month's expenses #+begin_src ledger :cmdline bal -p "last month" ^Expenses: :results output :exports results !include /path/to/ledger.lgr #+end_src
(You may need to ensure that ob-ledger
is loaded into your org-mode;
check the org-mode manual for details.) Now, if you type C-c C-c
on
this entry, it will generate a report of your expenses last month.
By using babel, you can generate a lot of useful reports and keep them up-to-date, exporing them to HTML or PDF for printing, etc.
But some of the really useful features come when you keep your ledger
in ledger files, and use org-mode’s noweb to weave a budget together.
noweb allows, among other things, you to include source blocks in
other blocks. First, we’ll set up our base budget, which will be
included in every budget. Say we know that every month we will spend
$500 on food. We create ledger source block with a name,
base-budget
:
#+name: base-budget #+begin_src ledger ; :BUDGET: Budgeted:Expenses:Food $500 Budgeted:Savings #+end_src
This means that we want $500 to go to food, and the balance to savings. The first line is a tag to let identify this as a budget; this helps with some reports. Now, we can set up our budget block:
#+name: budget #+begin_src ledger :noweb yes = expr (date>=[2014] and account =~ /^Expenses:/) Budgeted:$account -1 Budget 1
:
2014/01/01 * Budget <<base-budget>> Budget -$1000 #+end_src
What we have done here is set up our automated transaction, as above,
and set up our first month’s budget. We have assumed that our first
month’s income is $1000, and we have included our base budget. When
this is woven by noweb, <<base-budget>>
will be replaced with the
base-budget
source block, and org-mode will pass the following on to
ledger:
2014/01/01 * Budget Budgeted:Expenses:Food $500 Budgeted:Savings Budget -$1000
This will set up $500 in our food budget, and the balance ($500) for savings.
Now, to generate a report, we can use the following:
#+begin_src ledger :cmdline bal -p "this year" ^Budgeted:Expenses -E :results output :exports results :noweb yes <<budget>> !include /path/to/ledger.lgr #+end_src
Running this report (C-c C-c
) will tell you your budget balance for
each expense. If you budgeted $500 for food and spent $600, your
balance will be -$100. If you spend $300, your balance will be $200.
For each month, you will create a new budget entry in your budget
source block, with monetary value of the:
Budget -$1000
line equaling your income that month.
Now, say that in June we take up cycling. We want to budget $100 per
month for this, but we want to start in June, not January. We will add
the following entry to our budget
source block:
2014/01/01 * Budget <<base-budget>> Budgeted:Expenses:Cycling $100 Budget -$1000
This means that going forward our budget will be $500 for food, $100 for cycling, and $400 for savings.
Now, you will need to keep adding the Cycling budget line every time
from now on, so you might want, at some point, to get complicated. You
could define a new source block with the name base-budget-new
,
include your old base-budget
, using <<base-budget>=
, and then
include that in your budget entries going forward, to avoid duplicate
typing. noweb should allow you to structure your budget entries
however you like.
Any updates on your workflow over the past few years? Thanks for sharing this!