Multicurrency tutorial (2018)

Anya begins using hledger without any currency symbols. She adds some journal entries like this (not bothering with descriptions, either):

2018/11/01
  income:gifts
  assets:bank          1000
    
2018/11/02
  assets:bank
  expenses:food         500

She knows hledger is filling in the missing amounts, which can be seen with print's -x/--explicit flag:

$ hledger print -x
2018/11/01
    income:gifts            -1000
    assets:bank              1000

2018/11/02
    assets:bank              -500
    expenses:food             500

The balance command with no arguments shows all balance changes. The total is zero, as Anya expects - each transaction sums to zero, and all transactions are included in this report, so the report also sums to zero:

$ hledger bal
                 500  assets:bank
                 500  expenses:food
               -1000  income:gifts
--------------------
                   0

Unlike partial balance reports (omitting some accounts), which typically do not have a zero total:

$ hledger bal food
                 500  expenses:food
--------------------
                 500

Anya maintains a popular free software project. She remembers that she added a Liberapay button to the project website yesterday, allowing donations. Her native currency is rubles, but Liberapay pays out US dollars or euros.

She realises she had better start tracking currencies in her journal or things will get confusing. So she adds currency symbols throughout her journal:

2018/11/01
  income:gifts
  assets:bank         ₽1000
    
2018/11/02
  assets:bank
  expenses:food        ₽500

Thinking ahead, she sees that entering euro symbols will be a bit unergonomic on her keyboard. She thinks perhaps she'll use standard alphabetic currency codes instead, and on the right-hand side:

2018/11/01
  income:gifts
  assets:bank          1000 RUB
    
2018/11/02
  assets:bank
  expenses:food         500 RUB

But she finds this a bit verbose. She decides to use single letters - R for rubles:

2018/11/01
  income:gifts
  assets:bank          1000 R
    
2018/11/02
  assets:bank
  expenses:food         500 R

Now her reports show the currency symbol:

$ hledger bal 
               500 R  assets:bank
               500 R  expenses:food
             -1000 R  income:gifts
--------------------
                   0

And she is ready for multicurrency accounting. Just in time, because next day a donation of 10 euros arrives! She records it, using E for euros:

2018/11/01
  income:gifts
  assets:bank          1000 R
    
2018/11/02
  assets:bank
  expenses:food         500 R

2018/11/03
  income:foss
  assets:liberapay       10 E

Now she has a multicurrency journal, and the balance report shows both currencies:

$ hledger bal 
                10 E        
               500 R  assets
               500 R    bank
                10 E    liberapay
               500 R  expenses:food
               -10 E        
             -1000 R  income
               -10 E    foss
             -1000 R    gifts
--------------------
                   0

However, it's a bit confusing. The assets and income parent accounts now have multicurrency balances, and each currency is displayed on its own line. She tries flat mode, and finds it clearer:

$ hledger bal --flat
               500 R  assets:bank
                10 E  assets:liberapay
               500 R  expenses:food
               -10 E  income:foss
             -1000 R  income:gifts
--------------------
                   0

But she has heard that hledger's tabular output is best for multicurrency reports, always showing amounts on one line. She starts using that, adding one of the report interval flags (-Y/--yearly) to activate it:

$ hledger bal -Y
Balance changes in 2018:

                  ||    2018 
==================++=========
 assets:bank      ||   500 R 
 assets:liberapay ||    10 E 
 expenses:food    ||   500 R 
 income:foss      ||   -10 E 
 income:gifts     || -1000 R 
------------------++---------
                  ||       0 

Anya requests a withdrawal of the Liberapay funds to her bank. Her bank holds rubles, so the euros will get converted. She's not sure of the exact exchange rate or fees, but next day, when the transaction clears, she can see that 10 euros left her liberapay account and 750 rubles arrived in her bank account. She decides to just record that:

2018/11/01
  income:gifts
  assets:bank          1000 R
    
2018/11/02
  assets:bank
  expenses:food         500 R

2018/11/03
  income:foss
  assets:liberapay       10 E

