fixes to price history support
This commit is contained in:
parent
39ee2ae3d8
commit
c892e8c7ad
6 changed files with 95 additions and 48 deletions
4
README
4
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.
|
||||
|
|
|
|||
26
amount.cc
26
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;
|
||||
|
||||
|
|
|
|||
60
ledger.cc
60
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
6
ledger.h
6
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
|
||||
|
||||
|
|
|
|||
12
parse.cc
12
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
|
||||
|
||||
|
|
|
|||
35
reports.cc
35
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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue