]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blob - test.py
Add Balance class tests
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / test.py
1 import portfolio
2 import unittest
3 from unittest import mock
4
5 class AmountTest(unittest.TestCase):
6 def test_values(self):
7 amount = portfolio.Amount("BTC", 0.65)
8 self.assertEqual(0.65, amount.value)
9 self.assertEqual("BTC", amount.currency)
10
11 amount = portfolio.Amount("BTC", 10, int_val=2000000000000000)
12 self.assertEqual(0.002, amount.value)
13
14 def test_in_currency(self):
15 amount = portfolio.Amount("ETC", 10)
16
17 self.assertEqual(amount, amount.in_currency("ETC", None))
18
19 ticker_mock = unittest.mock.Mock()
20 with mock.patch.object(portfolio.Amount, 'get_ticker', new=ticker_mock):
21 ticker_mock.return_value = None
22 portfolio.Amount.get_ticker = ticker_mock
23
24 self.assertRaises(Exception, amount.in_currency, "ETH", None)
25
26 with mock.patch.object(portfolio.Amount, 'get_ticker', new=ticker_mock):
27 ticker_mock.return_value = {
28 "average": 0.3,
29 "foo": "bar",
30 }
31 converted_amount = amount.in_currency("ETH", None)
32
33 self.assertEqual(3.0, converted_amount.value)
34 self.assertEqual("ETH", converted_amount.currency)
35 self.assertEqual(amount, converted_amount.linked_to)
36 self.assertEqual("bar", converted_amount.ticker["foo"])
37
38 @unittest.skip("TODO")
39 def test_get_ticker(self):
40 pass
41
42 def test__abs(self):
43 amount = portfolio.Amount("SC", -120)
44 self.assertEqual(120, abs(amount).value)
45 self.assertEqual("SC", abs(amount).currency)
46
47 amount = portfolio.Amount("SC", 10)
48 self.assertEqual(10, abs(amount).value)
49 self.assertEqual("SC", abs(amount).currency)
50
51 def test__add(self):
52 amount1 = portfolio.Amount("XVG", 12.9)
53 amount2 = portfolio.Amount("XVG", 13.1)
54
55 self.assertEqual(26, (amount1 + amount2).value)
56 self.assertEqual("XVG", (amount1 + amount2).currency)
57
58 amount3 = portfolio.Amount("ETH", 1.6)
59 with self.assertRaises(Exception):
60 amount1 + amount3
61
62 amount4 = portfolio.Amount("ETH", 0.0)
63 self.assertEqual(amount1, amount1 + amount4)
64
65 def test__radd(self):
66 amount = portfolio.Amount("XVG", 12.9)
67
68 self.assertEqual(amount, 0 + amount)
69 with self.assertRaises(Exception):
70 4 + amount
71
72 def test__sub(self):
73 amount1 = portfolio.Amount("XVG", 13.3)
74 amount2 = portfolio.Amount("XVG", 13.1)
75
76 self.assertEqual(0.2, (amount1 - amount2).value)
77 self.assertEqual("XVG", (amount1 - amount2).currency)
78
79 amount3 = portfolio.Amount("ETH", 1.6)
80 with self.assertRaises(Exception):
81 amount1 - amount3
82
83 amount4 = portfolio.Amount("ETH", 0.0)
84 self.assertEqual(amount1, amount1 - amount4)
85
86 def test__int(self):
87 amount = portfolio.Amount("XMR", 0.1)
88 self.assertEqual(100000000000000000, int(amount))
89
90 def test__mul(self):
91 amount = portfolio.Amount("XEM", 11)
92
93 self.assertEqual(38.5, (amount * 3.5).value)
94 self.assertEqual(33, (amount * 3).value)
95
96 with self.assertRaises(Exception):
97 amount * amount
98
99 def test__rmul(self):
100 amount = portfolio.Amount("XEM", 11)
101
102 self.assertEqual(38.5, (3.5 * amount).value)
103 self.assertEqual(33, (3 * amount).value)
104
105 def test__floordiv(self):
106 amount = portfolio.Amount("XEM", 11)
107
108 self.assertEqual(5.5, (amount // 2).value)
109 with self.assertRaises(TypeError):
110 amount // 2.5
111 self.assertEqual(1571428571428571428, (amount // 7)._value)
112
113 def test__div(self):
114 amount = portfolio.Amount("XEM", 11)
115
116 with self.assertRaises(TypeError):
117 amount / 2.5
118 self.assertEqual(5.5, (amount / 2).value)
119 self.assertEqual(1571428571428571428, (amount / 7)._value)
120
121 def test__lt(self):
122 amount1 = portfolio.Amount("BTD", 11.3)
123 amount2 = portfolio.Amount("BTD", 13.1)
124
125 self.assertTrue(amount1 < amount2)
126 self.assertFalse(amount2 < amount1)
127 self.assertFalse(amount1 < amount1)
128
129 amount3 = portfolio.Amount("BTC", 1.6)
130 with self.assertRaises(Exception):
131 amount1 < amount3
132
133 def test__eq(self):
134 amount1 = portfolio.Amount("BTD", 11.3)
135 amount2 = portfolio.Amount("BTD", 13.1)
136 amount3 = portfolio.Amount("BTD", 11.3)
137
138 self.assertFalse(amount1 == amount2)
139 self.assertFalse(amount2 == amount1)
140 self.assertTrue(amount1 == amount3)
141 self.assertFalse(amount2 == 0)
142
143 amount4 = portfolio.Amount("BTC", 1.6)
144 with self.assertRaises(Exception):
145 amount1 == amount4
146
147 amount5 = portfolio.Amount("BTD", 0)
148 self.assertTrue(amount5 == 0)
149
150 def test__str(self):
151 amount1 = portfolio.Amount("BTX", 32)
152 self.assertEqual("32.00000000 BTX", str(amount1))
153
154 amount2 = portfolio.Amount("USDT", 12000)
155 amount1.linked_to = amount2
156 self.assertEqual("32.00000000 BTX [12000.00000000 USDT]", str(amount1))
157
158 def test__repr(self):
159 amount1 = portfolio.Amount("BTX", 32)
160 self.assertEqual("Amount(32.00000000 BTX)", repr(amount1))
161
162 amount2 = portfolio.Amount("USDT", 12000)
163 amount1.linked_to = amount2
164 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT))", repr(amount1))
165
166 amount3 = portfolio.Amount("BTC", 0.1)
167 amount2.linked_to = amount3
168 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1))
169
170 class PortfolioTest(unittest.TestCase):
171 import urllib3
172 def fill_data(self):
173 if self.json_response is not None:
174 portfolio.Portfolio.data = self.json_response
175
176 def setUp(self):
177 super(PortfolioTest, self).setUp()
178
179 with open("test_portfolio.json") as example:
180 import json
181 self.json_response = json.load(example)
182
183 self.patcher = mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={})
184 self.patcher.start()
185
186 @mock.patch.object(urllib3, "disable_warnings")
187 @mock.patch.object(urllib3.poolmanager.PoolManager, "request")
188 @mock.patch.object(portfolio.Portfolio, "URL", new="foo://bar")
189 def test_get_cryptoportfolio(self, request, disable_warnings):
190 request.side_effect = [
191 type('', (), { "data": '{ "foo": "bar" }' }),
192 type('', (), { "data": 'System Error' }),
193 Exception("Connection error"),
194 ]
195
196 portfolio.Portfolio.get_cryptoportfolio()
197 self.assertIn("foo", portfolio.Portfolio.data)
198 self.assertEqual("bar", portfolio.Portfolio.data["foo"])
199 request.assert_called_with("GET", "foo://bar")
200
201 request.reset_mock()
202 portfolio.Portfolio.get_cryptoportfolio()
203 self.assertIsNone(portfolio.Portfolio.data)
204 request.assert_called_with("GET", "foo://bar")
205
206 request.reset_mock()
207 portfolio.Portfolio.data = "foo"
208 portfolio.Portfolio.get_cryptoportfolio()
209 request.assert_called_with("GET", "foo://bar")
210 self.assertEqual("foo", portfolio.Portfolio.data)
211 disable_warnings.assert_called_with()
212
213 @mock.patch.object(portfolio.Portfolio, "get_cryptoportfolio")
214 def test_parse_cryptoportfolio(self, mock_get):
215 mock_get.side_effect = self.fill_data
216
217 portfolio.Portfolio.parse_cryptoportfolio()
218
219 self.assertListEqual(
220 ["medium", "high"],
221 list(portfolio.Portfolio.liquidities.keys()))
222
223 liquidities = portfolio.Portfolio.liquidities
224 self.assertEqual(10, len(liquidities["medium"].keys()))
225 self.assertEqual(10, len(liquidities["high"].keys()))
226
227 expected = {'BTC': 2857, 'DGB': 1015, 'DOGE': 1805, 'SC': 623, 'ZEC': 3701}
228 self.assertDictEqual(expected, liquidities["high"]['2018-01-08'])
229
230 expected = {'ETC': 1000, 'FCT': 1000, 'GAS': 1000, 'NAV': 1000, 'OMG': 1000, 'OMNI': 1000, 'PPC': 1000, 'RIC': 1000, 'VIA': 1000, 'XCP': 1000}
231 self.assertDictEqual(expected, liquidities["medium"]['2018-01-08'])
232
233 # It doesn't refetch the data when available
234 portfolio.Portfolio.parse_cryptoportfolio()
235 mock_get.assert_called_once_with()
236
237 portfolio.Portfolio.data["portfolio_1"]["holding"]["direction"][3] = "short"
238 self.assertRaises(AssertionError, portfolio.Portfolio.parse_cryptoportfolio)
239
240 @mock.patch.object(portfolio.Portfolio, "get_cryptoportfolio")
241 def test_repartition_pertenthousand(self, mock_get):
242 mock_get.side_effect = self.fill_data
243
244 expected_medium = {'USDT': 1000, 'ETC': 1000, 'FCT': 1000, 'OMG': 1000, 'STEEM': 1000, 'STRAT': 1000, 'XEM': 1000, 'XMR': 1000, 'XVC': 1000, 'ZRX': 1000}
245 expected_high = {'USDT': 1226, 'BTC': 1429, 'ETC': 1127, 'ETH': 1569, 'FCT': 3341, 'GAS': 1308}
246
247 self.assertEqual(expected_medium, portfolio.Portfolio.repartition_pertenthousand())
248 self.assertEqual(expected_medium, portfolio.Portfolio.repartition_pertenthousand(liquidity="medium"))
249 self.assertEqual(expected_high, portfolio.Portfolio.repartition_pertenthousand(liquidity="high"))
250
251 def tearDown(self):
252 self.patcher.stop()
253
254 class BalanceTest(unittest.TestCase):
255 def setUp(self):
256 super(BalanceTest, self).setUp()
257
258 self.fetch_balance = {
259 "free": "foo",
260 "info": "bar",
261 "used": "baz",
262 "total": "bazz",
263 "USDT": {
264 "free": 6.0,
265 "used": 1.2,
266 "total": 7.2
267 },
268 "XVG": {
269 "free": 16,
270 "used": 0.0,
271 "total": 16
272 },
273 "XMR": {
274 "free": 0.0,
275 "used": 0.0,
276 "total": 0.0
277 },
278 }
279 self.patcher = mock.patch.multiple(portfolio.Balance, known_balances={}, trades={})
280 self.patcher.start()
281
282 def test_values(self):
283 balance = portfolio.Balance("BTC", 0.65, 0.35, 0.30)
284 self.assertEqual(0.65, balance.total.value)
285 self.assertEqual(0.35, balance.free.value)
286 self.assertEqual(0.30, balance.used.value)
287 self.assertEqual("BTC", balance.currency)
288
289 balance = portfolio.Balance.from_hash("BTC", { "total": 0.65, "free": 0.35, "used": 0.30})
290 self.assertEqual(0.65, balance.total.value)
291 self.assertEqual(0.35, balance.free.value)
292 self.assertEqual(0.30, balance.used.value)
293 self.assertEqual("BTC", balance.currency)
294
295 @mock.patch.object(portfolio.Amount, "get_ticker")
296 def test_in_currency(self, get_ticker):
297 portfolio.Balance.known_balances = {
298 "BTC": portfolio.Balance("BTC", 0.65, 0.35, 0.30),
299 "ETH": portfolio.Balance("ETH", 3, 3, 0),
300 }
301 market = mock.Mock()
302 get_ticker.return_value = {
303 "bid": 0.09,
304 "ask": 0.11,
305 "average": 0.1,
306 }
307
308 amounts = portfolio.Balance.in_currency("BTC", market)
309 self.assertEqual("BTC", amounts["ETH"].currency)
310 self.assertEqual(0.65, amounts["BTC"].value)
311 self.assertEqual(0.30, amounts["ETH"].value)
312
313 amounts = portfolio.Balance.in_currency("BTC", market, action="bid")
314 self.assertEqual(0.65, amounts["BTC"].value)
315 self.assertEqual(0.27, amounts["ETH"].value)
316
317 amounts = portfolio.Balance.in_currency("BTC", market, action="bid", type="used")
318 self.assertEqual(0.30, amounts["BTC"].value)
319 self.assertEqual(0, amounts["ETH"].value)
320
321 def test_currencies(self):
322 portfolio.Balance.known_balances = {
323 "BTC": portfolio.Balance("BTC", 0.65, 0.35, 0.30),
324 "ETH": portfolio.Balance("ETH", 3, 3, 0),
325 }
326 self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies()))
327
328 @mock.patch.object(portfolio.market, "fetch_balance")
329 def test_fetch_balances(self, fetch_balance):
330 fetch_balance.return_value = self.fetch_balance
331
332 portfolio.Balance.fetch_balances(portfolio.market)
333 self.assertNotIn("XMR", portfolio.Balance.currencies())
334 self.assertEqual(["USDT", "XVG"], list(portfolio.Balance.currencies()))
335
336 @mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand")
337 @mock.patch.object(portfolio.market, "fetch_balance")
338 def test_dispatch_assets(self, fetch_balance, repartition):
339 fetch_balance.return_value = self.fetch_balance
340 portfolio.Balance.fetch_balances(portfolio.market)
341
342 self.assertNotIn("XEM", portfolio.Balance.currencies())
343
344 repartition.return_value = {
345 "XEM": 7500,
346 "BTC": 2600,
347 }
348
349 amounts = portfolio.Balance.dispatch_assets(portfolio.Amount("BTC", 10.1))
350 self.assertIn("XEM", portfolio.Balance.currencies())
351 self.assertEqual(2.6, amounts["BTC"].value)
352 self.assertEqual(7.5, amounts["XEM"].value)
353
354 @mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand")
355 @mock.patch.object(portfolio.Amount, "get_ticker")
356 @mock.patch.object(portfolio.Trade, "compute_trades")
357 def test_prepare_trades(self, compute_trades, get_ticker, repartition):
358 repartition.return_value = {
359 "XEM": 7500,
360 "BTC": 2500,
361 }
362 get_ticker.side_effect = [
363 { "average": 0.0001 },
364 { "average": 0.000001 }
365 ]
366 market = mock.Mock()
367 market.fetch_balance.return_value = {
368 "USDT": {
369 "free": 10000.0,
370 "used": 0.0,
371 "total": 10000.0
372 },
373 "XVG": {
374 "free": 10000.0,
375 "used": 0.0,
376 "total": 10000.0
377 },
378 }
379 portfolio.Balance.prepare_trades(market)
380 compute_trades.assert_called()
381
382 call = compute_trades.call_args
383 self.assertEqual(market, call[1]["market"])
384 self.assertEqual(1, call[0][0]["USDT"].value)
385 self.assertEqual(0.01, call[0][0]["XVG"].value)
386 self.assertEqual(0.2525, call[0][1]["BTC"].value)
387 self.assertEqual(0.7575, call[0][1]["XEM"].value)
388
389 def test__repr(self):
390 balance = portfolio.Balance("BTX", 3, 1, 2)
391 self.assertEqual("Balance(BTX [1.00000000 BTX/2.00000000 BTX/3.00000000 BTX])", repr(balance))
392
393 def tearDown(self):
394 self.patcher.stop()
395
396 if __name__ == '__main__':
397 unittest.main()