On Sun, Jul 13, 2014 at 7:12 PM, Stefano Zacchiroli <z...@upsilon.cc> wrote:

> On Sun, Jul 13, 2014 at 01:27:30PM -0400, Martin Blais wrote:
> > That might even be incorrect, I'm not sure. I believe Ledger does not
> > reduce lots, it seems to be matching positive and negative lots by
> > "grouping" only at the reporting level (I'm not entirely sure about
> > this, Zach make a comment on the doc this morning pointing out the
> > --lot-prices and --lot-date options, and the behavior seems to point
> > that way, e.g., you can "reduce" a lot that does not exist. I should
> > go dig in the source code to find out). So if you have some past
> > history of lot additions and So if you have some past history of lot
> > additions and reductions, they might affect the average cost you end
> > up with using your method, whereas the current inventory may not
> > include all the lots that are being used.
>
> I've already witnessed first hand that Ledger does allow you to reduce
> lots that do not exist (going negative). So I'm still not seeing the
> risk of incorrect calculation that you hinted at; if you think it's
> real, can you try to build one? (or give me some extra hints so that I
> can try building it).
>

Sure.
I've attached two Ledger input files which are nearly identical.

The first one has two transactions:

2014/06/01 Deposit some cash
  Equity:OpeningBalances
  Assets:Investing:Cash        $20000

P MSFT $520

2014/07/02 Buy one lot
  Assets:Investing:Stocks       5 MSFT @ $530
  Assets:Investing:Cash

2014/07/02 Buy one lot
  Assets:Investing:Stocks       5 MSFT @ $540
  Assets:Investing:Cash

P MSFT $550


If you use your method to calculate the number of shares and the cost
basis, you get the correct results:

mandarine [default]:~/p/beancount$ ledger -f
/home/blais/p/ledger-experiments/avgcost1.lgr --no-color bal Stocks
             10 MSFT  Assets:Investing:Stocks
mandarine [default]:~/p/beancount$ ledger -f
/home/blais/p/ledger-experiments/avgcost1.lgr --no-color bal Stocks -B
               $5350  Assets:Investing:Stocks


So the average cost is 5350 $ / 10 MSFT = 535.00 $/MSFT.
This is correct.

Now add some past history of another transaction in the same commodity,
being bought and sold at a different price, so it's gone back to zero by
the time we replicate the transactions above:

2014/06/01 Deposit some cash
  Equity:OpeningBalances
  Assets:Investing:Cash        $20000

2014/06/02 Buy one lot
  Assets:Investing:Stocks       10 MSFT @ $500
  Assets:Investing:Cash

2014/06/03 Sell the lot
  Assets:Investing:Stocks      -10 MSFT @ $510
  Assets:Investing:Cash

P MSFT $520

2014/07/02 Buy one lot
  Assets:Investing:Stocks       5 MSFT @ $530
  Assets:Investing:Cash

2014/07/02 Buy one lot
  Assets:Investing:Stocks       5 MSFT @ $540
  Assets:Investing:Cash

P MSFT $550


Notice that ALL I've done is insert the purchase and sale on 6/2 and 6/3.
Everything else is exactly the same.
Now try calculating the average cost using your method:

mandarine [default]:~/p/beancount$ ledger -f
/home/blais/p/ledger-experiments/avgcost2.lgr --no-color bal Stocks
             10 MSFT  Assets:Investing:Stocks
mandarine [default]:~/p/beancount$ ledger -f
/home/blais/p/ledger-experiments/avgcost2.lgr --no-color bal Stocks -B
               $5250  Assets:Investing:Stocks


Oops... now the average cost basis is 5250 $/MSFT.
A bit of a difference, don't you think?
I'm not sure if Martin's patch would affect the outcome.
This is a real problem with the way you're computing your cost basis.

After my experiments with Ledger, I've come to think its model of
calculation is incorrect.
Here's what needs to be done to fix it:

(1) Inventory booking is not a feature that needs to be carried out at
reporting time. Matching inventory lots need to be done only once, with no
options, needs to be unambiguous, as part of the processing of the flow of
transactions. When a position is reduced, a single and appropriate matching
position must be selected.

(2) In order to carry this out properly, a distinction needs to be drawn
between currency conversions and commodities held at cost, because being
able to enforce booking correctly means that you will now force the user to
specify cost for all these currency conversion, and your currency
conversions would become a pain in the blank to do (nobody cares about the
cost basis of price conversions).

