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 ::
When printing accounts containing commodities, display the base
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 ::
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 * 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 {
return priced;
}
virtual void set_value(const amount * val);
virtual amount * per_item_price() const;
virtual bool is_zero() const;
virtual bool is_negative() const;
@ -158,6 +161,19 @@ amount * gmp_amount::copy() const
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
{
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);
if (! when)
when = &now;
amount * amt = copy();
@ -210,7 +228,7 @@ amount * gmp_amount::street(bool get_quotes) const
int max = 10;
while (--max >= 0) {
amount * price = amt->commdty()->price(&now, get_quotes);
amount * price = amt->commdty()->price(when, get_quotes);
if (! price)
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
{
if (conversion)
if (conversion || ! when)
return conversion;
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();
i != history.rend();
i++) {
if (*when >= (*i).first) {
if (std::difftime(*when, (*i).first) >= 0) {
age = (*i).first;
price = (*i).second;
break;
@ -48,9 +48,11 @@ amount * commodity::price(std::time_t * when, bool download) const
}
extern long pricing_leeway;
time_t now = time(NULL); // the time of the query
if (download && ! sought &&
(! price || (*when - age) > pricing_leeway)) {
std::difftime(now, *when) < pricing_leeway &&
(! price || std::difftime(*when, age) > pricing_leeway)) {
using namespace std;
// 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];
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 (feof(fp) || ! fgets(buf, 255, fp)) {
fclose(fp);
@ -73,12 +85,12 @@ amount * commodity::price(std::time_t * when, bool download) const
if (p) *p = '\0';
price = create_amount(buf);
const_cast<commodity *>(this)->set_price(price, when);
const_cast<commodity *>(this)->set_price(price, &now);
extern string price_db;
if (! price_db.empty()) {
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);
database << "P " << buf << " " << symbol << " "
<< price->as_str() << endl;
@ -224,34 +236,30 @@ bool entry::finalize(bool do_compute)
delete value;
}
// If one transaction is of a different commodity than the others,
// and it has no per-unit price, determine its price by dividing
// the unit count into the value of the balance.
//
// 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 one transaction of a two-line transaction is of a different
// commodity than the others, and it has no per-unit price,
// determine its price by dividing the unit count into the value of
// the balance. This is done for the last eligible commodity.
if (! balance.amounts.empty() && balance.amounts.size() == 2) {
for (std::list<transaction *>::iterator x = xacts.begin();
x != xacts.end();
x++) {
if ((*x)->is_virtual)
if ((*x)->is_virtual || (*x)->cost->has_price())
continue;
if (! (*x)->cost->has_price() &&
! (*x)->cost->commdty()->prefix &&
(*x)->cost->commdty()->separate) {
for (totals::iterator i = balance.amounts.begin();
i != balance.amounts.end();
i++) {
if ((*i).second->commdty() != (*x)->cost->commdty()) {
(*x)->cost->set_value((*i).second);
break;
}
for (totals::iterator i = balance.amounts.begin();
i != balance.amounts.end();
i++)
if ((*i).second->commdty() != (*x)->cost->commdty()) {
(*x)->cost->set_value((*i).second);
assert((*x)->cost->has_price());
(*x)->cost->commdty()->set_price((*x)->cost->per_item_price(),
&date);
break;
}
break;
}
break;
}
}

View file

@ -82,10 +82,12 @@ class amount
virtual amount * copy() 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 void set_value(const amount * pr) = 0;
virtual amount * per_item_price() const = 0;
// Comparison

View file

@ -420,7 +420,7 @@ int parse_ledger(book * ledger, std::istream& in,
case 'P': { // a pricing entry
in >> c;
time_t date;
std::time_t date;
std::string symbol;
in >> line; // the date
@ -429,6 +429,16 @@ int parse_ledger(book * ledger, std::istream& in,
<< ": Failed to parse date: " << line << std::endl;
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 >> line; // the price

View file

@ -274,7 +274,7 @@ void report_balances(std::ostream& out, regexps_list& regexps)
}
if (acct->checked == 1) {
amount * street = (*x)->cost->street(get_quotes);
amount * street = (*x)->cost->street(&end_date, get_quotes);
if (cost_basis &&
street->commdty() == (*x)->cost->commdty() &&
(*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
// specified
amount * street = xact->cost->street(get_quotes);
amount * street = xact->cost->street(&ent->date, get_quotes);
balance.credit(street);
// 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.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;
delete street;
}
@ -492,7 +492,7 @@ void print_register(std::ostream& out, const std::string& acct_name,
if (period == PERIOD_NONE) {
print_register_transaction(out, *i, *x, balance);
} else {
amount * street = (*x)->cost->street(get_quotes);
amount * street = (*x)->cost->street(&(*i)->date, get_quotes);
balance.credit(street);
if (period_sum) {
@ -544,12 +544,12 @@ static void equity_entry(account * acct, regexps_list& regexps,
transaction * xact = new transaction();
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);
xact = new transaction();
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();
opening.xacts.push_back(xact);
}
@ -833,7 +833,6 @@ int main(int argc, char * argv[])
case 'f': files.push_back(optarg); break;
case 'C': cleared_only = true; break;
case 'U': uncleared_only = true; break;
case 'B': cost_basis = true; break;
case 'R': show_virtual = false; break;
case 's': show_children = true; break;
case 'S': show_sorted = true; break;
@ -871,6 +870,12 @@ int main(int argc, char * argv[])
price_db = optarg;
break;
case 'B':
cost_basis = true;
get_quotes = false;
price_db = "";
break;
case 'l':
lower_limit = create_amount(optarg);
break;
@ -920,14 +925,16 @@ int main(int argc, char * argv[])
// If a price history file is specified with the environment
// variable PRICE_HIST, add it to the list of ledger files to read.
if (price_db.empty())
if (char * p = std::getenv("PRICE_HIST")) {
get_quotes = true;
price_db = p;
}
if (! cost_basis) {
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;
if (char * p = std::getenv("PRICE_EXP"))
pricing_leeway = std::atol(p) * 60;
}
// A ledger data file must be specified