fixes to price history support

This commit is contained in:
John Wiegley 2004-06-21 04:52:43 -04:00 committed by johnw
parent 39ee2ae3d8
commit c892e8c7ad
6 changed files with 95 additions and 48 deletions

4
README
View file

@ -1040,7 +1040,9 @@ launches =vi= to let you confirm that the entry looks appropriate.
-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 is used). (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
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.

View file

@ -56,11 +56,14 @@ class gmp_amount : public amount
virtual amount * copy() const; virtual amount * copy() const;
virtual amount * value(const amount *) const; virtual amount * value(const amount *) const;
virtual amount * street(bool get_quotes) const; virtual void set_value(const amount * val);
virtual amount * street(std::time_t * when = NULL,
bool get_quotes = false) const;
virtual bool has_price() const { virtual bool has_price() const {
return priced; return priced;
} }
virtual void set_value(const amount * val); virtual amount * per_item_price() const;
virtual bool is_zero() const; virtual bool is_zero() const;
virtual bool is_negative() const; virtual bool is_negative() const;
@ -158,6 +161,19 @@ amount * gmp_amount::copy() const
return new_amt; return new_amt;
} }
amount * gmp_amount::per_item_price() const
{
if (! priced)
return NULL;
gmp_amount * new_amt = new gmp_amount();
mpz_set(new_amt->quantity, price);
new_amt->quantity_comm = price_comm;
return new_amt;
}
amount * gmp_amount::value(const amount * pr) const amount * gmp_amount::value(const amount * pr) const
{ {
if (pr) { if (pr) {
@ -199,9 +215,11 @@ amount * gmp_amount::value(const amount * pr) const
} }
} }
amount * gmp_amount::street(bool get_quotes) const amount * gmp_amount::street(std::time_t * when, bool get_quotes) const
{ {
static std::time_t now = std::time(NULL); static std::time_t now = std::time(NULL);
if (! when)
when = &now;
amount * amt = copy(); amount * amt = copy();
@ -210,7 +228,7 @@ amount * gmp_amount::street(bool get_quotes) const
int max = 10; int max = 10;
while (--max >= 0) { while (--max >= 0) {
amount * price = amt->commdty()->price(&now, get_quotes); amount * price = amt->commdty()->price(when, get_quotes);
if (! price) if (! price)
break; break;

View file

@ -31,7 +31,7 @@ void commodity::set_price(amount * price, std::time_t * when)
amount * commodity::price(std::time_t * when, bool download) const amount * commodity::price(std::time_t * when, bool download) const
{ {
if (conversion) if (conversion || ! when)
return conversion; return conversion;
std::time_t age; std::time_t age;
@ -40,7 +40,7 @@ amount * commodity::price(std::time_t * when, bool download) const
for (price_map::reverse_iterator i = history.rbegin(); for (price_map::reverse_iterator i = history.rbegin();
i != history.rend(); i != history.rend();
i++) { i++) {
if (*when >= (*i).first) { if (std::difftime(*when, (*i).first) >= 0) {
age = (*i).first; age = (*i).first;
price = (*i).second; price = (*i).second;
break; break;
@ -48,9 +48,11 @@ amount * commodity::price(std::time_t * when, bool download) const
} }
extern long pricing_leeway; extern long pricing_leeway;
time_t now = time(NULL); // the time of the query
if (download && ! sought && if (download && ! sought &&
(! price || (*when - age) > pricing_leeway)) { std::difftime(now, *when) < pricing_leeway &&
(! price || std::difftime(*when, age) > pricing_leeway)) {
using namespace std; using namespace std;
// Only consult the Internet once for any commodity // Only consult the Internet once for any commodity
@ -59,7 +61,17 @@ amount * commodity::price(std::time_t * when, bool download) const
char buf[256]; char buf[256];
buf[0] = '\0'; buf[0] = '\0';
std::cout << "Consulting the Internet: " << symbol << std::endl; 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);
@ -73,12 +85,12 @@ amount * commodity::price(std::time_t * when, bool download) const
if (p) *p = '\0'; if (p) *p = '\0';
price = create_amount(buf); price = create_amount(buf);
const_cast<commodity *>(this)->set_price(price, when); const_cast<commodity *>(this)->set_price(price, &now);
extern string price_db; extern string price_db;
if (! price_db.empty()) { if (! price_db.empty()) {
char buf[128]; char buf[128];
strftime(buf, 127, "%Y/%m/%d", localtime(when)); strftime(buf, 127, "%Y/%m/%d %H:%M:%S", localtime(&now));
ofstream database(price_db.c_str(), ios_base::out | ios_base::app); ofstream database(price_db.c_str(), ios_base::out | ios_base::app);
database << "P " << buf << " " << symbol << " " database << "P " << buf << " " << symbol << " "
<< price->as_str() << endl; << price->as_str() << endl;
@ -224,34 +236,30 @@ bool entry::finalize(bool do_compute)
delete value; delete value;
} }
// If one transaction is of a different commodity than the others, // If one transaction of a two-line transaction is of a different
// and it has no per-unit price, determine its price by dividing // commodity than the others, and it has no per-unit price,
// the unit count into the value of the balance. // determine its price by dividing the unit count into the value of
// // the balance. This is done for the last eligible commodity.
// NOTE: We don't do this for prefix-style or joined-symbol
// commodities. Also, do it for the last eligible commodity first,
// if it meets the criteria.
if (! balance.amounts.empty() && balance.amounts.size() == 2) { if (! balance.amounts.empty() && balance.amounts.size() == 2) {
for (std::list<transaction *>::iterator x = xacts.begin(); for (std::list<transaction *>::iterator x = xacts.begin();
x != xacts.end(); x != xacts.end();
x++) { x++) {
if ((*x)->is_virtual) if ((*x)->is_virtual || (*x)->cost->has_price())
continue; continue;
if (! (*x)->cost->has_price() && for (totals::iterator i = balance.amounts.begin();
! (*x)->cost->commdty()->prefix && i != balance.amounts.end();
(*x)->cost->commdty()->separate) { i++)
for (totals::iterator i = balance.amounts.begin(); if ((*i).second->commdty() != (*x)->cost->commdty()) {
i != balance.amounts.end(); (*x)->cost->set_value((*i).second);
i++) { assert((*x)->cost->has_price());
if ((*i).second->commdty() != (*x)->cost->commdty()) { (*x)->cost->commdty()->set_price((*x)->cost->per_item_price(),
(*x)->cost->set_value((*i).second); &date);
break; break;
}
} }
break;
} break;
} }
} }

View file

@ -82,10 +82,12 @@ class amount
virtual amount * copy() const = 0; virtual amount * copy() const = 0;
virtual amount * value(const amount * pr = NULL) const = 0; virtual amount * value(const amount * pr = NULL) const = 0;
virtual amount * street(bool get_quotes) const = 0; virtual void set_value(const amount * pr) = 0;
virtual amount * street(std::time_t * when = NULL,
bool get_quotes = false) const = 0;
virtual bool has_price() const = 0; virtual bool has_price() const = 0;
virtual void set_value(const amount * pr) = 0; virtual amount * per_item_price() const = 0;
// Comparison // Comparison

View file

@ -420,7 +420,7 @@ int parse_ledger(book * ledger, std::istream& in,
case 'P': { // a pricing entry case 'P': { // a pricing entry
in >> c; in >> c;
time_t date; std::time_t date;
std::string symbol; std::string symbol;
in >> line; // the date in >> line; // the date
@ -429,6 +429,16 @@ int parse_ledger(book * ledger, std::istream& in,
<< ": Failed to parse date: " << line << std::endl; << ": Failed to parse date: " << line << std::endl;
break; break;
} }
int hour, min, sec;
in >> hour; // the time
in >> c;
in >> min;
in >> c;
in >> sec;
date = std::time_t(((unsigned long) date) +
hour * 3600 + min * 60 + sec);
in >> symbol; // the commodity in >> symbol; // the commodity
in >> line; // the price in >> line; // the price

View file

@ -274,7 +274,7 @@ void report_balances(std::ostream& out, regexps_list& regexps)
} }
if (acct->checked == 1) { if (acct->checked == 1) {
amount * street = (*x)->cost->street(get_quotes); amount * street = (*x)->cost->street(&end_date, 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 +362,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(get_quotes); amount * street = xact->cost->street(&ent->date, 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 +411,7 @@ 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(get_quotes); street = (*y)->cost->street(&ent->date, 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 +492,7 @@ 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(get_quotes); amount * street = (*x)->cost->street(&(*i)->date, get_quotes);
balance.credit(street); balance.credit(street);
if (period_sum) { if (period_sum) {
@ -544,12 +544,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(get_quotes); xact->cost = (*i).second->street(&end_date, 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(get_quotes); xact->cost = (*i).second->street(&end_date, get_quotes);
xact->cost->negate(); xact->cost->negate();
opening.xacts.push_back(xact); opening.xacts.push_back(xact);
} }
@ -833,7 +833,6 @@ int main(int argc, char * argv[])
case 'f': files.push_back(optarg); break; case 'f': files.push_back(optarg); break;
case 'C': cleared_only = true; break; case 'C': cleared_only = true; break;
case 'U': uncleared_only = true; break; case 'U': uncleared_only = true; break;
case 'B': cost_basis = true; break;
case 'R': show_virtual = false; break; case 'R': show_virtual = false; break;
case 's': show_children = true; break; case 's': show_children = true; break;
case 'S': show_sorted = true; break; case 'S': show_sorted = true; break;
@ -871,6 +870,12 @@ int main(int argc, char * argv[])
price_db = optarg; price_db = optarg;
break; break;
case 'B':
cost_basis = true;
get_quotes = false;
price_db = "";
break;
case 'l': case 'l':
lower_limit = create_amount(optarg); lower_limit = create_amount(optarg);
break; break;
@ -920,14 +925,16 @@ 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 (price_db.empty()) if (! cost_basis) {
if (char * p = std::getenv("PRICE_HIST")) { if (price_db.empty())
get_quotes = true; if (char * p = std::getenv("PRICE_HIST")) {
price_db = p; get_quotes = true;
} price_db = p;
}
if (char * p = std::getenv("PRICE_EXP")) if (char * p = std::getenv("PRICE_EXP"))
pricing_leeway = std::atol(p) * 60; pricing_leeway = std::atol(p) * 60;
}
// A ledger data file must be specified // A ledger data file must be specified