3 from unittest
import mock
5 class AmountTest(unittest
.TestCase
):
7 amount
= portfolio
.Amount("BTC", 0.65)
8 self
.assertEqual(0.65, amount
.value
)
9 self
.assertEqual("BTC", amount
.currency
)
11 amount
= portfolio
.Amount("BTC", 10, int_val
=2000000000000000)
12 self
.assertEqual(0.002, amount
.value
)
14 def test_in_currency(self
):
15 amount
= portfolio
.Amount("ETC", 10)
17 self
.assertEqual(amount
, amount
.in_currency("ETC", None))
19 ticker_mock
= unittest
.mock
.Mock()
20 with mock
.patch
.object(portfolio
.Trade
, 'get_ticker', new
=ticker_mock
):
21 ticker_mock
.return_value
= None
23 self
.assertRaises(Exception, amount
.in_currency
, "ETH", None)
25 with mock
.patch
.object(portfolio
.Trade
, 'get_ticker', new
=ticker_mock
):
26 ticker_mock
.return_value
= {
30 converted_amount
= amount
.in_currency("ETH", None)
32 self
.assertEqual(3.0, converted_amount
.value
)
33 self
.assertEqual("ETH", converted_amount
.currency
)
34 self
.assertEqual(amount
, converted_amount
.linked_to
)
35 self
.assertEqual("bar", converted_amount
.ticker
["foo"])
38 amount
= portfolio
.Amount("SC", -120)
39 self
.assertEqual(120, abs(amount
).value
)
40 self
.assertEqual("SC", abs(amount
).currency
)
42 amount
= portfolio
.Amount("SC", 10)
43 self
.assertEqual(10, abs(amount
).value
)
44 self
.assertEqual("SC", abs(amount
).currency
)
47 amount1
= portfolio
.Amount("XVG", 12.9)
48 amount2
= portfolio
.Amount("XVG", 13.1)
50 self
.assertEqual(26, (amount1
+ amount2
).value
)
51 self
.assertEqual("XVG", (amount1
+ amount2
).currency
)
53 amount3
= portfolio
.Amount("ETH", 1.6)
54 with self
.assertRaises(Exception):
57 amount4
= portfolio
.Amount("ETH", 0.0)
58 self
.assertEqual(amount1
, amount1
+ amount4
)
61 amount
= portfolio
.Amount("XVG", 12.9)
63 self
.assertEqual(amount
, 0 + amount
)
64 with self
.assertRaises(Exception):
68 amount1
= portfolio
.Amount("XVG", 13.3)
69 amount2
= portfolio
.Amount("XVG", 13.1)
71 self
.assertEqual(0.2, (amount1
- amount2
).value
)
72 self
.assertEqual("XVG", (amount1
- amount2
).currency
)
74 amount3
= portfolio
.Amount("ETH", 1.6)
75 with self
.assertRaises(Exception):
78 amount4
= portfolio
.Amount("ETH", 0.0)
79 self
.assertEqual(amount1
, amount1
- amount4
)
82 amount
= portfolio
.Amount("XMR", 0.1)
83 self
.assertEqual(100000000000000000, int(amount
))
86 amount
= portfolio
.Amount("XEM", 11)
88 self
.assertEqual(38.5, (amount
* 3.5).value
)
89 self
.assertEqual(33, (amount
* 3).value
)
91 with self
.assertRaises(Exception):
95 amount
= portfolio
.Amount("XEM", 11)
97 self
.assertEqual(38.5, (3.5 * amount
).value
)
98 self
.assertEqual(33, (3 * amount
).value
)
100 def test__floordiv(self
):
101 amount
= portfolio
.Amount("XEM", 11)
103 self
.assertEqual(5.5, (amount
// 2).value
)
104 with self
.assertRaises(TypeError):
106 self
.assertEqual(1571428571428571428, (amount
// 7)._value
)
109 amount
= portfolio
.Amount("XEM", 11)
111 with self
.assertRaises(TypeError):
113 self
.assertEqual(5.5, (amount
/ 2).value
)
114 self
.assertEqual(1571428571428571428, (amount
/ 7)._value
)
117 amount1
= portfolio
.Amount("BTD", 11.3)
118 amount2
= portfolio
.Amount("BTD", 13.1)
120 self
.assertTrue(amount1
< amount2
)
121 self
.assertFalse(amount2
< amount1
)
122 self
.assertFalse(amount1
< amount1
)
124 amount3
= portfolio
.Amount("BTC", 1.6)
125 with self
.assertRaises(Exception):
129 amount1
= portfolio
.Amount("BTD", 11.3)
130 amount2
= portfolio
.Amount("BTD", 13.1)
131 amount3
= portfolio
.Amount("BTD", 11.3)
133 self
.assertFalse(amount1
== amount2
)
134 self
.assertFalse(amount2
== amount1
)
135 self
.assertTrue(amount1
== amount3
)
136 self
.assertFalse(amount2
== 0)
138 amount4
= portfolio
.Amount("BTC", 1.6)
139 with self
.assertRaises(Exception):
142 amount5
= portfolio
.Amount("BTD", 0)
143 self
.assertTrue(amount5
== 0)
146 amount1
= portfolio
.Amount("BTX", 32)
147 self
.assertEqual("32.00000000 BTX", str(amount1
))
149 amount2
= portfolio
.Amount("USDT", 12000)
150 amount1
.linked_to
= amount2
151 self
.assertEqual("32.00000000 BTX [12000.00000000 USDT]", str(amount1
))
153 def test__repr(self
):
154 amount1
= portfolio
.Amount("BTX", 32)
155 self
.assertEqual("Amount(32.00000000 BTX)", repr(amount1
))
157 amount2
= portfolio
.Amount("USDT", 12000)
158 amount1
.linked_to
= amount2
159 self
.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT))", repr(amount1
))
161 amount3
= portfolio
.Amount("BTC", 0.1)
162 amount2
.linked_to
= amount3
163 self
.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1
))
165 class PortfolioTest(unittest
.TestCase
):
168 if self
.json_response
is not None:
169 portfolio
.Portfolio
.data
= self
.json_response
172 super(PortfolioTest
, self
).setUp()
174 with open("test_portfolio.json") as example
:
176 self
.json_response
= json
.load(example
)
178 self
.patcher
= mock
.patch
.multiple(portfolio
.Portfolio
, data
=None, liquidities
={})
181 @mock.patch.object(urllib3
, "disable_warnings")
182 @mock.patch.object(urllib3
.poolmanager
.PoolManager
, "request")
183 @mock.patch.object(portfolio
.Portfolio
, "URL", new
="foo://bar")
184 def test_get_cryptoportfolio(self
, request
, disable_warnings
):
185 request
.side_effect
= [
186 type('', (), { "data": '{ "foo": "bar" }
' }),
187 type('', (), { "data": 'System Error' }),
188 Exception("Connection error"),
191 portfolio.Portfolio.get_cryptoportfolio()
192 self.assertIn("foo", portfolio.Portfolio.data)
193 self.assertEqual("bar", portfolio.Portfolio.data["foo"])
194 request.assert_called_with("GET", "foo://bar")
197 portfolio.Portfolio.get_cryptoportfolio()
198 self.assertIsNone(portfolio.Portfolio.data)
199 request.assert_called_with("GET", "foo://bar")
202 portfolio.Portfolio.data = "foo"
203 portfolio.Portfolio.get_cryptoportfolio()
204 request.assert_called_with("GET", "foo://bar")
205 self.assertEqual("foo", portfolio.Portfolio.data)
206 disable_warnings.assert_called_with()
208 @mock.patch.object(portfolio.Portfolio, "get_cryptoportfolio")
209 def test_parse_cryptoportfolio(self, mock_get):
210 mock_get.side_effect = self.fill_data
212 portfolio.Portfolio.parse_cryptoportfolio()
214 self.assertListEqual(
216 list(portfolio.Portfolio.liquidities.keys()))
218 liquidities = portfolio.Portfolio.liquidities
219 self.assertEqual(10, len(liquidities["medium"].keys()))
220 self.assertEqual(10, len(liquidities["high"].keys()))
222 expected = {'BTC': 2857, 'DGB': 1015, 'DOGE': 1805, 'SC': 623, 'ZEC': 3701}
223 self.assertDictEqual(expected, liquidities["high"]['2018-01-08'])
225 expected = {'ETC': 1000, 'FCT': 1000, 'GAS': 1000, 'NAV': 1000, 'OMG': 1000, 'OMNI': 1000, 'PPC': 1000, 'RIC': 1000, 'VIA': 1000, 'XCP': 1000}
226 self.assertDictEqual(expected, liquidities["medium"]['2018-01-08'])
228 # It doesn't refetch the data when available
229 portfolio
.Portfolio
.parse_cryptoportfolio()
230 mock_get
.assert_called_once_with()
232 portfolio
.Portfolio
.data
["portfolio_1"]["holding"]["direction"][3] = "short"
233 self
.assertRaises(AssertionError, portfolio
.Portfolio
.parse_cryptoportfolio
)
235 @mock.patch.object(portfolio
.Portfolio
, "get_cryptoportfolio")
236 def test_repartition_pertenthousand(self
, mock_get
):
237 mock_get
.side_effect
= self
.fill_data
239 expected_medium
= {'USDT': 1000, 'ETC': 1000, 'FCT': 1000, 'OMG': 1000, 'STEEM': 1000, 'STRAT': 1000, 'XEM': 1000, 'XMR': 1000, 'XVC': 1000, 'ZRX': 1000}
240 expected_high
= {'USDT': 1226, 'BTC': 1429, 'ETC': 1127, 'ETH': 1569, 'FCT': 3341, 'GAS': 1308}
242 self
.assertEqual(expected_medium
, portfolio
.Portfolio
.repartition_pertenthousand())
243 self
.assertEqual(expected_medium
, portfolio
.Portfolio
.repartition_pertenthousand(liquidity
="medium"))
244 self
.assertEqual(expected_high
, portfolio
.Portfolio
.repartition_pertenthousand(liquidity
="high"))
249 class BalanceTest(unittest
.TestCase
):
251 super(BalanceTest
, self
).setUp()
253 self
.fetch_balance
= {
274 self
.patcher
= mock
.patch
.multiple(portfolio
.Balance
, known_balances
={})
277 def test_values(self
):
278 balance
= portfolio
.Balance("BTC", 0.65, 0.35, 0.30)
279 self
.assertEqual(0.65, balance
.total
.value
)
280 self
.assertEqual(0.35, balance
.free
.value
)
281 self
.assertEqual(0.30, balance
.used
.value
)
282 self
.assertEqual("BTC", balance
.currency
)
284 balance
= portfolio
.Balance
.from_hash("BTC", { "total": 0.65, "free": 0.35, "used": 0.30}
)
285 self
.assertEqual(0.65, balance
.total
.value
)
286 self
.assertEqual(0.35, balance
.free
.value
)
287 self
.assertEqual(0.30, balance
.used
.value
)
288 self
.assertEqual("BTC", balance
.currency
)
290 @mock.patch.object(portfolio
.Trade
, "get_ticker")
291 def test_in_currency(self
, get_ticker
):
292 portfolio
.Balance
.known_balances
= {
293 "BTC": portfolio
.Balance("BTC", 0.65, 0.35, 0.30),
294 "ETH": portfolio
.Balance("ETH", 3, 3, 0),
297 get_ticker
.return_value
= {
303 amounts
= portfolio
.Balance
.in_currency("BTC", market
)
304 self
.assertEqual("BTC", amounts
["ETH"].currency
)
305 self
.assertEqual(0.65, amounts
["BTC"].value
)
306 self
.assertEqual(0.30, amounts
["ETH"].value
)
308 amounts
= portfolio
.Balance
.in_currency("BTC", market
, action
="bid")
309 self
.assertEqual(0.65, amounts
["BTC"].value
)
310 self
.assertEqual(0.27, amounts
["ETH"].value
)
312 amounts
= portfolio
.Balance
.in_currency("BTC", market
, action
="bid", type="used")
313 self
.assertEqual(0.30, amounts
["BTC"].value
)
314 self
.assertEqual(0, amounts
["ETH"].value
)
316 def test_currencies(self
):
317 portfolio
.Balance
.known_balances
= {
318 "BTC": portfolio
.Balance("BTC", 0.65, 0.35, 0.30),
319 "ETH": portfolio
.Balance("ETH", 3, 3, 0),
321 self
.assertListEqual(["BTC", "ETH"], list(portfolio
.Balance
.currencies()))
323 @mock.patch.object(portfolio
.market
, "fetch_balance")
324 def test_fetch_balances(self
, fetch_balance
):
325 fetch_balance
.return_value
= self
.fetch_balance
327 portfolio
.Balance
.fetch_balances(portfolio
.market
)
328 self
.assertNotIn("XMR", portfolio
.Balance
.currencies())
329 self
.assertEqual(["USDT", "XVG"], list(portfolio
.Balance
.currencies()))
331 @mock.patch.object(portfolio
.Portfolio
, "repartition_pertenthousand")
332 @mock.patch.object(portfolio
.market
, "fetch_balance")
333 def test_dispatch_assets(self
, fetch_balance
, repartition
):
334 fetch_balance
.return_value
= self
.fetch_balance
335 portfolio
.Balance
.fetch_balances(portfolio
.market
)
337 self
.assertNotIn("XEM", portfolio
.Balance
.currencies())
339 repartition
.return_value
= {
344 amounts
= portfolio
.Balance
.dispatch_assets(portfolio
.Amount("BTC", 10.1))
345 self
.assertIn("XEM", portfolio
.Balance
.currencies())
346 self
.assertEqual(2.6, amounts
["BTC"].value
)
347 self
.assertEqual(7.5, amounts
["XEM"].value
)
349 @mock.patch.object(portfolio
.Portfolio
, "repartition_pertenthousand")
350 @mock.patch.object(portfolio
.Trade
, "get_ticker")
351 @mock.patch.object(portfolio
.Trade
, "compute_trades")
352 def test_prepare_trades(self
, compute_trades
, get_ticker
, repartition
):
353 repartition
.return_value
= {
357 get_ticker
.side_effect
= [
358 { "average": 0.0001 }
,
359 { "average": 0.000001 }
362 market
.fetch_balance
.return_value
= {
374 portfolio
.Balance
.prepare_trades(market
)
375 compute_trades
.assert_called()
377 call
= compute_trades
.call_args
378 self
.assertEqual(market
, call
[1]["market"])
379 self
.assertEqual(1, call
[0][0]["USDT"].value
)
380 self
.assertEqual(0.01, call
[0][0]["XVG"].value
)
381 self
.assertEqual(0.2525, call
[0][1]["BTC"].value
)
382 self
.assertEqual(0.7575, call
[0][1]["XEM"].value
)
384 def test__repr(self
):
385 balance
= portfolio
.Balance("BTX", 3, 1, 2)
386 self
.assertEqual("Balance(BTX [1.00000000 BTX/2.00000000 BTX/3.00000000 BTX])", repr(balance
))
391 class TradeTest(unittest
.TestCase
):
395 super(TradeTest
, self
).setUp()
397 self
.patcher
= mock
.patch
.multiple(portfolio
.Trade
,
399 ticker_cache_timestamp
=self
.time
.time(),
404 def test_get_ticker(self
):
406 market
.fetch_ticker
.side_effect
= [
407 { "bid": 1, "ask": 3 }
,
408 portfolio
.ccxt
.ExchangeError("foo"),
409 { "bid": 10, "ask": 40 }
,
410 portfolio
.ccxt
.ExchangeError("foo"),
411 portfolio
.ccxt
.ExchangeError("foo"),
414 ticker
= portfolio
.Trade
.get_ticker("ETH", "ETC", market
)
415 market
.fetch_ticker
.assert_called_with("ETH/ETC")
416 self
.assertEqual(1, ticker
["bid"])
417 self
.assertEqual(3, ticker
["ask"])
418 self
.assertEqual(2, ticker
["average"])
419 self
.assertFalse(ticker
["inverted"])
421 ticker
= portfolio
.Trade
.get_ticker("ETH", "XVG", market
)
422 self
.assertEqual(0.0625, ticker
["average"])
423 self
.assertTrue(ticker
["inverted"])
424 self
.assertIn("original", ticker
)
425 self
.assertEqual(10, ticker
["original"]["bid"])
427 ticker
= portfolio
.Trade
.get_ticker("XVG", "XMR", market
)
428 self
.assertIsNone(ticker
)
430 market
.fetch_ticker
.assert_has_calls([
431 mock
.call("ETH/ETC"),
432 mock
.call("ETH/XVG"),
433 mock
.call("XVG/ETH"),
434 mock
.call("XVG/XMR"),
435 mock
.call("XMR/XVG"),
438 market2
= mock
.Mock()
439 market2
.fetch_ticker
.side_effect
= [
440 { "bid": 1, "ask": 3 }
,
441 { "bid": 1.2, "ask": 3.5 }
,
443 ticker1
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
)
444 ticker2
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
)
445 ticker3
= portfolio
.Trade
.get_ticker("ETC", "ETH", market2
)
446 market2
.fetch_ticker
.assert_called_once_with("ETH/ETC")
447 self
.assertEqual(1, ticker1
["bid"])
448 self
.assertDictEqual(ticker1
, ticker2
)
449 self
.assertDictEqual(ticker1
, ticker3
["original"])
451 ticker4
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
, refresh
=True)
452 ticker5
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
)
453 self
.assertEqual(1.2, ticker4
["bid"])
454 self
.assertDictEqual(ticker4
, ticker5
)
456 market3
= mock
.Mock()
457 market3
.fetch_ticker
.side_effect
= [
458 { "bid": 1, "ask": 3 }
,
459 { "bid": 1.2, "ask": 3.5 }
,
461 ticker6
= portfolio
.Trade
.get_ticker("ETH", "ETC", market3
)
462 portfolio
.Trade
.ticker_cache_timestamp
-= 4
463 ticker7
= portfolio
.Trade
.get_ticker("ETH", "ETC", market3
)
464 portfolio
.Trade
.ticker_cache_timestamp
-= 2
465 ticker8
= portfolio
.Trade
.get_ticker("ETH", "ETC", market3
)
466 self
.assertDictEqual(ticker6
, ticker7
)
467 self
.assertEqual(1.2, ticker8
["bid"])
469 @unittest.skip("TODO")
470 def test_values_assertion(self
):
473 @unittest.skip("TODO")
474 def test_fetch_fees(self
):
477 @unittest.skip("TODO")
478 def test_compute_trades(self
):
481 @unittest.skip("TODO")
482 def test_action(self
):
485 @unittest.skip("TODO")
486 def test_action(self
):
489 @unittest.skip("TODO")
490 def test_order_action(self
):
493 @unittest.skip("TODO")
494 def test_prepare_order(self
):
497 @unittest.skip("TODO")
498 def test_all_orders(self
):
501 @unittest.skip("TODO")
502 def test_follow_orders(self
):
505 @unittest.skip("TODO")
506 def test__repr(self
):
512 if __name__
== '__main__':