Skip to content

Manual lot tracking

Dmitry Astapov edited this page Oct 11, 2024 · 4 revisions

Associated directory: 15-manual-lots

Manual lot tracking and reporting

Sometimes you can't treat a specific commodity as a single pool of fungible units. For example, if the disposal of the commodity is subject to the capital gains tax, you need to be able to figure out the original acquisition price for all the units of a commodity that you are selling to compute the difference between the selling price and the buying price.

In plain text accounting parlance, this problem is usually called "lot tracking", and there are plenty of texts out there that describe various techniques for lot tracking, usually in the context of managing stock and shares portfolios. There is, for example, a wonderful hledger companion program called hledger-lots that automates the process of keeping track of the cost basis (aka original purchase price), generating sell transactions that keep track of gains or losses and many more.

I do not want to cover the topics extensively covered elsewhere, and instead, I want to focus on two subjects which are, in my opinion, covered very sparsely:

  • How do organize your reporting so that you can see your lot information at a glance?

  • How do you track lots manually when they are weird and don't fit into a "stock-like" model?

To draw a distinction, let's talk about "stock-like" commodities. For them, you usually want to track prices (cost basis and disposal price), and you usually have a fixed method of choosing the lots to dispose of when you are doing the sale. Usually, you either choose the least recently acquired or the most recently acquired lot -- strategies that are abbreviated as FIFO (first in - first out) or LIFO (last in - first out).

Weird commodity example

So how would a "not-stock-like" commodity for which you still want to track lots look like?

Let's take a look at the UK annual pension allowance. In the UK, you have an annual limit to contributions to your tax-incentivised private pension. If you stay under the limit and have an unused allowance, you can use it up in any of the three subsequent tax years, and then it expires. If you go over the limit (plus any unused past allowance), you have to pay a tax charge. As you can see, it is necessary to keep track of your pension allowances for tax purposes.

So pension allowance:

  • Comes in lots that you acquire once a year

  • There is no cost basis and no disposal price when you dispose of these lots

  • When consuming the allowance, you first use up the current year's allowance (which sounds like LIFO), but then you move on to the oldest unusual allowance you still have (which now sounds like FIFO).

  • After three years, unused lots evaporate

Tracking

How can we track this in hledger? Firstly, as pension allowance is not something that you can buy, sell or convert, we are going to put it into a separate account category - I chose to use the virtual top-level account category for this. We will use virtual postings to introduce new pension allowance, and then we will distribute it between consumed and unconsumed parts, putting them into different subaccounts keyed by year:

$ hledger print -f all.journal virtual:pension 'date:2014-04-05'

2014-04-05
    (virtual:pension:allowance:2013/2014)                            £4000  ; how much the allowance was
    virtual:pension:allowance:2013/2014                                    = £0  ; how much of it is left
    virtual:pension:inputs:2013/2014                               £100.00  ; how much pension contributions were
    virtual:pension:allowance:unused:2013/2014 - 2016/2017                  ; remainder went to unused allowance

In this transaction, we introduced an allowance of £4000, and then immediately set the balance to zero to show that it all went elsewhere - £100 was consumed by this year's pension contribution, and the rest went into virtual:pension:allowance:unused:2013/2014 - 2016/2017. The name for the unused allowance account allows one to see at a glance when this allowance could be consumed, and when it would expire.

Reporting

So what can we do now reporting-wise? Firstly, we can produce the balance report of virtual:pension:inputs alongside your actual pension account (in my examples, it is assets:pension:avivia) to see whether they line up:

$ hledger -f all.journal balance "assets:pension:aviva" "virtual:pension:input" -b "2013-04-06" -p "every year" --transpose --depth 3

Balance changes in 2013-04-06..2018-04-05:

                        || assets:pension:aviva  virtual:pension:inputs |         
========================++==============================================+=========
 2013-04-06..2014-04-05 ||                    0                 £100.00 | £100.00 
 2014-04-06..2015-04-05 ||              £102.34                 £100.00 | £202.34 
 2015-04-06..2016-04-05 ||              £102.07                 £100.00 | £202.07 
 2016-04-06..2017-04-05 ||              £103.86                 £100.00 | £203.86 
 2017-04-06..2018-04-05 ||              £102.76                       0 | £102.76 

In the toy examples in this repository, there is no easy way to split pension contributions from pension value appreciation, but you can see that despite that we could see that our recorded values are in line.

We can also use the --historical switch for the balance command to see how the allowances changed over time, and how much of it was used, and remains unused:

$ hledger -f all.journal balance virtual:pension -b '2013-04-06' -p 'every year' --no-elide --tree --cumulative --historical

Ending balances (historical) in 2013-04-06..2018-04-05:

                               || 2014-04-05  2015-04-05  2016-04-05  2017-04-05  2018-04-05 
