Skip to content

Instantly share code, notes, and snippets.

@ony
Last active November 5, 2024 21:22
Show Gist options
  • Save ony/bbec599c0893e676b772559909b81de6 to your computer and use it in GitHub Desktop.
Save ony/bbec599c0893e676b772559909b81de6 to your computer and use it in GitHub Desktop.
Budgeting with hledger

The main answers I need from budgeting:

  • How much extra money I can spend without compromising ability to pay my bills?
  • How big my earnings should be to cover my expenses?
  • How can I optimize my expenses?

Tracking your expenses

One of my favorite reports hledger supports is

% hledger -f examples/sample.journal bal -Q
Balance changes in 2008:

                      ||  2008q1  2008q2  2008q3  2008q4 
======================++=================================
 assets:bank:checking ||      $1       0       0     $-1 
 assets:bank:saving   ||       0      $1       0       0 
 assets:cash          ||       0     $-2       0       0 
 expenses:food        ||       0      $1       0       0 
 expenses:supplies    ||       0      $1       0       0 
 income:gifts         ||       0     $-1       0       0 
 income:salary        ||     $-1       0       0       0 
 liabilities:debts    ||       0       0       0      $1 
----------------------++---------------------------------
                      ||       0       0       0       0 

To limit output only to expense just add query:

% hledger -f examples/sample.journal bal -Q '^expenses'
Balance changes in 2008q2:

                   ||  2008q2 
===================++=========
 expenses:food     ||      $1 
 expenses:supplies ||      $1 
-------------------++---------
                   ||      $2 

Now you can see your total expenses and distributed over your accounts.

No need to read further if you this report satisfy your needs.

Bucketing

Sometimes we don't need such detailed distribution of expenses. Thanks to hledger-rewrite addon we can re-write original journal. Let's re-distribute our expenses into a buckets of budget by adding virtual extra postings.

; examples/budget.journal

= ^expenses:clothes
    (budget:clothes)               *-1

= ^expenses:(food|supplies)
    (budget:grocery)               *-1

= ^expenses: not:^expenses:(clothes|food|supplies)
    (budget:misc)                  *-1

= ^equity:unbalanced
    (budget:unknown)               *-1

Now our query looks like this:

% hledger rewrite -f examples/budget.journal -f examples/sample.journal | hledger -f - bal -Q budget   
Balance changes in 2008q2:

                ||  2008q2 
================++=========
 budget:grocery ||     $-2 
----------------++---------
                ||     $-2 

This looks fine but isn't much different from prev section.

Planning/allocating budget

; examples/budget.journal

= ^expenses:clothes
    (budget:clothes)               *-1

= ^expenses:(food|supplies)
    (budget:grocery)               *-1

= ^expenses: not:^expenses:(clothes|food|supplies)
    (budget:misc)                  *-1

= ^equity:unbalanced
    (budget:unknown)               *-1

2008/4/1 Budget for 2008q2
    (budget:clothes)               $10
    (budget:grocery)                $5
    (budget:misc)                   $2

This makes our output look like:

Balance changes in 2008q2:

                ||  2008q2 
================++=========
 budget:clothes ||     $10 
 budget:grocery ||      $3 
 budget:misc    ||      $2 
----------------++---------
                ||     $15 

You can interpret this report like: you still can spend $3 for grocery and $10 for clothes.

Offset assets according to budget

This helps to identify how much money I left unbudgeted or free for extra expenses.

; examples/budget.journal

= ^expenses:clothes
    [budget:clothes]               *-1
    [assets:budget]                *1  ; compensate budgeted expense from assets

= ^expenses:(food|supplies)
    [budget:grocery]               *-1
    [assets:budget]                *1

= ^expenses: not:^expenses:(clothes|food|supplies)
    [budget:misc]                  *-1
    [assets:budget]                *1

= ^equity:unbalanced
    [budget:unknown]               *-1
    [assets:budget]                *1

2008/4/1 Budget for 2008q2
    [assets:budget]  ; subtract budget from assets
    [budget:clothes]               $10
    [budget:grocery]                $5
    [budget:misc]                   $2

Now balance for assets looks like this:

% hledger rewrite -f examples/budget.journal -f examples/sample.journal | hledger -f - bal assets   
                $-16  assets
                  $1    bank:saving
                $-15    budget
                 $-2    cash
--------------------
                $-16

This means that our current assets are off by $16 from our budget and current expenses.

Reset budget

Sometimes we start budgeting later than. I'd expect that something like this will work:

2016/12/31 Budget reset
    assets:budget  = $0
    assets:budget  = 0 EUR
    budget:clothes  = $0
    budget:clothes  = 0 EUR
    budget:grocery  = $0
    budget:misc  = $0

Be careful to list all accounts and currencies. Otherwise your transaction may fail to balance. You can use output of hledger-budget bal --flat -e 2016/12/31 -H budget to identify what you need to mention there.

Finally

I've added a simple bash script see hledger-budget.bash (drop .bash suffix). With year-based includes inside of ~/.hledger-budget.journal. Be careful about account aliases - they tend to miss budget file.

Since budget will be enabled only during hledger-budget I've changed from virtual postings to real ones.

#!/bin/bash
journal_file=${LEDGER_FILE:-$HOME/.hledger.journal}
budget_file=${LEDGER_BUDGET_FILE:-$HOME/.hledger-budget.journal}
if [[ -r "$budget_file" ]]; then
hledger-rewrite -f "$budget_file" -f "$journal_file" | exec hledger -f - "$@"
else
exec hledger "$@"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment