On Sun, 02 Jul 2000 01:02:57 -0400, the world broke into rejoicing as
Buddha Buck <[EMAIL PROTECTED]>  said:
> (I have already posted a potential datatype for GnuCash money before.  
> The only criticism I heard (good or bad) was over the fact that I write 
> it in C++, not C.  Like Christopher Browne says, the datatype is the 
> important thing, not the language.  It had slipped my mind that GnuCash 
> is C, not C++.  In truth, GnuCash is C/Scheme/Perl(still)/..., so 
> language doesn't really matter).

My irritation comes when the proposal is to redo GnuCash in C++ just
because it seems a neat idea to associate methods with data classes,
when that's "merely" syntactic sugar that can be emulated reasonably
well in other languages.

I'll actually mention a wider scope of languages than are in present
use; more anon...

> So far, GnuCash has been treating shares of stock as if it were a unit 
> of currency.  This has made it easier to write the engine -- stock 
> shares aren't a special case, any more than Euro and Dollars are.  I 
> think this is a reasonable idea to keep.
> 
> In fact, it is probably reasonable, if inventory tracking is ever added 
> to GnuCash, to extend the metaphor over to inventories as well.  Each 
> commodity owned (say, apples) would have it's own "currency".

Good observations.

> (The one place it breaks down is that in the case of stocks (and 
> probably inventory items) it is necessary to keep track of historical 
> prices for capital gains purposes.  When you sell 100 shares of stock 
> for $5000, you need to know you bought those 100 shares for $4000, so 
> you can declare $1000 in capital gains.  This might not be a major 
> problem, I dunno)

I don't think this is a _major_ problem; all that need be done is to
keep in mind that the original transaction, that of acquiring the
[whatever it is], is distinct from later attempts to "estimate the
current value of [whatever it is]."

> The fundamental things I see about money/stock/inventory/etc is that 
> there always seems to be some fundamental "quantum" that accounting is 
> done in.  With most stocks, it's (whole) shares.  With US Dollars, it's 
> the cent.  With UK Pounds, it's the penny, etc.  All quoted quantities 
> are given in terms of whole numbers of these fundamental quanta.  What 
> may vary is the number of these fundamental quanta per "unit".  Stock 
> shares have 1 quantum/share, US Dollars have 100 quanta/dollar, UK 
> Pounds have 240 quanta/pound (pre-1970) or 100 quanta/pound 
> (post-1970),  Yen have 1 quantum/yen, etc.  And these ratios of 
> quanta/unit are fixed for a given currency type.

The UK situation illustrates that the currency can change.  And so,
we have:

  a) "Old" UK Pounds, and
  b) "New" UK Pounds.

The same tends to happen fairly often in countries that suffer from
severe bouts of inflation.

> It also seems to me that there is a lot more to the currency types than 
> just the quanta/unit.  At the very least, there is info on how to print 
> values in that currency.  It makes sense to abstract that out:
> 
> struct {
>     char currname[4];     /* Abbreviation for currency */
>     unsigned int modulus; /* number of "quanta" per unit */
>     char *print(MValue value); /* convert value to string */
>     /* etc /*
> } MUnit;
> 
> You shouldn't be able to add Dollars and Euros (without some sort of 
> explicit conversion), just like you can't add apples and oranges.  
> Therefore, the MValue should have some sort of reference to its 
> currency, so such things can be checked:
> 
> struct {
>   MUnit *currency;    /* Currency of value */
>   arithmetic-unit value /* number of quanta of currency */
> } Mvalue;
> 
> To me, it makes sence to use a currency of "NULL" to error conditions:
> 
>   Mvalue euros,dollars;
>   Mvalue result;
> 
>   euros.currency   = &Euro;     /* Euro predefined Munit */
>   euros.value      = 100;
>   dollars.currency = &USDollar; /* USDollar predefined Munit */
>   dollars.value    = 100;
>   result = Madd(euros,dollars); /* result.currency = NULL;
> 
> If necessary, the value could be used to specify exactly what type of 
> error
> but it probably isn't necessary.
> 
> In my opinion, the only thing it makes sense to do with MValues 
> -directly- is addition and subtraction of like-currency Mvalues, and 
> multiplication by a scaler (currency-less) value:
> 
> Mvalue Madd(Mvalue addor,Mvalue addend);
> Mvalue Msub(Mvalue subtractor, Mvalue subtrahend); 
> Mvalue Mmul(Mvalue factor, double multiplier);
> 
> The last is used for taking percentages, etc.
> 
> I think Madd should be something like so:
> 
> Mvalue Madd(Mvalue addor,Mvalue addend) {
>   Mvalue sum;
> 
>   sum.currency = NULL; sum.value = 0;
>   if (addor.currency == NULL) {
>      return sum;  /* sum.currency == NULL means "error" */
>   }
>   if (addor.currency == addend.currency) {
>     sum.currency = addor.currency;
>     sum.value = addor.value + addend.value;
>   }
> }
> 
> Msub should be written similarly.  Mmul will have to handle the tricky 
> rounding issues.
> 
> IMPORTANT FOR REPORT WRITERS:  Mmul does -NOT- distrbute over Madd, so 
> you can't rely on being able to.  This is generally true for most 
> currency things, anyway.

All of this seems like reasonably good stuff.  I rather think that GnuCash
should pick a particular representation as what is used internally; the
point of this exercise should be to examine what properties are needed,
and what may thereby be inferred about what:
  arithmetic-unit value; /* number of quanta of currency */
should look like.

Another thing that I would suggest be supported is the notion of applying
some set of linear transformations on sequences of "arithmetic-units."

That is, you will want not only to be able to get "arithmetic-unit"
values, and add/subtract them, but also to produce sums of whole
sequences of them.

In CORBA, this would be provided by something like:

/* Structure that treats items as fractions, with up to 2^63 numerator and
   up to 2^31 denominator */
interface money {
  struct ourmoney {
   long long amount; long denominator; string currency; 
  };
  typedef sequence<ourmoney> bunchofamounts;   /* A list of amounts */
  /* Simple sum */
  void compute_sum (in bunchofamounts stuff, out ourmoney total);
  /* Sum with transformation */
  void complex_sum (in bunchofamounts stuff, in long long numerator,
                    in long denominator, out ourmoney result);
}

The "neat part" is that the two "summation" functions leave the
freedom for the engine to either compute something there, or perhaps
even make use of SQL functionality.  

We might thus have a further interface:
   void query_sum (in query_specification query, in long long numerator,
                   in long denominator, out bunchofamounts results);
which could push parts of the computation into the SQL engine:
   select sum(amount) from transactions group by currency 
        where date between "2000/01/10" and "2000/05/10";

The sequence<> type also parallels the way Common Lisp deals with
"sequences."  These are the set of data types that involve items arranged
in sequence, and include lists, vectors, arrays, and strings.  In SQL,
what you get out of a query is properly considered a "result set," and
that is more like a "sequence" than anything else.

> The only thing not covered is a way to convert from one Munit to 
> another.  I think a generalised "price" model is in order.
> 
> I beleive that prices are -fundamentally- different than monetary units 
> or stocks or inventories.  Prices represent conversion ratios between 
> different things.

They also tend to involve _estimates_.

We _assume_ that Microsoft Corporation is worth 300-something billion
dollars because a few shares traded on Friday at some given price.
The same is true for most of the prices that you see; values are
extrapolated based on the prices observed for some trades.

> Prices, in general, say that two amounts of two commodities have the 
> same value:  2 apples = 3 oranges, 105 yen = 1 US Dollar, etc.  Or, in 
> terms of the datatypes already developed, it says that two Mvalues that 
> have different currencies are equivilant and tradeable.
> 
> struct {
>   Mvalue a;   /* a.currency != b.currency if a.value != b.value */
>   Mvalue b;   /* a is equivilant to b */
> } Mprice;
> 
> This representation has the effect that we can represent historical 
> transactions more exactly.  For example:  If I bought 100 shares of 
> MSFT for $50, with a commission of $10, then the "price" I should 
> record is "100 MSFT = $5010", since I technically have to incorporate 
> the commission charge into the price paid.  After a 2:1 stock split, I 
> can simply change the recorded price to "200 MSFT = $5010" and the 
> number of shares bought to 200, and accounting wise, everything works 
> out OK -- including the cost basis of the shares for capital gains 
> purposes.

I like that property.

> I think that it should be the same "price" if fields a and b are 
> reversed:  2 apples for 3 oranges is the same as 3 oranges for 2 apples.
> 
> The basic API should be:
> 
> Mvalue cost = Mbuy(Mvalue items, Mprice price);
> 
> e.g.  $0.67 = Mbuy(2 apples, 3 apples = $1.00)
>       $0.67 = Mbuy(2 apples, $1.00 = 3 apples)
> 
> Please note that Mbuy must do rounding.  I think it makes sense for 
> Mbuy to always round -up-.  As such, for symmetry, I think there should 
> also be a Msell, which always rounds -down-.

I would leave the matter of how to manage rounding out of the basic
interface.  I've known there to be legally mandated rounding schemes in
some situations; in others, none are _mandated_.

If there is to be any "option" here, it should represent a _controllable_
option.

> e.g.  2 apples = Msell($0.90, 3 apples = $1.00)
>   but 3 apples = Mbuy ($0.90, 3 apples = $1.00)
> 
> Mbuy answers "how much will it cost me" and Msell answers "how many can 
> I get for this amount".
> 
> One question I don't have a definate answer for is:  How do you record 
> prices of the form "1 share XXXX = $12 3/8".  Do you create a new 
> currency for stock prices that has 8 "eighths" per dollar, so it gets 
> recorded as "1 share XXXX = $99/8", or do you keep to standard dollars, 
> normalize out the eighths, and record "8 shares XXXX = $99.00", or do 
> you simplify based on existing known quanta, and get "2 shares XXXX = 
> $24.75"?  I think I prefer the "8 shares XXXX = $99.00" method, myself.
> 
> So there are my thoughts.  Any comments?

What this last bit indicates is that _prices_ may not play as well as
you'd like.

If a _price_ is represented by a fraction, with numerator and denominator,
where denominator is "pretty big," then we have no problem.

For instance, a representation of price might be:

struct price {
   CTYPE from;
   CTYPE to;
   long long numerator;
   long denominator;
};

Thus, you'd have:
price P;
P.from = "OURSTOCK";
P.to = "USD";
P.numerator = 12*8 + 3;
P.denominator = 8;
--
[EMAIL PROTECTED] - <http://www.hex.net/~cbbrowne/lsf.html>
"Windows 98  Roast Specialty Blend  coffee beans - just  like ordinary
gourmet coffee except that processing is rushed to leave in the insect
larvae.  Also sold under the ``Chock Full o' Bugs'' brand name..."

--
Gnucash Developer's List
To unsubscribe send empty email to: [EMAIL PROTECTED]


Reply via email to