From 81a723b285a882aaf6c49f0da1213d2312810952 Mon Sep 17 00:00:00 2001 From: Chris Berkhout Date: Thu, 27 May 2021 18:36:26 +0200 Subject: [PATCH] Quantize after invert, not in outputs or formatter. --- src/pricehist/cli.py | 3 ++- src/pricehist/format.py | 16 ---------------- src/pricehist/outputs/beancount.py | 2 +- src/pricehist/outputs/csv.py | 2 +- src/pricehist/outputs/gnucashsql.py | 14 ++++++++++---- src/pricehist/outputs/ledger.py | 2 +- src/pricehist/series.py | 23 +++++++++++++++++++++++ 7 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/pricehist/cli.py b/src/pricehist/cli.py index a23423c..fb0e3a4 100644 --- a/src/pricehist/cli.py +++ b/src/pricehist/cli.py @@ -101,6 +101,8 @@ def cmd_fetch(args): if args.invert: series = series.invert() + if args.quantize is not None: + series = series.quantize(args.quantize) if args.renamebase: series = series.rename_base(args.renamebase) if args.renamequote: @@ -117,7 +119,6 @@ def cmd_fetch(args): thousands=if_not_none(args.formatthousands, default.thousands), symbol=if_not_none(args.formatsymbol, default.symbol), datesep=if_not_none(args.formatdatesep, default.datesep), - decimal_places=if_not_none(args.quantize, default.decimal_places), ) print(output.format(series, source, fmt=fmt), end="") diff --git a/src/pricehist/format.py b/src/pricehist/format.py index 889a452..0c9dcbe 100644 --- a/src/pricehist/format.py +++ b/src/pricehist/format.py @@ -9,19 +9,3 @@ class Format: thousands: str = "" symbol: str = "rightspace" datesep: str = "-" - decimal_places: int = None - - def quantize(self, num): - if self.decimal_places is None: - return num - else: - prec = getcontext().prec - digits = len(num.as_tuple().digits) - exponent = num.as_tuple().exponent - - fractional_digits = -exponent - whole_digits = digits - fractional_digits - max_decimal_places = prec - whole_digits - chosen_decimal_places = min(self.decimal_places, max_decimal_places) - - return num.quantize(Decimal("0." + ("0" * chosen_decimal_places))) diff --git a/src/pricehist/outputs/beancount.py b/src/pricehist/outputs/beancount.py index 2fbcf45..73bc2ef 100644 --- a/src/pricehist/outputs/beancount.py +++ b/src/pricehist/outputs/beancount.py @@ -8,7 +8,7 @@ class Beancount(BaseOutput): lines = [] for price in series.prices: - amount_parts = f"{fmt.quantize(price.amount):,}".split(".") + amount_parts = f"{price.amount:,}".split(".") amount_parts[0] = amount_parts[0].replace(",", fmt.thousands) amount = ".".join(amount_parts) diff --git a/src/pricehist/outputs/csv.py b/src/pricehist/outputs/csv.py index 272bd8e..93013e8 100644 --- a/src/pricehist/outputs/csv.py +++ b/src/pricehist/outputs/csv.py @@ -8,7 +8,7 @@ class CSV(BaseOutput): lines = ["date,base,quote,amount,source,type"] for price in series.prices: date = str(price.date).replace("-", fmt.datesep) - amount_parts = f"{fmt.quantize(price.amount):,}".split(".") + amount_parts = f"{price.amount:,}".split(".") amount_parts[0] = amount_parts[0].replace(",", fmt.thousands) amount = fmt.decimal.join(amount_parts) line = ",".join( diff --git a/src/pricehist/outputs/gnucashsql.py b/src/pricehist/outputs/gnucashsql.py index f4f5410..61b40cc 100644 --- a/src/pricehist/outputs/gnucashsql.py +++ b/src/pricehist/outputs/gnucashsql.py @@ -15,16 +15,22 @@ class GnuCashSQL(BaseOutput): values_parts = [] for price in series.prices: date = f"{price.date} {fmt.time}" - amount = fmt.quantize(price.amount) m = hashlib.sha256() m.update( "".join( - [date, series.base, series.quote, src, series.type, str(amount)] + [ + date, + series.base, + series.quote, + src, + series.type, + str(price.amount), + ] ).encode("utf-8") ) guid = m.hexdigest()[0:32] - value_num = str(amount).replace(".", "") - value_denom = 10 ** len(f"{amount}.".split(".")[1]) + value_num = str(price.amount).replace(".", "") + value_denom = 10 ** len(f"{price.amount}.".split(".")[1]) v = ( "(" f"'{guid}', " diff --git a/src/pricehist/outputs/ledger.py b/src/pricehist/outputs/ledger.py index 4301b87..8271b49 100644 --- a/src/pricehist/outputs/ledger.py +++ b/src/pricehist/outputs/ledger.py @@ -9,7 +9,7 @@ class Ledger(BaseOutput): for price in series.prices: date = str(price.date).replace("-", fmt.datesep) - amount_parts = f"{fmt.quantize(price.amount):,}".split(".") + amount_parts = f"{price.amount:,}".split(".") amount_parts[0] = amount_parts[0].replace(",", fmt.thousands) amount = fmt.decimal.join(amount_parts) diff --git a/src/pricehist/series.py b/src/pricehist/series.py index acf9119..d063b90 100644 --- a/src/pricehist/series.py +++ b/src/pricehist/series.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field, replace +from decimal import Decimal, getcontext from pricehist.price import Price @@ -20,8 +21,30 @@ class Series: prices=[Price(date=p.date, amount=(1 / p.amount)) for p in self.prices], ) + def quantize(self, decimal_places): + return replace( + self, + prices=[ + replace(p, amount=self._quantize(p.amount, decimal_places)) + for p in self.prices + ], + ) + def rename_base(self, new_base): return replace(self, base=new_base) def rename_quote(self, new_quote): return replace(self, quote=new_quote) + + def _quantize(self, amount, decimal_places): + digits = len(amount.as_tuple().digits) + exponent = amount.as_tuple().exponent + + fractional_digits = -exponent + whole_digits = digits - fractional_digits + max_decimal_places = getcontext().prec - whole_digits + + chosen_decimal_places = min(decimal_places, max_decimal_places) + rounding = Decimal("0." + ("0" * chosen_decimal_places)) + + return amount.quantize(rounding)