diff options
Diffstat (limited to 'src/commodity.cc')
-rw-r--r-- | src/commodity.cc | 467 |
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 |