summaryrefslogtreecommitdiff
path: root/src/commodity.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/commodity.cc')
-rw-r--r--src/commodity.cc563
1 files changed, 20 insertions, 543 deletions
diff --git a/src/commodity.cc b/src/commodity.cc
index 81c9b7e6..db7fabd6 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -33,13 +33,11 @@
#include "amount.h"
#include "commodity.h"
+#include "annotate.h"
+#include "pool.h"
namespace ledger {
-optional<path> commodity_t::price_db;
-long commodity_t::download_leeway = 86400;
-bool commodity_t::download_quotes;
-
void commodity_t::base_t::history_t::add_price(commodity_t& source,
const datetime_t& date,
const amount_t& price,
@@ -99,8 +97,9 @@ void commodity_t::base_t::varied_history_t::
hist->add_price(source, date, price, reflexive);
}
-bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date,
- commodity_t& comm)
+bool commodity_t::base_t::varied_history_t::
+ remove_price(const datetime_t& date,
+ commodity_t& comm)
{
DEBUG("commodity.prices.add", "varied_remove_price: " << date << ", " << comm);
@@ -109,104 +108,6 @@ bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date
return false;
}
-optional<price_point_t> commodity_t::parse_commodity_price(char * line)
-{
- char * date_field_ptr = line;
- char * time_field_ptr = next_element(date_field_ptr);
- if (! time_field_ptr) return none;
- string date_field = date_field_ptr;
-
- char * symbol_and_price;
- datetime_t datetime;
-
- if (std::isdigit(time_field_ptr[0])) {
- symbol_and_price = next_element(time_field_ptr);
- if (! symbol_and_price) return none;
- datetime = parse_datetime(date_field + " " + time_field_ptr);
- } else {
- symbol_and_price = time_field_ptr;
- datetime = parse_datetime(date_field);
- }
-
- string symbol;
- parse_symbol(symbol_and_price, symbol);
-
- price_point_t point;
- point.when = datetime;
- point.price.parse(symbol_and_price);
- VERIFY(point.price.valid());
-
- if (commodity_t * commodity =
- amount_t::current_pool->find_or_create(symbol)) {
- commodity->add_price(point.when, point.price, true);
- commodity->add_flags(COMMODITY_KNOWN);
- return point;
- }
-
- return none;
-}
-
-
-optional<price_point_t>
-commodity_t::download_quote(const optional<commodity_t&>& commodity) const
-{
- DEBUG("commodity.download", "downloading quote for symbol " << symbol());
-#if defined(DEBUG_ON)
- if (commodity)
- DEBUG("commodity.download",
- " in terms of commodity " << commodity->symbol());
-#endif
-
- char buf[256];
- buf[0] = '\0';
-
- string getquote_cmd("getquote \"");
- getquote_cmd += symbol();
- getquote_cmd += "\" \"";
- if (commodity)
- getquote_cmd += commodity->symbol();
- getquote_cmd += "\"";
-
- DEBUG("commodity.download", "invoking command: " << getquote_cmd);
-
- bool success = true;
- if (FILE * fp = popen(getquote_cmd.c_str(), "r")) {
- if (std::feof(fp) || ! std::fgets(buf, 255, fp))
- success = false;
- if (pclose(fp) != 0)
- success = false;
- } else {
- success = false;
- }
-
- if (success && buf[0]) {
- char * p = std::strchr(buf, '\n');
- if (p) *p = '\0';
-
- DEBUG("commodity.download", "downloaded quote: " << buf);
-
- optional<price_point_t> point = parse_commodity_price(buf);
-
- if (point) {
- if (price_db) {
-#if defined(__GNUG__) && __GNUG__ < 3
- ofstream database(*price_db, ios::out | ios::app);
-#else
- ofstream database(*price_db, std::ios_base::out | std::ios_base::app);
-#endif
- database << "P " << format_datetime(point->when, string("%Y/%m/%d %H:%M:%S"))
- << " " << symbol() << " " << point->price << std::endl;
- }
- return point;
- }
- } else {
- throw_(std::runtime_error,
- _("Failed to download price for '%1' (command: \"getquote %2\")")
- << symbol() << symbol());
- }
- return none;
-}
-
optional<price_point_t>
commodity_t::base_t::history_t::
find_price(const optional<datetime_t>& moment,
@@ -445,27 +346,27 @@ optional<price_point_t>
" found price " << best.price << " from " << best.when);
DEBUG("commodity.download",
"found price " << best.price << " from " << best.when);
- if (moment)
- DEBUG("commodity.download", "moment = " << *moment);
- DEBUG("commodity.download", "leeway = " << download_leeway);
- if (moment)
- DEBUG("commodity.download",
- "slip.moment = " << (*moment - best.when).total_seconds());
- else
- DEBUG("commodity.download",
- "slip.now = " << (CURRENT_TIME() - best.when).total_seconds());
#endif
- if (download_quotes &&
- ! source.has_flags(COMMODITY_NOMARKET) &&
- ((! moment &&
- (CURRENT_TIME() - best.when).total_seconds() > download_leeway) ||
- (moment &&
- (*moment - best.when).total_seconds() > download_leeway))) {
+#if 0
+ DEBUG("commodity.download", "leeway = " << download_leeway);
+ datetime_t::sec_type seconds_diff;
+ if (moment) {
+ seconds_diff = (*moment - best.when).total_seconds();
+ DEBUG("commodity.download", "moment = " << *moment);
+ DEBUG("commodity.download", "slip.moment = " << seconds_diff);
+ } else {
+ seconds_diff = (CURRENT_TIME() - best.when).total_seconds();
+ DEBUG("commodity.download", "slip.now = " << seconds_diff);
+ }
+
+ if (download_quotes && ! source.has_flags(COMMODITY_NOMARKET) &&
+ seconds_diff > download_leeway) {
DEBUG("commodity.download",
"attempting to download a more current quote...");
if (optional<price_point_t> quote = source.download_quote(commodity))
return quote;
}
+#endif
return best;
}
return none;
@@ -497,82 +398,6 @@ optional<commodity_t::base_t::history_t&>
return none;
}
-void commodity_t::exchange(commodity_t& commodity,
- const amount_t& per_unit_cost,
- const datetime_t& moment)
-{
- DEBUG("commodity.prices.add", "exchanging commodity " << commodity
- << " at per unit cost " << per_unit_cost << " on " << moment);
-
- commodity_t& base_commodity
- (commodity.annotated ?
- as_annotated_commodity(commodity).referent() : commodity);
-
- base_commodity.add_price(moment, per_unit_cost);
-}
-
-commodity_t::cost_breakdown_t
-commodity_t::exchange(const amount_t& amount,
- const amount_t& cost,
- const bool is_per_unit,
- const optional<datetime_t>& moment,
- const optional<string>& tag)
-{
- DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost);
- DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit);
-#if defined(DEBUG_ON)
- if (moment)
- DEBUG("commodity.prices.add", "exchange: moment = " << *moment);
- if (tag)
- DEBUG("commodity.prices.add", "exchange: tag = " << *tag);
-#endif
-
- commodity_t& commodity(amount.commodity());
-
- annotation_t * current_annotation = NULL;
- if (commodity.annotated)
- current_annotation = &as_annotated_commodity(commodity).details;
-
- amount_t per_unit_cost =
- (is_per_unit || amount.is_realzero() ? cost : cost / amount).abs();
-
- DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
-
- if (! per_unit_cost.is_realzero())
- exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
-
- cost_breakdown_t breakdown;
- breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
-
- DEBUG("commodity.prices.add",
- "exchange: final-cost = " << breakdown.final_cost);
-
- if (current_annotation && current_annotation->price)
- breakdown.basis_cost
- = (*current_annotation->price * amount).unrounded();
- else
- breakdown.basis_cost = breakdown.final_cost;
-
- DEBUG("commodity.prices.add",
- "exchange: basis-cost = " << breakdown.basis_cost);
-
- annotation_t annotation(per_unit_cost, moment ?
- moment->date() : optional<date_t>(), tag);
-
- annotation.add_flags(ANNOTATION_PRICE_CALCULATED);
- if (moment)
- annotation.add_flags(ANNOTATION_DATE_CALCULATED);
- if (tag)
- annotation.add_flags(ANNOTATION_TAG_CALCULATED);
-
- breakdown.amount = amount_t(amount, annotation);
-
- DEBUG("commodity.prices.add",
- "exchange: amount = " << breakdown.amount);
-
- return breakdown;
-}
-
commodity_t::operator bool() const
{
return this != parent().null_commodity;
@@ -752,159 +577,6 @@ bool commodity_t::valid() const
return true;
}
-void annotation_t::parse(std::istream& in)
-{
- do {
- istream_pos_type pos = in.tellg();
-
- char buf[256];
- char c = peek_next_nonws(in);
- if (c == '{') {
- if (price)
- throw_(amount_error, _("Commodity specifies more than one price"));
-
- in.get(c);
- c = peek_next_nonws(in);
- if (c == '=') {
- in.get(c);
- add_flags(ANNOTATION_PRICE_FIXATED);
- }
-
- READ_INTO(in, buf, 255, c, c != '}');
- if (c == '}')
- in.get(c);
- else
- throw_(amount_error, _("Commodity price lacks closing brace"));
-
- amount_t temp;
- temp.parse(buf, amount_t::PARSE_NO_MIGRATE);
-
- DEBUG("commodity.annotations", "Parsed annotation price: " << temp);
-
- // Since this price will maintain its own precision, make sure
- // it is at least as large as the base commodity, since the user
- // may have only specified {$1} or something similar.
-
- if (temp.has_commodity() &&
- temp.precision() > temp.commodity().precision())
- temp = temp.rounded(); // no need to retain individual precision
-
- price = temp;
- }
- else if (c == '[') {
- if (date)
- throw_(amount_error, _("Commodity specifies more than one date"));
-
- in.get(c);
- READ_INTO(in, buf, 255, c, c != ']');
- if (c == ']')
- in.get(c);
- else
- throw_(amount_error, _("Commodity date lacks closing bracket"));
-
- date = parse_date(buf);
- }
- else if (c == '(') {
- if (tag)
- throw_(amount_error, _("Commodity specifies more than one tag"));
-
- in.get(c);
- READ_INTO(in, buf, 255, c, c != ')');
- if (c == ')')
- in.get(c);
- else
- throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
-
- tag = buf;
- }
- else {
- in.clear();
- in.seekg(pos, std::ios::beg);
- break;
- }
- } while (true);
-
-#if defined(DEBUG_ON)
- if (SHOW_DEBUG("amounts.commodities") && *this) {
- DEBUG("amounts.commodities",
- "Parsed commodity annotations: " << std::endl << *this);
- }
-#endif
-}
-
-bool annotated_commodity_t::operator==(const commodity_t& comm) const
-{
- // If the base commodities don't match, the game's up.
- if (base != comm.base)
- return false;
-
- assert(annotated);
- if (! comm.annotated)
- return false;
-
- if (details != as_annotated_commodity(comm).details)
- return false;
-
- return true;
-}
-
-commodity_t&
-annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
-{
- DEBUG("commodity.annotated.strip",
- "Reducing commodity " << *this << std::endl
- << " keep price " << what_to_keep.keep_price << " "
- << " keep date " << what_to_keep.keep_date << " "
- << " keep tag " << what_to_keep.keep_tag);
-
- commodity_t * new_comm;
-
- bool keep_price = (what_to_keep.keep_price &&
- (! what_to_keep.only_actuals ||
- ! details.has_flags(ANNOTATION_PRICE_CALCULATED)));
- bool keep_date = (what_to_keep.keep_date &&
- (! what_to_keep.only_actuals ||
- ! details.has_flags(ANNOTATION_DATE_CALCULATED)));
- bool keep_tag = (what_to_keep.keep_tag &&
- (! what_to_keep.only_actuals ||
- ! details.has_flags(ANNOTATION_TAG_CALCULATED)));
-
- if ((keep_price && details.price) ||
- (keep_date && details.date) ||
- (keep_tag && details.tag))
- {
- new_comm = parent().find_or_create
- (referent(), annotation_t(keep_price ? details.price : none,
- keep_date ? details.date : none,
- keep_tag ? details.tag : none));
- } else {
- new_comm = parent().find_or_create(base_symbol());
- }
-
- assert(new_comm);
- return *new_comm;
-}
-
-void annotated_commodity_t::write_annotations(std::ostream& out) const
-{
- details.print(out, parent().keep_base);
-}
-
-void annotation_t::print(std::ostream& out, bool keep_base) const
-{
- if (price)
- out << " {"
- << (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "")
- << (keep_base ? *price : price->unreduced()).rounded()
- << '}';
-
- if (date)
- out << " [" << format_date(*date, string("%Y/%m/%d")) << ']';
-
- if (tag)
- out << " (" << *tag << ')';
-}
-
bool compare_amount_commodities::operator()(const amount_t * left,
const amount_t * right) const
{
@@ -972,199 +644,4 @@ bool compare_amount_commodities::operator()(const amount_t * left,
}
}
-commodity_pool_t::commodity_pool_t()
- : default_commodity(NULL), keep_base(false)
-{
- TRACE_CTOR(commodity_pool_t, "");
- null_commodity = create("");
- null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
-}
-
-commodity_t * commodity_pool_t::create(const string& symbol)
-{
- shared_ptr<commodity_t::base_t>
- base_commodity(new commodity_t::base_t(symbol));
- std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
-
- DEBUG("amounts.commodities", "Creating base commodity " << symbol);
-
- // Create the "qualified symbol" version of this commodity's symbol
- if (commodity_t::symbol_needs_quotes(symbol)) {
- commodity->qualified_symbol = "\"";
- *commodity->qualified_symbol += symbol;
- *commodity->qualified_symbol += "\"";
- }
-
- DEBUG("amounts.commodities",
- "Creating commodity '" << commodity->symbol() << "'");
-
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_map::value_type(commodity->mapping_key(),
- commodity.get()));
- assert(result.second);
-
- return commodity.release();
-}
-
-commodity_t * commodity_pool_t::find_or_create(const string& symbol)
-{
- DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
-
- commodity_t * commodity = find(symbol);
- if (commodity)
- return commodity;
- return create(symbol);
-}
-
-commodity_t * commodity_pool_t::find(const string& symbol)
-{
- DEBUG("amounts.commodities", "Find commodity " << symbol);
-
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
- return NULL;
-}
-
-commodity_t *
-commodity_pool_t::create(const string& symbol, const annotation_t& details)
-{
- commodity_t * new_comm = create(symbol);
- if (! new_comm)
- return NULL;
-
- if (details)
- return find_or_create(*new_comm, details);
- else
- return new_comm;
-}
-
-namespace {
- string make_qualified_name(const commodity_t& comm,
- const annotation_t& details)
- {
- assert(details);
-
- if (details.price && details.price->sign() < 0)
- throw_(amount_error, _("A commodity's price may not be negative"));
-
- std::ostringstream name;
- comm.print(name);
- details.print(name, comm.parent().keep_base);
-
- DEBUG("amounts.commodities", "make_qualified_name for "
- << *comm.qualified_symbol << std::endl << details);
- DEBUG("amounts.commodities", "qualified_name is " << name.str());
-
- return name.str();
- }
-}
-
-commodity_t *
-commodity_pool_t::find(const string& symbol, const annotation_t& details)
-{
- commodity_t * comm = find(symbol);
- if (! comm)
- return NULL;
-
- if (details) {
- string name = make_qualified_name(*comm, details);
-
- if (commodity_t * ann_comm = find(name)) {
- assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
- return ann_comm;
- }
- return NULL;
- } else {
- return comm;
- }
-}
-
-commodity_t *
-commodity_pool_t::find_or_create(const string& symbol,
- const annotation_t& details)
-{
- commodity_t * comm = find(symbol);
- if (! comm)
- return NULL;
-
- if (details)
- return find_or_create(*comm, details);
- else
- return comm;
-}
-
-commodity_t *
-commodity_pool_t::create(commodity_t& comm,
- const annotation_t& details,
- const string& mapping_key)
-{
- assert(comm);
- assert(details);
- assert(! mapping_key.empty());
-
- std::auto_ptr<commodity_t> commodity
- (new annotated_commodity_t(&comm, details));
-
- commodity->qualified_symbol = comm.symbol();
- assert(! commodity->qualified_symbol->empty());
-
- DEBUG("amounts.commodities", "Creating annotated commodity "
- << "symbol " << commodity->symbol()
- << " key " << mapping_key << std::endl << details);
-
- // Add the fully annotated name to the map, so that this symbol may
- // quickly be found again.
- commodity->mapping_key_ = mapping_key;
-
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_map::value_type(mapping_key,
- commodity.get()));
- assert(result.second);
-
- return commodity.release();
-}
-
-commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
- const annotation_t& details)
-{
- assert(comm);
- assert(details);
-
- string name = make_qualified_name(comm, details);
- assert(! name.empty());
-
- if (commodity_t * ann_comm = find(name)) {
- assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
- return ann_comm;
- }
- return create(comm, details, name);
-}
-
-commodity_t *
-commodity_pool_t::parse_commodity_prices(const std::string& str,
- const bool add_prices,
- const optional<datetime_t>& moment)
-{
- scoped_array<char> buf(new char[str.length() + 1]);
-
- std::strcpy(buf.get(), str.c_str());
-
- char * price = std::strchr(buf.get(), '=');
- if (price)
- *price++ = '\0';
-
- if (commodity_t * commodity = find_or_create(trim_ws(buf.get()))) {
- if (price && add_prices) {
- for (char * p = std::strtok(price, ";");
- p;
- p = std::strtok(NULL, ";")) {
- commodity->add_price(moment ? *moment : CURRENT_TIME(), amount_t(p));
- }
- }
- return commodity;
- }
- return NULL;
-}
-
} // namespace ledger