===============================++============================================================
 virtual                       ||   £4000.00    £8000.00    £8050.00    £4240.00    £4240.00 
   pension                     ||   £4000.00    £8000.00    £8050.00    £4240.00    £4240.00 
     allowance                 ||   £3900.00    £7800.00    £7750.00    £3840.00    £3840.00 
       unused                  ||   £3900.00    £7800.00    £7750.00    £3840.00    £3840.00 
         2013/2014 - 2016/2017 ||   £3900.00    £3900.00    £3850.00           0           0 
         2014/2015 - 2017/2018 ||          0    £3900.00    £3900.00    £3840.00    £3840.00 
     inputs                    ||    £100.00     £200.00     £300.00     £400.00     £400.00 
       2013/2014               ||    £100.00     £100.00     £100.00     £100.00     £100.00 
       2014/2015               ||          0     £100.00     £100.00     £100.00     £100.00 
       2015/2016               ||          0           0     £100.00     £100.00     £100.00 
       2016/2017               ||          0           0           0     £100.00     £100.00 
-------------------------------++------------------------------------------------------------
                               ||   £4000.00    £8000.00    £8050.00    £4240.00    £4240.00 

The balance of virtual:pension:inputs shows how much was contributed over time (as a running total).

The balance of virtual:pension:allowance:unused shows the amount of unused allowance, and one can take a look at subaccounts to see which allowance should be consumed first, and how much of it is left, or has expired. For instance, we can see that £3850 of 2013/2014 - 2016/2017 allowance was unused when it expired in 2017.

If we want to see just the running total of unused allowance, and how it changed over time, we can use the register command with --depth, which would cause subaccounts to be aggregated to the parent:

$ hledger -f all.journal register virtual:pension:allowance:unused -b '2013-04-06' --depth 4

2014-04-05                                                                     virtual:pension:allowance:unused                                         £3900.00      £3900.00
2015-04-05                                                                     virtual:pension:allowance:unused                                         £3900.00      £7800.00
2016-04-05                                                                     virtual:pension:allowance:unused                                          £-50.00      £7750.00
                                                                               virtual:pension:allowance:unused                                                0      £7750.00
2017-04-01 Expired                                                             (virtual:pension:allowance:unused)                                      £-3850.00      £3900.00
2017-04-05                                                                     virtual:pension:allowance:unused                                          £-60.00      £3840.00
                                                                               virtual:pension:allowance:unused                                                0      £3840.00

These commands could be put in a script that is run every time export.sh is run, and code in 15-manual-lots does exactly that.

Traditional commodity example.

To contrast the weirdness of the pension allowance, I've added a simple example of "stock options" to the journals in this repository, where a certain amount of "stock option units" is granted every year (free of charge), and then they vest two years later. Vested stock options could then be valued, provided that there are price directives in the journal.

Our setup could be much easier:

$ hledger print -f all.journal virtual:stock -b 2014 -e 2017

2014-12-30 Stock options
    virtual:stock options:granted
    virtual:stock options:vesting:2016         5 UNITS

2015-12-30 Stock Options
    virtual:stock options:granted
    virtual:stock options:vesting:2017        10 UNITS

2016-12-30 Stock Options
    virtual:stock options:granted
    virtual:stock options:vesting:2018        20 UNITS

2016-12-30 Stock Options
    virtual:stock options:vested
    virtual:stock options:vesting:2016                 = 0 UNITS

Much like before, we could use balance --cumulative to see the history of vesting:

$ hledger balance 'stock options:vest' -f all.journal -Y -b2014 --cumulative

Ending balances (cumulative) in 2014-01-01..2017-12-31:

                                    || 2014-12-31  2015-12-31  2016-12-31  2017-12-31 
====================================++================================================
 virtual:stock options:vested       ||          0           0     5 UNITS    15 UNITS 
 virtual:stock options:vesting:2016 ||    5 UNITS     5 UNITS           0           0 
 virtual:stock options:vesting:2017 ||          0    10 UNITS    10 UNITS           0 
 virtual:stock options:vesting:2018 ||          0           0    20 UNITS    20 UNITS 
 virtual:stock options:vesting:2019 ||          0           0           0    25 UNITS 
------------------------------------++------------------------------------------------
                                    ||    5 UNITS    15 UNITS    35 UNITS    60 UNITS 

We can use balance --valuechange to track the valuation of all vested options over time:

$ hledger balance 'stock options:vested' -f all.journal -Y -b2014 --cumulative --transpose --value=end,$ --valuechange --no-total

Cumulative period-end value changes in 2014-01-01..2017-12-31:

            || virtual:stock options:vested 
============++==============================
 2014-12-31 ||                            0 
 2015-12-31 ||                            0 
 2016-12-31 ||                     $4255.60 
 2017-12-31 ||                    $13529.55 

You can find the resulting setup in 15-manual-lots or diffs/14-to-15.diff.

Next steps

Fetching prices