aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-01-20 13:44:30 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-01-20 14:15:53 +0100
commit5ab23e1c86de4caf0a34b9b91e5b9eddb0efa222 (patch)
treeb44fffede07dfde6d832f533a877e156d4e0dc2d
parent089d5d9df3d9d93e3ce789be16ee6cdd99dc2b52 (diff)
downloadTrader-5ab23e1c86de4caf0a34b9b91e5b9eddb0efa222.tar.gz
Trader-5ab23e1c86de4caf0a34b9b91e5b9eddb0efa222.tar.zst
Trader-5ab23e1c86de4caf0a34b9b91e5b9eddb0efa222.zip
Change integer to decimals
-rw-r--r--market.py51
-rw-r--r--portfolio.py50
-rw-r--r--test.py100
3 files changed, 118 insertions, 83 deletions
diff --git a/market.py b/market.py
index a34636e..63eff5a 100644
--- a/market.py
+++ b/market.py
@@ -1,5 +1,56 @@
1import ccxt 1import ccxt
2import decimal
2 3
4def exchange_sum(self, *args):
5 return sum([arg for arg in args if isinstance(arg, (float, int, decimal.Decimal))])
6ccxt.Exchange.sum = exchange_sum
7def poloniex_fetch_balance(self, params={}):
8 self.load_markets()
9 balances = self.privatePostReturnCompleteBalances(self.extend({
10 'account': 'all',
11 }, params))
12 result = {'info': balances}
13 currencies = list(balances.keys())
14 for c in range(0, len(currencies)):
15 id = currencies[c]
16 balance = balances[id]
17 currency = self.common_currency_code(id)
18 account = {
19 'free': decimal.Decimal(balance['available']),
20 'used': decimal.Decimal(balance['onOrders']),
21 'total': decimal.Decimal(0.0),
22 }
23 account['total'] = self.sum(account['free'], account['used'])
24 result[currency] = account
25 return self.parse_balance(result)
26ccxt.poloniex.fetch_balance = poloniex_fetch_balance
27
28def poloniex_parse_ticker(self, ticker, market=None):
29 timestamp = self.milliseconds()
30 symbol = None
31 if market:
32 symbol = market['symbol']
33 return {
34 'symbol': symbol,
35 'timestamp': timestamp,
36 'datetime': self.iso8601(timestamp),
37 'high': decimal.Decimal(ticker['high24hr']),
38 'low': decimal.Decimal(ticker['low24hr']),
39 'bid': decimal.Decimal(ticker['highestBid']),
40 'ask': decimal.Decimal(ticker['lowestAsk']),
41 'vwap': None,
42 'open': None,
43 'close': None,
44 'first': None,
45 'last': decimal.Decimal(ticker['last']),
46 'change': decimal.Decimal(ticker['percentChange']),
47 'percentage': None,
48 'average': None,
49 'baseVolume': decimal.Decimal(ticker['quoteVolume']),
50 'quoteVolume': decimal.Decimal(ticker['baseVolume']),
51 'info': ticker,
52 }
53ccxt.poloniex.parse_ticker = poloniex_parse_ticker
3market = ccxt.poloniex({ 54market = ccxt.poloniex({
4 "apiKey": "XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX", 55 "apiKey": "XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX",
5 "secret": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 56 "secret": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
diff --git a/portfolio.py b/portfolio.py
index b0f9256..e1ee1f8 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -1,5 +1,6 @@
1import ccxt 1import ccxt
2import time 2import time
3from decimal import Decimal as D
3# Put your poloniex api key in market.py 4# Put your poloniex api key in market.py
4from market import market 5from market import market
5 6
@@ -37,7 +38,9 @@ class Portfolio:
37 except Exception: 38 except Exception:
38 return 39 return
39 try: 40 try:
40 cls.data = json.loads(r.data) 41 cls.data = json.loads(r.data,
42 parse_int=D,
43 parse_float=D)
41 except json.JSONDecodeError: 44 except json.JSONDecodeError:
42 cls.data = None 45 cls.data = None
43 46
@@ -83,22 +86,15 @@ class Portfolio:
83class Amount: 86class Amount:
84 MAX_DIGITS = 18 87 MAX_DIGITS = 18
85 88
86 def __init__(self, currency, value, int_val=None, linked_to=None, ticker=None): 89 def __init__(self, currency, value, linked_to=None, ticker=None):
87 self.currency = currency 90 self.currency = currency
88 if int_val is None: 91 self.value = D(value)
89 self._value = int(value * 10**self.MAX_DIGITS)
90 else:
91 self._value = int_val
92 self.linked_to = linked_to 92 self.linked_to = linked_to
93 self.ticker = ticker 93 self.ticker = ticker
94 94
95 self.ticker_cache = {} 95 self.ticker_cache = {}
96 self.ticker_cache_timestamp = time.time() 96 self.ticker_cache_timestamp = time.time()
97 97
98 @property
99 def value(self):
100 return self._value / 10 ** self.MAX_DIGITS
101
102 def in_currency(self, other_currency, market, action="average"): 98 def in_currency(self, other_currency, market, action="average"):
103 if other_currency == self.currency: 99 if other_currency == self.currency:
104 return self 100 return self
@@ -106,20 +102,19 @@ class Amount:
106 if asset_ticker is not None: 102 if asset_ticker is not None:
107 return Amount( 103 return Amount(
108 other_currency, 104 other_currency,
109 0, 105 self.value * asset_ticker[action],
110 int_val=int(self._value * asset_ticker[action]),
111 linked_to=self, 106 linked_to=self,
112 ticker=asset_ticker) 107 ticker=asset_ticker)
113 else: 108 else:
114 raise Exception("This asset is not available in the chosen market") 109 raise Exception("This asset is not available in the chosen market")
115 110
116 def __abs__(self): 111 def __abs__(self):
117 return Amount(self.currency, 0, int_val=abs(self._value)) 112 return Amount(self.currency, abs(self.value))
118 113
119 def __add__(self, other): 114 def __add__(self, other):
120 if other.currency != self.currency and other._value * self._value != 0: 115 if other.currency != self.currency and other.value * self.value != 0:
121 raise Exception("Summing amounts must be done with same currencies") 116 raise Exception("Summing amounts must be done with same currencies")
122 return Amount(self.currency, 0, int_val=self._value + other._value) 117 return Amount(self.currency, self.value + other.value)
123 118
124 def __radd__(self, other): 119 def __radd__(self, other):
125 if other == 0: 120 if other == 0:
@@ -128,25 +123,22 @@ class Amount:
128 return self.__add__(other) 123 return self.__add__(other)
129 124
130 def __sub__(self, other): 125 def __sub__(self, other):
131 if other.currency != self.currency and other._value * self._value != 0: 126 if other.currency != self.currency and other.value * self.value != 0:
132 raise Exception("Summing amounts must be done with same currencies") 127 raise Exception("Summing amounts must be done with same currencies")
133 return Amount(self.currency, 0, int_val=self._value - other._value) 128 return Amount(self.currency, self.value - other.value)
134
135 def __int__(self):
136 return self._value
137 129
138 def __mul__(self, value): 130 def __mul__(self, value):
139 if type(value) != int and type(value) != float: 131 if type(value) != int and type(value) != float and type(value) != D:
140 raise TypeError("Amount may only be multiplied by numbers") 132 raise TypeError("Amount may only be multiplied by numbers")
141 return Amount(self.currency, 0, int_val=(self._value * value)) 133 return Amount(self.currency, self.value * value)
142 134
143 def __rmul__(self, value): 135 def __rmul__(self, value):
144 return self.__mul__(value) 136 return self.__mul__(value)
145 137
146 def __floordiv__(self, value): 138 def __floordiv__(self, value):
147 if type(value) != int: 139 if type(value) != int and type(value) != float and type(value) != D:
148 raise TypeError("Amount may only be multiplied by integers") 140 raise TypeError("Amount may only be multiplied by integers")
149 return Amount(self.currency, 0, int_val=(self._value // value)) 141 return Amount(self.currency, self.value / value)
150 142
151 def __truediv__(self, value): 143 def __truediv__(self, value):
152 return self.__floordiv__(value) 144 return self.__floordiv__(value)
@@ -154,14 +146,14 @@ class Amount:
154 def __lt__(self, other): 146 def __lt__(self, other):
155 if self.currency != other.currency: 147 if self.currency != other.currency:
156 raise Exception("Comparing amounts must be done with same currencies") 148 raise Exception("Comparing amounts must be done with same currencies")
157 return self._value < other._value 149 return self.value < other.value
158 150
159 def __eq__(self, other): 151 def __eq__(self, other):
160 if other == 0: 152 if other == 0:
161 return self._value == 0 153 return self.value == 0
162 if self.currency != other.currency: 154 if self.currency != other.currency:
163 raise Exception("Comparing amounts must be done with same currencies") 155 raise Exception("Comparing amounts must be done with same currencies")
164 return self._value == other._value 156 return self.value == other.value
165 157
166 def __str__(self): 158 def __str__(self):
167 if self.linked_to is None: 159 if self.linked_to is None:
@@ -266,7 +258,7 @@ class Trade:
266 def invert(ticker): 258 def invert(ticker):
267 return { 259 return {
268 "inverted": True, 260 "inverted": True,
269 "average": (float(1/ticker["bid"]) + float(1/ticker["ask"]) ) / 2, 261 "average": (1/ticker["bid"] + 1/ticker["ask"]) / 2,
270 "original": ticker, 262 "original": ticker,
271 } 263 }
272 def augment_ticker(ticker): 264 def augment_ticker(ticker):
@@ -416,6 +408,8 @@ class Order:
416 408
417def print_orders(market, base_currency="BTC"): 409def print_orders(market, base_currency="BTC"):
418 Balance.prepare_trades(market, base_currency=base_currency) 410 Balance.prepare_trades(market, base_currency=base_currency)
411 for currency, balance in Balance.known_balances.items():
412 print(balance)
419 for currency, trade in Trade.trades.items(): 413 for currency, trade in Trade.trades.items():
420 print(trade) 414 print(trade)
421 for order in trade.orders: 415 for order in trade.orders:
diff --git a/test.py b/test.py
index 7608061..fde3a06 100644
--- a/test.py
+++ b/test.py
@@ -1,16 +1,14 @@
1import portfolio 1import portfolio
2import unittest 2import unittest
3from decimal import Decimal as D
3from unittest import mock 4from unittest import mock
4 5
5class AmountTest(unittest.TestCase): 6class AmountTest(unittest.TestCase):
6 def test_values(self): 7 def test_values(self):
7 amount = portfolio.Amount("BTC", 0.65) 8 amount = portfolio.Amount("BTC", "0.65")
8 self.assertEqual(0.65, amount.value) 9 self.assertEqual(D("0.65"), amount.value)
9 self.assertEqual("BTC", amount.currency) 10 self.assertEqual("BTC", amount.currency)
10 11
11 amount = portfolio.Amount("BTC", 10, int_val=2000000000000000)
12 self.assertEqual(0.002, amount.value)
13
14 def test_in_currency(self): 12 def test_in_currency(self):
15 amount = portfolio.Amount("ETC", 10) 13 amount = portfolio.Amount("ETC", 10)
16 14
@@ -24,12 +22,12 @@ class AmountTest(unittest.TestCase):
24 22
25 with mock.patch.object(portfolio.Trade, 'get_ticker', new=ticker_mock): 23 with mock.patch.object(portfolio.Trade, 'get_ticker', new=ticker_mock):
26 ticker_mock.return_value = { 24 ticker_mock.return_value = {
27 "average": 0.3, 25 "average": D("0.3"),
28 "foo": "bar", 26 "foo": "bar",
29 } 27 }
30 converted_amount = amount.in_currency("ETH", None) 28 converted_amount = amount.in_currency("ETH", None)
31 29
32 self.assertEqual(3.0, converted_amount.value) 30 self.assertEqual(D("3.0"), converted_amount.value)
33 self.assertEqual("ETH", converted_amount.currency) 31 self.assertEqual("ETH", converted_amount.currency)
34 self.assertEqual(amount, converted_amount.linked_to) 32 self.assertEqual(amount, converted_amount.linked_to)
35 self.assertEqual("bar", converted_amount.ticker["foo"]) 33 self.assertEqual("bar", converted_amount.ticker["foo"])
@@ -44,13 +42,13 @@ class AmountTest(unittest.TestCase):
44 self.assertEqual("SC", abs(amount).currency) 42 self.assertEqual("SC", abs(amount).currency)
45 43
46 def test__add(self): 44 def test__add(self):
47 amount1 = portfolio.Amount("XVG", 12.9) 45 amount1 = portfolio.Amount("XVG", "12.9")
48 amount2 = portfolio.Amount("XVG", 13.1) 46 amount2 = portfolio.Amount("XVG", "13.1")
49 47
50 self.assertEqual(26, (amount1 + amount2).value) 48 self.assertEqual(26, (amount1 + amount2).value)
51 self.assertEqual("XVG", (amount1 + amount2).currency) 49 self.assertEqual("XVG", (amount1 + amount2).currency)
52 50
53 amount3 = portfolio.Amount("ETH", 1.6) 51 amount3 = portfolio.Amount("ETH", "1.6")
54 with self.assertRaises(Exception): 52 with self.assertRaises(Exception):
55 amount1 + amount3 53 amount1 + amount3
56 54
@@ -58,35 +56,31 @@ class AmountTest(unittest.TestCase):
58 self.assertEqual(amount1, amount1 + amount4) 56 self.assertEqual(amount1, amount1 + amount4)
59 57
60 def test__radd(self): 58 def test__radd(self):
61 amount = portfolio.Amount("XVG", 12.9) 59 amount = portfolio.Amount("XVG", "12.9")
62 60
63 self.assertEqual(amount, 0 + amount) 61 self.assertEqual(amount, 0 + amount)
64 with self.assertRaises(Exception): 62 with self.assertRaises(Exception):
65 4 + amount 63 4 + amount
66 64
67 def test__sub(self): 65 def test__sub(self):
68 amount1 = portfolio.Amount("XVG", 13.3) 66 amount1 = portfolio.Amount("XVG", "13.3")
69 amount2 = portfolio.Amount("XVG", 13.1) 67 amount2 = portfolio.Amount("XVG", "13.1")
70 68
71 self.assertEqual(0.2, (amount1 - amount2).value) 69 self.assertEqual(D("0.2"), (amount1 - amount2).value)
72 self.assertEqual("XVG", (amount1 - amount2).currency) 70 self.assertEqual("XVG", (amount1 - amount2).currency)
73 71
74 amount3 = portfolio.Amount("ETH", 1.6) 72 amount3 = portfolio.Amount("ETH", "1.6")
75 with self.assertRaises(Exception): 73 with self.assertRaises(Exception):
76 amount1 - amount3 74 amount1 - amount3
77 75
78 amount4 = portfolio.Amount("ETH", 0.0) 76 amount4 = portfolio.Amount("ETH", 0.0)
79 self.assertEqual(amount1, amount1 - amount4) 77 self.assertEqual(amount1, amount1 - amount4)
80 78
81 def test__int(self):
82 amount = portfolio.Amount("XMR", 0.1)
83 self.assertEqual(100000000000000000, int(amount))
84
85 def test__mul(self): 79 def test__mul(self):
86 amount = portfolio.Amount("XEM", 11) 80 amount = portfolio.Amount("XEM", 11)
87 81
88 self.assertEqual(38.5, (amount * 3.5).value) 82 self.assertEqual(D("38.5"), (amount * D("3.5")).value)
89 self.assertEqual(33, (amount * 3).value) 83 self.assertEqual(D("33"), (amount * 3).value)
90 84
91 with self.assertRaises(Exception): 85 with self.assertRaises(Exception):
92 amount * amount 86 amount * amount
@@ -94,24 +88,20 @@ class AmountTest(unittest.TestCase):
94 def test__rmul(self): 88 def test__rmul(self):
95 amount = portfolio.Amount("XEM", 11) 89 amount = portfolio.Amount("XEM", 11)
96 90
97 self.assertEqual(38.5, (3.5 * amount).value) 91 self.assertEqual(D("38.5"), (D("3.5") * amount).value)
98 self.assertEqual(33, (3 * amount).value) 92 self.assertEqual(D("33"), (3 * amount).value)
99 93
100 def test__floordiv(self): 94 def test__floordiv(self):
101 amount = portfolio.Amount("XEM", 11) 95 amount = portfolio.Amount("XEM", 11)
102 96
103 self.assertEqual(5.5, (amount // 2).value) 97 self.assertEqual(D("5.5"), (amount / 2).value)
104 with self.assertRaises(TypeError): 98 self.assertEqual(D("4.4"), (amount / D("2.5")).value)
105 amount // 2.5
106 self.assertEqual(1571428571428571428, (amount // 7)._value)
107 99
108 def test__div(self): 100 def test__div(self):
109 amount = portfolio.Amount("XEM", 11) 101 amount = portfolio.Amount("XEM", 11)
110 102
111 with self.assertRaises(TypeError): 103 self.assertEqual(D("5.5"), (amount / 2).value)
112 amount / 2.5 104 self.assertEqual(D("4.4"), (amount / D("2.5")).value)
113 self.assertEqual(5.5, (amount / 2).value)
114 self.assertEqual(1571428571428571428, (amount / 7)._value)
115 105
116 def test__lt(self): 106 def test__lt(self):
117 amount1 = portfolio.Amount("BTD", 11.3) 107 amount1 = portfolio.Amount("BTD", 11.3)
@@ -290,32 +280,32 @@ class BalanceTest(unittest.TestCase):
290 @mock.patch.object(portfolio.Trade, "get_ticker") 280 @mock.patch.object(portfolio.Trade, "get_ticker")
291 def test_in_currency(self, get_ticker): 281 def test_in_currency(self, get_ticker):
292 portfolio.Balance.known_balances = { 282 portfolio.Balance.known_balances = {
293 "BTC": portfolio.Balance("BTC", 0.65, 0.35, 0.30), 283 "BTC": portfolio.Balance("BTC", "0.65", "0.35", "0.30"),
294 "ETH": portfolio.Balance("ETH", 3, 3, 0), 284 "ETH": portfolio.Balance("ETH", 3, 3, 0),
295 } 285 }
296 market = mock.Mock() 286 market = mock.Mock()
297 get_ticker.return_value = { 287 get_ticker.return_value = {
298 "bid": 0.09, 288 "bid": D("0.09"),
299 "ask": 0.11, 289 "ask": D("0.11"),
300 "average": 0.1, 290 "average": D("0.1"),
301 } 291 }
302 292
303 amounts = portfolio.Balance.in_currency("BTC", market) 293 amounts = portfolio.Balance.in_currency("BTC", market)
304 self.assertEqual("BTC", amounts["ETH"].currency) 294 self.assertEqual("BTC", amounts["ETH"].currency)
305 self.assertEqual(0.65, amounts["BTC"].value) 295 self.assertEqual(D("0.65"), amounts["BTC"].value)
306 self.assertEqual(0.30, amounts["ETH"].value) 296 self.assertEqual(D("0.30"), amounts["ETH"].value)
307 297
308 amounts = portfolio.Balance.in_currency("BTC", market, action="bid") 298 amounts = portfolio.Balance.in_currency("BTC", market, action="bid")
309 self.assertEqual(0.65, amounts["BTC"].value) 299 self.assertEqual(D("0.65"), amounts["BTC"].value)
310 self.assertEqual(0.27, amounts["ETH"].value) 300 self.assertEqual(D("0.27"), amounts["ETH"].value)
311 301
312 amounts = portfolio.Balance.in_currency("BTC", market, action="bid", type="used") 302 amounts = portfolio.Balance.in_currency("BTC", market, action="bid", type="used")
313 self.assertEqual(0.30, amounts["BTC"].value) 303 self.assertEqual(D("0.30"), amounts["BTC"].value)
314 self.assertEqual(0, amounts["ETH"].value) 304 self.assertEqual(0, amounts["ETH"].value)
315 305
316 def test_currencies(self): 306 def test_currencies(self):
317 portfolio.Balance.known_balances = { 307 portfolio.Balance.known_balances = {
318 "BTC": portfolio.Balance("BTC", 0.65, 0.35, 0.30), 308 "BTC": portfolio.Balance("BTC", "0.65", "0.35", "0.30"),
319 "ETH": portfolio.Balance("ETH", 3, 3, 0), 309 "ETH": portfolio.Balance("ETH", 3, 3, 0),
320 } 310 }
321 self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies())) 311 self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies()))
@@ -341,10 +331,10 @@ class BalanceTest(unittest.TestCase):
341 "BTC": 2600, 331 "BTC": 2600,
342 } 332 }
343 333
344 amounts = portfolio.Balance.dispatch_assets(portfolio.Amount("BTC", 10.1)) 334 amounts = portfolio.Balance.dispatch_assets(portfolio.Amount("BTC", "10.1"))
345 self.assertIn("XEM", portfolio.Balance.currencies()) 335 self.assertIn("XEM", portfolio.Balance.currencies())
346 self.assertEqual(2.6, amounts["BTC"].value) 336 self.assertEqual(D("2.6"), amounts["BTC"].value)
347 self.assertEqual(7.5, amounts["XEM"].value) 337 self.assertEqual(D("7.5"), amounts["XEM"].value)
348 338
349 @mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand") 339 @mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand")
350 @mock.patch.object(portfolio.Trade, "get_ticker") 340 @mock.patch.object(portfolio.Trade, "get_ticker")
@@ -355,20 +345,20 @@ class BalanceTest(unittest.TestCase):
355 "BTC": 2500, 345 "BTC": 2500,
356 } 346 }
357 get_ticker.side_effect = [ 347 get_ticker.side_effect = [
358 { "average": 0.0001 }, 348 { "average": D("0.0001") },
359 { "average": 0.000001 } 349 { "average": D("0.000001") }
360 ] 350 ]
361 market = mock.Mock() 351 market = mock.Mock()
362 market.fetch_balance.return_value = { 352 market.fetch_balance.return_value = {
363 "USDT": { 353 "USDT": {
364 "free": 10000.0, 354 "free": D("10000.0"),
365 "used": 0.0, 355 "used": D("0.0"),
366 "total": 10000.0 356 "total": D("10000.0")
367 }, 357 },
368 "XVG": { 358 "XVG": {
369 "free": 10000.0, 359 "free": D("10000.0"),
370 "used": 0.0, 360 "used": D("0.0"),
371 "total": 10000.0 361 "total": D("10000.0")
372 }, 362 },
373 } 363 }
374 portfolio.Balance.prepare_trades(market) 364 portfolio.Balance.prepare_trades(market)
@@ -377,9 +367,9 @@ class BalanceTest(unittest.TestCase):
377 call = compute_trades.call_args 367 call = compute_trades.call_args
378 self.assertEqual(market, call[1]["market"]) 368 self.assertEqual(market, call[1]["market"])
379 self.assertEqual(1, call[0][0]["USDT"].value) 369 self.assertEqual(1, call[0][0]["USDT"].value)
380 self.assertEqual(0.01, call[0][0]["XVG"].value) 370 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
381 self.assertEqual(0.2525, call[0][1]["BTC"].value) 371 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value)
382 self.assertEqual(0.7575, call[0][1]["XEM"].value) 372 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
383 373
384 def test__repr(self): 374 def test__repr(self):
385 balance = portfolio.Balance("BTX", 3, 1, 2) 375 balance = portfolio.Balance("BTX", 3, 1, 2)