Manage your accounts on the command line

dollar and command line

As the years go by, the chil­dren grow up and the time comes for high­er ed­u­ca­tion (and as­so­ci­at­ed cost­s). This is an op­por­tu­ni­ty to ­take back con­trol of my per­son­al fi­nances a lit­tle more se­ri­ous­ly.

I tried gnu­cash that I have al­ready used a few years ago, but I met some dif­fi­cul­ties:

  • I had a lot of sta­bil­i­ty prob­lems (seg­fault­),
  • syn­chro­niza­tion with OFX files re­trieved from the bank re­quired ­far too many man­u­al ac­tion­s.

Af­ter some re­search on the we­b, I de­cid­ed to try ledger-­cli and I think I have now adopt­ed it. My ­con­fig­u­ra­tion now al­lows:

  • re­cov­ery of bank­ing trans­ac­tions via we­boob,
  • au­to­mat­ic syn­chro­niza­tion with my ac­count book via legder-au­tosync,
  • anal­y­sis of syn­chro­niza­tion-re­lat­ed ad­di­tions us­ing ver­sion man­age­ment like mer­cu­ri­al or git,
  • edit­ing var­i­ous text re­ports us­ing the com­mand line ledger,
  • easy ad­di­tion of trans­ac­tions to vim us­ing the vim-ledger’ plug­in,
  • mul­ti­-de­vice syn­chro­niza­tion (an­droid, lin­ux) via sync­thing or the ver­sion man­age­ment soft­ware,
  • the mod­i­fi­ca­tion of ledger files and edi­tion of re­ports on the phone thanks ­to the use of ledger tool in the ter­mux ter­mi­nal.

It’s not easy for Mrs. Jones to set up, but what a plea­sure to work on the ­com­mand line with tools that work so well.

Books of accounts of the tailor Louis-Hippolyte Leroy. "General Ledger - No. 5"

Books of ac­counts of the tai­lor Louis-Hip­poly­te Leroy. “Gen­er­al Ledger - No. 5” (Gal­li­ca)

Reference documentation

The web­site http­s://­plain­tex­tac­count­ing.org/ is a mine of in­for­ma­tion, as well as of course the on­line doc­u­men­ta­tion of ledger.

This “ledger CLI cheat sheet” is al­so very use­ful.

Double-entry accounting

Dou­ble-en­try ac­count­ing is the base prin­ci­ple of the ac­count­ing sys­tem used by ledger. It sounds com­pli­cat­ed at first, but it just means that each trans­ac­tion shall con­cerned at least two ac­counts, one which is deb­it­ed and one which is cred­it­ed, and that the sum of the cred­its and deb­its of this trans­ac­tion is nul­l.

For ex­am­ple, we can have the fol­low­ing trans­ac­tion­s:

2019/03/08 Doctor
   assets:bank:current           -30 €
   expenses:health                30 €

2019/03/12 Amazon
   assets:bank:current          -118.84 €
   expenses:house:maintenance    114.99 €
   expenses:house:supply           3.85 €

This means that on March 8, I deb­it­ed €30 from my cur­rent ac­count to ­pay the doc­tor which is record­ed as a health ex­pense. And on March 12, I pur­chased main­te­nance equip­ment and sup­plies on Ama­zon for an amount of €118.84.

What you see here above is ex­act­ly the con­tent of the ledger file. A sim­ple ascii text file. Easy to type and to man­age. A sim­ple hg diff (or git diff) al­lows you to see the cur­rent changes.

The ledger tool can then give the bal­ance of ac­counts:

ledger -f test.ledger bal
           -148.84 € assets:bank:current
            148.84 € expenses
            118.84 €   house
            114.99 €     maintenance
              3.85 €     supply
             30.00 €   health
--------------------
                   0

Note that in a trans­ac­tion you can leave one of line with­out amount in which ­case ledger com­putes the miss­ing val­ue in or­der to en­sure a bal­anced ­trans­ac­tion.

File organization

I have sev­er­al ac­counts in dif­fer­ent banks and for the ease of syn­chro­ni­sa­tionņ I have cho­sen to sep­a­rate the trans­ac­tions in sev­er­al ­files, one per bank ac­coun­t. For ex­am­ple, I have one main file per­so.ledger which in­cludes all the oth­er files:

include bank1-current.ledger
include bank2-current.ledger
include bank2-livret.ledger

Data import

OXF downloading

I wrote the fol­low­ing lit­tle script that re­cov­ers ac­counts in files OXF :

#!/bin/bash

backends="-b bppc,boursorama"
accountsdir="/$HOME/banques/OXF"
format="${1:-oxf}"

function accountsList()
{
      boobank --auto-update $backends list --formatter simple --no-header\
              --no-keys --select id --condition 'type=1 OR type=2'
}

