hive-nectar 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hive-nectar might be problematic. Click here for more details.
- hive_nectar-0.0.2.dist-info/METADATA +182 -0
- hive_nectar-0.0.2.dist-info/RECORD +86 -0
- hive_nectar-0.0.2.dist-info/WHEEL +4 -0
- hive_nectar-0.0.2.dist-info/entry_points.txt +2 -0
- hive_nectar-0.0.2.dist-info/licenses/LICENSE.txt +23 -0
- nectar/__init__.py +32 -0
- nectar/account.py +4371 -0
- nectar/amount.py +475 -0
- nectar/asciichart.py +270 -0
- nectar/asset.py +82 -0
- nectar/block.py +446 -0
- nectar/blockchain.py +1178 -0
- nectar/blockchaininstance.py +2284 -0
- nectar/blockchainobject.py +221 -0
- nectar/blurt.py +563 -0
- nectar/cli.py +6285 -0
- nectar/comment.py +1217 -0
- nectar/community.py +513 -0
- nectar/constants.py +111 -0
- nectar/conveyor.py +309 -0
- nectar/discussions.py +1709 -0
- nectar/exceptions.py +149 -0
- nectar/hive.py +546 -0
- nectar/hivesigner.py +420 -0
- nectar/imageuploader.py +72 -0
- nectar/instance.py +129 -0
- nectar/market.py +1013 -0
- nectar/memo.py +449 -0
- nectar/message.py +357 -0
- nectar/nodelist.py +444 -0
- nectar/price.py +557 -0
- nectar/profile.py +65 -0
- nectar/rc.py +308 -0
- nectar/snapshot.py +726 -0
- nectar/steem.py +582 -0
- nectar/storage.py +53 -0
- nectar/transactionbuilder.py +622 -0
- nectar/utils.py +545 -0
- nectar/version.py +2 -0
- nectar/vote.py +557 -0
- nectar/wallet.py +472 -0
- nectar/witness.py +617 -0
- nectarapi/__init__.py +11 -0
- nectarapi/exceptions.py +123 -0
- nectarapi/graphenerpc.py +589 -0
- nectarapi/node.py +178 -0
- nectarapi/noderpc.py +229 -0
- nectarapi/rpcutils.py +97 -0
- nectarapi/version.py +2 -0
- nectarbase/__init__.py +14 -0
- nectarbase/ledgertransactions.py +75 -0
- nectarbase/memo.py +243 -0
- nectarbase/objects.py +429 -0
- nectarbase/objecttypes.py +22 -0
- nectarbase/operationids.py +102 -0
- nectarbase/operations.py +1297 -0
- nectarbase/signedtransactions.py +48 -0
- nectarbase/transactions.py +11 -0
- nectarbase/version.py +2 -0
- nectargrapheneapi/__init__.py +6 -0
- nectargraphenebase/__init__.py +27 -0
- nectargraphenebase/account.py +846 -0
- nectargraphenebase/aes.py +52 -0
- nectargraphenebase/base58.py +192 -0
- nectargraphenebase/bip32.py +494 -0
- nectargraphenebase/bip38.py +134 -0
- nectargraphenebase/chains.py +149 -0
- nectargraphenebase/dictionary.py +3 -0
- nectargraphenebase/ecdsasig.py +326 -0
- nectargraphenebase/objects.py +123 -0
- nectargraphenebase/objecttypes.py +6 -0
- nectargraphenebase/operationids.py +3 -0
- nectargraphenebase/operations.py +23 -0
- nectargraphenebase/prefix.py +11 -0
- nectargraphenebase/py23.py +38 -0
- nectargraphenebase/signedtransactions.py +201 -0
- nectargraphenebase/types.py +419 -0
- nectargraphenebase/unsignedtransactions.py +283 -0
- nectargraphenebase/version.py +2 -0
- nectarstorage/__init__.py +38 -0
- nectarstorage/base.py +306 -0
- nectarstorage/exceptions.py +16 -0
- nectarstorage/interfaces.py +237 -0
- nectarstorage/masterpassword.py +239 -0
- nectarstorage/ram.py +30 -0
- nectarstorage/sqlite.py +334 -0
nectar/price.py
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from fractions import Fraction
|
|
4
|
+
|
|
5
|
+
from nectar.instance import shared_blockchain_instance
|
|
6
|
+
from nectargraphenebase.py23 import integer_types, string_types
|
|
7
|
+
|
|
8
|
+
from .amount import Amount
|
|
9
|
+
from .asset import Asset
|
|
10
|
+
from .exceptions import InvalidAssetException
|
|
11
|
+
from .utils import assets_from_string, formatTimeString
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def check_asset(other, self, stm):
|
|
15
|
+
if isinstance(other, dict) and "asset" in other and isinstance(self, dict) and "asset" in self:
|
|
16
|
+
if not Asset(other["asset"], blockchain_instance=stm) == Asset(
|
|
17
|
+
self["asset"], blockchain_instance=stm
|
|
18
|
+
):
|
|
19
|
+
raise AssertionError()
|
|
20
|
+
else:
|
|
21
|
+
if not other == self:
|
|
22
|
+
raise AssertionError()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Price(dict):
|
|
26
|
+
"""This class deals with all sorts of prices of any pair of assets to
|
|
27
|
+
simplify dealing with the tuple::
|
|
28
|
+
|
|
29
|
+
(quote, base)
|
|
30
|
+
|
|
31
|
+
each being an instance of :class:`nectar.amount.Amount`. The
|
|
32
|
+
amount themselves define the price.
|
|
33
|
+
|
|
34
|
+
.. note::
|
|
35
|
+
|
|
36
|
+
The price (floating) is derived as ``base/quote``
|
|
37
|
+
|
|
38
|
+
:param list args: Allows to deal with different representations of a price
|
|
39
|
+
:param Asset base: Base asset
|
|
40
|
+
:param Asset quote: Quote asset
|
|
41
|
+
:param Steem blockchain_instance: Steem instance
|
|
42
|
+
:returns: All data required to represent a price
|
|
43
|
+
:rtype: dictionary
|
|
44
|
+
|
|
45
|
+
Way to obtain a proper instance:
|
|
46
|
+
|
|
47
|
+
* ``args`` is a str with a price and two assets
|
|
48
|
+
* ``args`` can be a floating number and ``base`` and ``quote`` being instances of :class:`nectar.asset.Asset`
|
|
49
|
+
* ``args`` can be a floating number and ``base`` and ``quote`` being instances of ``str``
|
|
50
|
+
* ``args`` can be dict with keys ``price``, ``base``, and ``quote`` (*graphene balances*)
|
|
51
|
+
* ``args`` can be dict with keys ``base`` and ``quote``
|
|
52
|
+
* ``args`` can be dict with key ``receives`` (filled orders)
|
|
53
|
+
* ``args`` being a list of ``[quote, base]`` both being instances of :class:`nectar.amount.Amount`
|
|
54
|
+
* ``args`` being a list of ``[quote, base]`` both being instances of ``str`` (``amount symbol``)
|
|
55
|
+
* ``base`` and ``quote`` being instances of :class:`nectar.asset.Amount`
|
|
56
|
+
|
|
57
|
+
This allows instanciations like:
|
|
58
|
+
|
|
59
|
+
* ``Price("0.315 SBD/STEEM")``
|
|
60
|
+
* ``Price(0.315, base="SBD", quote="STEEM")``
|
|
61
|
+
* ``Price(0.315, base=Asset("SBD"), quote=Asset("STEEM"))``
|
|
62
|
+
* ``Price({"base": {"amount": 1, "asset_id": "SBD"}, "quote": {"amount": 10, "asset_id": "SBD"}})``
|
|
63
|
+
* ``Price(quote="10 STEEM", base="1 SBD")``
|
|
64
|
+
* ``Price("10 STEEM", "1 SBD")``
|
|
65
|
+
* ``Price(Amount("10 STEEM"), Amount("1 SBD"))``
|
|
66
|
+
* ``Price(1.0, "SBD/STEEM")``
|
|
67
|
+
|
|
68
|
+
Instances of this class can be used in regular mathematical expressions
|
|
69
|
+
(``+-*/%``) such as:
|
|
70
|
+
|
|
71
|
+
.. code-block:: python
|
|
72
|
+
|
|
73
|
+
>>> from nectar.price import Price
|
|
74
|
+
>>> from nectar import Steem
|
|
75
|
+
>>> stm = Steem("https://api.steemit.com")
|
|
76
|
+
>>> Price("0.3314 SBD/STEEM", blockchain_instance=stm) * 2
|
|
77
|
+
0.662804 SBD/STEEM
|
|
78
|
+
>>> Price(0.3314, "SBD", "STEEM", blockchain_instance=stm)
|
|
79
|
+
0.331402 SBD/STEEM
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
price=None,
|
|
86
|
+
base=None,
|
|
87
|
+
quote=None,
|
|
88
|
+
base_asset=None, # to identify sell/buy
|
|
89
|
+
blockchain_instance=None,
|
|
90
|
+
**kwargs,
|
|
91
|
+
):
|
|
92
|
+
if blockchain_instance is None:
|
|
93
|
+
if kwargs.get("steem_instance"):
|
|
94
|
+
blockchain_instance = kwargs["steem_instance"]
|
|
95
|
+
elif kwargs.get("hive_instance"):
|
|
96
|
+
blockchain_instance = kwargs["hive_instance"]
|
|
97
|
+
self.blockchain = blockchain_instance or shared_blockchain_instance()
|
|
98
|
+
if price == "":
|
|
99
|
+
price = None
|
|
100
|
+
if price is not None and isinstance(price, string_types) and not base and not quote:
|
|
101
|
+
price, assets = price.split(" ")
|
|
102
|
+
base_symbol, quote_symbol = assets_from_string(assets)
|
|
103
|
+
base = Asset(base_symbol, blockchain_instance=self.blockchain)
|
|
104
|
+
quote = Asset(quote_symbol, blockchain_instance=self.blockchain)
|
|
105
|
+
frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
|
|
106
|
+
self["quote"] = Amount(
|
|
107
|
+
amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
|
|
108
|
+
)
|
|
109
|
+
self["base"] = Amount(
|
|
110
|
+
amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
elif price is not None and isinstance(price, dict) and "base" in price and "quote" in price:
|
|
114
|
+
if "price" in price:
|
|
115
|
+
raise AssertionError("You cannot provide a 'price' this way")
|
|
116
|
+
# Regular 'price' objects according to steem-core
|
|
117
|
+
# base_id = price["base"]["asset_id"]
|
|
118
|
+
# if price["base"]["asset_id"] == base_id:
|
|
119
|
+
self["base"] = Amount(price["base"], blockchain_instance=self.blockchain)
|
|
120
|
+
self["quote"] = Amount(price["quote"], blockchain_instance=self.blockchain)
|
|
121
|
+
# else:
|
|
122
|
+
# self["quote"] = Amount(price["base"], blockchain_instance=self.blockchain)
|
|
123
|
+
# self["base"] = Amount(price["quote"], blockchain_instance=self.blockchain)
|
|
124
|
+
|
|
125
|
+
elif price is not None and isinstance(base, Asset) and isinstance(quote, Asset):
|
|
126
|
+
frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
|
|
127
|
+
self["quote"] = Amount(
|
|
128
|
+
amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
|
|
129
|
+
)
|
|
130
|
+
self["base"] = Amount(
|
|
131
|
+
amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
elif (
|
|
135
|
+
price is not None and isinstance(base, string_types) and isinstance(quote, string_types)
|
|
136
|
+
):
|
|
137
|
+
base = Asset(base, blockchain_instance=self.blockchain)
|
|
138
|
+
quote = Asset(quote, blockchain_instance=self.blockchain)
|
|
139
|
+
frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
|
|
140
|
+
self["quote"] = Amount(
|
|
141
|
+
amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
|
|
142
|
+
)
|
|
143
|
+
self["base"] = Amount(
|
|
144
|
+
amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
elif price is None and isinstance(base, string_types) and isinstance(quote, string_types):
|
|
148
|
+
self["quote"] = Amount(quote, blockchain_instance=self.blockchain)
|
|
149
|
+
self["base"] = Amount(base, blockchain_instance=self.blockchain)
|
|
150
|
+
elif (
|
|
151
|
+
price is not None and isinstance(price, string_types) and isinstance(base, string_types)
|
|
152
|
+
):
|
|
153
|
+
self["quote"] = Amount(price, blockchain_instance=self.blockchain)
|
|
154
|
+
self["base"] = Amount(base, blockchain_instance=self.blockchain)
|
|
155
|
+
# len(args) > 1
|
|
156
|
+
|
|
157
|
+
elif isinstance(price, Amount) and isinstance(base, Amount):
|
|
158
|
+
self["quote"], self["base"] = price, base
|
|
159
|
+
|
|
160
|
+
# len(args) == 0
|
|
161
|
+
elif price is None and isinstance(base, Amount) and isinstance(quote, Amount):
|
|
162
|
+
self["quote"] = quote
|
|
163
|
+
self["base"] = base
|
|
164
|
+
|
|
165
|
+
elif (
|
|
166
|
+
isinstance(price, float)
|
|
167
|
+
or isinstance(price, integer_types)
|
|
168
|
+
or isinstance(price, Decimal)
|
|
169
|
+
) and isinstance(base, string_types):
|
|
170
|
+
base_symbol, quote_symbol = assets_from_string(base)
|
|
171
|
+
base = Asset(base_symbol, blockchain_instance=self.blockchain)
|
|
172
|
+
quote = Asset(quote_symbol, blockchain_instance=self.blockchain)
|
|
173
|
+
frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
|
|
174
|
+
self["quote"] = Amount(
|
|
175
|
+
amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
|
|
176
|
+
)
|
|
177
|
+
self["base"] = Amount(
|
|
178
|
+
amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
else:
|
|
182
|
+
raise ValueError("Couldn't parse 'Price'.")
|
|
183
|
+
|
|
184
|
+
def __setitem__(self, key, value):
|
|
185
|
+
dict.__setitem__(self, key, value)
|
|
186
|
+
if (
|
|
187
|
+
"quote" in self and "base" in self and self["base"] and self["quote"]
|
|
188
|
+
): # don't derive price for deleted Orders
|
|
189
|
+
dict.__setitem__(
|
|
190
|
+
self, "price", self._safedivide(self["base"]["amount"], self["quote"]["amount"])
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def copy(self):
|
|
194
|
+
return Price(
|
|
195
|
+
None,
|
|
196
|
+
base=self["base"].copy(),
|
|
197
|
+
quote=self["quote"].copy(),
|
|
198
|
+
blockchain_instance=self.blockchain,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def _safedivide(self, a, b):
|
|
202
|
+
if b != 0.0:
|
|
203
|
+
return a / b
|
|
204
|
+
else:
|
|
205
|
+
return float("Inf")
|
|
206
|
+
|
|
207
|
+
def symbols(self):
|
|
208
|
+
return self["base"]["symbol"], self["quote"]["symbol"]
|
|
209
|
+
|
|
210
|
+
def as_base(self, base):
|
|
211
|
+
"""Returns the price instance so that the base asset is ``base``.
|
|
212
|
+
|
|
213
|
+
.. note:: This makes a copy of the object!
|
|
214
|
+
|
|
215
|
+
.. code-block:: python
|
|
216
|
+
|
|
217
|
+
>>> from nectar.price import Price
|
|
218
|
+
>>> from nectar import Steem
|
|
219
|
+
>>> stm = Steem("https://api.steemit.com")
|
|
220
|
+
>>> Price("0.3314 SBD/STEEM", blockchain_instance=stm).as_base("STEEM")
|
|
221
|
+
3.017483 STEEM/SBD
|
|
222
|
+
|
|
223
|
+
"""
|
|
224
|
+
if base == self["base"]["symbol"]:
|
|
225
|
+
return self.copy()
|
|
226
|
+
elif base == self["quote"]["symbol"]:
|
|
227
|
+
return self.copy().invert()
|
|
228
|
+
else:
|
|
229
|
+
raise InvalidAssetException
|
|
230
|
+
|
|
231
|
+
def as_quote(self, quote):
|
|
232
|
+
"""Returns the price instance so that the quote asset is ``quote``.
|
|
233
|
+
|
|
234
|
+
.. note:: This makes a copy of the object!
|
|
235
|
+
|
|
236
|
+
.. code-block:: python
|
|
237
|
+
|
|
238
|
+
>>> from nectar.price import Price
|
|
239
|
+
>>> from nectar import Steem
|
|
240
|
+
>>> stm = Steem("https://api.steemit.com")
|
|
241
|
+
>>> Price("0.3314 SBD/STEEM", blockchain_instance=stm).as_quote("SBD")
|
|
242
|
+
3.017483 STEEM/SBD
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
if quote == self["quote"]["symbol"]:
|
|
246
|
+
return self.copy()
|
|
247
|
+
elif quote == self["base"]["symbol"]:
|
|
248
|
+
return self.copy().invert()
|
|
249
|
+
else:
|
|
250
|
+
raise InvalidAssetException
|
|
251
|
+
|
|
252
|
+
def invert(self):
|
|
253
|
+
"""Invert the price (e.g. go from ``SBD/STEEM`` into ``STEEM/SBD``)
|
|
254
|
+
|
|
255
|
+
.. code-block:: python
|
|
256
|
+
|
|
257
|
+
>>> from nectar.price import Price
|
|
258
|
+
>>> from nectar import Steem
|
|
259
|
+
>>> stm = Steem("https://api.steemit.com")
|
|
260
|
+
>>> Price("0.3314 SBD/STEEM", blockchain_instance=stm).invert()
|
|
261
|
+
3.017483 STEEM/SBD
|
|
262
|
+
|
|
263
|
+
"""
|
|
264
|
+
tmp = self["quote"]
|
|
265
|
+
self["quote"] = self["base"]
|
|
266
|
+
self["base"] = tmp
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
def json(self):
|
|
270
|
+
return {"base": self["base"].json(), "quote": self["quote"].json()}
|
|
271
|
+
|
|
272
|
+
def __repr__(self):
|
|
273
|
+
return "{price:.{precision}f} {base}/{quote}".format(
|
|
274
|
+
price=self["price"],
|
|
275
|
+
base=self["base"]["symbol"],
|
|
276
|
+
quote=self["quote"]["symbol"],
|
|
277
|
+
precision=(self["base"]["asset"]["precision"] + self["quote"]["asset"]["precision"]),
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def __float__(self):
|
|
281
|
+
return float(self["price"])
|
|
282
|
+
|
|
283
|
+
def _check_other(self, other):
|
|
284
|
+
if not other["base"]["symbol"] == self["base"]["symbol"]:
|
|
285
|
+
raise AssertionError()
|
|
286
|
+
if not other["quote"]["symbol"] == self["quote"]["symbol"]:
|
|
287
|
+
raise AssertionError()
|
|
288
|
+
|
|
289
|
+
def __mul__(self, other):
|
|
290
|
+
a = self.copy()
|
|
291
|
+
if isinstance(other, Price):
|
|
292
|
+
# Rotate/invert other
|
|
293
|
+
if (
|
|
294
|
+
self["quote"]["symbol"] not in other.symbols()
|
|
295
|
+
and self["base"]["symbol"] not in other.symbols()
|
|
296
|
+
):
|
|
297
|
+
raise InvalidAssetException
|
|
298
|
+
|
|
299
|
+
# base/quote = a/b
|
|
300
|
+
# a/b * b/c = a/c
|
|
301
|
+
a = self.copy()
|
|
302
|
+
if self["quote"]["symbol"] == other["base"]["symbol"]:
|
|
303
|
+
a["base"] = Amount(
|
|
304
|
+
float(self["base"]) * float(other["base"]),
|
|
305
|
+
self["base"]["symbol"],
|
|
306
|
+
blockchain_instance=self.blockchain,
|
|
307
|
+
)
|
|
308
|
+
a["quote"] = Amount(
|
|
309
|
+
float(self["quote"]) * float(other["quote"]),
|
|
310
|
+
other["quote"]["symbol"],
|
|
311
|
+
blockchain_instance=self.blockchain,
|
|
312
|
+
)
|
|
313
|
+
# a/b * c/a = c/b
|
|
314
|
+
elif self["base"]["symbol"] == other["quote"]["symbol"]:
|
|
315
|
+
a["base"] = Amount(
|
|
316
|
+
float(self["base"]) * float(other["base"]),
|
|
317
|
+
other["base"]["symbol"],
|
|
318
|
+
blockchain_instance=self.blockchain,
|
|
319
|
+
)
|
|
320
|
+
a["quote"] = Amount(
|
|
321
|
+
float(self["quote"]) * float(other["quote"]),
|
|
322
|
+
self["quote"]["symbol"],
|
|
323
|
+
blockchain_instance=self.blockchain,
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
raise ValueError("Wrong rotation of prices")
|
|
327
|
+
elif isinstance(other, Amount):
|
|
328
|
+
check_asset(other["asset"], self["quote"]["asset"], self.blockchain)
|
|
329
|
+
a = other.copy() * self["price"]
|
|
330
|
+
a["asset"] = self["base"]["asset"].copy()
|
|
331
|
+
a["symbol"] = self["base"]["asset"]["symbol"]
|
|
332
|
+
else:
|
|
333
|
+
a["base"] *= other
|
|
334
|
+
return a
|
|
335
|
+
|
|
336
|
+
def __imul__(self, other):
|
|
337
|
+
if isinstance(other, Price):
|
|
338
|
+
tmp = self * other
|
|
339
|
+
self["base"] = tmp["base"]
|
|
340
|
+
self["quote"] = tmp["quote"]
|
|
341
|
+
else:
|
|
342
|
+
self["base"] *= other
|
|
343
|
+
return self
|
|
344
|
+
|
|
345
|
+
def __div__(self, other):
|
|
346
|
+
a = self.copy()
|
|
347
|
+
if isinstance(other, Price):
|
|
348
|
+
# Rotate/invert other
|
|
349
|
+
if sorted(self.symbols()) == sorted(other.symbols()):
|
|
350
|
+
return float(self.as_base(self["base"]["symbol"])) / float(
|
|
351
|
+
other.as_base(self["base"]["symbol"])
|
|
352
|
+
)
|
|
353
|
+
elif self["quote"]["symbol"] in other.symbols():
|
|
354
|
+
other = other.as_base(self["quote"]["symbol"])
|
|
355
|
+
elif self["base"]["symbol"] in other.symbols():
|
|
356
|
+
other = other.as_base(self["base"]["symbol"])
|
|
357
|
+
else:
|
|
358
|
+
raise InvalidAssetException
|
|
359
|
+
a["base"] = Amount(
|
|
360
|
+
float(self["base"].amount / other["base"].amount),
|
|
361
|
+
other["quote"]["symbol"],
|
|
362
|
+
blockchain_instance=self.blockchain,
|
|
363
|
+
)
|
|
364
|
+
a["quote"] = Amount(
|
|
365
|
+
float(self["quote"].amount / other["quote"].amount),
|
|
366
|
+
self["quote"]["symbol"],
|
|
367
|
+
blockchain_instance=self.blockchain,
|
|
368
|
+
)
|
|
369
|
+
elif isinstance(other, Amount):
|
|
370
|
+
check_asset(other["asset"], self["quote"]["asset"], self.blockchain)
|
|
371
|
+
a = other.copy() / self["price"]
|
|
372
|
+
a["asset"] = self["base"]["asset"].copy()
|
|
373
|
+
a["symbol"] = self["base"]["asset"]["symbol"]
|
|
374
|
+
else:
|
|
375
|
+
a["base"] /= other
|
|
376
|
+
return a
|
|
377
|
+
|
|
378
|
+
def __idiv__(self, other):
|
|
379
|
+
if isinstance(other, Price):
|
|
380
|
+
tmp = self / other
|
|
381
|
+
self["base"] = tmp["base"]
|
|
382
|
+
self["quote"] = tmp["quote"]
|
|
383
|
+
else:
|
|
384
|
+
self["base"] /= other
|
|
385
|
+
return self
|
|
386
|
+
|
|
387
|
+
def __floordiv__(self, other):
|
|
388
|
+
raise NotImplementedError("This is not possible as the price is a ratio")
|
|
389
|
+
|
|
390
|
+
def __ifloordiv__(self, other):
|
|
391
|
+
raise NotImplementedError("This is not possible as the price is a ratio")
|
|
392
|
+
|
|
393
|
+
def __lt__(self, other):
|
|
394
|
+
if isinstance(other, Price):
|
|
395
|
+
self._check_other(other)
|
|
396
|
+
return self["price"] < other["price"]
|
|
397
|
+
else:
|
|
398
|
+
return self["price"] < float(other or 0)
|
|
399
|
+
|
|
400
|
+
def __le__(self, other):
|
|
401
|
+
if isinstance(other, Price):
|
|
402
|
+
self._check_other(other)
|
|
403
|
+
return self["price"] <= other["price"]
|
|
404
|
+
else:
|
|
405
|
+
return self["price"] <= float(other or 0)
|
|
406
|
+
|
|
407
|
+
def __eq__(self, other):
|
|
408
|
+
if isinstance(other, Price):
|
|
409
|
+
self._check_other(other)
|
|
410
|
+
return self["price"] == other["price"]
|
|
411
|
+
else:
|
|
412
|
+
return self["price"] == float(other or 0)
|
|
413
|
+
|
|
414
|
+
def __ne__(self, other):
|
|
415
|
+
if isinstance(other, Price):
|
|
416
|
+
self._check_other(other)
|
|
417
|
+
return self["price"] != other["price"]
|
|
418
|
+
else:
|
|
419
|
+
return self["price"] != float(other or 0)
|
|
420
|
+
|
|
421
|
+
def __ge__(self, other):
|
|
422
|
+
if isinstance(other, Price):
|
|
423
|
+
self._check_other(other)
|
|
424
|
+
return self["price"] >= other["price"]
|
|
425
|
+
else:
|
|
426
|
+
return self["price"] >= float(other or 0)
|
|
427
|
+
|
|
428
|
+
def __gt__(self, other):
|
|
429
|
+
if isinstance(other, Price):
|
|
430
|
+
self._check_other(other)
|
|
431
|
+
return self["price"] > other["price"]
|
|
432
|
+
else:
|
|
433
|
+
return self["price"] > float(other or 0)
|
|
434
|
+
|
|
435
|
+
__truediv__ = __div__
|
|
436
|
+
__truemul__ = __mul__
|
|
437
|
+
__str__ = __repr__
|
|
438
|
+
|
|
439
|
+
@property
|
|
440
|
+
def market(self):
|
|
441
|
+
"""Open the corresponding market
|
|
442
|
+
|
|
443
|
+
:returns: Instance of :class:`nectar.market.Market` for the
|
|
444
|
+
corresponding pair of assets.
|
|
445
|
+
"""
|
|
446
|
+
from .market import Market
|
|
447
|
+
|
|
448
|
+
return Market(
|
|
449
|
+
base=self["base"]["asset"],
|
|
450
|
+
quote=self["quote"]["asset"],
|
|
451
|
+
blockchain_instance=self.blockchain,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class Order(Price):
|
|
456
|
+
"""This class inherits :class:`nectar.price.Price` but has the ``base``
|
|
457
|
+
and ``quote`` Amounts not only be used to represent the price (as a
|
|
458
|
+
ratio of base and quote) but instead has those amounts represent the
|
|
459
|
+
amounts of an actual order!
|
|
460
|
+
|
|
461
|
+
:param Steem blockchain_instance: Steem instance
|
|
462
|
+
|
|
463
|
+
.. note::
|
|
464
|
+
|
|
465
|
+
If an order is marked as deleted, it will carry the
|
|
466
|
+
'deleted' key which is set to ``True`` and all other
|
|
467
|
+
data be ``None``.
|
|
468
|
+
"""
|
|
469
|
+
|
|
470
|
+
def __init__(self, base, quote=None, blockchain_instance=None, **kwargs):
|
|
471
|
+
self.blockchain = blockchain_instance or shared_blockchain_instance()
|
|
472
|
+
|
|
473
|
+
if isinstance(base, dict) and "sell_price" in base:
|
|
474
|
+
super(Order, self).__init__(base["sell_price"], blockchain_instance=self.blockchain)
|
|
475
|
+
self["id"] = base.get("id")
|
|
476
|
+
elif isinstance(base, dict) and "min_to_receive" in base and "amount_to_sell" in base:
|
|
477
|
+
super(Order, self).__init__(
|
|
478
|
+
Amount(base["min_to_receive"], blockchain_instance=self.blockchain),
|
|
479
|
+
Amount(base["amount_to_sell"], blockchain_instance=self.blockchain),
|
|
480
|
+
blockchain_instance=self.blockchain,
|
|
481
|
+
)
|
|
482
|
+
self["id"] = base.get("id")
|
|
483
|
+
elif isinstance(base, Amount) and isinstance(quote, Amount):
|
|
484
|
+
super(Order, self).__init__(
|
|
485
|
+
None, base=base, quote=quote, blockchain_instance=self.blockchain
|
|
486
|
+
)
|
|
487
|
+
else:
|
|
488
|
+
raise ValueError("Unknown format to load Order")
|
|
489
|
+
|
|
490
|
+
def __repr__(self):
|
|
491
|
+
if "deleted" in self and self["deleted"]:
|
|
492
|
+
return "deleted order %s" % self["id"]
|
|
493
|
+
else:
|
|
494
|
+
t = ""
|
|
495
|
+
if "time" in self and self["time"]:
|
|
496
|
+
t += "(%s) " % self["time"]
|
|
497
|
+
if "type" in self and self["type"]:
|
|
498
|
+
t += "%s " % str(self["type"])
|
|
499
|
+
if "quote" in self and self["quote"]:
|
|
500
|
+
t += "%s " % str(self["quote"])
|
|
501
|
+
if "base" in self and self["base"]:
|
|
502
|
+
t += "%s " % str(self["base"])
|
|
503
|
+
return t + "@ " + Price.__repr__(self)
|
|
504
|
+
|
|
505
|
+
__str__ = __repr__
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
class FilledOrder(Price):
|
|
509
|
+
"""This class inherits :class:`nectar.price.Price` but has the ``base``
|
|
510
|
+
and ``quote`` Amounts not only be used to represent the price (as a
|
|
511
|
+
ratio of base and quote) but instead has those amounts represent the
|
|
512
|
+
amounts of an actually filled order!
|
|
513
|
+
|
|
514
|
+
:param Steem blockchain_instance: Steem instance
|
|
515
|
+
|
|
516
|
+
.. note:: Instances of this class come with an additional ``date`` key
|
|
517
|
+
that shows when the order has been filled!
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
def __init__(self, order, blockchain_instance=None, **kwargs):
|
|
521
|
+
self.blockchain = blockchain_instance or shared_blockchain_instance()
|
|
522
|
+
if isinstance(order, dict) and "current_pays" in order and "open_pays" in order:
|
|
523
|
+
# filled orders from account history
|
|
524
|
+
if "op" in order:
|
|
525
|
+
order = order["op"]
|
|
526
|
+
|
|
527
|
+
super(FilledOrder, self).__init__(
|
|
528
|
+
Amount(order["open_pays"], blockchain_instance=self.blockchain),
|
|
529
|
+
Amount(order["current_pays"], blockchain_instance=self.blockchain),
|
|
530
|
+
blockchain_instance=self.blockchain,
|
|
531
|
+
)
|
|
532
|
+
if "date" in order:
|
|
533
|
+
self["date"] = formatTimeString(order["date"])
|
|
534
|
+
|
|
535
|
+
else:
|
|
536
|
+
raise ValueError("Couldn't parse 'Price'.")
|
|
537
|
+
|
|
538
|
+
def json(self):
|
|
539
|
+
return {
|
|
540
|
+
"date": formatTimeString(self["date"]),
|
|
541
|
+
"current_pays": self["base"].json(),
|
|
542
|
+
"open_pays": self["quote"].json(),
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
def __repr__(self):
|
|
546
|
+
t = ""
|
|
547
|
+
if "date" in self and self["date"]:
|
|
548
|
+
t += "(%s) " % self["date"]
|
|
549
|
+
if "type" in self and self["type"]:
|
|
550
|
+
t += "%s " % str(self["type"])
|
|
551
|
+
if "quote" in self and self["quote"]:
|
|
552
|
+
t += "%s " % str(self["quote"])
|
|
553
|
+
if "base" in self and self["base"]:
|
|
554
|
+
t += "%s " % str(self["base"])
|
|
555
|
+
return t + "@ " + Price.__repr__(self)
|
|
556
|
+
|
|
557
|
+
__str__ = __repr__
|
nectar/profile.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from collections.abc import Mapping # noqa
|
|
6
|
+
except ImportError:
|
|
7
|
+
from collections import Mapping # noqa
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DotDict(dict):
|
|
11
|
+
def __init__(self, *args):
|
|
12
|
+
"""This class simplifies the use of "."-separated
|
|
13
|
+
keys when defining a nested dictionary:::
|
|
14
|
+
|
|
15
|
+
>>> from nectar.profile import Profile
|
|
16
|
+
>>> keys = ['profile.url', 'profile.img']
|
|
17
|
+
>>> values = ["http:", "foobar"]
|
|
18
|
+
>>> p = Profile(keys, values)
|
|
19
|
+
>>> print(p["profile"]["url"])
|
|
20
|
+
http:
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
if len(args) == 2:
|
|
24
|
+
for i, item in enumerate(args[0]):
|
|
25
|
+
t = self
|
|
26
|
+
parts = item.split(".")
|
|
27
|
+
for j, part in enumerate(parts):
|
|
28
|
+
if j < len(parts) - 1:
|
|
29
|
+
t = t.setdefault(part, {})
|
|
30
|
+
else:
|
|
31
|
+
t[part] = args[1][i]
|
|
32
|
+
elif len(args) == 1 and isinstance(args[0], dict):
|
|
33
|
+
for k, v in args[0].items():
|
|
34
|
+
self[k] = v
|
|
35
|
+
elif len(args) == 1 and isinstance(args[0], str):
|
|
36
|
+
for k, v in json.loads(args[0]).items():
|
|
37
|
+
self[k] = v
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Profile(DotDict):
|
|
41
|
+
"""This class is a template to model a user's on-chain
|
|
42
|
+
profile according to
|
|
43
|
+
|
|
44
|
+
* https://github.com/adcpm/steemscript
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, *args, **kwargs):
|
|
48
|
+
super(Profile, self).__init__(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
def __str__(self):
|
|
51
|
+
return json.dumps(self)
|
|
52
|
+
|
|
53
|
+
def update(self, u):
|
|
54
|
+
for k, v in u.items():
|
|
55
|
+
if isinstance(v, Mapping):
|
|
56
|
+
self.setdefault(k, {}).update(v)
|
|
57
|
+
else:
|
|
58
|
+
self[k] = u[k]
|
|
59
|
+
|
|
60
|
+
def remove(self, key):
|
|
61
|
+
parts = key.split(".")
|
|
62
|
+
if len(parts) > 1:
|
|
63
|
+
self[parts[0]].pop(".".join(parts[1:]))
|
|
64
|
+
else:
|
|
65
|
+
super(Profile, self).pop(parts[0], None)
|