further pricing fixes
This commit is contained in:
parent
f077b655d0
commit
adb3965e94
4 changed files with 278 additions and 179 deletions
228
README
228
README
|
|
@ -128,7 +128,7 @@ $ ledger -f ledger.dat register checking
|
||||||
$ ledger -f ledger.dat register bell
|
$ ledger -f ledger.dat register bell
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
* Building the program
|
** Building the program
|
||||||
|
|
||||||
Ledger is written in ANSI C++, and should compile on any platform. It
|
Ledger is written in ANSI C++, and should compile on any platform. It
|
||||||
depends only on the GNU multiprecision integer library (libgmp), and
|
depends only on the GNU multiprecision integer library (libgmp), and
|
||||||
|
|
@ -364,6 +364,63 @@ Euro=MD 0.75
|
||||||
This is a roundabout way of reporting AAPL shares in their Deutsch
|
This is a roundabout way of reporting AAPL shares in their Deutsch
|
||||||
Mark equivalent.
|
Mark equivalent.
|
||||||
|
|
||||||
|
*** Commodity price histories
|
||||||
|
|
||||||
|
Whenever a commodity is purchased using a different commodity (such as
|
||||||
|
a share of common stock using dollars), it establishes a price for
|
||||||
|
that commodity on that day. It is also possible, by recording price
|
||||||
|
details in a ledger file, to specify other prices for commodities at
|
||||||
|
any given time. Such price entries might look like those below:
|
||||||
|
|
||||||
|
<example>
|
||||||
|
P 2004/06/21 02:17:58 TWCUX $27.76
|
||||||
|
P 2004/06/21 02:17:59 AGTHX $25.41
|
||||||
|
P 2004/06/21 02:18:00 OPTFX $39.31
|
||||||
|
P 2004/06/21 02:18:01 FEQTX $22.49
|
||||||
|
P 2004/06/21 02:18:02 AAPL $32.91
|
||||||
|
</example>
|
||||||
|
|
||||||
|
By default, ledger will not consider commodity prices when generating
|
||||||
|
its various reports. It will always report balances in terms of the
|
||||||
|
commodity total, rather than the current value of those commodities.
|
||||||
|
To enable pricing reports, three options are possible:
|
||||||
|
|
||||||
|
**-B** ::
|
||||||
|
Report commodities in terms of their "basis cost", or what they cost
|
||||||
|
at the time of purchase. Totals in the register and balance report
|
||||||
|
reflect the total amount spent.
|
||||||
|
|
||||||
|
**-L MINS** ::
|
||||||
|
When using the =-P= or =-Q= flags, the Internet is consulted only
|
||||||
|
if current pricing data is older than MINS minutes.
|
||||||
|
|
||||||
|
**-P** ::
|
||||||
|
Report commodities in terms of their market price. The Internet is
|
||||||
|
consulted for current prices, by calling an external script named
|
||||||
|
=getquote= (a sample Perl script is provided, but the interface is
|
||||||
|
kept simple so replacements may be made). Register reports always
|
||||||
|
give the total market value for the date of the entry -- which means
|
||||||
|
they may vary greatly from the sum of the values of the individual
|
||||||
|
entries.
|
||||||
|
|
||||||
|
**-Q FILE** ::
|
||||||
|
Works like the =-P= flag, except it reads and saves downloaded price
|
||||||
|
information in =FILE=, accumulating a history of prices for each
|
||||||
|
commodity. This is the same as using =-P= with the environment
|
||||||
|
variable =PRICE_HIST= set to =FILE=.
|
||||||
|
|
||||||
|
**-T** ::
|
||||||
|
Report only commodity totals, not the market value or basis cost.
|
||||||
|
|
||||||
|
**-V** ::
|
||||||
|
Report the market value for commodities, but without consulting the
|
||||||
|
Internet for current prices. This uses only the pricing data saved
|
||||||
|
in the ledger file, or in the history file referenced by the
|
||||||
|
environment variable =PRICE_HIST=.
|
||||||
|
|
||||||
|
Note that the =-B=, =-T=, =-V=, and =-P= and =-Q= flags are all
|
||||||
|
mutually exclusive. Whichever option appears last is used.
|
||||||
|
|
||||||
** Accounts and Inventories
|
** Accounts and Inventories
|
||||||
|
|
||||||
Since Ledger's accounts and commodity system is so flexible, you can
|
Since Ledger's accounts and commodity system is so flexible, you can
|
||||||
|
|
@ -490,7 +547,8 @@ output all the earlier entries to a file called =ledger-old.dat=.
|
||||||
2002 is mentioned in the following command):
|
2002 is mentioned in the following command):
|
||||||
|
|
||||||
<example>
|
<example>
|
||||||
$ ledger -f ledger.dat -b 2000/1/1 -e 2002/1/1 print > ledger-old.dat
|
$ ledger -f ledger.dat -b 2000/1/1 -e 2002/1/1 print \
|
||||||
|
> ledger-old.dat
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
To delete older data from the current ledger file, use "print" again,
|
To delete older data from the current ledger file, use "print" again,
|
||||||
|
|
@ -830,12 +888,9 @@ and how it should be parsed. The possibilities are:
|
||||||
NUMBER ::
|
NUMBER ::
|
||||||
A line starting with a number denotes a regular ledger entry. It
|
A line starting with a number denotes a regular ledger entry. It
|
||||||
may be followed by any number of lines that beginning whitespace, to
|
may be followed by any number of lines that beginning whitespace, to
|
||||||
denote account transactions. The format of an entry is:
|
denote account transactions. The format of the header line is:
|
||||||
<example>
|
<example>
|
||||||
DATE [*] [(CODE)] DESC
|
DATE [*] [(CODE)] DESC
|
||||||
ACCOUNT AMOUNT
|
|
||||||
ACCOUNT AMOUNT
|
|
||||||
...
|
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
+ ::
|
+ ::
|
||||||
|
|
@ -843,26 +898,17 @@ DATE [*] [(CODE)] DESC
|
||||||
will always be considered, as if it had been specified by the user
|
will always be considered, as if it had been specified by the user
|
||||||
at the end of their command-line.
|
at the end of their command-line.
|
||||||
|
|
||||||
- ::
|
**-** ::
|
||||||
If a line begins with minus, it denotes an exclusion regexp that
|
If a line begins with minus, it denotes an exclusion regexp that
|
||||||
will always be considered, as if it had been specified by the user
|
will always be considered, as if it had been specified by the user
|
||||||
at the end of their command-line.
|
at the end of their command-line.
|
||||||
|
|
||||||
<literal>=</literal> ::
|
**<verbatim>=</verbatim>** ::
|
||||||
If a line begins with equals, it denotes an automated transaction.
|
If a line begins with equals, it denotes an automated transaction.
|
||||||
The next item on the line must be a regular expression. Any number
|
The next item on the line must be a regular expression. Any number
|
||||||
of such lines may appear, with no intervening whitespace.
|
of such lines may appear, with no intervening whitespace.
|
||||||
Following this block of lines can be a list of account transactions
|
Following this block of lines can be a list of account transactions
|
||||||
preceded by whitespace. The format is:
|
preceded by whitespace.
|
||||||
<example>
|
|
||||||
= REGEXP
|
|
||||||
= REGEXP
|
|
||||||
= REGEXP
|
|
||||||
...
|
|
||||||
ACCOUNT AMOUNT
|
|
||||||
ACCOUNT AMOUNT
|
|
||||||
...
|
|
||||||
</example>
|
|
||||||
|
|
||||||
!WORD ::
|
!WORD ::
|
||||||
A line beginning with an exclamation mark denotes a command
|
A line beginning with an exclamation mark denotes a command
|
||||||
|
|
@ -964,33 +1010,6 @@ Equity transactions are used to establish the starting value of an
|
||||||
account. You might think of equity as the "ether" from which initial
|
account. You might think of equity as the "ether" from which initial
|
||||||
balances appear.
|
balances appear.
|
||||||
|
|
||||||
The "equity" command makes it easy to archiving past years, and then
|
|
||||||
remove them without changing any current balances. For example, if
|
|
||||||
it's now 2004 and we want to archive all of 2003's transactions to
|
|
||||||
another file, write:
|
|
||||||
|
|
||||||
<example>
|
|
||||||
export LEDGER=ledger.dat
|
|
||||||
ledger -e 2004/1/1 print > ledger-2003.dat
|
|
||||||
ledger -e 2004/1/1 equity > /tmp/balances
|
|
||||||
ledger -b 2004/1/1 print > /tmp/current
|
|
||||||
cat /tmp/balances /tmp/current > ledger.dat
|
|
||||||
rm /tmp/balances /tmp/current
|
|
||||||
</example>
|
|
||||||
|
|
||||||
After these commands, **ledger-2003.dat** will contain all the
|
|
||||||
transactions up to year 2004, with **ledger.dat** containing only those
|
|
||||||
since 2004. However, the balances reported from **ledger.dat** will still
|
|
||||||
be the same.
|
|
||||||
|
|
||||||
Sometimes you will not want to carry forward certain balances, such as
|
|
||||||
those for Expense and Income. To do this, change the second command
|
|
||||||
above to:
|
|
||||||
|
|
||||||
<example>
|
|
||||||
ledger -e 2004/1/1 equity -^Income -^Expenses > /tmp/balances
|
|
||||||
</example>
|
|
||||||
|
|
||||||
*** price
|
*** price
|
||||||
|
|
||||||
This commands displays the last known current price for a given
|
This commands displays the last known current price for a given
|
||||||
|
|
@ -1046,93 +1065,87 @@ launches =vi= to let you confirm that the entry looks appropriate.
|
||||||
|
|
||||||
** Option summary
|
** Option summary
|
||||||
|
|
||||||
-B ::
|
**-B** ::
|
||||||
When printing accounts containing commodities, display the base
|
When printing accounts containing commodities, display the base
|
||||||
price for the commodity, rather than the quantity of that commodity
|
price for the commodity, rather than the quantity of that commodity
|
||||||
(the default) or its current price (if =-P= or =-Q= is used). This
|
(the default) or its current price (if =-P= or =-Q= is used). This
|
||||||
option causes only the price at time(s) of purchase to be
|
option causes only the price at time(s) of purchase to be
|
||||||
considered, not the current or historical price afterwards.
|
considered, not the current or historical price afterwards.
|
||||||
|
|
||||||
-b DATE ::
|
**-b DATE** ::
|
||||||
Only consider entries occuring on or after the given date.
|
Only consider entries occuring on or after the given date.
|
||||||
|
|
||||||
-e DATE ::
|
**-C** ::
|
||||||
Only consider entries occuring before the given date. The date is
|
|
||||||
not inclusive, so any entries occurring on that date will not be
|
|
||||||
used.
|
|
||||||
|
|
||||||
-c ::
|
|
||||||
Only consider entries occurring on or before the current date.
|
|
||||||
|
|
||||||
-C ::
|
|
||||||
Only consider entries whose cleared flag has been set. The default
|
Only consider entries whose cleared flag has been set. The default
|
||||||
is to consider both.
|
is to consider both.
|
||||||
|
|
||||||
-d DATE ::
|
**-c** ::
|
||||||
|
Only consider entries occurring on or before the current date.
|
||||||
|
|
||||||
|
**-d DATE** ::
|
||||||
Only consider entries fitting the given date mask. DATE in this
|
Only consider entries fitting the given date mask. DATE in this
|
||||||
case may be the name of a month, or a year, or a year and month,
|
case may be the name of a month, or a year, or a year and month,
|
||||||
such as "2004/05". It's a shorthand for having to specify -b and -e
|
such as "2004/05". It's a shorthand for having to specify -b and -e
|
||||||
together.
|
together.
|
||||||
|
|
||||||
-E ::
|
**-E** ::
|
||||||
Also show empty accounts in the balance totals report.
|
Also show empty accounts in the balance totals report.
|
||||||
|
|
||||||
-f FILE[=ACCOUNT] ::
|
**-e DATE** ::
|
||||||
Read ledger entries from FILE. This takes precedence over the
|
Only consider entries occuring before the given date. The date is
|
||||||
environment variable LEDGER. If "=ACCOUNT" is appended to the
|
not inclusive, so any entries occurring on that date will not be
|
||||||
filename, then all of the entries are seen as if the transactions
|
used.
|
||||||
accounts were prefixed by "ACCOUNT:". There may be multiple
|
|
||||||
occurrences of the -f option.
|
|
||||||
|
|
||||||
-F ::
|
**-F** ::
|
||||||
Print full account names in all cases, such as "Assets:Checking"
|
Print full account names in all cases, such as "Assets:Checking"
|
||||||
instead of just "Checking". Only used current by the "balance"
|
instead of just "Checking". Only used current by the "balance"
|
||||||
command.
|
command.
|
||||||
|
|
||||||
-h ::
|
**-f FILE[<verbatim>=</verbatim>ACCOUNT]** ::
|
||||||
|
Read ledger entries from FILE. This takes precedence over the
|
||||||
|
environment variable LEDGER. If "<verbatim>=</verbatim>ACCOUNT" is
|
||||||
|
appended to the filename, then all of the entries are seen as if the
|
||||||
|
transactions accounts were prefixed by "ACCOUNT:". There may be
|
||||||
|
multiple occurrences of the =-f= option.
|
||||||
|
|
||||||
|
**-G** ::
|
||||||
|
Modifies the output generated by -M to be friendly to programs like
|
||||||
|
Gnuplot. It strips away the commodity label, and outputs only two
|
||||||
|
columns: the date and the amount.
|
||||||
|
|
||||||
|
**-h** ::
|
||||||
Print out quick help on the various options and commands.
|
Print out quick help on the various options and commands.
|
||||||
|
|
||||||
-i FILE ::
|
**-i FILE** ::
|
||||||
Read in the list of patterns to include/exclude from FILE.
|
Read in the list of patterns to include/exclude from FILE.
|
||||||
Ordinarily, these are specified as arguments after the command.
|
Ordinarily, these are specified as arguments after the command.
|
||||||
|
|
||||||
-L MINS ::
|
**-L MINS** ::
|
||||||
Specifies the number of minutes old that pricing data can be, before
|
Specifies the number of minutes old that pricing data can be, before
|
||||||
the =-Q= and =-P= options will download a new quote from the
|
the =-Q= and =-P= options will download a new quote from the
|
||||||
Internet. =-P= only downloads the information, while =-Q= maintains
|
Internet. =-P= only downloads the information, while =-Q= maintains
|
||||||
the information in a history file. The default value for this
|
the information in a history file. The default value for this
|
||||||
option is one day, or 1440 minutes.
|
option is one day, or 1440 minutes.
|
||||||
|
|
||||||
-M ::
|
**-l AMT** ::
|
||||||
|
Limit balance reports to those which are greater than AMT.
|
||||||
|
|
||||||
|
**-M** ::
|
||||||
When used with the "register" command, causes only monthly subtotals
|
When used with the "register" command, causes only monthly subtotals
|
||||||
to appear. This can be useful for looking at spending patterns.
|
to appear. This can be useful for looking at spending patterns.
|
||||||
TODO: Accept an argument which specifies the period to use.
|
TODO: Accept an argument which specifies the period to use.
|
||||||
|
|
||||||
-G ::
|
**-N REGEXP** ::
|
||||||
Modifies the output generated by -M to be friendly to programs like
|
|
||||||
Gnuplot. It strips away the commodity label, and outputs only two
|
|
||||||
columns: the date and the amount.
|
|
||||||
|
|
||||||
-n ::
|
|
||||||
Do not show subtotals in the balance report, or split transactions
|
|
||||||
in the register report.
|
|
||||||
|
|
||||||
-N REGEXP ::
|
|
||||||
If an account matches REGEXP, only display it in the balance report
|
If an account matches REGEXP, only display it in the balance report
|
||||||
if its total is negative. Useful to avoid seeing credit in accounts
|
if its total is negative. Useful to avoid seeing credit in accounts
|
||||||
where one cannot spend that credit, and it will soon become negative
|
where one cannot spend that credit, and it will soon become negative
|
||||||
anyway (such as credit cards).
|
anyway (such as credit cards).
|
||||||
|
|
||||||
-p ARG ::
|
**-n** ::
|
||||||
If a string, such as "COMM=$1.20", the commodity COMM will be
|
Do not show subtotals in the balance report, or split transactions
|
||||||
reported only in terms of the conversion factor, which supersedes
|
in the register report.
|
||||||
all other pricing histories for that commodity. This can be used to
|
|
||||||
perform arbitrary value substitutions. For example, to report the
|
|
||||||
value of your dollars in terms of the ounces of gold they would buy,
|
|
||||||
use: -p "$=0.00280112 AU" (or whatever the current exchange rate
|
|
||||||
is).
|
|
||||||
|
|
||||||
-P ::
|
**-P** ::
|
||||||
Download current prices for all commodities by calling the script
|
Download current prices for all commodities by calling the script
|
||||||
"getquote". There is a "getquote" script included with ledger,
|
"getquote". There is a "getquote" script included with ledger,
|
||||||
although any similar program could be used. It must take a single
|
although any similar program could be used. It must take a single
|
||||||
|
|
@ -1141,7 +1154,16 @@ launches =vi= to let you confirm that the entry looks appropriate.
|
||||||
commodity has no price, nothing should be output and the exit code
|
commodity has no price, nothing should be output and the exit code
|
||||||
should be set to a non-zero value.
|
should be set to a non-zero value.
|
||||||
|
|
||||||
-Q FILE ::
|
**-p ARG** ::
|
||||||
|
If a string, such as "COMM=$1.20", the commodity COMM will be
|
||||||
|
reported only in terms of the conversion factor, which supersedes
|
||||||
|
all other pricing histories for that commodity. This can be used to
|
||||||
|
perform arbitrary value substitutions. For example, to report the
|
||||||
|
value of your dollars in terms of the ounces of gold they would buy,
|
||||||
|
use: -p "$=0.00280112 AU" (or whatever the current exchange rate
|
||||||
|
is).
|
||||||
|
|
||||||
|
**-Q FILE** ::
|
||||||
This option, like =-P=, downloads commodities prices from the
|
This option, like =-P=, downloads commodities prices from the
|
||||||
Internet as needed, by calling the script "getquote" (see above).
|
Internet as needed, by calling the script "getquote" (see above).
|
||||||
However, this option takes a string argument: the file to write the
|
However, this option takes a string argument: the file to write the
|
||||||
|
|
@ -1154,39 +1176,45 @@ launches =vi= to let you confirm that the entry looks appropriate.
|
||||||
command-line. Also, it is recommended that the =-Q= option always
|
command-line. Also, it is recommended that the =-Q= option always
|
||||||
appear after all uses of =-f=.
|
appear after all uses of =-f=.
|
||||||
|
|
||||||
-R ::
|
**-R** ::
|
||||||
Ignore all virtual transactions, and report only the real balance
|
Ignore all virtual transactions, and report only the real balance
|
||||||
for each account.
|
for each account.
|
||||||
|
|
||||||
-s ::
|
**-S** ::
|
||||||
If an account has children, show them in the balance report.
|
|
||||||
|
|
||||||
-S ::
|
|
||||||
Sort the ledger after reading it. This may affect "register" and
|
Sort the ledger after reading it. This may affect "register" and
|
||||||
"print" output.
|
"print" output.
|
||||||
|
|
||||||
-T ::
|
**-s** ::
|
||||||
|
If an account has children, show them in the balance report.
|
||||||
|
|
||||||
|
**-T** ::
|
||||||
Show only commodities totals, do not convert to the basis cost or
|
Show only commodities totals, do not convert to the basis cost or
|
||||||
the current market value. This disables the effect of =-B=, =-P=
|
the current market value. This disables the effect of =-B=, =-P=
|
||||||
and =-Q=.
|
and =-Q=.
|
||||||
|
|
||||||
-U ::
|
**-U** ::
|
||||||
Show only uncleared transactions. The default is to consider both.
|
Show only uncleared transactions. The default is to consider both.
|
||||||
|
|
||||||
-v ::
|
**-V** ::
|
||||||
|
Report the market value for commodities, but without consulting the
|
||||||
|
Internet for current prices. This uses only the pricing data saved
|
||||||
|
in the ledger file, or in the history file referenced by the
|
||||||
|
environment variable =PRICE_HIST=.
|
||||||
|
|
||||||
|
**-v** ::
|
||||||
Display the version of ledger being used.
|
Display the version of ledger being used.
|
||||||
|
|
||||||
** Environment variables
|
** Environment variables
|
||||||
|
|
||||||
LEDGER ::
|
=LEDGER= ::
|
||||||
A colon-separated list of files to be parsed whenever ledger is run.
|
A colon-separated list of files to be parsed whenever ledger is run.
|
||||||
Easier than typing =-f= all the time.
|
Easier than typing =-f= all the time.
|
||||||
|
|
||||||
<verbatim>PRICE_HIST</verbatim> ::
|
=PRICE_HIST= ::
|
||||||
The ledger file used to hold pricing data. =~/.pricedb= would be a
|
The ledger file used to hold pricing data. =~/.pricedb= would be a
|
||||||
good choice.
|
good choice.
|
||||||
|
|
||||||
<verbatim>PRICE_EXP</verbatim> ::
|
=PRICE_EXP= ::
|
||||||
The number of minutes before pricing data becomes out-of-date. The
|
The number of minutes before pricing data becomes out-of-date. The
|
||||||
default is one day. Use =-L= to temporarily decrease or increase
|
default is one day. Use =-L= to temporarily decrease or increase
|
||||||
the value.
|
the value.
|
||||||
|
|
|
||||||
17
ledger.cc
17
ledger.cc
|
|
@ -448,6 +448,23 @@ void totals::print(std::ostream& out, int width) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void totals::print_street(std::ostream& out, int width, std::time_t * when,
|
||||||
|
bool use_history, bool download) const
|
||||||
|
{
|
||||||
|
totals street_balance;
|
||||||
|
|
||||||
|
for (const_iterator i = amounts.begin(); i != amounts.end(); i++) {
|
||||||
|
if ((*i).second->is_zero())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
amount * street = (*i).second->street(when, use_history, download);
|
||||||
|
street_balance.credit(street);
|
||||||
|
delete street;
|
||||||
|
}
|
||||||
|
|
||||||
|
street_balance.print(out, width);
|
||||||
|
}
|
||||||
|
|
||||||
account::~account()
|
account::~account()
|
||||||
{
|
{
|
||||||
for (accounts_map_iterator i = children.begin();
|
for (accounts_map_iterator i = children.begin();
|
||||||
|
|
|
||||||
5
ledger.h
5
ledger.h
|
|
@ -237,6 +237,10 @@ class totals
|
||||||
bool is_negative() const;
|
bool is_negative() const;
|
||||||
|
|
||||||
void print(std::ostream& out, int width) const;
|
void print(std::ostream& out, int width) const;
|
||||||
|
void print_street(std::ostream& out, int width,
|
||||||
|
std::time_t * when = NULL,
|
||||||
|
bool use_history = false,
|
||||||
|
bool download = false) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -347,7 +351,6 @@ extern void read_regexps(const std::string& path, regexps_list& regexps);
|
||||||
extern bool matches(const regexps_list& regexps, const std::string& str,
|
extern bool matches(const regexps_list& regexps, const std::string& str,
|
||||||
bool * by_exclusion = NULL);
|
bool * by_exclusion = NULL);
|
||||||
|
|
||||||
extern void read_prices(const std::string& path);
|
|
||||||
extern void parse_price_setting(const std::string& setting);
|
extern void parse_price_setting(const std::string& setting);
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
191
reports.cc
191
reports.cc
|
|
@ -9,7 +9,6 @@ namespace ledger {
|
||||||
|
|
||||||
static bool cleared_only = false;
|
static bool cleared_only = false;
|
||||||
static bool uncleared_only = false;
|
static bool uncleared_only = false;
|
||||||
static bool cost_basis = false;
|
|
||||||
static bool show_virtual = true;
|
static bool show_virtual = true;
|
||||||
static bool show_children = false;
|
static bool show_children = false;
|
||||||
static bool show_sorted = false;
|
static bool show_sorted = false;
|
||||||
|
|
@ -19,6 +18,9 @@ static bool full_names = false;
|
||||||
static bool print_monthly = false;
|
static bool print_monthly = false;
|
||||||
static bool gnuplot_safe = false;
|
static bool gnuplot_safe = false;
|
||||||
|
|
||||||
|
static bool cost_basis = false;
|
||||||
|
static bool use_history = false;
|
||||||
|
static bool read_prices = false;
|
||||||
static bool get_quotes = false;
|
static bool get_quotes = false;
|
||||||
long pricing_leeway = 24 * 3600;
|
long pricing_leeway = 24 * 3600;
|
||||||
std::string price_db;
|
std::string price_db;
|
||||||
|
|
@ -71,6 +73,58 @@ static bool matches_date_range(entry * ent)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static amount * resolve_amount(amount * amt,
|
||||||
|
std::time_t * when = NULL,
|
||||||
|
totals * balance = NULL,
|
||||||
|
bool add_base_value = false,
|
||||||
|
bool free_memory = false)
|
||||||
|
{
|
||||||
|
amount * value;
|
||||||
|
bool alloced = true;
|
||||||
|
|
||||||
|
if (! use_history) {
|
||||||
|
value = amt;
|
||||||
|
alloced = false;
|
||||||
|
}
|
||||||
|
else if (cost_basis) {
|
||||||
|
value = amt->value();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = amt->street(when ? when : (have_ending ? &end_date : NULL),
|
||||||
|
use_history, get_quotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance) {
|
||||||
|
if (add_base_value)
|
||||||
|
balance->credit(cost_basis ? value : amt);
|
||||||
|
else
|
||||||
|
balance->credit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_memory && alloced) {
|
||||||
|
delete value;
|
||||||
|
value = NULL;
|
||||||
|
}
|
||||||
|
else if (! free_memory && ! alloced) {
|
||||||
|
value = value->copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void print_resolved_balance(std::ostream& out,
|
||||||
|
std::time_t * when,
|
||||||
|
totals& balance,
|
||||||
|
bool added_base_value = false)
|
||||||
|
{
|
||||||
|
if (! added_base_value || ! use_history || cost_basis)
|
||||||
|
balance.print(out, 12);
|
||||||
|
else
|
||||||
|
balance.print_street(out, 12,
|
||||||
|
when ? when : (have_ending ? &end_date : NULL),
|
||||||
|
use_history, get_quotes);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Balance reporting code
|
// Balance reporting code
|
||||||
|
|
@ -274,16 +328,7 @@ void report_balances(std::ostream& out, regexps_list& regexps)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acct->checked == 1) {
|
if (acct->checked == 1) {
|
||||||
amount * street = (*x)->cost->street(have_ending ? &end_date : NULL,
|
resolve_amount((*x)->cost, NULL, &acct->balance, false, true);
|
||||||
cost_basis || get_quotes,
|
|
||||||
get_quotes);
|
|
||||||
if (cost_basis &&
|
|
||||||
street->commdty() == (*x)->cost->commdty() &&
|
|
||||||
(*x)->cost->has_price()) {
|
|
||||||
street = (*x)->cost->value();
|
|
||||||
}
|
|
||||||
acct->balance.credit(street);
|
|
||||||
delete street;
|
|
||||||
}
|
}
|
||||||
else if (show_subtotals) {
|
else if (show_subtotals) {
|
||||||
if (! regexps.empty() && ! match) {
|
if (! regexps.empty() && ! match) {
|
||||||
|
|
@ -364,9 +409,7 @@ void print_register_transaction(std::ostream& out, entry *ent,
|
||||||
// Always display the street value, if prices have been
|
// Always display the street value, if prices have been
|
||||||
// specified
|
// specified
|
||||||
|
|
||||||
amount * street = xact->cost->street(&ent->date, cost_basis || get_quotes,
|
amount * street = resolve_amount(xact->cost, &ent->date, &balance, true);
|
||||||
get_quotes);
|
|
||||||
balance.credit(street);
|
|
||||||
|
|
||||||
// If there are two transactions, use the one which does not
|
// If there are two transactions, use the one which does not
|
||||||
// refer to this account. If there are more than two, print
|
// refer to this account. If there are more than two, print
|
||||||
|
|
@ -395,7 +438,7 @@ void print_register_transaction(std::ostream& out, entry *ent,
|
||||||
out << std::right << street->as_str(true);
|
out << std::right << street->as_str(true);
|
||||||
delete street;
|
delete street;
|
||||||
|
|
||||||
balance.print(out, 12);
|
print_resolved_balance(out, &ent->date, balance, true);
|
||||||
|
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
|
|
||||||
|
|
@ -412,17 +455,16 @@ void print_register_transaction(std::ostream& out, entry *ent,
|
||||||
|
|
||||||
out.width(22);
|
out.width(22);
|
||||||
out << std::left << truncated((*y)->acct_as_str(), 22) << " ";
|
out << std::left << truncated((*y)->acct_as_str(), 22) << " ";
|
||||||
|
|
||||||
out.width(12);
|
out.width(12);
|
||||||
street = (*y)->cost->street(&ent->date, cost_basis || get_quotes,
|
|
||||||
get_quotes);
|
street = resolve_amount((*y)->cost, &ent->date);
|
||||||
out << std::right << street->as_str(true) << std::endl;
|
out << std::right << street->as_str(true) << std::endl;
|
||||||
delete street;
|
delete street;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_register_period(std::ostream& out, std::time_t date,
|
void print_register_period(std::ostream& out, std::time_t date,
|
||||||
account *acct, amount& sum, totals& balance)
|
account * acct, amount& sum, totals& balance)
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[32];
|
||||||
std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&date));
|
std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&date));
|
||||||
|
|
@ -448,7 +490,7 @@ void print_register_period(std::ostream& out, std::time_t date,
|
||||||
out << std::right << sum.as_str();
|
out << std::right << sum.as_str();
|
||||||
|
|
||||||
if (! gnuplot_safe)
|
if (! gnuplot_safe)
|
||||||
balance.print(out, 12);
|
print_resolved_balance(out, &date, balance, true);
|
||||||
|
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
}
|
}
|
||||||
|
|
@ -496,11 +538,8 @@ void print_register(std::ostream& out, const std::string& acct_name,
|
||||||
if (period == PERIOD_NONE) {
|
if (period == PERIOD_NONE) {
|
||||||
print_register_transaction(out, *i, *x, balance);
|
print_register_transaction(out, *i, *x, balance);
|
||||||
} else {
|
} else {
|
||||||
amount * street = (*x)->cost->street(&(*i)->date,
|
amount * street = resolve_amount((*x)->cost, &(*i)->date,
|
||||||
cost_basis || get_quotes,
|
&balance, true);
|
||||||
get_quotes);
|
|
||||||
balance.credit(street);
|
|
||||||
|
|
||||||
if (period_sum) {
|
if (period_sum) {
|
||||||
period_sum->credit(street);
|
period_sum->credit(street);
|
||||||
delete street;
|
delete street;
|
||||||
|
|
@ -550,16 +589,12 @@ static void equity_entry(account * acct, regexps_list& regexps,
|
||||||
|
|
||||||
transaction * xact = new transaction();
|
transaction * xact = new transaction();
|
||||||
xact->acct = const_cast<account *>(acct);
|
xact->acct = const_cast<account *>(acct);
|
||||||
xact->cost = (*i).second->street(have_ending ? &end_date : NULL,
|
xact->cost = (*i).second->copy();
|
||||||
cost_basis || get_quotes,
|
|
||||||
get_quotes);
|
|
||||||
opening.xacts.push_back(xact);
|
opening.xacts.push_back(xact);
|
||||||
|
|
||||||
xact = new transaction();
|
xact = new transaction();
|
||||||
xact->acct = main_ledger->find_account("Equity:Opening Balances");
|
xact->acct = main_ledger->find_account("Equity:Opening Balances");
|
||||||
xact->cost = (*i).second->street(have_ending ? &end_date : NULL,
|
xact->cost = (*i).second->copy();
|
||||||
cost_basis || get_quotes,
|
|
||||||
get_quotes);
|
|
||||||
xact->cost->negate();
|
xact->cost->negate();
|
||||||
opening.xacts.push_back(xact);
|
opening.xacts.push_back(xact);
|
||||||
}
|
}
|
||||||
|
|
@ -605,8 +640,7 @@ void price_report(std::ostream& out, regexps_list& regexps)
|
||||||
i++)
|
i++)
|
||||||
if (regexps.empty() || matches(regexps, (*i).first)) {
|
if (regexps.empty() || matches(regexps, (*i).first)) {
|
||||||
amount * price = (*i).second->price(have_ending ? &end_date : NULL,
|
amount * price = (*i).second->price(have_ending ? &end_date : NULL,
|
||||||
cost_basis || get_quotes,
|
use_history, get_quotes);
|
||||||
get_quotes);
|
|
||||||
if (price && ! price->is_zero()) {
|
if (price && ! price->is_zero()) {
|
||||||
out.width(20);
|
out.width(20);
|
||||||
out << std::right << price->as_str() << " " << (*i).first
|
out << std::right << price->as_str() << " " << (*i).first
|
||||||
|
|
@ -787,28 +821,33 @@ static void show_help(std::ostream& out)
|
||||||
<< "usage: ledger [options] COMMAND [options] [REGEXPS]" << std::endl
|
<< "usage: ledger [options] COMMAND [options] [REGEXPS]" << std::endl
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< "ledger options:" << std::endl
|
<< "ledger options:" << std::endl
|
||||||
|
<< " -B report commodities in terms of their basis cost" << std::endl
|
||||||
<< " -b DATE specify a beginning date" << std::endl
|
<< " -b DATE specify a beginning date" << std::endl
|
||||||
<< " -e DATE specify an ending date" << std::endl
|
|
||||||
<< " -c do not show future entries (same as -e TODAY)" << std::endl
|
|
||||||
<< " -C show only cleared transactions and balances" << std::endl
|
<< " -C show only cleared transactions and balances" << std::endl
|
||||||
|
<< " -c do not show future entries (same as -e TODAY)" << std::endl
|
||||||
<< " -d DATE specify a date mask ('-d mon', for all mondays)" << std::endl
|
<< " -d DATE specify a date mask ('-d mon', for all mondays)" << std::endl
|
||||||
<< " -E also show accounts with zero totals" << std::endl
|
<< " -E also show accounts with zero totals" << std::endl
|
||||||
<< " -f FILE specify pathname of ledger data file" << std::endl
|
<< " -e DATE specify an ending date" << std::endl
|
||||||
<< " -F print each account's full name" << std::endl
|
<< " -F print each account's full name" << std::endl
|
||||||
|
<< " -f FILE specify pathname of ledger data file" << std::endl
|
||||||
|
<< " -G use with -M to produce gnuplot-friendly output" << std::endl
|
||||||
<< " -h display this help text" << std::endl
|
<< " -h display this help text" << std::endl
|
||||||
<< " -i FILE read the list of inclusion regexps from FILE" << std::endl
|
<< " -i FILE read the list of inclusion regexps from FILE" << std::endl
|
||||||
|
<< " -L MINS fetch price quotes if info older than MINS" << std::endl
|
||||||
<< " -l AMT don't print balance totals whose abs value is <AMT" << std::endl
|
<< " -l AMT don't print balance totals whose abs value is <AMT" << std::endl
|
||||||
<< " -M print register using monthly sub-totals" << std::endl
|
<< " -M print register using monthly sub-totals" << std::endl
|
||||||
<< " -G use with -M to produce gnuplot-friendly output" << std::endl
|
|
||||||
<< " -n do not calculate parent account totals" << std::endl
|
|
||||||
<< " -N REGEX accounts matching REGEXP only display if negative" << std::endl
|
<< " -N REGEX accounts matching REGEXP only display if negative" << std::endl
|
||||||
<< " -p ARG set a price, or read prices from a file" << std::endl
|
<< " -n do not calculate parent account totals" << std::endl
|
||||||
<< " -P download price quotes from the Internet" << std::endl
|
<< " -P download price quotes from the Internet" << std::endl
|
||||||
<< " (works by running the command \"getquote SYMBOL\")" << std::endl
|
<< " (works by running the command \"getquote SYMBOL\")" << std::endl
|
||||||
|
<< " -p ARG set a direct price conversion: COMM=PRICE" << std::endl
|
||||||
|
<< " -Q FILE keep price histories in FILE (implies -P)" << std::endl
|
||||||
<< " -R do not factor in virtual transactions" << std::endl
|
<< " -R do not factor in virtual transactions" << std::endl
|
||||||
<< " -s show sub-accounts in balance totals" << std::endl
|
|
||||||
<< " -S sort the output of \"print\" by date" << std::endl
|
<< " -S sort the output of \"print\" by date" << std::endl
|
||||||
|
<< " -s show sub-accounts in balance totals" << std::endl
|
||||||
|
<< " -T report only commodities totals, not their value" << std::endl
|
||||||
<< " -U show only uncleared transactions and balances" << std::endl
|
<< " -U show only uncleared transactions and balances" << std::endl
|
||||||
|
<< " -V report commodity values, but don't download quotes" << std::endl
|
||||||
<< " -v display version information" << std::endl << std::endl
|
<< " -v display version information" << std::endl << std::endl
|
||||||
<< "commands:" << std::endl
|
<< "commands:" << std::endl
|
||||||
<< " balance show balance totals" << std::endl
|
<< " balance show balance totals" << std::endl
|
||||||
|
|
@ -830,17 +869,24 @@ int main(int argc, char * argv[])
|
||||||
std::string prices;
|
std::string prices;
|
||||||
std::string limit;
|
std::string limit;
|
||||||
regexps_list regexps;
|
regexps_list regexps;
|
||||||
bool no_history = false;
|
|
||||||
|
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
|
||||||
main_ledger = new book;
|
main_ledger = new book;
|
||||||
|
|
||||||
|
// Initialize some variables based on environment variable settings
|
||||||
|
|
||||||
|
if (char * p = std::getenv("PRICE_HIST"))
|
||||||
|
price_db = p;
|
||||||
|
|
||||||
|
if (char * p = std::getenv("PRICE_EXP"))
|
||||||
|
pricing_leeway = std::atol(p) * 60;
|
||||||
|
|
||||||
// Parse the command-line options
|
// Parse the command-line options
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while (-1 != (c = getopt(argc, argv,
|
while (-1 != (c = getopt(argc, argv,
|
||||||
"+b:e:d:cCUhBRV:f:i:p:PL:Q:TvsSEnFMGl:N:"))) {
|
"+Bb:Ccd:Ee:Ff:Ghi:L:l:MN:nPp:Q:RSsTUVv"))) {
|
||||||
switch (char(c)) {
|
switch (char(c)) {
|
||||||
case 'b':
|
case 'b':
|
||||||
have_beginning = true;
|
have_beginning = true;
|
||||||
|
|
@ -900,24 +946,43 @@ int main(int argc, char * argv[])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
|
cost_basis = false;
|
||||||
|
use_history = true;
|
||||||
get_quotes = true;
|
get_quotes = true;
|
||||||
break;
|
read_prices = true;
|
||||||
|
|
||||||
case 'L':
|
|
||||||
pricing_leeway = std::atol(optarg) * 60;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Q':
|
case 'Q':
|
||||||
|
cost_basis = false;
|
||||||
|
use_history = true;
|
||||||
get_quotes = true;
|
get_quotes = true;
|
||||||
|
read_prices = true;
|
||||||
price_db = optarg;
|
price_db = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
cost_basis = false;
|
||||||
|
use_history = true;
|
||||||
|
get_quotes = false;
|
||||||
|
read_prices = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'B':
|
case 'B':
|
||||||
cost_basis = true;
|
cost_basis = true;
|
||||||
// fall through...
|
use_history = true;
|
||||||
case 'T':
|
|
||||||
no_history = true;
|
|
||||||
get_quotes = false;
|
get_quotes = false;
|
||||||
|
read_prices = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
cost_basis = false;
|
||||||
|
use_history = false;
|
||||||
|
get_quotes = false;
|
||||||
|
read_prices = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'L':
|
||||||
|
pricing_leeway = std::atol(optarg) * 60;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
|
|
@ -966,20 +1031,6 @@ int main(int argc, char * argv[])
|
||||||
for (; index < argc; index++)
|
for (; index < argc; index++)
|
||||||
regexps.push_back(mask(argv[index]));
|
regexps.push_back(mask(argv[index]));
|
||||||
|
|
||||||
// If a price history file is specified with the environment
|
|
||||||
// variable PRICE_HIST, add it to the list of ledger files to read.
|
|
||||||
|
|
||||||
if (! no_history) {
|
|
||||||
if (price_db.empty())
|
|
||||||
if (char * p = std::getenv("PRICE_HIST")) {
|
|
||||||
get_quotes = true;
|
|
||||||
price_db = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char * p = std::getenv("PRICE_EXP"))
|
|
||||||
pricing_leeway = std::atol(p) * 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A ledger data file must be specified
|
// A ledger data file must be specified
|
||||||
|
|
||||||
int entry_count = 0;
|
int entry_count = 0;
|
||||||
|
|
@ -989,8 +1040,8 @@ int main(int argc, char * argv[])
|
||||||
for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":")) {
|
for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":")) {
|
||||||
char * sep = std::strrchr(p, '=');
|
char * sep = std::strrchr(p, '=');
|
||||||
if (sep) *sep++ = '\0';
|
if (sep) *sep++ = '\0';
|
||||||
entry_count += parse_ledger_file(main_ledger, std::string(p),
|
entry_count += parse_ledger_file(main_ledger, std::string(p), regexps,
|
||||||
regexps, command == "equity", sep);
|
command == "equity", sep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1001,14 +1052,14 @@ int main(int argc, char * argv[])
|
||||||
std::strcpy(p, (*i).c_str());
|
std::strcpy(p, (*i).c_str());
|
||||||
char * sep = std::strrchr(p, '=');
|
char * sep = std::strrchr(p, '=');
|
||||||
if (sep) *sep++ = '\0';
|
if (sep) *sep++ = '\0';
|
||||||
entry_count += parse_ledger_file(main_ledger, std::string(p),
|
entry_count += parse_ledger_file(main_ledger, std::string(p), regexps,
|
||||||
regexps, command == "equity", sep);
|
command == "equity", sep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! no_history && ! price_db.empty())
|
if (read_prices && ! price_db.empty())
|
||||||
entry_count += parse_ledger_file(main_ledger, price_db,
|
entry_count += parse_ledger_file(main_ledger, price_db, regexps,
|
||||||
regexps, command == "equity");
|
command == "equity");
|
||||||
|
|
||||||
if (entry_count == 0) {
|
if (entry_count == 0) {
|
||||||
std::cerr << ("Please specify ledger file(s) using -f option "
|
std::cerr << ("Please specify ledger file(s) using -f option "
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue