pricing fixes, added price command

This commit is contained in:
John Wiegley 2004-06-21 05:37:40 -04:00 committed by johnw
parent c892e8c7ad
commit f077b655d0
6 changed files with 86 additions and 31 deletions

View file

@ -4,7 +4,7 @@ OBJS = $(patsubst %.cc,%.o,$(CODE))
CXX = g++ CXX = g++
CFLAGS = #-Wall -ansi -pedantic CFLAGS = #-Wall -ansi -pedantic
DFLAGS = -O3 -fomit-frame-pointer DFLAGS = -O3 -fomit-frame-pointer
DFLAGS = #-g -DDEBUG=1 #DFLAGS = -g -DDEBUG=1
INCS = -I/sw/include -I/usr/include/gcc/darwin/3.3/c++ -I/usr/include/gcc/darwin/3.3/c++/ppc-darwin INCS = -I/sw/include -I/usr/include/gcc/darwin/3.3/c++ -I/usr/include/gcc/darwin/3.3/c++/ppc-darwin
LIBS = -L/sw/lib -lgmpxx -lgmp -lpcre LIBS = -L/sw/lib -lgmpxx -lgmp -lpcre

18
README
View file

@ -991,6 +991,15 @@ above to:
ledger -e 2004/1/1 equity -^Income -^Expenses > /tmp/balances ledger -e 2004/1/1 equity -^Income -^Expenses > /tmp/balances
</example> </example>
*** price
This commands displays the last known current price for a given
commodity, using the specified end date for the cutoff (default is the
present moment). It takes a list of regexps, which can match the
commodities used in the ledger file. This command is helpful to
quickly seeing the last current price for a specific commodity, or all
commodities referenced by a ledger.
*** entry *** entry
The three most laborious tasks of keeping a ledger are: adding new The three most laborious tasks of keeping a ledger are: adding new
@ -1156,6 +1165,11 @@ launches =vi= to let you confirm that the entry looks appropriate.
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 ::
Show only commodities totals, do not convert to the basis cost or
the current market value. This disables the effect of =-B=, =-P=
and =-Q=.
-U :: -U ::
Show only uncleared transactions. The default is to consider both. Show only uncleared transactions. The default is to consider both.
@ -1168,11 +1182,11 @@ 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.
PRICE_HIST :: <verbatim>PRICE_HIST</verbatim> ::
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.
PRICE_EXP :: <verbatim>PRICE_EXP</verbatim> ::
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.

View file

@ -58,7 +58,8 @@ class gmp_amount : public amount
virtual amount * value(const amount *) const; virtual amount * value(const amount *) const;
virtual void set_value(const amount * val); virtual void set_value(const amount * val);
virtual amount * street(std::time_t * when = NULL, virtual amount * street(std::time_t * when = NULL,
bool get_quotes = false) const; bool use_history = false,
bool download = false) const;
virtual bool has_price() const { virtual bool has_price() const {
return priced; return priced;
@ -215,7 +216,8 @@ amount * gmp_amount::value(const amount * pr) const
} }
} }
amount * gmp_amount::street(std::time_t * when, bool get_quotes) const amount * gmp_amount::street(std::time_t * when,
bool use_history, bool download) const
{ {
static std::time_t now = std::time(NULL); static std::time_t now = std::time(NULL);
if (! when) if (! when)
@ -228,7 +230,7 @@ amount * gmp_amount::street(std::time_t * when, bool get_quotes) const
int max = 10; int max = 10;
while (--max >= 0) { while (--max >= 0) {
amount * price = amt->commdty()->price(when, get_quotes); amount * price = amt->commdty()->price(when, use_history, download);
if (! price) if (! price)
break; break;

View file

@ -29,9 +29,10 @@ void commodity::set_price(amount * price, std::time_t * when)
conversion = price; conversion = price;
} }
amount * commodity::price(std::time_t * when, bool download) const amount * commodity::price(std::time_t * when,
bool use_history, bool download) const
{ {
if (conversion || ! when) if (conversion || ! when || ! use_history)
return conversion; return conversion;
std::time_t age; std::time_t age;
@ -61,17 +62,6 @@ amount * commodity::price(std::time_t * when, bool download) const
char buf[256]; char buf[256];
buf[0] = '\0'; buf[0] = '\0';
cout << "Consulting the Internet for " << symbol << endl;
strftime(buf, 127, "%Y/%m/%d %H:%M:%S", localtime(when));
cout << " when: " << buf << endl;
if (price) {
strftime(buf, 127, "%Y/%m/%d %H:%M:%S", localtime(&age));
cout << " age: " << buf << endl;
cout << " diff (when, age): " << difftime(*when, age) << endl;
} else {
cout << " diff (now, when): " << difftime(now, *when) << endl;
}
if (FILE * fp = popen((string("getquote ") + symbol).c_str(), "r")) { if (FILE * fp = popen((string("getquote ") + symbol).c_str(), "r")) {
if (feof(fp) || ! fgets(buf, 255, fp)) { if (feof(fp) || ! fgets(buf, 255, fp)) {
fclose(fp); fclose(fp);

View file

@ -64,7 +64,8 @@ class commodity
~commodity(); ~commodity();
void set_price(amount * price, std::time_t * when = NULL); void set_price(amount * price, std::time_t * when = NULL);
amount * price(std::time_t * when = NULL, bool download = false) const; amount * price(std::time_t * when = NULL,
bool use_history = false, bool download = false) const;
}; };
typedef std::map<const std::string, commodity *> commodities_map; typedef std::map<const std::string, commodity *> commodities_map;
@ -84,7 +85,8 @@ class amount
virtual amount * value(const amount * pr = NULL) const = 0; virtual amount * value(const amount * pr = NULL) const = 0;
virtual void set_value(const amount * pr) = 0; virtual void set_value(const amount * pr) = 0;
virtual amount * street(std::time_t * when = NULL, virtual amount * street(std::time_t * when = NULL,
bool get_quotes = false) const = 0; bool use_history = false,
bool download = false) const = 0;
virtual bool has_price() const = 0; virtual bool has_price() const = 0;
virtual amount * per_item_price() const = 0; virtual amount * per_item_price() const = 0;

View file

@ -274,7 +274,9 @@ void report_balances(std::ostream& out, regexps_list& regexps)
} }
if (acct->checked == 1) { if (acct->checked == 1) {
amount * street = (*x)->cost->street(&end_date, get_quotes); amount * street = (*x)->cost->street(have_ending ? &end_date : NULL,
cost_basis || get_quotes,
get_quotes);
if (cost_basis && if (cost_basis &&
street->commdty() == (*x)->cost->commdty() && street->commdty() == (*x)->cost->commdty() &&
(*x)->cost->has_price()) { (*x)->cost->has_price()) {
@ -362,7 +364,8 @@ 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, get_quotes); amount * street = xact->cost->street(&ent->date, cost_basis || get_quotes,
get_quotes);
balance.credit(street); 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
@ -411,7 +414,8 @@ void print_register_transaction(std::ostream& out, entry *ent,
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, get_quotes); street = (*y)->cost->street(&ent->date, cost_basis || get_quotes,
get_quotes);
out << std::right << street->as_str(true) << std::endl; out << std::right << street->as_str(true) << std::endl;
delete street; delete street;
} }
@ -492,7 +496,9 @@ 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, get_quotes); amount * street = (*x)->cost->street(&(*i)->date,
cost_basis || get_quotes,
get_quotes);
balance.credit(street); balance.credit(street);
if (period_sum) { if (period_sum) {
@ -544,12 +550,16 @@ 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(&end_date, get_quotes); xact->cost = (*i).second->street(have_ending ? &end_date : NULL,
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(&end_date, get_quotes); xact->cost = (*i).second->street(have_ending ? &end_date : NULL,
cost_basis || get_quotes,
get_quotes);
xact->cost->negate(); xact->cost->negate();
opening.xacts.push_back(xact); opening.xacts.push_back(xact);
} }
@ -577,8 +587,39 @@ void equity_ledger(std::ostream& out, regexps_list& regexps)
equity_entry((*i).second, regexps, out); equity_entry((*i).second, regexps, out);
} }
//////////////////////////////////////////////////////////////////////
//
// Report on the price of any commodities matching REGEXPS. This can
// be used to see what something was worth at a specific time.
//
void price_report(std::ostream& out, regexps_list& regexps)
{
if (! have_ending) {
end_date = std::time(NULL);
have_ending = true;
}
for (commodities_map_iterator i = main_ledger->commodities.begin();
i != main_ledger->commodities.end();
i++)
if (regexps.empty() || matches(regexps, (*i).first)) {
amount * price = (*i).second->price(have_ending ? &end_date : NULL,
cost_basis || get_quotes,
get_quotes);
if (price && ! price->is_zero()) {
out.width(20);
out << std::right << price->as_str() << " " << (*i).first
<< std::endl;
}
}
}
//////////////////////////////////////////////////////////////////////
//
// Add a new entry, using hueristic logic to simplify the entry // Add a new entry, using hueristic logic to simplify the entry
// requirements // requirements
//
void add_new_entry(int index, int argc, char **argv) void add_new_entry(int index, int argc, char **argv)
{ {
@ -789,6 +830,7 @@ 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;
@ -798,7 +840,7 @@ int main(int argc, char * argv[])
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:vsSEnFMGl:N:"))) { "+b:e:d:cCUhBRV:f:i:p:PL:Q:TvsSEnFMGl:N:"))) {
switch (char(c)) { switch (char(c)) {
case 'b': case 'b':
have_beginning = true; have_beginning = true;
@ -872,8 +914,10 @@ int main(int argc, char * argv[])
case 'B': case 'B':
cost_basis = true; cost_basis = true;
// fall through...
case 'T':
no_history = true;
get_quotes = false; get_quotes = false;
price_db = "";
break; break;
case 'l': case 'l':
@ -925,7 +969,7 @@ int main(int argc, char * argv[])
// If a price history file is specified with the environment // If a price history file is specified with the environment
// variable PRICE_HIST, add it to the list of ledger files to read. // variable PRICE_HIST, add it to the list of ledger files to read.
if (! cost_basis) { if (! no_history) {
if (price_db.empty()) if (price_db.empty())
if (char * p = std::getenv("PRICE_HIST")) { if (char * p = std::getenv("PRICE_HIST")) {
get_quotes = true; get_quotes = true;
@ -962,7 +1006,7 @@ int main(int argc, char * argv[])
} }
} }
if (! price_db.empty()) if (! no_history && ! price_db.empty())
entry_count += parse_ledger_file(main_ledger, price_db, entry_count += parse_ledger_file(main_ledger, price_db,
regexps, command == "equity"); regexps, command == "equity");
@ -991,6 +1035,9 @@ int main(int argc, char * argv[])
else if (command == "equity") { else if (command == "equity") {
equity_ledger(std::cout, regexps); equity_ledger(std::cout, regexps);
} }
else if (command == "price" || command == "prices") {
price_report(std::cout, regexps);
}
else if (command == "entry") { else if (command == "entry") {
add_new_entry(index, argc, argv); add_new_entry(index, argc, argv);
} }