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 ::
|
-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.
|
||||||
|
|
|
||||||
26
amount.cc
26
amount.cc
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
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
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
ledger.h
6
ledger.h
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
12
parse.cc
12
parse.cc
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
35
reports.cc
35
reports.cc
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue