summaryrefslogtreecommitdiff
path: root/src/commodity.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/commodity.cc')
-rw-r--r--src/commodity.cc467
1 files changed, 43 insertions, 424 deletions
diff --git a/src/commodity.cc b/src/commodity.cc
index 67a86b87..900fe07d 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -33,6 +33,8 @@
#include "amount.h"
#include "commodity.h"
+#include "annotate.h"
+#include "pool.h"
namespace ledger {
@@ -95,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);
@@ -107,8 +110,8 @@ bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date
optional<price_point_t>
commodity_t::base_t::history_t::
- find_price(const optional<datetime_t>& moment,
- const optional<datetime_t>& oldest
+ find_price(const optional<datetime_t>& moment,
+ const optional<datetime_t>& oldest
#if defined(DEBUG_ON)
, const int indent
#endif
@@ -185,16 +188,6 @@ optional<price_point_t>
}
}
-#if 0
- if (! has_flags(COMMODITY_NOMARKET) && parent().get_quote) {
- if (optional<amount_t> quote = parent().get_quote
- (*this, age, moment,
- (hist && hist->prices.size() > 0 ?
- (*hist->prices.rbegin()).first : optional<datetime_t>())))
- return *quote;
- }
-#endif
-
if (! found) {
#if defined(DEBUG_ON)
DEBUG_INDENT("commodity.prices.find", indent);
@@ -351,6 +344,8 @@ optional<price_point_t>
DEBUG_INDENT("commodity.prices.find", indent);
DEBUG("commodity.prices.find",
" found price " << best.price << " from " << best.when);
+ DEBUG("commodity.download",
+ "found price " << best.price << " from " << best.when);
#endif
return best;
}
@@ -368,7 +363,8 @@ optional<commodity_t::base_t::history_t&>
#if 0
// jww (2008-09-20): Document which option switch to use here
throw_(commodity_error,
- _("Cannot determine price history: prices known for multiple commodities (use -x)"));
+ _("Cannot determine price history: "
+ "prices known for multiple commodities (use -x)"));
#endif
comm = (*histories.begin()).first;
} else {
@@ -382,78 +378,40 @@ 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)
+optional<price_point_t>
+commodity_t::check_for_updated_price(const optional<price_point_t>& point,
+ const optional<datetime_t>& moment,
+ const optional<commodity_t&>& in_terms_of)
{
- 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 ? cost : cost / amount).abs();
-
- DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
-
- exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
-
- cost_breakdown_t breakdown;
- breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
+ if (parent().get_quotes && ! has_flags(COMMODITY_NOMARKET)) {
+ bool exceeds_leeway = true;
- 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);
+ if (point) {
+ time_duration_t::sec_type seconds_diff;
+ if (moment) {
+ seconds_diff = (*moment - point->when).total_seconds();
+ DEBUG("commodity.download", "moment = " << *moment);
+ DEBUG("commodity.download", "slip.moment = " << seconds_diff);
+ } else {
+ seconds_diff = (CURRENT_TIME() - point->when).total_seconds();
+ DEBUG("commodity.download", "slip.now = " << seconds_diff);
+ }
- DEBUG("commodity.prices.add",
- "exchange: amount = " << breakdown.amount);
+ DEBUG("commodity.download", "leeway = " << parent().quote_leeway);
+ if (seconds_diff < parent().quote_leeway)
+ exceeds_leeway = false;
+ }
- return breakdown;
+ if (exceeds_leeway) {
+ DEBUG("commodity.download",
+ "attempting to download a more current quote...");
+ if (optional<price_point_t> quote =
+ parent().get_commodity_quote(*this, in_terms_of)) {
+ return quote;
+ }
+ }
+ }
+ return point;
}
commodity_t::operator bool() const
@@ -499,14 +457,14 @@ void commodity_t::parse_symbol(std::istream& in, string& symbol)
{
// Invalid commodity characters:
// SPACE, TAB, NEWLINE, RETURN
- // 0-9 . , ; - + * / ^ ? : & | ! = %
+ // 0-9 . , ; - + * / ^ ? : & | ! =
// < > { } [ ] ( ) @
static int invalid_chars[256] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
/* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* 20 */ 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
/* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
@@ -635,159 +593,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
{
@@ -855,190 +660,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);
-}
-
-void commodity_pool_t::parse_commodity_price(char * optarg)
-{
- char * equals = std::strchr(optarg, '=');
- if (! equals)
- return;
-
- optarg = skip_ws(optarg);
- while (equals > optarg && std::isspace(*(equals - 1)))
- equals--;
-
- std::string symbol(optarg, 0, equals - optarg);
- amount_t price(equals + 1);
-
- if (commodity_t * commodity = find_or_create(symbol))
- commodity->add_price(CURRENT_TIME(), price);
-}
-
} // namespace ledger