echo "Get accounts list..."
accountsList | while read account
do
      filename="$accountsdir/$account"-$(date +%Y%m%d)".$format"
      printf "Dump $account to $filename..."
      boobank history "$account" -f "$format" -n 999 > "$filename" &&\
        printf "        ok!\n" || printf "      failed!\n"
done

The boobank back­ends are con­fig­ured in the file ~/.­­con­­fig/we­­boob/back­­ends. For ex­am­ple:

[bppc]
module = banquepopulaire
password = `runcached pass show weboob/bppc`
website = www.ibps.mediterranee.banquepopulaire.fr
login = XXXXXXXXXX

[boursorama]
module = boursorama
enable_twofactors = True
device = weboob
pin_code =
login = XXXXXXXXXXXX
password = `runcached pass show weboob/boursorama`

My pass­words are man­aged by the pass ap­pli­ca­tion and en­crypt­ed via a yu­bikey. run­cached is a script that al­lows me to cache the pass­words a few min­utes so that I don’t have to re-en­ter it for all re­quests car­ried out by boobank.

Synchronization with ledger

I then use ledger-au­­tosync to syn­chro­nize OXF files with the ­cor­re­spond­ing ledger file. It’s done with a scrip­t, which ba­si­cal­ly does ­for each ac­coun­t:

ledger-autosync -a "actif:banque:bppc" --assertions --fid "40618" \
     -l "bppc-courant.ledger" "$OXFDIR/CPTYYYYYY@bppc.oxf" \
     | sed -e 's/Expenses:Misc/expenses:Unknown/' -e 's/\<EUR\>/€/' \
       >>  bppc-courant.ledger

The prob­lem you then have to deal with, is to as­sign the right ex­pense or in­come ac­count to trans­ac­tions in place of the de­fault ex­pens­es:Un­known ac­coun­t.

For­tu­nate­ly, ledger al­lows you to as­so­ciate an ac­count with sev­er­al ­pay­ees, and re­place the Un­known ac­count with the one as­so­ci­at­ed with the ­trans­ac­tion pay­ee.

Thus, the fol­low­ing ledger file:

account expenses:health
   paid Doctor


2019/03/08 Doctor
   assets:bank:current -30 €
   Expenses:Unknown

is in­ter­pret­ed as:

ledger -f test.ledger bal
              -30 € assets:bank:current
               30 € expenses:health
-------------------
                  0

For each file ac­count­name.ledger, I have a file ac­count­name.ac­count which is in­clud­ed and de­fines the pay­ees as­so­ci­at­ed with each ledger ac­coun­t.

Daily use

Account hierarchy

You are free to de­fine the ac­count hi­er­ar­chy your in­ter­est­ed in­. As far as I’m con­cerned, I have some­thing like:

assets:bank1:current
assets:bank1:livret
assets:bank2:current
assets:initial balance
expenses:administrative
expenses:food
expenses:food:canteen
expenses:food:restaurant
expenses:bank:fees
expenses:gift
expenses:communication
expenses:miscellaneous
expenses:donation
expenses:housing:insurance
expenses:housing:DIY
expenses:housing:construction
expenses:housing:water
expenses:housing:maintenance
expenses:housing:supply
expenses:housing:garden
expenses:housing:furniture
expenses:housing:household appliances
expenses:housing:energy
expenses:tax
expenses:tax:property tax
expenses:tax:housing
expenses:tax:income
expenses:cash
expenses:leisure
expenses:leisure:art
expenses:leisure:computer
expenses:leisure:book
expenses:leisure:software
expenses:leisure:music
expenses:leisure:photo
expenses:leisure:press
expenses:leisure:exhibition
expenses:leisure:sport
expenses:leisure:travel
expenses:mobility
expenses:mobility:airplane
expenses:mobility:rail
expenses:mobility:car
expenses:mobility:car:fine
expenses:mobility:car:insurance
expenses:mobility:car:fuel
expenses:mobility:car:maintenance
expenses:mobility:car:toll
expenses:mobility:car:parking
expenses:mobility:car:electricity
expenses:mobility:bicycle
expenses:reimbursement
expenses:health
expenses:service:web
expenses:clothing
liabilities:loan
income:insurance:family
income:insurance:health
income:bank
income:bank:interest
income:reimbursement
income:salary

Example of requests

  • State of my fi­nances:

    ledger bal assets liabilities
    
  • Bal­ance sheet of gains or loss­es over 2018:

    ledger bal income expenses -p 2018
    
  • Av­er­age month­ly dis­tri­bu­tion of ex­pens­es for 2018:

    ledger bal -E -p 2018 expenses --display-total "display_total/12"
    
  • To­tal month­ly ex­pens­es:

    ledger --group-by 'format_date(date, "%Y/%m")' bal expenses --depth 1
    

    which gives for ex­am­ple:

    2018/11
             2078.02 € expenses
    
    2018/12
             1775.98 € expenses
    
    2019/01
             2973.27 € expenses
    
    2019/02
             2988.48 € expenses
    
  • Bal­ance per month over 2019:

    ledger --group-by : format_date(date, "%Y/%m")' bal -p 2019
    

Automatic transactions

Au­to­mat­ic transat­cions is a fea­ture I use to au­to­mat­i­cal­ly ad­just some ­trans­ac­tion­s. For ex­am­ple, I don’t want to en­ter the de­tails of my cash ­ex­pens­es, but I es­ti­mate that 70% of my cash with­drawals are used to buy ­food.

Thus I added the fol­low­ing au­to­mat­ic trans­ac­tion:

= expenses:cash
      expenses:food     0.7 ; 70% for food (estimated)
      expenses:cash    -0.7 ;

In the same way, I es­ti­mate that for each elec­tric­i­ty bil­l, I have about €45 ded­i­cat­ed to the elec­tric car. So I added the fol­low­ing au­to­mat­ic trans­ac­tion:

= /expenses:housing:energy/ and (expr payee =~ /Enercoop/)
      expenses:mobility:car:electricity   45 €; estimated zoé monthly cost
      expenses:housing:energy            -45€

Thus, for each trans­ac­tion as­so­ciate with the ac­coun­t Ex­pense:­hous­ing:en­er­gy and whose pay­ee is En­er­coop, I de­crease the mo­bil­i­ty:­car:­elec­tric­i­ty ac­count by €45 and I cred­it back­ the ex­pense ac­count hous­ing:en­er­gy with €45.

Account to account transfers

I have a month­ly mon­ey trans­fer from the bank that re­ceives my salary ­to the bank who hosts my ev­ery­day ac­coun­t.

Those trans­ac­tions are record­ed in the file bank1.ledger as ­fol­lows:

2019/03/01 * PERMANENT TRSFR. Salary
   assets:bank1:current          -1000.00 €
   expenses:transfer to bank2     1000.00 €

and in the file bank2.ledger as fol­lows:

2019/03/04 * Salary
   assets:bank2:current           1000.00 €
   income:transfer from bank1    -1000.00 €

The prob­lem is that the bal­ance dis­plays both trans­fer ac­counts, which pol­lutes all the re­port­s:

ledger -f test.ledger bal
                 0 assets
        -1000.00 € bank1:current
         1000.00 € bank2:current
         1000.00 € expenses:transfer to bank2
        -1000.00 € income:transfer from bank1
------------------
                 0

The sim­ple and el­e­gant so­lu­tion is to in­form ledger that ex­pens­es:­trans­fer to bank2 and in­come:­trans­fer from bank1 are in­deed the same ac­coun­t. This is done through the alias di­rec­tive:

alias expenses:transfer to bank2=income:transfer from bank1

This gives the ex­pect­ed re­sult:

ledger -f test.ledger bal
                 0 assets
        -1000.00 € bank1:current
         1000.00 € bank2:current
------------------
                 0

Budget

It is al­so pos­si­ble to de­fine a bud­get. For ex­am­ple, I have at the top the per­so.ledger file:

~  Monthly
   expenses:food          444 €
   expenses:communication 56  €
   expenses:miscellaneous 29  €
   expenses:housing:DIY   35  €

~ Yearly
   expenses:housing:insurance      380 €
   expenses:mobility:car:insurance 330 €

It then al­lows me to keep an eye on un­bud­get­ed ex­pens­es:

ledger bal --unbudgeted expenses -p "this month"
            27.35 € expenses
             0.06 €  bank:fees
            23.90 €  gift
             3.39 €  service:web
-------------------
            27.35 €

Or to re­view the bud­get for the year (the re­port gives a progress per­cent­age of the ac­counts with re­spect to the bud­get):

ledger budget -p 2019 expenses income

The doc­u­men­ta­tion al­so talks about the pos­si­bil­i­ty of mak­ing fore­casts (with­ the --­­fore­­cast op­tion), but it does­n’t work and many bugs re­port­s re­lat­ed to this fea­ture are open on the project github.

Conclusion

Ledger-­cli is a re­al­ly pow­er­ful tool. Once con­fig­ured, I ap­pre­ci­ate the op­por­tu­ni­ty to work with ev­ery­day tools (text ed­i­tor, VC­S ­tool, com­mand line, au­to­mat­ic com­ple­tion,…).

Many fea­tures have not been cov­ered in this ar­ti­cle. I will prob­a­bly do an ad­di­tion­al ar­ti­cle to track the evo­lu­tion of my way of work­ing with ledger-­­cli.

Leave a comment
The name you want to show others