diff --git a/beem/amount.py b/beem/amount.py index 873b76dd33bb7287896b90e310b6ac452eef961b..c632cba3c061e1cb9f941855cffcc79fdbb1c85f 100644 --- a/beem/amount.py +++ b/beem/amount.py @@ -8,6 +8,7 @@ from future.utils import python_2_unicode_compatible from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type from beem.instance import shared_steem_instance from beem.asset import Asset +from decimal import Decimal, ROUND_DOWN def check_asset(other, self): @@ -19,6 +20,13 @@ def check_asset(other, self): raise AssertionError() +def quantize(amount, precision): + # make sure amount is decimal and has the asset precision + amount = Decimal(amount) + places = Decimal(10) ** (-precision) + return amount.quantize(places, rounding=ROUND_DOWN) + + @python_2_unicode_compatible class Amount(dict): """ This class deals with Amounts of any asset to simplify dealing with the tuple:: @@ -81,14 +89,14 @@ class Amount(dict): elif amount and asset is None and isinstance(amount, list) and len(amount) == 3: # Copy Asset object - self["amount"] = int(amount[0]) / (10 ** amount[1]) + self["amount"] = Decimal(amount[0]) / Decimal(10 ** amount[1]) self["asset"] = Asset(amount[2], steem_instance=self.steem) self["symbol"] = self["asset"]["symbol"] elif amount and asset is None and isinstance(amount, dict) and "amount" in amount and "nai" in amount and "precision" in amount: # Copy Asset object self.new_appbase_format = True - self["amount"] = int(amount["amount"]) / (10 ** amount["precision"]) + self["amount"] = Decimal(amount["amount"]) / Decimal(10 ** amount["precision"]) self["asset"] = Asset(amount["nai"], steem_instance=self.steem) self["symbol"] = self["asset"]["symbol"] @@ -99,12 +107,12 @@ class Amount(dict): elif (amount and asset is None and isinstance(amount, dict) and "amount" in amount and "asset_id" in amount): self["asset"] = Asset(amount["asset_id"], steem_instance=self.steem) self["symbol"] = self["asset"]["symbol"] - self["amount"] = int(amount["amount"]) / 10 ** self["asset"]["precision"] + self["amount"] = Decimal(amount["amount"]) / Decimal(10 ** self["asset"]["precision"]) elif (amount and asset is None and isinstance(amount, dict) and "amount" in amount and "asset" in amount): self["asset"] = Asset(amount["asset"], steem_instance=self.steem) self["symbol"] = self["asset"]["symbol"] - self["amount"] = int(amount["amount"]) / 10 ** self["asset"]["precision"] + self["amount"] = Decimal(amount["amount"]) / Decimal(10 ** self["asset"]["precision"]) elif amount and asset and isinstance(asset, Asset): self["amount"] = amount @@ -116,25 +124,23 @@ class Amount(dict): self["asset"] = Asset(asset, steem_instance=self.steem) self["symbol"] = self["asset"]["symbol"] - elif isinstance(amount, (integer_types, float)) and asset and isinstance(asset, Asset): + elif isinstance(amount, (integer_types, float, Decimal)) and asset and isinstance(asset, Asset): self["amount"] = amount self["asset"] = asset self["symbol"] = self["asset"]["symbol"] - elif isinstance(amount, (integer_types, float)) and asset and isinstance(asset, dict): + elif isinstance(amount, (integer_types, float, Decimal)) and asset and isinstance(asset, dict): self["amount"] = amount self["asset"] = asset self["symbol"] = self["asset"]["symbol"] - elif isinstance(amount, (integer_types, float)) and asset and isinstance(asset, string_types): + elif isinstance(amount, (integer_types, float, Decimal)) and asset and isinstance(asset, string_types): self["amount"] = amount self["asset"] = Asset(asset, steem_instance=self.steem) self["symbol"] = asset else: raise ValueError - - # make sure amount is a float - self["amount"] = float(self["amount"]) + self["amount"] = quantize(self["amount"], self["asset"]["precision"]) def copy(self): """ Copy the instance and make sure not to use a reference @@ -196,7 +202,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) a["amount"] += other["amount"] else: - a["amount"] += float(other) + a["amount"] += quantize(other, self["asset"]["precision"]) return a def __sub__(self, other): @@ -205,7 +211,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) a["amount"] -= other["amount"] else: - a["amount"] -= float(other) + a["amount"] -= quantize(other, self["asset"]["precision"]) return a def __mul__(self, other): @@ -214,7 +220,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) a["amount"] *= other["amount"] else: - a["amount"] *= other + a["amount"] *= quantize(other, self["asset"]["precision"]) return a def __floordiv__(self, other): @@ -224,7 +230,7 @@ class Amount(dict): from .price import Price return Price(self, other, steem_instance=self.steem) else: - a["amount"] //= other + a["amount"] //= quantize(other, self["asset"]["precision"]) return a def __div__(self, other): @@ -234,7 +240,7 @@ class Amount(dict): from .price import Price return Price(self, other, steem_instance=self.steem) else: - a["amount"] /= other + a["amount"] /= quantize(other, self["asset"]["precision"]) return a def __mod__(self, other): @@ -243,7 +249,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) a["amount"] %= other["amount"] else: - a["amount"] %= other + a["amount"] %= quantize(other, self["asset"]["precision"]) return a def __pow__(self, other): @@ -252,7 +258,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) a["amount"] **= other["amount"] else: - a["amount"] **= other + a["amount"] **= quantize(other, self["asset"]["precision"]) return a def __iadd__(self, other): @@ -260,7 +266,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) self["amount"] += other["amount"] else: - self["amount"] += other + self["amount"] += quantize(other, self["asset"]["precision"]) return self def __isub__(self, other): @@ -268,7 +274,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) self["amount"] -= other["amount"] else: - self["amount"] -= other + self["amount"] -= quantize(other, self["asset"]["precision"]) return self def __imul__(self, other): @@ -276,7 +282,7 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) self["amount"] *= other["amount"] else: - self["amount"] *= other + self["amount"] *= quantize(other, self["asset"]["precision"]) return self def __idiv__(self, other): @@ -284,14 +290,14 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) return self["amount"] / other["amount"] else: - self["amount"] /= other + self["amount"] /= quantize(other, self["asset"]["precision"]) return self def __ifloordiv__(self, other): if isinstance(other, Amount): self["amount"] //= other["amount"] else: - self["amount"] //= other + self["amount"] //= quantize(other, self["asset"]["precision"]) return self def __imod__(self, other): @@ -299,11 +305,14 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) self["amount"] %= other["amount"] else: - self["amount"] %= other + self["amount"] %= quantize(other, self["asset"]["precision"]) return self def __ipow__(self, other): - self["amount"] **= other + if isinstance(other, Amount): + self["amount"] **= other + else: + self["amount"] **= quantize(other, self["asset"]["precision"]) return self def __lt__(self, other): @@ -311,42 +320,42 @@ class Amount(dict): check_asset(other["asset"], self["asset"]) return self["amount"] < other["amount"] else: - return self["amount"] < float(other or 0) + return self["amount"] < quantize((other or 0), self["asset"]["precision"]) def __le__(self, other): if isinstance(other, Amount): check_asset(other["asset"], self["asset"]) return self["amount"] <= other["amount"] else: - return self["amount"] <= float(other or 0) + return self["amount"] <= quantize((other or 0), self["asset"]["precision"]) def __eq__(self, other): if isinstance(other, Amount): check_asset(other["asset"], self["asset"]) return self["amount"] == other["amount"] else: - return self["amount"] == float(other or 0) + return self["amount"] == quantize((other or 0), self["asset"]["precision"]) def __ne__(self, other): if isinstance(other, Amount): check_asset(other["asset"], self["asset"]) return self["amount"] != other["amount"] else: - return self["amount"] != float(other or 0) + return self["amount"] != quantize((other or 0), self["asset"]["precision"]) def __ge__(self, other): if isinstance(other, Amount): check_asset(other["asset"], self["asset"]) return self["amount"] >= other["amount"] else: - return self["amount"] >= float(other or 0) + return self["amount"] >= quantize((other or 0), self["asset"]["precision"]) def __gt__(self, other): if isinstance(other, Amount): check_asset(other["asset"], self["asset"]) return self["amount"] > other["amount"] else: - return self["amount"] > float(other or 0) + return self["amount"] > quantize((other or 0), self["asset"]["precision"]) __repr__ = __str__ __truediv__ = __div__ diff --git a/beem/price.py b/beem/price.py index 85f4f984803ed6f9fa27e77ac08fc3b00c518855..3c17341c01e5d9272272bf9b9c03e704a87b0019 100644 --- a/beem/price.py +++ b/beem/price.py @@ -14,6 +14,7 @@ from .amount import Amount from .asset import Asset from .utils import formatTimeString from .utils import parse_time, assets_from_string +from decimal import Decimal @python_2_unicode_compatible @@ -245,7 +246,7 @@ class Price(dict): ) def __float__(self): - return self["price"] + return float(self["price"]) def _check_other(self, other): if not other["base"]["symbol"] == self["base"]["symbol"]: diff --git a/tests/beem/test_amount.py b/tests/beem/test_amount.py index 26fd940c4d046b0ad0e47bb5b0d86acb4915983e..0e579294d40bb9ff431b3660286b8f166ebde494 100644 --- a/tests/beem/test_amount.py +++ b/tests/beem/test_amount.py @@ -10,6 +10,7 @@ from beem.amount import Amount from beem.asset import Asset from beem.nodelist import NodeList from beem.instance import set_shared_steem_instance, SharedInstance +from decimal import Decimal class Testcases(unittest.TestCase): @@ -38,7 +39,7 @@ class Testcases(unittest.TestCase): self.assertEqual(float(ret), float(amount)) self.assertEqual(ret["symbol"], symbol) self.assertIsInstance(ret["asset"], dict) - self.assertIsInstance(ret["amount"], float) + self.assertIsInstance(ret["amount"], Decimal) def test_init(self): stm = self.bts @@ -78,6 +79,9 @@ class Testcases(unittest.TestCase): # keyword inits amount = Amount(amount=1.3, asset=Asset("SBD", steem_instance=stm), steem_instance=stm) self.dotest(amount, 1.3, symbol) + + amount = Amount(amount=1.3001, asset=Asset("SBD", steem_instance=stm), steem_instance=stm) + self.dotest(amount, 1.3, symbol) # keyword inits amount = Amount(amount=1.3, asset=dict(Asset("SBD", steem_instance=stm)), steem_instance=stm) @@ -152,6 +156,8 @@ class Testcases(unittest.TestCase): self.dotest(a2, 3, self.symbol) a2 += 5 self.dotest(a2, 8, self.symbol) + a2 += Decimal(2) + self.dotest(a2, 10, self.symbol) with self.assertRaises(Exception): a1 += Amount(1, asset=self.asset2) @@ -250,6 +256,7 @@ class Testcases(unittest.TestCase): self.assertTrue(a1 >= a2) self.assertTrue(a1 <= 1) self.assertTrue(a1 >= 1) + self.assertTrue(a1 == 1.0001) def test_ne(self): a1 = Amount(1, self.symbol)