2018/11/04
  assets:liberapay      -10 E
  assets:bank           750 R

This is her first multicurrency transaction. She hasn't written the exchange rate explicitly, but the manual says hledger can figure it out. It seems to work:

$ hledger bal  -Y
Balance changes in 2018:

               ||         2018 
===============++==============
 assets:bank   ||       1250 R 
 expenses:food ||        500 R 
 income:foss   ||        -10 E 
 income:gifts  ||      -1000 R 
---------------++--------------
               || -10 E, 750 R 

However, two things surprise her. First, where has the liberapay account gone ? She remembers that balance reports hide zero-balance accounts by default, and adds -E/--empty to show it. (She also notes that zero amounts are displayed without a currency symbol, and would be a little clearer with currency symbols on the left):

$ hledger bal  -YE
Balance changes in 2018:

                  ||         2018 
==================++==============
 assets:bank      ||       1250 R 
 assets:liberapay ||            0 
 expenses:food    ||        500 R 
 income:foss      ||        -10 E 
 income:gifts     ||      -1000 R 
------------------++--------------
                  || -10 E, 750 R 

Second, the balance report is now showing a non-zero total. The individual euro and ruble totals look correct, but why isn't it zero ? Is the journal unbalanced ?

Anya asks for help on the #hledger IRC channel and is advised to add the -B/--cost flag. Sure enough, the total is now zero:

$ hledger bal -YEB
Balance changes in 2018:

                  ||         2018 
==================++==============
 assets:bank      ||       1250 R 
 assets:liberapay || 10 E, -750 R 
 expenses:food    ||        500 R 
 income:foss      ||        -10 E 
 income:gifts     ||      -1000 R 
------------------++--------------
                  ||            0 

But now the liberapay account, which should be empty, is showing a positive euro and negative ruble balance. As if one had not been converted into the other. Why is this ?

With a little help, Anya goes troubleshooting. Inspecting the multicurrency transaction with print -x (and a date filter to exclude the rest) shows how hledger has parsed it:

$ hledger print -x date:20181104
2018/11/04
    assets:liberapay    -10 E @@ 750 R
    assets:bank                  750 R

The manual makes this a bit clearer. Anya wrote the entry in costs style 3 ("let hledger infer the price that balances the transaction"). hledger has converted this to style 2 ("@@ TOTALPRICE after the amount"), recording that the 10 euro were priced at 750 rubles in this transaction.

With -B added, the 10 euro is converted to its cost in rubles:

$ hledger print -x date:20181104 -B
2018/11/04
    assets:liberapay          -750 R
    assets:bank                750 R

The register command shows how the balance reports above calculate the liberapay balance. Without -B: 10 euro are added, 10 euro are removed, the liberapay account's end balance is zero:

$ hledger reg liberapay
2018/11/03                     assets:liberapay               10 E          10 E
2018/11/04                     assets:liberapay              -10 E             0

With -B: 10 euro are added, 750 rubles are removed, the liberapay account's end balance is "10 euro, -750 rubles". (With each currency on its own line, again. Also, it seems that register aligns the account name with the top amount, unlike the balance command):

$ hledger reg liberapay -B
2018/11/03                      assets:liberapay              10 E          10 E
2018/11/04                      assets:liberapay            -750 R          10 E
                                                                          -750 R

In summary, it seems that the balance report must sum either the primary posting amounts (bal), or the cost amounts (bal -B), consistently for both the account balances above the line, and the total below the line. Otherwise the total would be incorrect. Which means that one or the other of these will be displayed as an unconverted multicurrency amount.

Anya decides to find out more about the other currency-related flag: -V.

TBD:

  • declaring a market price corresponding to the price in the fourth transaction ( P 2018/11/01 E 75 R ) and adding -V will show everything completely in rubles (with or without -B, at least in this case), preserving the zero total

  • declaring an accurate market price instead ( P 2018/11/01 E 74.91 R ), there will be a small non zero total, which corresponds to the gain/loss due to exchanging at a slightly different price. After adding an explicit gain/loss transaction, the zero total is restored.

  • The new -X and --value options.