Many fixes to --market and --exchange
Also, --exchange now accepted multiple, comma-separated commodities.
This commit is contained in:
parent
ea418c7fbc
commit
de6de07bac
8 changed files with 324 additions and 151 deletions
|
|
@ -515,12 +515,25 @@ amount_t::value(const bool primary_only,
|
|||
const optional<commodity_t&>& in_terms_of) const
|
||||
{
|
||||
if (quantity) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG("commodity.prices.find",
|
||||
"amount_t::value of " << commodity().symbol());
|
||||
if (moment)
|
||||
DEBUG("commodity.prices.find",
|
||||
"amount_t::value: moment = " << *moment);
|
||||
if (in_terms_of)
|
||||
DEBUG("commodity.prices.find",
|
||||
"amount_t::value: in_terms_of = " << in_terms_of->symbol());
|
||||
#endif
|
||||
if (has_commodity() &&
|
||||
(! primary_only || commodity().has_flags(COMMODITY_PRIMARY)) &&
|
||||
(! in_terms_of || commodity() != *in_terms_of)) {
|
||||
optional<price_point_t> point(commodity().find_price(in_terms_of, moment));
|
||||
if (point)
|
||||
(! primary_only || ! commodity().has_flags(COMMODITY_PRIMARY))) {
|
||||
if (in_terms_of && commodity() == *in_terms_of) {
|
||||
return *this;
|
||||
}
|
||||
else if (optional<price_point_t> point =
|
||||
commodity().find_price(in_terms_of, moment)) {
|
||||
return (point->price * number()).rounded();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw_(amount_error, "Cannot determine value of an uninitialized amount");
|
||||
|
|
@ -996,7 +1009,7 @@ void amount_t::print(std::ostream& _out) const
|
|||
|
||||
if (comm.annotated) {
|
||||
annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
|
||||
assert(&*ann.details.price != this);
|
||||
assert(! ann.details.price || &*ann.details.price != this);
|
||||
ann.write_annotations(out);
|
||||
}
|
||||
|
||||
|
|
|
|||
184
src/commodity.cc
184
src/commodity.cc
|
|
@ -38,8 +38,9 @@ void commodity_t::base_t::history_t::add_price(commodity_t& source,
|
|||
const amount_t& price,
|
||||
const bool reflexive)
|
||||
{
|
||||
DEBUG("commodity.prices",
|
||||
"add_price to " << source << " : " << date << ", " << price);
|
||||
DEBUG("commodity.prices.add", "add_price to " << source
|
||||
<< (reflexive ? " (secondary)" : " (primary)")
|
||||
<< " : " << date << ", " << price);
|
||||
|
||||
history_map::iterator i = prices.find(date);
|
||||
if (i != prices.end()) {
|
||||
|
|
@ -51,18 +52,19 @@ void commodity_t::base_t::history_t::add_price(commodity_t& source,
|
|||
}
|
||||
|
||||
if (reflexive) {
|
||||
if (! price.commodity().has_flags(COMMODITY_NOMARKET)) {
|
||||
amount_t inverse = price.inverted();
|
||||
inverse.set_commodity(const_cast<commodity_t&>(source));
|
||||
price.commodity().add_price(date, inverse, false);
|
||||
}
|
||||
amount_t inverse = price.inverted();
|
||||
inverse.set_commodity(const_cast<commodity_t&>(source));
|
||||
price.commodity().add_price(date, inverse, false);
|
||||
} else {
|
||||
DEBUG("commodity.prices.add",
|
||||
"marking commodity " << source.symbol() << " as primary");
|
||||
source.add_flags(COMMODITY_PRIMARY);
|
||||
}
|
||||
}
|
||||
|
||||
bool commodity_t::base_t::history_t::remove_price(const datetime_t& date)
|
||||
{
|
||||
DEBUG("commodity.prices", "remove_price: " << date);
|
||||
DEBUG("commodity.prices.add", "remove_price: " << date);
|
||||
|
||||
history_map::size_type n = prices.erase(date);
|
||||
if (n > 0)
|
||||
|
|
@ -93,7 +95,7 @@ void commodity_t::base_t::varied_history_t::
|
|||
bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date,
|
||||
commodity_t& comm)
|
||||
{
|
||||
DEBUG("commodity.prices", "varied_remove_price: " << date << ", " << comm);
|
||||
DEBUG("commodity.prices.add", "varied_remove_price: " << date << ", " << comm);
|
||||
|
||||
if (optional<history_t&> hist = history(comm))
|
||||
return hist->remove_price(date);
|
||||
|
|
@ -120,22 +122,22 @@ optional<price_point_t>
|
|||
} while (false)
|
||||
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
if (moment)
|
||||
DEBUG("commodity.prices", "find price nearest before or on: " << *moment);
|
||||
DEBUG("commodity.prices.find", "find price nearest before or on: " << *moment);
|
||||
else
|
||||
DEBUG("commodity.prices", "find any price");
|
||||
DEBUG("commodity.prices.find", "find any price");
|
||||
|
||||
if (oldest) {
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " but no older than: " << *oldest);
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " but no older than: " << *oldest);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (prices.size() == 0) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " there are no prices in this history");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " there are no prices in this history");
|
||||
#endif
|
||||
return none;
|
||||
}
|
||||
|
|
@ -146,8 +148,8 @@ optional<price_point_t>
|
|||
point.price = (*r).second;
|
||||
found = true;
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " using most recent price");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " using most recent price");
|
||||
#endif
|
||||
} else {
|
||||
history_map::const_iterator i = prices.lower_bound(*moment);
|
||||
|
|
@ -157,8 +159,8 @@ optional<price_point_t>
|
|||
point.price = (*r).second;
|
||||
found = true;
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " using last price");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " using last price");
|
||||
#endif
|
||||
} else {
|
||||
point.when = (*i).first;
|
||||
|
|
@ -174,8 +176,8 @@ optional<price_point_t>
|
|||
found = true;
|
||||
}
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " using found price");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " using found price");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -192,29 +194,29 @@ optional<price_point_t>
|
|||
|
||||
if (! found) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " could not find a price");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " could not find a price");
|
||||
#endif
|
||||
return none;
|
||||
}
|
||||
else if (moment && point.when > *moment) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " price is too young ");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " price is too young ");
|
||||
#endif
|
||||
return none;
|
||||
}
|
||||
else if (oldest && point.when < *oldest) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " price is too old ");
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " price is too old ");
|
||||
#endif
|
||||
return none;
|
||||
}
|
||||
else {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices",
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find",
|
||||
" returning price: " << point.when << ", " << point.price);
|
||||
#endif
|
||||
return point;
|
||||
|
|
@ -238,23 +240,23 @@ optional<price_point_t>
|
|||
assert(! commodity || source != *commodity);
|
||||
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", "varied_find_price for: " << source);
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", "varied_find_price for: " << source);
|
||||
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
if (commodity)
|
||||
DEBUG("commodity.prices", " looking for: commodity '" << *commodity << "'");
|
||||
DEBUG("commodity.prices.find", " looking for: commodity '" << *commodity << "'");
|
||||
else
|
||||
DEBUG("commodity.prices", " looking for: any commodity");
|
||||
DEBUG("commodity.prices.find", " looking for: any commodity");
|
||||
|
||||
if (moment) {
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " time index: " << *moment);
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " time index: " << *moment);
|
||||
}
|
||||
|
||||
if (oldest) {
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices", " only consider prices younger than: " << *oldest);
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find", " only consider prices younger than: " << *oldest);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -271,8 +273,8 @@ optional<price_point_t>
|
|||
continue;
|
||||
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices",
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find",
|
||||
" searching for price via commodity '" << comm << "'");
|
||||
#endif
|
||||
|
||||
|
|
@ -288,8 +290,8 @@ optional<price_point_t>
|
|||
|
||||
if (commodity && comm != *commodity) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices", " looking for translation price");
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find", " looking for translation price");
|
||||
#endif
|
||||
|
||||
xlat = comm.find_price(commodity, moment, limit
|
||||
|
|
@ -299,8 +301,8 @@ optional<price_point_t>
|
|||
);
|
||||
if (xlat) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices", " found translated price "
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find", " found translated price "
|
||||
<< xlat->price << " from " << xlat->when);
|
||||
#endif
|
||||
|
||||
|
|
@ -308,15 +310,15 @@ optional<price_point_t>
|
|||
if (xlat->when < point->when) {
|
||||
point->when = xlat->when;
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices",
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find",
|
||||
" adjusting date of result back to " << point->when);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices", " saw no translated price there");
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find", " saw no translated price there");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
|
@ -324,8 +326,8 @@ optional<price_point_t>
|
|||
|
||||
assert(! commodity || point->price.commodity() == *commodity);
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices",
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find",
|
||||
" saw a price there: " << point->price << " from " << point->when);
|
||||
#endif
|
||||
if (! limit || point->when > *limit) {
|
||||
|
|
@ -335,16 +337,16 @@ optional<price_point_t>
|
|||
}
|
||||
} else {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent + 1);
|
||||
DEBUG("commodity.prices", " saw no price there");
|
||||
DEBUG_INDENT("commodity.prices.find", indent + 1);
|
||||
DEBUG("commodity.prices.find", " saw no price there");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
#if defined(DEBUG_ON)
|
||||
DEBUG_INDENT("commodity.prices", indent);
|
||||
DEBUG("commodity.prices",
|
||||
DEBUG_INDENT("commodity.prices.find", indent);
|
||||
DEBUG("commodity.prices.find",
|
||||
" found price " << best.price << " from " << best.when);
|
||||
#endif
|
||||
return best;
|
||||
|
|
@ -381,16 +383,14 @@ void commodity_t::exchange(commodity_t& commodity,
|
|||
const amount_t& per_unit_cost,
|
||||
const datetime_t& moment)
|
||||
{
|
||||
if (! commodity.has_flags(COMMODITY_NOMARKET)) {
|
||||
DEBUG("commodity.prices", "exchanging commodity " << commodity
|
||||
<< " at per unit cost " << per_unit_cost << " on " << 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);
|
||||
commodity_t& base_commodity
|
||||
(commodity.annotated ?
|
||||
as_annotated_commodity(commodity).referent() : commodity);
|
||||
|
||||
base_commodity.add_price(moment, per_unit_cost);
|
||||
}
|
||||
base_commodity.add_price(moment, per_unit_cost);
|
||||
}
|
||||
|
||||
commodity_t::cost_breakdown_t
|
||||
|
|
@ -400,17 +400,12 @@ commodity_t::exchange(const amount_t& amount,
|
|||
const optional<datetime_t>& moment,
|
||||
const optional<string>& tag)
|
||||
{
|
||||
// (let* ((commodity (amount-commodity amount))
|
||||
// (current-annotation
|
||||
// (and (annotated-commodity-p commodity)
|
||||
// (commodity-annotation commodity)))
|
||||
// (base-commodity (if (annotated-commodity-p commodity)
|
||||
// (get-referent commodity)
|
||||
// commodity))
|
||||
// (per-unit-cost (or per-unit-cost
|
||||
// (divide total-cost amount)))
|
||||
// (total-cost (or total-cost
|
||||
// (multiply per-unit-cost amount))))
|
||||
DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost);
|
||||
DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit);
|
||||
if (moment)
|
||||
DEBUG("commodity.prices.add", "exchange: moment = " << *moment);
|
||||
if (tag)
|
||||
DEBUG("commodity.prices.add", "exchange: tag = " << *tag);
|
||||
|
||||
commodity_t& commodity(amount.commodity());
|
||||
|
||||
|
|
@ -418,44 +413,33 @@ commodity_t::exchange(const amount_t& amount,
|
|||
if (commodity.annotated)
|
||||
current_annotation = &as_annotated_commodity(commodity).details;
|
||||
|
||||
commodity_t& base_commodity
|
||||
(current_annotation ?
|
||||
as_annotated_commodity(commodity).referent() : commodity);
|
||||
amount_t per_unit_cost(is_per_unit ? cost : (cost / amount).unrounded());
|
||||
|
||||
amount_t per_unit_cost(is_per_unit ? cost : cost / amount);
|
||||
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;
|
||||
breakdown.final_cost = ! is_per_unit ? cost : (cost * amount).unrounded();
|
||||
|
||||
// Add a price history entry for this conversion if we know when it took
|
||||
// place
|
||||
|
||||
// (if (and moment (not (commodity-no-market-price-p base-commodity)))
|
||||
// (add-price base-commodity per-unit-cost moment))
|
||||
|
||||
if (moment && ! commodity.has_flags(COMMODITY_NOMARKET))
|
||||
base_commodity.add_price(*moment, per_unit_cost);
|
||||
|
||||
// ;; returns: ANNOTATED-AMOUNT TOTAL-COST BASIS-COST
|
||||
// (values (annotate-commodity
|
||||
// amount
|
||||
// (make-commodity-annotation :price per-unit-cost
|
||||
// :date moment
|
||||
// :tag tag))
|
||||
// total-cost
|
||||
// (if current-annotation
|
||||
// (multiply (annotation-price current-annotation) amount)
|
||||
// total-cost))))
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: final-cost = " << breakdown.final_cost);
|
||||
|
||||
if (current_annotation && current_annotation->price)
|
||||
breakdown.basis_cost = *current_annotation->price * amount;
|
||||
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);
|
||||
|
||||
breakdown.amount =
|
||||
amount_t(amount, annotation_t(per_unit_cost, moment ?
|
||||
moment->date() : optional<date_t>(), tag));
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: amount = " << breakdown.amount);
|
||||
|
||||
return breakdown;
|
||||
}
|
||||
|
||||
|
|
@ -755,7 +739,7 @@ void annotated_commodity_t::write_annotations(std::ostream& out,
|
|||
out << " {" << *info.price << '}';
|
||||
|
||||
if (info.date)
|
||||
out << " [" << format_date(*info.date) << ']';
|
||||
out << " [" << format_date(*info.date, string("%Y/%m/%d")) << ']';
|
||||
|
||||
if (info.tag)
|
||||
out << " (" << *info.tag << ')';
|
||||
|
|
|
|||
103
src/entry.cc
103
src/entry.cc
|
|
@ -85,9 +85,6 @@ bool entry_base_t::finalize()
|
|||
// for auto-calculating the value of entries with no cost, and the per-unit
|
||||
// price of unpriced commodities.
|
||||
|
||||
// (let ((balance 0)
|
||||
// null-xact)
|
||||
|
||||
value_t balance;
|
||||
xact_t * null_xact = NULL;
|
||||
|
||||
|
|
@ -119,8 +116,8 @@ bool entry_base_t::finalize()
|
|||
|
||||
DEBUG("entry.finalize", "initial balance = " << balance);
|
||||
|
||||
// If there is only one xact, balance against the default account if
|
||||
// one has been set.
|
||||
// If there is only one xact, balance against the default account if one has
|
||||
// been set.
|
||||
|
||||
if (journal && journal->basket && xacts.size() == 1 && ! balance.is_null()) {
|
||||
// jww (2008-07-24): Need to make the rest of the code aware of what to do
|
||||
|
|
@ -131,9 +128,9 @@ bool entry_base_t::finalize()
|
|||
}
|
||||
|
||||
if (null_xact != NULL) {
|
||||
// If one xact has no value at all, its value will become the
|
||||
// inverse of the rest. If multiple commodities are involved, multiple
|
||||
// xacts are generated to balance them all.
|
||||
// If one xact has no value at all, its value will become the inverse of
|
||||
// the rest. If multiple commodities are involved, multiple xacts are
|
||||
// generated to balance them all.
|
||||
|
||||
if (balance.is_balance()) {
|
||||
bool first = true;
|
||||
|
|
@ -161,41 +158,88 @@ bool entry_base_t::finalize()
|
|||
else if (balance.is_balance() &&
|
||||
balance.as_balance().amounts.size() == 2) {
|
||||
// When an entry involves two different commodities (regardless of how
|
||||
// many xacts there are) determine the conversion ratio by dividing
|
||||
// the total value of one commodity by the total value of the other. This
|
||||
// establishes the per-unit cost for this xact for both
|
||||
// commodities.
|
||||
// many xacts there are) determine the conversion ratio by dividing the
|
||||
// total value of one commodity by the total value of the other. This
|
||||
// establishes the per-unit cost for this xact for both commodities.
|
||||
|
||||
const balance_t& bal(balance.as_balance());
|
||||
DEBUG("entry.finalize", "there were exactly two commodities");
|
||||
|
||||
balance_t::amounts_map::const_iterator a = bal.amounts.begin();
|
||||
bool saw_cost = false;
|
||||
xact_t * top_xact = NULL;
|
||||
|
||||
const amount_t& x((*a++).second);
|
||||
const amount_t& y((*a++).second);
|
||||
foreach (xact_t * xact, xacts) {
|
||||
if (! xact->amount.is_null())
|
||||
if (xact->amount.is_annotated())
|
||||
top_xact = xact;
|
||||
else if (! top_xact)
|
||||
top_xact = xact;
|
||||
|
||||
if (! y.is_realzero()) {
|
||||
amount_t per_unit_cost = (x / y).abs();
|
||||
if (xact->cost) {
|
||||
saw_cost = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
commodity_t& comm(x.commodity());
|
||||
if (! saw_cost && top_xact) {
|
||||
const balance_t& bal(balance.as_balance());
|
||||
|
||||
DEBUG("entry.finalize", "there were no costs, and a valid top_xact");
|
||||
|
||||
balance_t::amounts_map::const_iterator a = bal.amounts.begin();
|
||||
|
||||
const amount_t * x = &(*a++).second;
|
||||
const amount_t * y = &(*a++).second;
|
||||
|
||||
if (x->commodity() != top_xact->amount.commodity()) {
|
||||
const amount_t * t = x;
|
||||
x = y;
|
||||
y = t;
|
||||
}
|
||||
|
||||
DEBUG("entry.finalize", "primary amount = " << *y);
|
||||
DEBUG("entry.finalize", "secondary amount = " << *x);
|
||||
|
||||
commodity_t& comm(x->commodity());
|
||||
amount_t per_unit_cost;
|
||||
amount_t total_cost;
|
||||
|
||||
foreach (xact_t * xact, xacts) {
|
||||
if (xact != top_xact && xact->must_balance() &&
|
||||
! xact->amount.is_null() &&
|
||||
xact->amount.is_annotated() &&
|
||||
xact->amount.annotation().price) {
|
||||
amount_t temp = *xact->amount.annotation().price * xact->amount;
|
||||
if (total_cost.is_null()) {
|
||||
total_cost = temp;
|
||||
y = &total_cost;
|
||||
} else {
|
||||
total_cost += temp;
|
||||
}
|
||||
DEBUG("entry.finalize", "total_cost = " << total_cost);
|
||||
}
|
||||
}
|
||||
per_unit_cost = (*y / *x).abs();
|
||||
|
||||
DEBUG("entry.finalize", "per_unit_cost = " << per_unit_cost);
|
||||
|
||||
foreach (xact_t * xact, xacts) {
|
||||
const amount_t& amt(xact->amount);
|
||||
|
||||
if (! (xact->cost || ! xact->must_balance() ||
|
||||
amt.commodity() == comm)) {
|
||||
if (xact->must_balance() && amt.commodity() == comm) {
|
||||
balance -= amt;
|
||||
xact->cost = per_unit_cost * amt;
|
||||
balance += *xact->cost;
|
||||
}
|
||||
|
||||
DEBUG("entry.finalize", "set xact->cost to = " << *xact->cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("entry.finalize", "resolved balance = " << balance);
|
||||
}
|
||||
|
||||
// Now that the xact list has its final form, calculate the balance
|
||||
// once more in terms of total cost, accounting for any possible gain/loss
|
||||
// Now that the xact list has its final form, calculate the balance once
|
||||
// more in terms of total cost, accounting for any possible gain/loss
|
||||
// amounts.
|
||||
|
||||
foreach (xact_t * xact, xacts) {
|
||||
|
|
@ -204,9 +248,12 @@ bool entry_base_t::finalize()
|
|||
throw_(balance_error, "Transaction's cost must be of a different commodity");
|
||||
|
||||
commodity_t::cost_breakdown_t breakdown =
|
||||
commodity_t::exchange(xact->amount, *xact->cost);
|
||||
commodity_t::exchange(xact->amount, *xact->cost, false,
|
||||
datetime_t(date(), time_duration(0, 0, 0, 0)));
|
||||
|
||||
if (xact->amount.is_annotated())
|
||||
if (xact->amount.is_annotated() &&
|
||||
breakdown.basis_cost.commodity() ==
|
||||
breakdown.final_cost.commodity())
|
||||
add_or_set_value(balance, (breakdown.basis_cost -
|
||||
breakdown.final_cost).rounded());
|
||||
else
|
||||
|
|
@ -231,8 +278,8 @@ bool entry_base_t::finalize()
|
|||
if (! xact->amount.is_null()) {
|
||||
all_null = false;
|
||||
|
||||
// jww (2008-08-09): For now, this feature only works for
|
||||
// non-specific commodities.
|
||||
// jww (2008-08-09): For now, this feature only works for non-specific
|
||||
// commodities.
|
||||
add_or_set_value(xact->account->xdata().value, xact->amount);
|
||||
|
||||
DEBUG("entry.finalize.totals",
|
||||
|
|
|
|||
|
|
@ -128,26 +128,22 @@ value_t report_t::fn_market_value(call_scope_t& scope)
|
|||
scoped_array<char> buf(new char[args.get<string>(2).length() + 1]);
|
||||
std::strcpy(buf.get(), args.get<string>(2).c_str());
|
||||
|
||||
bool primary_only = false;
|
||||
for (char * p = std::strtok(buf.get(), ",");
|
||||
p;
|
||||
p = std::strtok(NULL, ",")) {
|
||||
if (commodity_t * commodity = amount_t::current_pool->find(trim_ws(p))) {
|
||||
value_t result =
|
||||
args.value_at(0).value(primary_only, args.has(1) ?
|
||||
args.value_at(0).value(false, args.has(1) ?
|
||||
args.get<datetime_t>(1) :
|
||||
optional<datetime_t>(), *commodity);
|
||||
if (! result.is_null())
|
||||
return result;
|
||||
}
|
||||
// For subsequent, secondary commodities, don't convert primaries
|
||||
primary_only = true;
|
||||
}
|
||||
} else {
|
||||
value_t result =
|
||||
args.value_at(0).value(! args.has(2), args.has(1) ?
|
||||
args.get<datetime_t>(1) :
|
||||
optional<datetime_t>());
|
||||
args.value_at(0).value(true, args.has(1) ?
|
||||
args.get<datetime_t>(1) : optional<datetime_t>());
|
||||
if (! result.is_null())
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -411,9 +411,9 @@ public:
|
|||
OPTION_(report_t, market, DO() { // -V
|
||||
parent->HANDLER(revalued).on_only();
|
||||
parent->HANDLER(display_amount_)
|
||||
.set_expr("market(amount_expr, now, exchange)");
|
||||
.set_expr("market(amount_expr, date, exchange)");
|
||||
parent->HANDLER(display_total_)
|
||||
.set_expr("market(total_expr, now, exchange)");
|
||||
.set_expr("market(total_expr, date, exchange)");
|
||||
});
|
||||
|
||||
OPTION_(report_t, monthly, DO() { // -M
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ void instance_t::price_entry_directive(char * line)
|
|||
|
||||
if (commodity_t * commodity =
|
||||
amount_t::current_pool->find_or_create(symbol)) {
|
||||
commodity->add_price(datetime, price);
|
||||
commodity->add_price(datetime, price, true);
|
||||
commodity->add_flags(COMMODITY_KNOWN);
|
||||
} else {
|
||||
assert(false);
|
||||
|
|
@ -895,19 +895,11 @@ xact_t * instance_t::parse_xact(char * line,
|
|||
if (xact->cost->sign() < 0)
|
||||
throw parse_error("A transaction's cost may not be negative");
|
||||
|
||||
amount_t per_unit_cost(*xact->cost);
|
||||
if (per_unit)
|
||||
*xact->cost *= xact->amount;
|
||||
else
|
||||
per_unit_cost /= xact->amount;
|
||||
|
||||
commodity_t::exchange(xact->amount.commodity(),
|
||||
per_unit_cost, datetime_t(xact->date()));
|
||||
|
||||
DEBUG("textual.parse", "line " << linenum << ": "
|
||||
<< "Total cost is " << *xact->cost);
|
||||
DEBUG("textual.parse", "line " << linenum << ": "
|
||||
<< "Per-unit cost is " << per_unit_cost);
|
||||
DEBUG("textual.parse", "line " << linenum << ": "
|
||||
<< "Annotated amount is " << xact->amount);
|
||||
|
||||
|
|
|
|||
81
test/baseline/opt-exchange.test
Normal file
81
test/baseline/opt-exchange.test
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
reg --exchange=' C, A '
|
||||
<<<
|
||||
2009/01/01 January 1st, 2009 (1)
|
||||
Assets:Brokerage 100 A
|
||||
Assets:Brokerage -200 B
|
||||
|
||||
2009/01/01 January 1st, 2009 (2)
|
||||
Assets:Brokerage 100 A
|
||||
Assets:Brokerage -300 B
|
||||
|
||||
2009/01/01 January 1st, 2009 (3)
|
||||
Assets:Brokerage 100 A
|
||||
Assets:Brokerage -400 B
|
||||
|
||||
2009/01/02 January 2nd, 2009
|
||||
Assets:Brokerage 250 B
|
||||
Assets:Brokerage -500 C
|
||||
|
||||
2009/01/03 January 3rd, 2009
|
||||
Assets:Brokerage 600 C
|
||||
Assets:Brokerage -1000 D
|
||||
|
||||
2009/01/04 January 4th, 2009
|
||||
Assets:Brokerage 300 A
|
||||
Assets:Brokerage -15000 F
|
||||
|
||||
2009/01/05 January 5th, 2009
|
||||
Assets:Brokerage 2000 E
|
||||
Assets:Brokerage -8000 F
|
||||
|
||||
2009/01/06 January 6th, 2009
|
||||
Assets:Brokerage 155 A @ 2 D
|
||||
Assets:Brokerage
|
||||
|
||||
2009/01/07 January 7th, 2009
|
||||
Assets:Brokerage 155 A @@ 200 C
|
||||
Assets:Brokerage
|
||||
|
||||
2009/01/08 January 8th, 2009
|
||||
Assets:Brokerage 155 A (A123) @@ 500 F
|
||||
Assets:Brokerage
|
||||
|
||||
2009/01/09 January 9th, 2009
|
||||
Assets:Brokerage 1000.00 E
|
||||
Assets:Brokerage -155 A {2 D}
|
||||
|
||||
2009/01/10 January 10th, 2009
|
||||
Assets:Brokerage $2,000.00
|
||||
Assets:Brokerage -155 A [2009/01/06]
|
||||
>>>1
|
||||
09-Jan-01 January 1st, 2009 (1) Assets:Brokerage 100 A 100 A
|
||||
Assets:Brokerage -50 A 100 A
|
||||
-200 B
|
||||
09-Jan-01 January 1st, 2009 (2) Assets:Brokerage 100 A 200 A
|
||||
-200 B
|
||||
Assets:Brokerage -75 A 200 A
|
||||
-500 B
|
||||
09-Jan-01 January 1st, 2009 (3) Assets:Brokerage 100 A 300 A
|
||||
-500 B
|
||||
Assets:Brokerage -100 A 300 A
|
||||
-900 B
|
||||
09-Jan-02 January 2nd, 2009 Assets:Brokerage 500 C 1100 C
|
||||
Assets:Brokerage -500 C 600 C
|
||||
09-Jan-03 January 3rd, 2009 Assets:Brokerage 600 C 1200 C
|
||||
Assets:Brokerage -600 C 600 C
|
||||
09-Jan-04 January 4th, 2009 Assets:Brokerage 2400 C 3000 C
|
||||
Assets:Brokerage -2400 C 600 C
|
||||
09-Jan-05 January 5th, 2009 Assets:Brokerage 1280 C 1880 C
|
||||
Assets:Brokerage -1280 C 600 C
|
||||
09-Jan-06 January 6th, 2009 Assets:Brokerage 186 C -1254 C
|
||||
Assets:Brokerage -186 C -1440 C
|
||||
09-Jan-07 January 7th, 2009 Assets:Brokerage 200 C -1258 C
|
||||
Assets:Brokerage -200 C -1458 C
|
||||
09-Jan-08 January 8th, 2009 Assets:Brokerage 200 C -6871 C
|
||||
Assets:Brokerage -200 C -7071 C
|
||||
09-Jan-09 January 9th, 2009 Assets:Brokerage 200 C -9671 C
|
||||
Assets:Brokerage -200 C -9871 C
|
||||
09-Jan-10 January 10th, 2009 Assets:Brokerage 200 C -9671 C
|
||||
Assets:Brokerage -200 C -9871 C
|
||||
>>>2
|
||||
=== 0
|
||||
60
test/baseline/opt-market.test
Normal file
60
test/baseline/opt-market.test
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
reg --market stocks
|
||||
<<<
|
||||
2009/01/01 Sample 1a
|
||||
Assets:Brokerage:Stocks 100 S
|
||||
Assets:Brokerage:Cash -100 P
|
||||
|
||||
P 2009/01/01 00:00:00 S 2 P
|
||||
|
||||
2009/02/01 Sample 2a
|
||||
Assets:Brokerage:Stocks 100 S @ 1 P
|
||||
Assets:Brokerage:Cash
|
||||
|
||||
P 2009/02/01 00:00:00 S 4 P
|
||||
|
||||
2009/03/01 Sample 3a
|
||||
Assets:Brokerage:Stocks 100 S @@ 100 P
|
||||
Assets:Brokerage:Cash
|
||||
|
||||
P 2009/03/01 00:00:00 S 8 P
|
||||
|
||||
2009/04/01 Sample 4a
|
||||
Assets:Brokerage:Cash 100 P
|
||||
Assets:Brokerage:Stocks -100 S {1 P}
|
||||
|
||||
P 2009/04/01 00:00:00 S 16 P
|
||||
|
||||
; In this usage case, the top amount is always secondary
|
||||
; 2010/01/01 Sample 1b
|
||||
; Assets:Brokerage:Cash -100 P
|
||||
; Assets:Brokerage:Stocks 100 S
|
||||
;
|
||||
; P 2010/01/01 00:00:00 S 2 P
|
||||
|
||||
2010/02/01 Sample 2b
|
||||
Assets:Brokerage:Cash
|
||||
Assets:Brokerage:Stocks 100 S @ 1 P
|
||||
|
||||
P 2010/02/01 00:00:00 S 4 P
|
||||
|
||||
2010/03/01 Sample 3b
|
||||
Assets:Brokerage:Cash
|
||||
Assets:Brokerage:Stocks 100 S @@ 100 P
|
||||
|
||||
P 2010/03/01 00:00:00 S 8 P
|
||||
|
||||
2010/04/01 Sample 4b
|
||||
Assets:Brokerage:Stocks -100 S {1 P}
|
||||
Assets:Brokerage:Cash 100 P
|
||||
|
||||
P 2010/04/01 00:00:00 S 16 P
|
||||
>>>1
|
||||
09-Jan-01 Sample 1a As:Brokerage:Stocks 200 P 200 P
|
||||
09-Feb-01 Sample 2a As:Brokerage:Stocks 400 P 800 P
|
||||
09-Mar-01 Sample 3a As:Brokerage:Stocks 800 P 2400 P
|
||||
09-Apr-01 Sample 4a As:Brokerage:Stocks -1600 P 3200 P
|
||||
10-Feb-01 Sample 2b As:Brokerage:Stocks 400 P 1200 P
|
||||
10-Mar-01 Sample 3b As:Brokerage:Stocks 800 P 3200 P
|
||||
10-Apr-01 Sample 4b As:Brokerage:Stocks -1600 P 4800 P
|
||||
>>>2
|
||||
=== 0
|
||||
Loading…
Add table
Reference in a new issue