def _cache_accounts_last_commodity_for_account_and_commodities(self): with self.slave_lock: try: self.pipe.send( (CMD_GET_A_LCFA_C, IFCHANGED if "accounts" in self.cache else UNCONDITIONAL)) result = self.pipe.recv() if isinstance(result, BaseException): raise result if result == UNCHANGED: assert "accounts" in self.cache else: accounts = result[0] last_commodity_for_account = dict( (acc, ledger.Amount(amt)) for acc, amt in list(result[1].items())) all_commodities = [ledger.Amount(c) for c in result[2]] self.cache["accounts"] = accounts self.cache["last_commodity_for_account"] = ( last_commodity_for_account) self.cache["all_commodities"] = all_commodities except BaseException: self.cache = {} self._start_slave() raise
def prompt_for_amount(fdin, fdout, prompt, commodity_example): cols = get_terminal_width(fdin) line = prompt + ("" if not commodity_example else " '': %s" % commodity_example) print_line_ellipsized(fdout, cols, line) x = [] match = commodity_example while True: char = read_one_character(fdin) if char in "\n\r\t": break elif char == "\x7f": if x: x.pop() else: x.append(char) inp = "".join(x) try: match = ledger.Amount(inp) * commodity_example except ArithmeticError: try: match = ledger.Amount(inp) except ArithmeticError: match = "" cols = get_terminal_width(fdin) go_cursor_up(fdout) blank_line(fdout, cols) go_cursor_up(fdout) line = prompt + " " + "'%s': %s" % (inp, match) print_line_ellipsized(fdout, cols, line) assert match is not None return match
def __init__(self, number, date, amount, acct): self.number = number self.date = date self.amount = amount quantity = ledger.Amount(amount.strip_annotations()) self.price = self.amount.price() / quantity self.account = acct
def parse_ledger_bal(self, text): """Demands '--balance-format=++ %(account)\n%(amount)\n' format. Demands '--date-format=%Y-%m-%d' date format.""" lines = [x.strip() for x in text.splitlines() if x.strip()] account = None for line in lines: if line.startswith("++ "): account = line[3:] else: amount = ledger.Amount(line) date = re.findall(r'\[\d\d\d\d-\d\d-\d\d]', line) assert len(date) < 2 if date: date = common.parse_date(date[0][1:-1]) else: date = None try: lot = Lot(self.nextnum(), date, amount, account) self.lots.append(lot) except TypeError: # At this point, we know the commodity does not have a price. # So we ignore this. pass
def get_quote(self, commodity, denominated_in): """Returns the price in the denominated_in currency, and the datetime. Args: commodity: a Ledger commodity denominated_in: a Ledger commodity Returns: price: ledger.Amount instance datetime: datetime.datetime instance """ if not isinstance(commodity, ledger.Commodity): raise ValueError("commodity must be a Ledger commodity") if not isinstance(denominated_in, ledger.Commodity): raise ValueError("denominated_in must be a Ledger commodity") if str(commodity) not in ["BTC", "XBT"]: raise ValueError( "bitcoin charts can only provide quotes for BTC / XBT") data = json_from_uri( "https://api.bitcoincharts.com/v1/weighted_prices.json") try: k = "USD" if str(denominated_in) == "$" else str(denominated_in) amount = data[k].get("24h", data[k]["7d"]) except KeyError: raise ValueError("bitcoin charts can't provide quotes in %s" % denominated_in) a = ledger.Amount(amount) a.commodity = denominated_in d = datetime.datetime.now() return a, d
def entry_changed(self, w, *args): self._adjust_entry_size(w) if self.donotreact: return text = self.entry.get_text() i = text.find("@") if i != -1: price = text[i:] if text[i:] else None text = text[:i] else: price = None try: p = ledger.Amount(text) except ArithmeticError: self.set_amount_and_price(None, price, True) self.emit("changed") return if not str(p.commodity): p.commodity = self.default_commodity if str(p): self.set_amount_and_price(p, price, True) else: self.set_amount_and_price(None, price, True) self.emit("changed")
def main(): s = common.Settings.load_or_defaults( os.path.expanduser("~/.ledgerhelpers.ini")) j = journal.Journal.from_file(common.find_ledger_file(), None) accts, commodities = j.accounts_and_last_commodity_for_account() when = common.prompt_for_date(sys.stdin, sys.stdout, "When?", s.get("last_date", datetime.date.today())) if when == datetime.date.today(): del s["last_date"] else: s["last_date"] = when asset1 = common.prompt_for_account(sys.stdin, sys.stdout, accts, "From where?", s.get("last_withdrawal_account", None)) assert asset1, "Not an account: %s" % asset1 s["last_withdrawal_account"] = asset1 asset1_currency = commodities.get(asset1, ledger.Amount("$ 1")) asset2 = common.prompt_for_account(sys.stdin, sys.stdout, accts, "To where?", s.get("last_deposit_account", None)) assert asset2, "Not an account: %s" % asset2 s["last_deposit_account"] = asset2 asset2_currency = commodities.get(asset2, ledger.Amount("$ 1")) amount1 = common.prompt_for_amount(sys.stdin, sys.stdout, "How much?", asset1_currency) amount2 = common.prompt_for_amount(sys.stdin, sys.stdout, "What was deposited?", asset2_currency) lines = j.generate_record("Withdrawal", when, None, "", [ (asset1, -1 * amount1), (asset2, amount2), ]) print "========== Record ==========" print "\n".join(lines) save = common.yesno( sys.stdin, sys.stderr, "Hit ENTER or y to save it to the file, BACKSPACE or n to skip saving: " ) if save: j.add_text_to_file(lines)
def depreciate(amount, rate, t0, T, ndigits=2): """Return the depreciation between two points in time. This function returns positive values for depreciation and negative values for appreciation. """ c, v = amount.commodity, amount.to_double() vt = round(v * (exp(-rate * t0) - exp(-rate * T)), ndigits) return ledger.Amount(c.symbol + str(vt))
def __render_json_balance(self, request, upr): if not upr.path == b'/accountbalance.json': return False query = urllib.parse.parse_qs(upr.query.decode("utf8"), strict_parsing=False) request.setHeader(b"content-type", b"application/json") if not "account" in query: return b"{}" qryaccts = query["account"] cur_amtsum = defaultdict(lambda: ledger.Amount(0, "")) for acct, amt in accounts_: if acct.startswith(qryaccts[0]): cur_amtsum[amt.currency] += amt return json.dumps({ "account": qryaccts[0], "balance": list(map(str, cur_amtsum.values())) }).encode("utf-8")
def f2p(data, Posting): from re import escape postings = [] for i in xrange(len(data.getlist('account'))): amount = data.getlist('amount')[i] commodity = data.getlist('commodity')[i] sign = data.getlist('dir')[i] if amount: if commodity and (commodity != '$'): amount = amount + ' ' + escape(commodity) else: amount = '$' + amount amount = ledger.Amount(amount) if sign == 'from': amount = -amount postings.append(Posting(data.getlist('account')[i], amount)) return postings
def main(): args = parse_args() env = Environment( loader=FileSystemLoader(os.path.dirname(sys.argv[0])), autoescape=True) template = env.get_template('assets.html.j2') journal = ledger.read_journal(args.file) balances_by_date = {} # type: Dict[date, Dict[AccountName, Dict[CommodityName, float]]] last_date = None balances = {} # type: Dict[AccountName, Dict[CommodityName, float]] for post in sorted(journal.query(args.query), key=lambda p: p.xact.date): date = post.xact.date account = post.account.fullname() commodity = post.amount.commodity.symbol.strip('"') amount = post.amount.to_double() if date != last_date: if last_date is not None: balances_by_date[last_date] = deepcopy(balances) last_date = date if account not in balances: balances[account] = {} # type: Dict[CommodityName, float] if commodity not in balances[account]: balances[account][commodity] = 0 balances[account][commodity] += amount balances_by_date[last_date] = deepcopy(balances) base_commodity = ledger.commodities.find(args.commodity) worth_by_date = { date: { account: amount_sum(date, base_commodity, (ledger.Amount('{:f}'.format(amount)) .with_commodity(ledger.commodities.find(commodity)) for commodity, amount in commodity_balances.items())) for account, commodity_balances in balances.items()} for date, balances in balances_by_date.items()} with file_or_std(args.output, sys.stdout) as f: print(template.render( balances=sorted(list(worth_by_date.items())), keys=sorted(list(balances.keys()))), file=f)
def get_quote(self, commodity, denominated_in, commodity_is_currency_pair=False): """Returns the price in the appraised_as currency, and the datetime. Args: commodity: a Ledger commodity representing a non-currency commodity denominated_in: a Ledger commodity Returns: price: ledger.Amount instance datetime: datetime.datetime instance """ if not isinstance(commodity, ledger.Commodity): raise ValueError("commodity must be a Ledger commodity") if not isinstance(denominated_in, ledger.Commodity): raise ValueError("denominated_in must be a Ledger commodity") if commodity_is_currency_pair: source = str(commodity) source = source if source != "$" else "USD" target = str(denominated_in) target = target if target != "$" else "USD" pair = source + target s = yahoo_finance.Currency(pair) try: price, date = s.get_rate(), s.get_trade_datetime() except KeyError: raise ValueError("Yahoo! Finance can't find currency pair %s" % pair) else: if str(denominated_in) not in ["$", "USD"]: raise ValueError("Yahoo! Finance can't quote in %s" % denominated_in) s = yahoo_finance.Share(str(commodity)) try: price, date = s.get_price(), s.get_trade_datetime() except KeyError: raise ValueError("Yahoo! Finance can't find commodity %s" % commodity) a = ledger.Amount(price) a.commodity = denominated_in d = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S UTC+0000') return a, d
def __init__(self, display=True): Gtk.Grid.__init__(self) self.amount = None self.entry = Gtk.Entry() self.entry.set_width_chars(8) if display: self.display = Gtk.Label() else: self.display = None self.entry.set_alignment(1.0) self.attach(self.entry, 0, 0, 1, 1) if self.display: self.attach(self.display, 1, 0, 1, 1) self.display.set_halign(Gtk.Align.END) self.display.set_justify(Gtk.Justification.RIGHT) self.set_column_spacing(4) self.donotreact = False self.entry.connect("changed", self.entry_changed) self.set_default_commodity(ledger.Amount("$ 1").commodity) self.set_activates_default = self.entry.set_activates_default
def entry_changed(self, w, *args): self._adjust_entry_size(w) if self.donotreact: return text = self.entry.get_text() try: p = ledger.Amount(text) except ArithmeticError: self.set_amount(None, True) self.emit("changed") return if not str(p.commodity): p.commodity = self.default_commodity if str(p): self.set_amount(p, True) else: self.set_amount(None, True) self.emit("changed")
from __future__ import print_function import ledger eur = ledger.commodities.find_or_create('EUR') total_eur = ledger.Amount("0.00 EUR") total_gbp = ledger.Amount("0.00 GBP") total = ledger.Amount("0.00 EUR") for post in ledger.read_journal("test/regress/78AB4B87.dat").query("^income:"): print(post.amount) print(post.amount.commodity) if post.amount.commodity == "EUR": total_eur += post.amount elif post.amount.commodity == "GBP": total_gbp += post.amount a = post.amount.value(eur) if a: print("Total is presently: (%s)" % total) print("Converted to EUR: (%s)" % a) total += a print("Total is now: (%s)" % total) else: print("Cannot convert '%s'" % post.amount) print() print(total)
def discount(amount, rate, time, ndigits=2): """Return the amount discounted by continuous rate and time.""" c, v = amount.commodity, amount.to_double() vt = round(v * exp(-rate * time), ndigits) return ledger.Amount(c.symbol + str(vt))
def commodity(self, label, create=False): pool = ledger.Amount("$ 1").commodity.pool() if create: return pool.find_or_create(label) else: return pool.find(label)
for symbol in comms.iterkeys(): pass for commodity in comms.itervalues(): pass #for symbol, commodity in comms.iteritems(): # pass #for symbol, commodity in comms: # pass # Another important thing about commodities is that they remember if they've # been exchanged for another commodity, and what the conversion rate was on # that date. You can record specific conversion rates for any date using the # `exchange' method. comms.exchange(eur, ledger.Amount('$0.77')) # Trade 1 EUR for $0.77 comms.exchange(eur, ledger.Amount('$0.66'), datetime.now()) # For the most part, however, you won't be interacting with commodities # directly, except maybe to look at their `symbol'. assertEqual('$', usd.symbol) assertEqual('$', comms['$'].symbol) ############################################################################### # # AMOUNTS & BALANCES # # Ledger deals with two basic numerical values: Amount and Balance objects. # An Amount is an infinite-precision rational with an associated commodity # (even if it is the null commodity, which is called an "uncommoditized
#assert(eurofix.isBalanced()) #### Variante 2: ein Posting für jede Ware gewinn_transactions = [] for pp in problematic_transaction.postings: if pp.account in inventory_accounts_ and pp.account in acct_currency_amt_dict and not pp.post_posting_assert_amount is None and pp.post_posting_assert_amount.currency in acct_currency_amt_dict[pp.account]: gt = ledger.Transaction("GEWINN %s" % pp.post_posting_assert_amount.currency, problematic_transaction.date) diff = round(pp.post_posting_assert_amount.quantity - acct_currency_amt_dict[pp.account][pp.post_posting_assert_amount.currency].quantity,4) if diff == 0: continue if diff > 0: gt.addDescription("WARNING: commoditiy difference is positive!!!!! WARNING") gt.addDescription("WARNING: looks like an inventory count mistake or undocumented commodity donation") einkaufspreis = acct_currency_amt_dict[pp.account][pp.post_posting_assert_amount.currency].perunitprice verkaufspreis = pp.amount.perunitprice gt.addPosting(ledger.Posting(pp.account, ledger.Amount(diff,pp.post_posting_assert_amount.currency).addPerUnitPrice(einkaufspreis))) #add comment: % gewinn gumsatz = verkaufspreis.quantity * diff geinkaufspreis = gt.postings[0].amount.totalprice.quantity revenue_acct = revenue_accounts_[inventory_accounts_.index(pp.account)] gt.addPosting(ledger.Posting(revenue_acct, None).addComment("(%.2f%% Gewinn)" % ( 100*(1.0+(geinkaufspreis/gumsatz)) ))) gt.addPosting(ledger.Posting(register_account_, ledger.Amount(gumsatz * -1,verkaufspreis.currency))) gt.unelideJokerPostings() gewinn_transactions.append(gt) register_assert_euro_postings = [p for p in problematic_transaction.postings if p.account == register_account_ and p.post_posting_assert_amount.currency == default_currency_] register_previousassert_euro_postings = [p for p in journal[last_good_inventur_transaction_index].postings if p.account == register_account_ and p.post_posting_assert_amount.currency == default_currency_] if len(register_assert_euro_postings) == 1: register_assert_euro = register_assert_euro_postings[0].post_posting_assert_amount.quantity if len(register_previousassert_euro_postings) == 1:
( r"[0-9]+(\.[0-9]+)?", number ), # Arbitrary numbers. Values must be positive, and values <1 must be written with a zero before the decimal place (ex: 0.5, not .5) (r"\n+", endofline), # newlines = end of statement (r"#.*", comment), # comments start with hashes (#) (r"\s+", None), # whitespace, which is ignored ]) # housekeeping vars linenum = 0 # stores variable state var_dict = {} # zero, used later in subz zero = ledger.Amount(0) # to strip the $ off of variables re_devar = re.compile("\$(\w+)") # read in the command files line by line for cmdfile_index in range(4, len(sys.argv)): cmdfile = sys.argv[cmdfile_index] with open(cmdfile, 'r') as f: for line in f: linenum += 1 # parse it tokens, remainder = scanner.scan(line) if remainder:
def main(): journal, s = gui.load_journal_and_settings_for_gui() accts, unused_commodities = journal.accounts_and_last_commodity_for_account( ) saleacct = common.prompt_for_account( sys.stdin, sys.stdout, accts, "Which account was the sold commodity stored in?", s.get("last_sellstock_account", None)) assert saleacct, "Not an account: %s" % saleacct s["last_sellstock_account"] = saleacct commissionsaccount = common.prompt_for_account( sys.stdin, sys.stdout, accts, "Which account to account for commissions?", s.get("last_commissions_account", None)) assert commissionsaccount, "Not an account: %s" % commissionsaccount s["last_commissions_account"] = commissionsaccount gainslossesacct = common.prompt_for_account( sys.stdin, sys.stdout, accts, "Which account to credit gains and losses?", s.get("last_gainslosses_account", "Capital:Recognized gains and losses")) assert gainslossesacct, "Not an account: %s" % gainslossesacct s["last_gainslosses_account"] = gainslossesacct target_amount = common.prompt_for_amount( sys.stdin, sys.stdout, "How many units of what commodity?", ledger.Amount("$ 1")) target_sale_price = common.prompt_for_amount( sys.stdin, sys.stdout, "What is the sale price of the commodity?", ledger.Amount("$ 1")) commission = common.prompt_for_amount( sys.stdin, sys.stdout, "What was the commission of the trade?", ledger.Amount("$ 1")) all_lots = Lots() lots_text = subprocess.check_output([ 'ledger', 'bal', '--lots', '--lot-dates', '--lot-prices', '--date-format=%Y-%m-%d', '--sort=date', '--balance-format=++ %(account)\n%(amount)\n', saleacct ]) all_lots.parse_ledger_bal(lots_text) print("=========== Read ===========") for l in all_lots: print(l) lots_produced = all_lots.subtract(target_amount) print("========= Computed =========") for l in lots_produced: print(l) print("=========== Left ===========") for l in all_lots: print(l) lines = [] tpl = "%s {%s}%s @ %s" datetpl = ' [%s]' for l in lots_produced: m = -1 * l.amount if m.commodity.details.date: datetext = datetpl % m.commodity.details.date.strftime("%Y-%m-%d") else: datetext = '' lines.append((l.account, tpl % (m.strip_annotations(), m.commodity.details.price, datetext, target_sale_price))) diff = (l.price - target_sale_price) * l.amount lines.append((gainslossesacct, diff)) totalsale = target_sale_price * sum(l.amount.number() for l in lots_produced) lines.append((saleacct, totalsale - commission)) lines.append((commissionsaccount, commission)) lines = journal.generate_record( "Sale of %s" % (target_amount), datetime.date.today(), None, "", lines, ) print("========== Record ==========") print("\n".join(lines)) save = common.yesno( sys.stdin, sys.stderr, "Hit ENTER or y to save it to the file, BACKSPACE or n to skip saving: " ) if save: journal.add_text_to_file(lines)
# There are a few built-in commodities: null, %, h, m and s assertEqual([u'', u'$', u'%', u'EUR', u'XCD', u'h', u'm', u's'], sorted(pool.keys())) for symbol in pool.iterkeys(): pass for commodity in pool.itervalues(): pass # jww (2009-11-19): Not working: missing conversion from std::pair #for symbol, commodity in pool.iteritems(): pass #for symbol, commodity in pool: pass # This creates a price exchange entry, trading EUR for $0.77 each at the # current time. pool.exchange(eur, ledger.Amount('$0.77')) # AMOUNTS & BALANCES # When two amounts are multipied or divided, the result carries the commodity # of the first term. So, 1 EUR / $0.77 == roughly 1.2987 EUR amt = ledger.Amount('$100.12') market = ((ledger.Amount('1 EUR') / ledger.Amount('$0.77')) * amt) # An amount's "precision" is a notional thing only. Since Ledger uses # rational numbers throughout, and only renders to decimal form for printing # to the user, the meaning of amt.precision should not be relied on as # meaningful. It only controls how much precision unrounded numbers (those # for which keep_precision is True, and thus that ignore display_precision) # are rendered into strings. This is the case, btw, for all uncommoditized # amounts.