3 from decimal
import Decimal
as D
4 from unittest
import mock
8 class WebMockTestCase(unittest
.TestCase
):
12 super(WebMockTestCase
, self
).setUp()
13 self
.wm
= requests_mock
.Mocker()
17 mock
.patch
.multiple(portfolio
.Balance
, known_balances
={}),
18 mock
.patch
.multiple(portfolio
.Portfolio
, data
=None, liquidities
={}),
19 mock
.patch
.multiple(portfolio
.Trade
,
21 ticker_cache_timestamp
=self
.time
.time(),
24 mock
.patch
.multiple(portfolio
.Computation
,
25 computations
=portfolio
.Computation
.computations
)
27 for patcher
in self
.patchers
:
32 for patcher
in self
.patchers
:
35 super(WebMockTestCase
, self
).tearDown()
37 class PortfolioTest(WebMockTestCase
):
39 if self
.json_response
is not None:
40 portfolio
.Portfolio
.data
= self
.json_response
43 super(PortfolioTest
, self
).setUp()
45 with open("test_portfolio.json") as example
:
46 self
.json_response
= example
.read()
48 self
.wm
.get(portfolio
.Portfolio
.URL
, text
=self
.json_response
)
50 def test_get_cryptoportfolio(self
):
51 self
.wm
.get(portfolio
.Portfolio
.URL
, [
52 {"text":'{ "foo": "bar" }
', "status_code": 200},
53 {"text": "System Error", "status_code": 500},
54 {"exc": requests.exceptions.ConnectTimeout},
56 portfolio.Portfolio.get_cryptoportfolio()
57 self.assertIn("foo", portfolio.Portfolio.data)
58 self.assertEqual("bar", portfolio.Portfolio.data["foo"])
59 self.assertTrue(self.wm.called)
60 self.assertEqual(1, self.wm.call_count)
62 portfolio.Portfolio.get_cryptoportfolio()
63 self.assertIsNone(portfolio.Portfolio.data)
64 self.assertEqual(2, self.wm.call_count)
66 portfolio.Portfolio.data = "Foo"
67 portfolio.Portfolio.get_cryptoportfolio()
68 self.assertEqual("Foo", portfolio.Portfolio.data)
69 self.assertEqual(3, self.wm.call_count)
71 def test_parse_cryptoportfolio(self):
72 portfolio.Portfolio.parse_cryptoportfolio()
76 list(portfolio.Portfolio.liquidities.keys()))
78 liquidities = portfolio.Portfolio.liquidities
79 self.assertEqual(10, len(liquidities["medium"].keys()))
80 self.assertEqual(10, len(liquidities["high"].keys()))
83 'BTC
': (D("0.2857"), "long"),
84 'DGB
': (D("0.1015"), "long"),
85 'DOGE
': (D("0.1805"), "long"),
86 'SC
': (D("0.0623"), "long"),
87 'ZEC
': (D("0.3701"), "long"),
89 self.assertDictEqual(expected, liquidities["high"]['2018-01-08'])
92 'BTC
': (D("1.1102e-16"), "long"),
93 'ETC
': (D("0.1"), "long"),
94 'FCT
': (D("0.1"), "long"),
95 'GAS
': (D("0.1"), "long"),
96 'NAV
': (D("0.1"), "long"),
97 'OMG
': (D("0.1"), "long"),
98 'OMNI
': (D("0.1"), "long"),
99 'PPC
': (D("0.1"), "long"),
100 'RIC
': (D("0.1"), "long"),
101 'VIA
': (D("0.1"), "long"),
102 'XCP
': (D("0.1"), "long"),
104 self.assertDictEqual(expected, liquidities["medium"]['2018-01-08'])
106 # It doesn't refetch the data when available
107 portfolio
.Portfolio
.parse_cryptoportfolio()
109 self
.assertEqual(1, self
.wm
.call_count
)
111 def test_repartition(self
):
113 'BTC': (D("1.1102e-16"), "long"),
114 'USDT': (D("0.1"), "long"),
115 'ETC': (D("0.1"), "long"),
116 'FCT': (D("0.1"), "long"),
117 'OMG': (D("0.1"), "long"),
118 'STEEM': (D("0.1"), "long"),
119 'STRAT': (D("0.1"), "long"),
120 'XEM': (D("0.1"), "long"),
121 'XMR': (D("0.1"), "long"),
122 'XVC': (D("0.1"), "long"),
123 'ZRX': (D("0.1"), "long"),
126 'USDT': (D("0.1226"), "long"),
127 'BTC': (D("0.1429"), "long"),
128 'ETC': (D("0.1127"), "long"),
129 'ETH': (D("0.1569"), "long"),
130 'FCT': (D("0.3341"), "long"),
131 'GAS': (D("0.1308"), "long"),
134 self
.assertEqual(expected_medium
, portfolio
.Portfolio
.repartition())
135 self
.assertEqual(expected_medium
, portfolio
.Portfolio
.repartition(liquidity
="medium"))
136 self
.assertEqual(expected_high
, portfolio
.Portfolio
.repartition(liquidity
="high"))
138 class AmountTest(WebMockTestCase
):
139 def test_values(self
):
140 amount
= portfolio
.Amount("BTC", "0.65")
141 self
.assertEqual(D("0.65"), amount
.value
)
142 self
.assertEqual("BTC", amount
.currency
)
144 def test_in_currency(self
):
145 amount
= portfolio
.Amount("ETC", 10)
147 self
.assertEqual(amount
, amount
.in_currency("ETC", None))
149 ticker_mock
= unittest
.mock
.Mock()
150 with mock
.patch
.object(portfolio
.Trade
, 'get_ticker', new
=ticker_mock
):
151 ticker_mock
.return_value
= None
153 self
.assertRaises(Exception, amount
.in_currency
, "ETH", None)
155 with mock
.patch
.object(portfolio
.Trade
, 'get_ticker', new
=ticker_mock
):
156 ticker_mock
.return_value
= {
162 converted_amount
= amount
.in_currency("ETH", None)
164 self
.assertEqual(D("3.0"), converted_amount
.value
)
165 self
.assertEqual("ETH", converted_amount
.currency
)
166 self
.assertEqual(amount
, converted_amount
.linked_to
)
167 self
.assertEqual("bar", converted_amount
.ticker
["foo"])
169 converted_amount
= amount
.in_currency("ETH", None, action
="bid", compute_value
="default")
170 self
.assertEqual(D("2"), converted_amount
.value
)
172 converted_amount
= amount
.in_currency("ETH", None, compute_value
="ask")
173 self
.assertEqual(D("4"), converted_amount
.value
)
175 converted_amount
= amount
.in_currency("ETH", None, rate
=D("0.02"))
176 self
.assertEqual(D("0.2"), converted_amount
.value
)
178 def test__round(self
):
179 amount
= portfolio
.Amount("BAR", portfolio
.D("1.23456789876"))
180 self
.assertEqual(D("1.23456789"), round(amount
).value
)
181 self
.assertEqual(D("1.23"), round(amount
, 2).value
)
184 amount
= portfolio
.Amount("SC", -120)
185 self
.assertEqual(120, abs(amount
).value
)
186 self
.assertEqual("SC", abs(amount
).currency
)
188 amount
= portfolio
.Amount("SC", 10)
189 self
.assertEqual(10, abs(amount
).value
)
190 self
.assertEqual("SC", abs(amount
).currency
)
193 amount1
= portfolio
.Amount("XVG", "12.9")
194 amount2
= portfolio
.Amount("XVG", "13.1")
196 self
.assertEqual(26, (amount1
+ amount2
).value
)
197 self
.assertEqual("XVG", (amount1
+ amount2
).currency
)
199 amount3
= portfolio
.Amount("ETH", "1.6")
200 with self
.assertRaises(Exception):
203 amount4
= portfolio
.Amount("ETH", 0.0)
204 self
.assertEqual(amount1
, amount1
+ amount4
)
206 def test__radd(self
):
207 amount
= portfolio
.Amount("XVG", "12.9")
209 self
.assertEqual(amount
, 0 + amount
)
210 with self
.assertRaises(Exception):
214 amount1
= portfolio
.Amount("XVG", "13.3")
215 amount2
= portfolio
.Amount("XVG", "13.1")
217 self
.assertEqual(D("0.2"), (amount1
- amount2
).value
)
218 self
.assertEqual("XVG", (amount1
- amount2
).currency
)
220 amount3
= portfolio
.Amount("ETH", "1.6")
221 with self
.assertRaises(Exception):
224 amount4
= portfolio
.Amount("ETH", 0.0)
225 self
.assertEqual(amount1
, amount1
- amount4
)
228 amount
= portfolio
.Amount("XEM", 11)
230 self
.assertEqual(D("38.5"), (amount
* D("3.5")).value
)
231 self
.assertEqual(D("33"), (amount
* 3).value
)
233 with self
.assertRaises(Exception):
236 def test__rmul(self
):
237 amount
= portfolio
.Amount("XEM", 11)
239 self
.assertEqual(D("38.5"), (D("3.5") * amount
).value
)
240 self
.assertEqual(D("33"), (3 * amount
).value
)
242 def test__floordiv(self
):
243 amount
= portfolio
.Amount("XEM", 11)
245 self
.assertEqual(D("5.5"), (amount
/ 2).value
)
246 self
.assertEqual(D("4.4"), (amount
/ D("2.5")).value
)
248 def test__truediv(self
):
249 amount
= portfolio
.Amount("XEM", 11)
251 self
.assertEqual(D("5.5"), (amount
/ 2).value
)
252 self
.assertEqual(D("4.4"), (amount
/ D("2.5")).value
)
255 amount1
= portfolio
.Amount("BTD", 11.3)
256 amount2
= portfolio
.Amount("BTD", 13.1)
258 self
.assertTrue(amount1
< amount2
)
259 self
.assertFalse(amount2
< amount1
)
260 self
.assertFalse(amount1
< amount1
)
262 amount3
= portfolio
.Amount("BTC", 1.6)
263 with self
.assertRaises(Exception):
267 amount1
= portfolio
.Amount("BTD", 11.3)
268 amount2
= portfolio
.Amount("BTD", 13.1)
270 self
.assertTrue(amount1
<= amount2
)
271 self
.assertFalse(amount2
<= amount1
)
272 self
.assertTrue(amount1
<= amount1
)
274 amount3
= portfolio
.Amount("BTC", 1.6)
275 with self
.assertRaises(Exception):
279 amount1
= portfolio
.Amount("BTD", 11.3)
280 amount2
= portfolio
.Amount("BTD", 13.1)
282 self
.assertTrue(amount2
> amount1
)
283 self
.assertFalse(amount1
> amount2
)
284 self
.assertFalse(amount1
> amount1
)
286 amount3
= portfolio
.Amount("BTC", 1.6)
287 with self
.assertRaises(Exception):
291 amount1
= portfolio
.Amount("BTD", 11.3)
292 amount2
= portfolio
.Amount("BTD", 13.1)
294 self
.assertTrue(amount2
>= amount1
)
295 self
.assertFalse(amount1
>= amount2
)
296 self
.assertTrue(amount1
>= amount1
)
298 amount3
= portfolio
.Amount("BTC", 1.6)
299 with self
.assertRaises(Exception):
303 amount1
= portfolio
.Amount("BTD", 11.3)
304 amount2
= portfolio
.Amount("BTD", 13.1)
305 amount3
= portfolio
.Amount("BTD", 11.3)
307 self
.assertFalse(amount1
== amount2
)
308 self
.assertFalse(amount2
== amount1
)
309 self
.assertTrue(amount1
== amount3
)
310 self
.assertFalse(amount2
== 0)
312 amount4
= portfolio
.Amount("BTC", 1.6)
313 with self
.assertRaises(Exception):
316 amount5
= portfolio
.Amount("BTD", 0)
317 self
.assertTrue(amount5
== 0)
320 amount1
= portfolio
.Amount("BTD", 11.3)
321 amount2
= portfolio
.Amount("BTD", 13.1)
322 amount3
= portfolio
.Amount("BTD", 11.3)
324 self
.assertTrue(amount1
!= amount2
)
325 self
.assertTrue(amount2
!= amount1
)
326 self
.assertFalse(amount1
!= amount3
)
327 self
.assertTrue(amount2
!= 0)
329 amount4
= portfolio
.Amount("BTC", 1.6)
330 with self
.assertRaises(Exception):
333 amount5
= portfolio
.Amount("BTD", 0)
334 self
.assertFalse(amount5
!= 0)
337 amount1
= portfolio
.Amount("BTD", "11.3")
339 self
.assertEqual(portfolio
.D("-11.3"), (-amount1
).value
)
342 amount1
= portfolio
.Amount("BTX", 32)
343 self
.assertEqual("32.00000000 BTX", str(amount1
))
345 amount2
= portfolio
.Amount("USDT", 12000)
346 amount1
.linked_to
= amount2
347 self
.assertEqual("32.00000000 BTX [12000.00000000 USDT]", str(amount1
))
349 def test__repr(self
):
350 amount1
= portfolio
.Amount("BTX", 32)
351 self
.assertEqual("Amount(32.00000000 BTX)", repr(amount1
))
353 amount2
= portfolio
.Amount("USDT", 12000)
354 amount1
.linked_to
= amount2
355 self
.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT))", repr(amount1
))
357 amount3
= portfolio
.Amount("BTC", 0.1)
358 amount2
.linked_to
= amount3
359 self
.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1
))
361 class BalanceTest(WebMockTestCase
):
363 super(BalanceTest
, self
).setUp()
365 self
.fetch_balance
= {
392 def test_values(self
):
393 balance
= portfolio
.Balance("BTC", {
394 "exchange_total": "0.65",
395 "exchange_free": "0.35",
396 "exchange_used": "0.30",
397 "margin_total": "-10",
398 "margin_borrowed": "-10",
400 "margin_position_type": "short",
401 "margin_borrowed_base_currency": "USDT",
402 "margin_liquidation_price": "1.20",
403 "margin_pending_gain": "10",
404 "margin_lending_fees": "0.4",
405 "margin_borrowed_base_price": "0.15",
407 self
.assertEqual(portfolio
.D("0.65"), balance
.exchange_total
.value
)
408 self
.assertEqual(portfolio
.D("0.35"), balance
.exchange_free
.value
)
409 self
.assertEqual(portfolio
.D("0.30"), balance
.exchange_used
.value
)
410 self
.assertEqual("BTC", balance
.exchange_total
.currency
)
411 self
.assertEqual("BTC", balance
.exchange_free
.currency
)
412 self
.assertEqual("BTC", balance
.exchange_total
.currency
)
414 self
.assertEqual(portfolio
.D("-10"), balance
.margin_total
.value
)
415 self
.assertEqual(portfolio
.D("-10"), balance
.margin_borrowed
.value
)
416 self
.assertEqual(portfolio
.D("0"), balance
.margin_free
.value
)
417 self
.assertEqual("BTC", balance
.margin_total
.currency
)
418 self
.assertEqual("BTC", balance
.margin_borrowed
.currency
)
419 self
.assertEqual("BTC", balance
.margin_free
.currency
)
421 self
.assertEqual("BTC", balance
.currency
)
423 self
.assertEqual(portfolio
.D("0.4"), balance
.margin_lending_fees
.value
)
424 self
.assertEqual("USDT", balance
.margin_lending_fees
.currency
)
426 @mock.patch.object(portfolio
.Trade
, "get_ticker")
427 def test_in_currency(self
, get_ticker
):
428 portfolio
.Balance
.known_balances
= {
429 "BTC": portfolio
.Balance("BTC", {
431 "exchange_total":"0.65",
432 "exchange_free": "0.35",
433 "exchange_used": "0.30"}),
434 "ETH": portfolio
.Balance("ETH", {
438 "exchange_used": 0}),
441 get_ticker
.return_value
= {
447 amounts
= portfolio
.Balance
.in_currency("BTC", market
)
448 self
.assertEqual("BTC", amounts
["ETH"].currency
)
449 self
.assertEqual(D("0.65"), amounts
["BTC"].value
)
450 self
.assertEqual(D("0.30"), amounts
["ETH"].value
)
452 amounts
= portfolio
.Balance
.in_currency("BTC", market
, compute_value
="bid")
453 self
.assertEqual(D("0.65"), amounts
["BTC"].value
)
454 self
.assertEqual(D("0.27"), amounts
["ETH"].value
)
456 amounts
= portfolio
.Balance
.in_currency("BTC", market
, compute_value
="bid", type="exchange_used")
457 self
.assertEqual(D("0.30"), amounts
["BTC"].value
)
458 self
.assertEqual(0, amounts
["ETH"].value
)
460 def test_currencies(self
):
461 portfolio
.Balance
.known_balances
= {
462 "BTC": portfolio
.Balance("BTC", {
464 "exchange_total":"0.65",
465 "exchange_free": "0.35",
466 "exchange_used": "0.30"}),
467 "ETH": portfolio
.Balance("ETH", {
471 "exchange_used": 0}),
473 self
.assertListEqual(["BTC", "ETH"], list(portfolio
.Balance
.currencies()))
475 @unittest.expectedFailure
476 @mock.patch.object(portfolio
.market
, "fetch_balance")
477 def test_fetch_balances(self
, fetch_balance
):
478 fetch_balance
.return_value
= self
.fetch_balance
480 portfolio
.Balance
.fetch_balances(portfolio
.market
)
481 self
.assertNotIn("XMR", portfolio
.Balance
.currencies())
482 self
.assertListEqual(["USDT", "XVG"], list(portfolio
.Balance
.currencies()))
484 portfolio
.Balance
.known_balances
["ETC"] = portfolio
.Balance("ETC", "1", "0", "1")
485 portfolio
.Balance
.fetch_balances(portfolio
.market
)
486 self
.assertEqual(0, portfolio
.Balance
.known_balances
["ETC"].total
)
487 self
.assertListEqual(["USDT", "XVG", "ETC"], list(portfolio
.Balance
.currencies()))
489 @unittest.expectedFailure
490 @mock.patch.object(portfolio
.Portfolio
, "repartition")
491 @mock.patch.object(portfolio
.market
, "fetch_balance")
492 def test_dispatch_assets(self
, fetch_balance
, repartition
):
493 fetch_balance
.return_value
= self
.fetch_balance
494 portfolio
.Balance
.fetch_balances(portfolio
.market
)
496 self
.assertNotIn("XEM", portfolio
.Balance
.currencies())
498 repartition
.return_value
= {
499 "XEM": (D("0.75"), "long"),
500 "BTC": (D("0.26"), "long"),
503 amounts
= portfolio
.Balance
.dispatch_assets(portfolio
.Amount("BTC", "10.1"))
504 self
.assertIn("XEM", portfolio
.Balance
.currencies())
505 self
.assertEqual(D("2.6"), amounts
["BTC"].value
)
506 self
.assertEqual(D("7.5"), amounts
["XEM"].value
)
508 @unittest.expectedFailure
509 @mock.patch.object(portfolio
.Portfolio
, "repartition")
510 @mock.patch.object(portfolio
.Trade
, "get_ticker")
511 @mock.patch.object(portfolio
.Trade
, "compute_trades")
512 def test_prepare_trades(self
, compute_trades
, get_ticker
, repartition
):
513 repartition
.return_value
= {
514 "XEM": (D("0.75"), "long"),
515 "BTC": (D("0.25"), "long"),
517 def _get_ticker(c1
, c2
, market
):
518 if c1
== "USDT" and c2
== "BTC":
519 return { "average": D("0.0001") }
520 if c1
== "XVG" and c2
== "BTC":
521 return { "average": D("0.000001") }
522 if c1
== "XEM" and c2
== "BTC":
523 return { "average": D("0.001") }
524 self
.fail("Should be called with {}, {}".format(c1
, c2
))
525 get_ticker
.side_effect
= _get_ticker
528 market
.fetch_balance
.return_value
= {
530 "free": D("10000.0"),
532 "total": D("10000.0")
535 "free": D("10000.0"),
537 "total": D("10000.0")
540 portfolio
.Balance
.prepare_trades(market
)
541 compute_trades
.assert_called()
543 call
= compute_trades
.call_args
544 self
.assertEqual(market
, call
[1]["market"])
545 self
.assertEqual(1, call
[0][0]["USDT"].value
)
546 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
547 self
.assertEqual(D("0.2525"), call
[0][1]["BTC"].value
)
548 self
.assertEqual(D("0.7575"), call
[0][1]["XEM"].value
)
550 @unittest.skip("TODO")
551 def test_update_trades(self
):
554 @unittest.expectedFailure
555 def test__repr(self
):
556 balance
= portfolio
.Balance("BTX", 3, 1, 2)
557 self
.assertEqual("Balance(BTX [1.00000000 BTX/2.00000000 BTX/3.00000000 BTX])", repr(balance
))
559 class TradeTest(WebMockTestCase
):
561 def test_get_ticker(self
):
563 market
.fetch_ticker
.side_effect
= [
564 { "bid": 1, "ask": 3 }
,
565 portfolio
.ExchangeError("foo"),
566 { "bid": 10, "ask": 40 }
,
567 portfolio
.ExchangeError("foo"),
568 portfolio
.ExchangeError("foo"),
571 ticker
= portfolio
.Trade
.get_ticker("ETH", "ETC", market
)
572 market
.fetch_ticker
.assert_called_with("ETH/ETC")
573 self
.assertEqual(1, ticker
["bid"])
574 self
.assertEqual(3, ticker
["ask"])
575 self
.assertEqual(2, ticker
["average"])
576 self
.assertFalse(ticker
["inverted"])
578 ticker
= portfolio
.Trade
.get_ticker("ETH", "XVG", market
)
579 self
.assertEqual(0.0625, ticker
["average"])
580 self
.assertTrue(ticker
["inverted"])
581 self
.assertIn("original", ticker
)
582 self
.assertEqual(10, ticker
["original"]["bid"])
584 ticker
= portfolio
.Trade
.get_ticker("XVG", "XMR", market
)
585 self
.assertIsNone(ticker
)
587 market
.fetch_ticker
.assert_has_calls([
588 mock
.call("ETH/ETC"),
589 mock
.call("ETH/XVG"),
590 mock
.call("XVG/ETH"),
591 mock
.call("XVG/XMR"),
592 mock
.call("XMR/XVG"),
595 market2
= mock
.Mock()
596 market2
.fetch_ticker
.side_effect
= [
597 { "bid": 1, "ask": 3 }
,
598 { "bid": 1.2, "ask": 3.5 }
,
600 ticker1
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
)
601 ticker2
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
)
602 ticker3
= portfolio
.Trade
.get_ticker("ETC", "ETH", market2
)
603 market2
.fetch_ticker
.assert_called_once_with("ETH/ETC")
604 self
.assertEqual(1, ticker1
["bid"])
605 self
.assertDictEqual(ticker1
, ticker2
)
606 self
.assertDictEqual(ticker1
, ticker3
["original"])
608 ticker4
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
, refresh
=True)
609 ticker5
= portfolio
.Trade
.get_ticker("ETH", "ETC", market2
)
610 self
.assertEqual(1.2, ticker4
["bid"])
611 self
.assertDictEqual(ticker4
, ticker5
)
613 market3
= mock
.Mock()
614 market3
.fetch_ticker
.side_effect
= [
615 { "bid": 1, "ask": 3 }
,
616 { "bid": 1.2, "ask": 3.5 }
,
618 ticker6
= portfolio
.Trade
.get_ticker("ETH", "ETC", market3
)
619 portfolio
.Trade
.ticker_cache_timestamp
-= 4
620 ticker7
= portfolio
.Trade
.get_ticker("ETH", "ETC", market3
)
621 portfolio
.Trade
.ticker_cache_timestamp
-= 2
622 ticker8
= portfolio
.Trade
.get_ticker("ETH", "ETC", market3
)
623 self
.assertDictEqual(ticker6
, ticker7
)
624 self
.assertEqual(1.2, ticker8
["bid"])
626 @unittest.skip("TODO")
627 def test_values_assertion(self
):
628 value_from
= Amount("BTC", "1.0")
629 value_from
.linked_to
= Amount("ETH", "10.0")
630 value_to
= Amount("BTC", "1.0")
631 trade
= portfolioTrade(value_from
, value_to
, "ETH")
632 self
.assertEqual("BTC", trade
.base_currency
)
633 self
.assertEqual("ETH", trade
.currency
)
635 with self
.assertRaises(AssertionError):
636 portfolio
.Trade(value_from
, value_to
, "ETC")
637 with self
.assertRaises(AssertionError):
638 value_from
.linked_to
= None
639 portfolio
.Trade(value_from
, value_to
, "ETH")
640 with self
.assertRaises(AssertionError):
641 value_from
.currency
= "ETH"
642 portfolio
.Trade(value_from
, value_to
, "ETH")
644 @unittest.skip("TODO")
645 def test_fetch_fees(self
):
648 @unittest.skip("TODO")
649 def test_compute_trades(self
):
652 @unittest.skip("TODO")
653 def test_action(self
):
656 @unittest.skip("TODO")
657 def test_action(self
):
660 @unittest.skip("TODO")
661 def test_order_action(self
):
664 @unittest.skip("TODO")
665 def test_prepare_order(self
):
668 @unittest.skip("TODO")
669 def test_all_orders(self
):
672 @unittest.skip("TODO")
673 def test_follow_orders(self
):
676 @unittest.skip("TODO")
677 def test_compute_value(self
):
680 @unittest.skip("TODO")
681 def test__repr(self
):
684 class AcceptanceTest(WebMockTestCase
):
685 @unittest.expectedFailure
686 def test_success_sell_only_necessary(self
):
701 "total": D("1000.0"),
705 "ETH": (D("0.25"), "long"),
706 "ETC": (D("0.25"), "long"),
707 "BTC": (D("0.4"), "long"),
708 "BTD": (D("0.01"), "short"),
709 "B2X": (D("0.04"), "long"),
710 "USDT": (D("0.05"), "long"),
713 def fetch_ticker(symbol
):
714 if symbol
== "ETH/BTC":
720 if symbol
== "ETC/BTC":
726 if symbol
== "XVG/BTC":
732 if symbol
== "BTD/BTC":
738 if symbol
== "B2X/BTC":
744 if symbol
== "USDT/BTC":
745 raise portfolio
.ExchangeError
746 if symbol
== "BTC/USDT":
748 "symbol": "BTC/USDT",
752 self
.fail("Shouldn't have been called with {}".format(symbol
))
755 market
.fetch_balance
.return_value
= fetch_balance
756 market
.fetch_ticker
.side_effect
= fetch_ticker
757 with mock
.patch
.object(portfolio
.Portfolio
, "repartition", return_value
=repartition
):
759 portfolio
.Balance
.prepare_trades(market
)
761 balances
= portfolio
.Balance
.known_balances
762 self
.assertEqual(portfolio
.Amount("ETH", 1), balances
["ETH"].total
)
763 self
.assertEqual(portfolio
.Amount("ETC", 4), balances
["ETC"].total
)
764 self
.assertEqual(portfolio
.Amount("XVG", 1000), balances
["XVG"].total
)
767 trades
= portfolio
.Trade
.trades
768 self
.assertEqual(portfolio
.Amount("BTC", D("0.15")), trades
["ETH"].value_from
)
769 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
["ETH"].value_to
)
770 self
.assertEqual("sell", trades
["ETH"].action
)
772 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
["ETC"].value_from
)
773 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
["ETC"].value_to
)
774 self
.assertEqual("buy", trades
["ETC"].action
)
776 self
.assertNotIn("BTC", trades
)
778 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["BTD"].value_from
)
779 self
.assertEqual(portfolio
.Amount("BTC", D("0.002")), trades
["BTD"].value_to
)
780 self
.assertEqual("buy", trades
["BTD"].action
)
782 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["B2X"].value_from
)
783 self
.assertEqual(portfolio
.Amount("BTC", D("0.008")), trades
["B2X"].value_to
)
784 self
.assertEqual("buy", trades
["B2X"].action
)
786 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["USDT"].value_from
)
787 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
["USDT"].value_to
)
788 self
.assertEqual("buy", trades
["USDT"].action
)
790 self
.assertEqual(portfolio
.Amount("BTC", D("0.04")), trades
["XVG"].value_from
)
791 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["XVG"].value_to
)
792 self
.assertEqual("sell", trades
["XVG"].action
)
795 portfolio
.Trade
.prepare_orders(only
="sell", compute_value
=lambda x
, y
: x
["bid"] * D("1.001"))
797 all_orders
= portfolio
.Trade
.all_orders()
798 self
.assertEqual(2, len(all_orders
))
799 self
.assertEqual(2, 3*all_orders
[0].amount
.value
)
800 self
.assertEqual(D("0.14014"), all_orders
[0].rate
)
801 self
.assertEqual(1000, all_orders
[1].amount
.value
)
802 self
.assertEqual(D("0.00003003"), all_orders
[1].rate
)
805 def create_order(symbol
, type, action
, amount
, price
=None, account
="exchange"):
806 self
.assertEqual("limit", type)
807 if symbol
== "ETH/BTC":
808 self
.assertEqual("sell", action
)
809 self
.assertEqual(D('0.66666666'), amount
)
810 self
.assertEqual(D("0.14014"), price
)
811 elif symbol
== "XVG/BTC":
812 self
.assertEqual("sell", action
)
813 self
.assertEqual(1000, amount
)
814 self
.assertEqual(D("0.00003003"), price
)
816 self
.fail("I shouldn't have been called")
821 market
.create_order
.side_effect
= create_order
822 market
.order_precision
.return_value
= 8
825 portfolio
.Trade
.run_orders()
827 self
.assertEqual("open", all_orders
[0].status
)
828 self
.assertEqual("open", all_orders
[1].status
)
830 market
.fetch_order
.return_value
= { "status": "closed" }
831 with mock
.patch
.object(portfolio
.time
, "sleep") as sleep
:
833 portfolio
.Trade
.follow_orders(verbose
=False)
835 sleep
.assert_called_with(30)
837 for order
in all_orders
:
838 self
.assertEqual("closed", order
.status
)
842 "free": D("1.0") / 3,
844 "total": D("1.0") / 3,
862 market
.fetch_balance
.return_value
= fetch_balance
864 with mock
.patch
.object(portfolio
.Portfolio
, "repartition", return_value
=repartition
):
866 portfolio
.Balance
.update_trades(market
, only
="buy", compute_value
="average")
868 balances
= portfolio
.Balance
.known_balances
869 self
.assertEqual(portfolio
.Amount("ETH", 1 / D("3")), balances
["ETH"].total
)
870 self
.assertEqual(portfolio
.Amount("ETC", 4), balances
["ETC"].total
)
871 self
.assertEqual(portfolio
.Amount("BTC", D("0.134")), balances
["BTC"].total
)
872 self
.assertEqual(portfolio
.Amount("XVG", 0), balances
["XVG"].total
)
875 trades
= portfolio
.Trade
.trades
876 self
.assertEqual(portfolio
.Amount("BTC", D("0.15")), trades
["ETH"].value_from
)
877 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
["ETH"].value_to
)
878 self
.assertEqual("sell", trades
["ETH"].action
)
880 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
["ETC"].value_from
)
881 self
.assertEqual(portfolio
.Amount("BTC", D("0.0485")), trades
["ETC"].value_to
)
882 self
.assertEqual("buy", trades
["ETC"].action
)
884 self
.assertNotIn("BTC", trades
)
886 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["BTD"].value_from
)
887 self
.assertEqual(portfolio
.Amount("BTC", D("0.00194")), trades
["BTD"].value_to
)
888 self
.assertEqual("buy", trades
["BTD"].action
)
890 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["B2X"].value_from
)
891 self
.assertEqual(portfolio
.Amount("BTC", D("0.00776")), trades
["B2X"].value_to
)
892 self
.assertEqual("buy", trades
["B2X"].action
)
894 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["USDT"].value_from
)
895 self
.assertEqual(portfolio
.Amount("BTC", D("0.0097")), trades
["USDT"].value_to
)
896 self
.assertEqual("buy", trades
["USDT"].action
)
898 self
.assertEqual(portfolio
.Amount("BTC", D("0.04")), trades
["XVG"].value_from
)
899 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
["XVG"].value_to
)
900 self
.assertEqual("sell", trades
["XVG"].action
)
903 portfolio
.Trade
.prepare_orders(only
="buy", compute_value
=lambda x
, y
: x
["ask"])
905 all_orders
= portfolio
.Trade
.all_orders(state
="pending")
906 self
.assertEqual(4, len(all_orders
))
907 self
.assertEqual(portfolio
.Amount("ETC", D("12.83333333")), round(all_orders
[0].amount
))
908 self
.assertEqual(D("0.003"), all_orders
[0].rate
)
909 self
.assertEqual("buy", all_orders
[0].action
)
910 self
.assertEqual("long", all_orders
[0].trade_type
)
912 self
.assertEqual(portfolio
.Amount("BTD", D("1.61666666")), round(all_orders
[1].amount
))
913 self
.assertEqual(D("0.0012"), all_orders
[1].rate
)
914 self
.assertEqual("sell", all_orders
[1].action
)
915 self
.assertEqual("short", all_orders
[1].trade_type
)
917 diff
= portfolio
.Amount("B2X", D("19.4")/3) - all_orders
[2].amount
918 self
.assertAlmostEqual(0, diff
.value
)
919 self
.assertEqual(D("0.0012"), all_orders
[2].rate
)
920 self
.assertEqual("buy", all_orders
[2].action
)
921 self
.assertEqual("long", all_orders
[2].trade_type
)
923 self
.assertEqual(portfolio
.Amount("BTC", D("0.0097")), all_orders
[3].amount
)
924 self
.assertEqual(D("16000"), all_orders
[3].rate
)
925 self
.assertEqual("sell", all_orders
[3].action
)
926 self
.assertEqual("long", all_orders
[3].trade_type
)
930 # Move balances to margin
934 # portfolio.Trade.run_orders()
936 with mock
.patch
.object(portfolio
.time
, "sleep") as sleep
:
938 portfolio
.Trade
.follow_orders(verbose
=False)
940 sleep
.assert_called_with(30)
942 if __name__
== '__main__':