‹ Curious Bicycle

Average cost capital gains with hledger

Sep 23, 2023

I use hledger, a plain text accounting program to keep track of my stock investments and the capital gains I need to declare for UK tax.

Disclaimer: I’m not a tax professional and while I do use similar calculations for my personal tax reporting, I do not claim that any of this is correct or in agreement with HMRC rules.

Consider an example stock, with ticker FOO. I buy 3 units of stock FOO on the 1st of January for $100 each:

2023/01/01 open accounts
    Assets:Cash                                 1000 USD
    Equity                                     -1000 USD

2023/01/01 buy foo
    Assets:FOO                                     3 FOO
    Assets:Cash                                 -300 USD

Of course, with double-entry bookkeeping, I have to balance the accounts to zero, so I introduce another account that’s called a trading account. This is a double-currency account which will always have the number of FOO units that I currently own and the amount of money I have paid (and not sold back):

2023/01/01 buy foo
    Assets:FOO                                     3 FOO
    Income:Trading:FOO                            -3 FOO
    Income:Trading:FOO                           300 USD
    Assets:Cash                                 -300 USD

now the accounts balance (notice the zero at the end):

$ hledger balance
             USD 700  Assets:Cash
               3 FOO  Assets:FOO
           USD -1000  Equity
              -3 FOO
             USD 300  Income:Trading:FOO
--------------------
                   0

Now let’s show the actual value of each account, by adding a pricing entry:

P 2023/01/01 FOO USD 100
$ hledger bal --value=then --empty
             USD 700  Assets:Cash
             USD 300  Assets:FOO
           USD -1000  Equity
                   0  Income:Trading:FOO
--------------------
                   0

The Income:Trading:FOO’s value is zero because the USD and FOO value cancel each other out.

This should always be the case when considering the average cost of the commodity. The value of the Income:Trading account should always be zero. Unfortunately, hledger reports don’t seem to support average cost. So that’s the last time I’ll use --value=then in this article. In practice, I use the Income:Trading account to keep track of the trades and always look at the actual value of each commodity in that account (i.e. -3 FOO and $300) rather than converting to a single commodity.

Average cost

More importantly, the trading account helps with calculating the average cost, which is a principle of UK capital gains accounting. The rule states that, when buying shares in the same company for different prices, the value that you use to calculate your capital gains or losses is based on the average purchase value of the stock.

Here’s how much FOO and $ there are in the Income:Trading account:

$ hledger bal
             USD 700  Assets:Cash
               3 FOO  Assets:FOO
           USD -1000  Equity
              -3 FOO
             USD 300  Income:Trading:FOO
--------------------
                   0

I’ve bought 3 FOO for $300, so my average cost of FOO is $100. This account will reliably keep track of the average cost. For example, say I were to buy more FOO a month later, when the price has increased:

P 2023/02/01 FOO USD 120
2023/02/01 buy foo
    Assets:FOO                                     1 FOO
    Income:Trading:FOO                            -1 FOO
    Income:Trading:FOO                           120 USD
    Assets:Cash                                 -120 USD
$ hledger bal
             USD 580  Assets:Cash
               4 FOO  Assets:FOO
           USD -1000  Equity
              -4 FOO
             USD 420  Income:Trading:FOO
--------------------
                   0

The Income:Trading account correctly shows that I’ve historically bought 4 FOO for $420, which means that my average cost was $105 per FOO.

Selling

Say that in March, after the price has gone up again, I want to sell one unit of FOO:

P 2023/03/01 FOO USD 150
2023/03/01 sell foo
    Assets:FOO                                    -1 FOO
    Income:Trading:FOO                             1 FOO
    Income:Trading:FOO                          -150 USD
    Assets:Cash                                  150 USD
$ hledger bal
             USD 730  Assets:Cash
               3 FOO  Assets:FOO
           USD -1000  Equity
              -3 FOO
             USD 270  Income:Trading:FOO
--------------------

There are two problems with this. The average cost basis, which I calculate by dividing the USD in the Income:Trading account to the number of FOO in that account has changed: $270/3 = $90 (down from $105 above). But it should’ve stayed the same because I have not bought more FOO.

So there’s an inconsistency here. I should always make sure that I take money from that account at the same rate as my average cost:

2023/03/01 sell foo
    Assets:FOO                                    -1 FOO
    Income:Trading:FOO                             1 FOO
    Income:Trading:FOO                          -105 USD
    Assets:Cash                                  150 USD

But now hledger complains because the transactions don’t balance anymore:

$ hledger bal
hledger: Error: ... 
32 | 2023-03-01 sell foo
   |     Assets:FOO                  -1 FOO
   |     Income:Trading:FOO           1 FOO
   |     Income:Trading:FOO        USD -105
   |     Assets:Cash                USD 150

This multi-commodity transaction is unbalanced.
The real postings' sum should be 0 but is: USD 45
Consider adjusting this entry's amounts, adding missing postings,
or recording conversion price(s) with @, @@ or equity postings.

That $45 has to come from somewhere. And it corresponds exactly to my capital gains:

P 2023/03/01 FOO USD 150
2023/03/01 sell foo
    Assets:FOO                                    -1 FOO
    Income:Trading:FOO                             1 FOO
    Income:Trading:FOO                          -105 USD
    Income:Gains                                 -45 USD
    Assets:Cash                                  150 USD

$ hledger bal
             USD 730  Assets:Cash
               3 FOO  Assets:FOO
           USD -1000  Equity
             USD -45  Income:Gains
              -3 FOO
             USD 315  Income:Trading:FOO
--------------------
                   0

Now the accounts balance again, the Income:Trading account still shows my average cost ($315/3 = $105) and I know how much my capital gains are!

References

For reference, this is the full ledger file:

2023/01/01 open accounts
    Assets:Cash                                 1000 USD
    Equity                                     -1000 USD

P 2023/01/01 FOO USD 100
2023/01/01 buy foo
    Assets:FOO                                     3 FOO
    Income:Trading:FOO                            -3 FOO
    Income:Trading:FOO                           300 USD
    Assets:Cash                                 -300 USD

P 2023/02/01 FOO USD 120
2023/02/01 buy foo
    Assets:FOO                                     1 FOO
    Income:Trading:FOO                            -1 FOO
    Income:Trading:FOO                           120 USD
    Assets:Cash                                 -120 USD

P 2023/03/01 FOO USD 150
2023/03/01 sell foo
    Assets:FOO                                    -1 FOO
    Income:Trading:FOO                             1 FOO
    Income:Trading:FOO                          -105 USD
    Income:Gains                                 -45 USD
    Assets:Cash                                  150 USD

The method of keeping track of average costs using a Income:Trading accounts is inspired by Peter Selinger’s tutorial on multiple currency accounting.