summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2004-06-21 04:52:43 -0400
committerjohnw <johnw@newartisans.com>2004-06-21 04:52:43 -0400
commitc892e8c7ad0aaf6c47ff6e9075149a76188ae670 (patch)
treec21e7f6145722e430439b00785ebe63f609823bd
parent39ee2ae3d846b4bffa4e42ba4f3a9bc320ca9270 (diff)
downloadfork-ledger-c892e8c7ad0aaf6c47ff6e9075149a76188ae670.tar.gz
fork-ledger-c892e8c7ad0aaf6c47ff6e9075149a76188ae670.tar.bz2
fork-ledger-c892e8c7ad0aaf6c47ff6e9075149a76188ae670.zip
fixes to price history support
-rw-r--r--README4
-rw-r--r--amount.cc26
-rw-r--r--ledger.cc60
-rw-r--r--ledger.h6
-rw-r--r--parse.cc12
-rw-r--r--reports.cc35
6 files changed, 95 insertions, 48 deletions
diff --git a/README b/README
index 5e464729..72754eb8 100644
--- a/README
+++ b/README
@@ -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.
diff --git a/amount.cc b/amount.cc
index d6914c39..f2e1c871 100644
--- a/amount.cc
+++ b/amount.cc
@@ -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;
diff --git a/ledger.cc b/ledger.cc
index c49b58bd..c3139cab 100644
--- a/ledger.cc
+++ b/ledger.cc
@@ -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;
}
}
diff --git a/ledger.h b/ledger.h
index 077a091b..60e5160c 100644
--- a/ledger.h
+++ b/ledger.h
@@ -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
diff --git a/parse.cc b/parse.cc
index 938983e0..e4850973 100644
--- a/parse.cc
+++ b/parse.cc
@@ -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
diff --git a/reports.cc b/reports.cc
index 7ba0aaf7..aa38cd21 100644
--- a/reports.cc
+++ b/reports.cc
@@ -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