hledger_site icon indicating copy to clipboard operation
hledger_site copied to clipboard

Improvements to ACB tracking on "Calculate unrealized gain" page

Open lfos opened this issue 5 months ago • 5 comments

Not sure if this is the right spot for website content feedback, but feel free to point me elsewhere if needed.

I just went over https://hledger.org/gain.html#different-cost-base-calculations and it's great to see examples of different cost basis methods there. That said, I found the use of "lot tracking" subaccounts in the ACB example slightly surprising for two reasons:

  1. Mental model: ACB is all about treating your shares as one big homogeneous pool, but lot tracking forces you to pick arbitrary shares from specific lots when selling (pretending the sale maps to a specific lot when that's not really how ACB works conceptually).
  2. Complexity: Every time you buy more shares, you have to update the cost basis across all your lots. This scales badly.

I would have instead expected the example to look more like this:

2021-01-04 opening balances
    assets:cash                      $100.00
    equity:opening/closing balances

P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4

2021-01-19 buying stock
    assets:stocks:ABC           5 ABC @ $4.40
    assets:cash               $-22.00

P 2021-01-25 ABC $5
P 2021-02-01 ABC $6

2021-02-02 buying more stock
    assets:stocks:ABC           4 ABC @ $6.50
    assets:cash               $-26.00
    ; optional: explicitly capture new cost basis
    assets:stocks:ABC          -5 ABC @ $4.40
    assets:stocks:ABC          -4 ABC @ $6.50
    assets:stocks:ABC           9 ABC @ $5.33
    equity:rounding

P 2021-02-08 ABC $5
P 2021-02-15 ABC $6
P 2021-02-22 ABC $7

2021-02-23 sell some stock
    assets:cash                $12.00
    assets:stocks:ABC          -2 ABC @ $5.33
    income:capital gains

P 2021-03-01 ABC $8

2021-03-02 sell remaining stock
    assets:cash                $54.00
    assets:stocks:ABC          -7 ABC @ $5.33
    income:capital gains

This feels way more natural for ACB; actually working with that pooled share concept. Unless I am missing anything, this also makes cost basis updates both optional and simpler (at most three entriest per transaction).

Am I missing anything? Are there any downsides to this approach?

lfos avatar Aug 08 '25 15:08 lfos

Exactly the right place, thanks for the feedback !

That sounds right to me. I'd welcome even minimal cleanups to this page, which looks slightly out of date. Cc'ing @chvp the original author also.

simonmichael avatar Aug 08 '25 16:08 simonmichael

PS the example looks great - easy to follow. A little tedious to recalculate the average on every purchase, that's probably quite automatable.

You say the re-costing is optional - what happens if you don't do it ? (Some reports will show the original costs, for one.)

simonmichael avatar Aug 08 '25 17:08 simonmichael

PS the example looks great - easy to follow. A little tedious to recalculate the average on every purchase, that's probably quite automatable.

Thanks!

You say the re-costing is optional - what happens if you don't do it ? (Some reports will show the original costs, for one.)

Will it actually change what's in the reports? (Which reports?) If we ignore rounding errors, the total commodity amount and the total cost stay the same before/after re-costing. It was meant mainly as a memory aid to document the current ACB.

Edit: It looks like it does make a difference in reports that break down amounts with different costs (e.g., ledger close --show-cost). The re-costing version results in cleaner output here.

I'd be happy to propose some changes in a MR!


Tangential remark: By the way, I'd love to instead be able to do something like this:

2021-01-19 buying stock
    assets:stocks:ABC           10 ABC @ $10
    assets:cash               $-100

2021-02-02 buying more stock
    assets:stocks:ABC           5 ABC @ $25
    assets:cash               $-125

; we now have 15 shares at an ACB of $15

2021-02-23 sell two shares of ACB at a price of $20
    ; "cost assertion" to confirm cost basis
    assets:stocks:ABC           0 ABC =@ $15
    assets:stocks:ABC          -2 ABC @ $15
    assets:cash                $40
    income:capital gains

That'd help us ensure our cost basis is correct when we sell. More thoughts:

  • There could be a =@@ variant to check total cost instead. In the example above, =@@ $225 would pass.
  • This would also be useful for other cost base methods, e.g., FIFO, to ensure the correct cost base is used and the capital gains are calculated correctly.
  • =@ and =@@ are just placeholders. I don't care as much about the syntax as I do care about the feature. As far as I know, this doesn't exist yet unless one uses the equity method with regular balance assertions instead.

Has something like this been discussed before? Would this be a good feature request to report on the hledger issue tracker?

lfos avatar Aug 08 '25 17:08 lfos

@lfos I'm happy to merge any changes to that page, which is in src/gain.md.

Re the tangential remark, I didn't fully understand it yet, but yes, that's best discussed in the main hledger tracker (and/or in chat). In case it's relevant, I'll link to my notes on lot tracking plans also: https://joyful.com/hledger+lot+tracking

simonmichael avatar Aug 10 '25 12:08 simonmichael

@lfos I'm happy to merge any changes to that page, which is in src/gain.md.

Re the tangential remark, I didn't fully understand it yet, but yes, that's best discussed in the main hledger tracker (and/or in chat). In case it's relevant, I'll link to my notes on lot tracking plans also: https://joyful.com/hledger+lot+tracking

@simonmichael - Thank you for sharing your notes. I've been thinking about this a bit more.

The "one account per lot" idea from your notes may end up being useful for ACB as well -- but instead of multiple lots, there could be a single "pooled" lot per commodity, combined with an additional temporary lot for each purchase.

Example:

2020/01/01
    ; Buy 100 ACB at a price of $100 per share
    Assets:ABC:20200101 @ 100.00USD          100 ABC @ 100.00 USD
    Assets:Cash                       -10,000.00 USD

2020/01/02
    ; Buy 120 ACB at a price of $120 per share
    Assets:ABC:20200102 @ 120.00USD          120 ABC @ 120.00 USD
    Assets:Cash                       -14,400.00 USD
    ; Revalue with updated ACB
    Assets:ABC:20200101 @ 100.00USD         -100 ABC @ 100.00 USD = 0 ABC
    Assets:ABC:20200102 @ 120.00USD         -120 ABC @ 120.00 USD = 0 ABC
    Assets:ABC:20200102 @ 110.909091USD      220 ABC @ 110.909091 USD = 220 ABC

2020/01/03
    ; Sell 50 ACB at a price of $130 per share
    Assets:ABC:20200102 @ 110.909091USD      -50 ABC @ 110.909091 USD
    Assets:Cash                         6,500.00 USD
    Income:Capital Gains                 -954.55 USD

2020/01/04
    ; Buy 100 ACB at a price of $140 per share
    Assets:ABC:20200104 @ 140.00USD          100 ABC @ 140.00 USD
    Assets:Cash                       -14,000.00 USD
    ; Revalue with updated ACB
    Assets:ABC:20200102 @ 110.909091USD     -170 ABC @ 110.909091 USD = 0 ABC
    Assets:ABC:20200104 @ 140.00USD         -100 ABC @ 140.00 USD = 0 ABC
    Assets:ABC:20200104 @ 121.683502USD      270 ABC @ 121.683502 USD = 270 ABC

The benefit of this -- as compared to my simpler earlier example -- is that correctness (e.g., of capital gains) is automatically checked through hledger assertions, as long as every entry is individually consistent. This would essentially eliminate the need for cost assertions as proposed in my tangential remark.

Of course, this gets quite complex quickly, so automation is likely required, either built into hledger or on top of hledger.

Still unsure if this is the best way forward, I'll take a bit more time to think through this and alternatives.

lfos avatar Aug 23 '25 03:08 lfos