Of course I'm more than a little biased: this is the model I've implemented
in Beancount.
A nice side-effect of (1) is that I'm able to output a list of inventory
reductions, a list of "trades."
I used to have this report in the first version, but have removed it. I'll
bring it back shortly after my next release.
(Some reporting authorities require you to provide the detail of your
trades, so that's a practically useful report to generate.)


> > I'm left wondering whether average cost basis should be a per account
> > > property, rather than (or maybe: in addition to) something that
> > > auto-magically happen the first time you post using "{*}".
>
> On Sun, Jul 13, 2014 at 01:29:10PM -0400, Martin Blais wrote:
> > Actually, there already is one, right here:
> >
> https://docs.google.com/document/d/1F8IJ_7fMHZ75XFPocMokLxVZczAhrBRBVN9uMhQFCZ4/edit#heading=h.k18hdhviv191
>
> Your comments made me realize two things:
>
> 1) what I actually had in mind was the ability to declare *mandatory*
>    average cost posting for a given account, inhibiting usage of other
>    booking methods on it. That was not entirely clear to me when I wrote
>    my previous message, so thanks for making me realize that :)
>
>    What it is currently supported by your proposal is only setting the
>    *default* booking method, but that does not stop others methods from
>    being used.  The invariant I've mentioned in the previous post
>    (ensuring that only one lot exists) can be guaranteed only by
>    mandatory booking method.
>

So if you never select a particular lot, how is just using a default
different than imposing it?
Are you worried about making mistakes and would like to detect it?
Or is it that you would prefer the inventory _always_ only include a single
lot for that commodity, somehow?

If so... why? The cost basis of the aggregation should always sum to the
same value, many lots of just-one-lot.
>From a software implementation perspective, it's nice to just enforce one
lot, but in terms of the calculations, it will make no difference.
Leaving the multiple lots to hang around until the next aggregation time
_does_ provide the opportunity for a user to book differently.

I'm not so sure I understand your concern. Please be specific (this is
subtle).
If you use {} to specify the cost, e.g.,

  ...    -10 MSFT {}

this is only unambiguous in the case the inventory has a single lot for
that commodity, so that degenerates to the same as the average cost (the
average cost of one lot is ... just the cost of that lot).
Otherwise, you'd have to use {*} everywhere there might be ambiguity:

  ...    -10 MSFT {*}

In which case every lot reduction triggers an aggregation.

Besides, after a single aggregation of multiple lots takes place, a user
will have little idea of the actual per-unit cost basis, so it's rather
unlikely he will specify that oddball cost.



   [ Aside: upon re-reading the above section, the AVERAGE option is
>      kinda confusing. My understanding of the FIFO and LIFO options is
>      that they are limited to disambiguating among the lots that have
>      been selected by your matching algorithms, i.e., they are not
>      LIFO/FIFO across all lots. OTOH AVERAGE makes sense only when
>      computed across all lots. So discussing FIFO/LIFO together with
>      AVERAGE is puzzling. If that is really what you want, this "detail"
>      should be communicated more clearly, IMHO. ]
>

That's an interesting point. From an implementor's point-of-view, it makes
all the sense in the world to speak of these together. But from a user's
point-of-view, it does raise questions. The question is: Will an average
Beancount user be required to understand the inventory booking methods well
enough to use them? If so, it would make little difference to discuss it
together. I'm not sure.

In any case, your bringing it up tells me I need to discuss the subtlety in
the final documentation. I'll have to discuss the possibility of merging
methods and probably warn against it.



>
> 2) in terms of design, it'd probably be better to enforce the constraint
>    of having a single lot as some sort of post-booking check, rather
>    than overloading even more the expressivity of account declaration
>    options. (Note: I'm not a Beancounter user, so it's not clear to me
>    if you've enough expressivity in your checks to encode this. But that
>    sounds like a better place where to put this.)
>

Yes, this is a good idea. In general I like to perform checks in separate
stages, it makes the code a lot nicer and opens up the possibility to shove
this check into a plugin that you could enable only if you want it. An easy
way to do this without a syntax extension would be to specify a
AVERAGE_ONLY booking type, that would barf if the user attempts to use a
non-average method and that might trigger the aggregation on additions as
well.

I'll update the proposal doc.

Thanks a lot for putting some effort in understanding and discussing this
long proposal Stefano.

-- 

--- 
You received this message because you are subscribed to the Google Groups 
"Ledger" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to ledger-cli+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Attachment: avgcost1.lgr
Description: Binary data

Attachment: avgcost2.lgr
Description: Binary data

Reply via email to