aboutsummaryrefslogtreecommitdiff
path: root/test.py
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-10 13:52:46 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-10 15:25:27 +0100
commit6ca5a1ec669593fa915a2824efca068c975f9caa (patch)
tree375b3f21b4f228eafea7fae364caa2e7bfb8422b /test.py
parentc51687d2b0cbad5460d8424f550014502d84696e (diff)
downloadTrader-6ca5a1ec669593fa915a2824efca068c975f9caa.tar.gz
Trader-6ca5a1ec669593fa915a2824efca068c975f9caa.tar.zst
Trader-6ca5a1ec669593fa915a2824efca068c975f9caa.zip
Separate store and add helper
Diffstat (limited to 'test.py')
-rw-r--r--test.py668
1 files changed, 337 insertions, 331 deletions
diff --git a/test.py b/test.py
index b27eb74..aae1dc8 100644
--- a/test.py
+++ b/test.py
@@ -5,6 +5,7 @@ from unittest import mock
5import requests 5import requests
6import requests_mock 6import requests_mock
7from io import StringIO 7from io import StringIO
8import helper
8 9
9class WebMockTestCase(unittest.TestCase): 10class WebMockTestCase(unittest.TestCase):
10 import time 11 import time
@@ -15,16 +16,18 @@ class WebMockTestCase(unittest.TestCase):
15 self.wm.start() 16 self.wm.start()
16 17
17 self.patchers = [ 18 self.patchers = [
18 mock.patch.multiple(portfolio.Balance, known_balances={}), 19 mock.patch.multiple(portfolio.BalanceStore,
20 all={},),
21 mock.patch.multiple(portfolio.TradeStore,
22 all=[],
23 debug=False),
19 mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={}), 24 mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={}),
20 mock.patch.multiple(portfolio.Trade,
21 ticker_cache={},
22 ticker_cache_timestamp=self.time.time(),
23 fees_cache={},
24 debug=False,
25 trades=[]),
26 mock.patch.multiple(portfolio.Computation, 25 mock.patch.multiple(portfolio.Computation,
27 computations=portfolio.Computation.computations) 26 computations=portfolio.Computation.computations),
27 mock.patch.multiple(helper,
28 fees_cache={},
29 ticker_cache={},
30 ticker_cache_timestamp=self.time.time()),
28 ] 31 ]
29 for patcher in self.patchers: 32 for patcher in self.patchers:
30 patcher.start() 33 patcher.start()
@@ -149,12 +152,12 @@ class AmountTest(WebMockTestCase):
149 self.assertEqual(amount, amount.in_currency("ETC", None)) 152 self.assertEqual(amount, amount.in_currency("ETC", None))
150 153
151 ticker_mock = unittest.mock.Mock() 154 ticker_mock = unittest.mock.Mock()
152 with mock.patch.object(portfolio.Trade, 'get_ticker', new=ticker_mock): 155 with mock.patch.object(helper, 'get_ticker', new=ticker_mock):
153 ticker_mock.return_value = None 156 ticker_mock.return_value = None
154 157
155 self.assertRaises(Exception, amount.in_currency, "ETH", None) 158 self.assertRaises(Exception, amount.in_currency, "ETH", None)
156 159
157 with mock.patch.object(portfolio.Trade, 'get_ticker', new=ticker_mock): 160 with mock.patch.object(helper, 'get_ticker', new=ticker_mock):
158 ticker_mock.return_value = { 161 ticker_mock.return_value = {
159 "bid": D("0.2"), 162 "bid": D("0.2"),
160 "ask": D("0.4"), 163 "ask": D("0.4"),
@@ -361,37 +364,6 @@ class AmountTest(WebMockTestCase):
361 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1)) 364 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1))
362 365
363class BalanceTest(WebMockTestCase): 366class BalanceTest(WebMockTestCase):
364 def setUp(self):
365 super(BalanceTest, self).setUp()
366
367 self.fetch_balance = {
368 "ETC": {
369 "exchange_free": 0,
370 "exchange_used": 0,
371 "exchange_total": 0,
372 "margin_total": 0,
373 },
374 "USDT": {
375 "exchange_free": D("6.0"),
376 "exchange_used": D("1.2"),
377 "exchange_total": D("7.2"),
378 "margin_total": 0,
379 },
380 "XVG": {
381 "exchange_free": 16,
382 "exchange_used": 0,
383 "exchange_total": 16,
384 "margin_total": 0,
385 },
386 "XMR": {
387 "exchange_free": 0,
388 "exchange_used": 0,
389 "exchange_total": 0,
390 "margin_total": D("-1.0"),
391 "margin_free": 0,
392 },
393 }
394
395 def test_values(self): 367 def test_values(self):
396 balance = portfolio.Balance("BTC", { 368 balance = portfolio.Balance("BTC", {
397 "exchange_total": "0.65", 369 "exchange_total": "0.65",
@@ -426,91 +398,100 @@ class BalanceTest(WebMockTestCase):
426 self.assertEqual(portfolio.D("0.4"), balance.margin_lending_fees.value) 398 self.assertEqual(portfolio.D("0.4"), balance.margin_lending_fees.value)
427 self.assertEqual("USDT", balance.margin_lending_fees.currency) 399 self.assertEqual("USDT", balance.margin_lending_fees.currency)
428 400
429 @mock.patch.object(portfolio.Trade, "get_ticker") 401 def test__repr(self):
430 def test_in_currency(self, get_ticker): 402 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX])",
431 portfolio.Balance.known_balances = { 403 repr(portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 })))
432 "BTC": portfolio.Balance("BTC", { 404 balance = portfolio.Balance("BTX", { "exchange_total": 3,
433 "total": "0.65", 405 "exchange_used": 1, "exchange_free": 2 })
434 "exchange_total":"0.65", 406 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance))
435 "exchange_free": "0.35",
436 "exchange_used": "0.30"}),
437 "ETH": portfolio.Balance("ETH", {
438 "total": 3,
439 "exchange_total": 3,
440 "exchange_free": 3,
441 "exchange_used": 0}),
442 }
443 market = mock.Mock()
444 get_ticker.return_value = {
445 "bid": D("0.09"),
446 "ask": D("0.11"),
447 "average": D("0.1"),
448 }
449 407
450 amounts = portfolio.Balance.in_currency("BTC", market) 408 balance = portfolio.Balance("BTX", { "margin_total": 3,
451 self.assertEqual("BTC", amounts["ETH"].currency) 409 "margin_borrowed": 1, "margin_free": 2 })
452 self.assertEqual(D("0.65"), amounts["BTC"].value) 410 self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance))
453 self.assertEqual(D("0.30"), amounts["ETH"].value)
454 411
455 amounts = portfolio.Balance.in_currency("BTC", market, compute_value="bid") 412 balance = portfolio.Balance("BTX", { "margin_total": -3,
456 self.assertEqual(D("0.65"), amounts["BTC"].value) 413 "margin_borrowed_base_price": D("0.1"),
457 self.assertEqual(D("0.27"), amounts["ETH"].value) 414 "margin_borrowed_base_currency": "BTC",
415 "margin_lending_fees": D("0.002") })
416 self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
458 417
459 amounts = portfolio.Balance.in_currency("BTC", market, compute_value="bid", type="exchange_used") 418class HelperTest(WebMockTestCase):
460 self.assertEqual(D("0.30"), amounts["BTC"].value) 419 def test_get_ticker(self):
461 self.assertEqual(0, amounts["ETH"].value) 420 market = mock.Mock()
421 market.fetch_ticker.side_effect = [
422 { "bid": 1, "ask": 3 },
423 helper.ExchangeError("foo"),
424 { "bid": 10, "ask": 40 },
425 helper.ExchangeError("foo"),
426 helper.ExchangeError("foo"),
427 ]
462 428
463 def test_currencies(self): 429 ticker = helper.get_ticker("ETH", "ETC", market)
464 portfolio.Balance.known_balances = { 430 market.fetch_ticker.assert_called_with("ETH/ETC")
465 "BTC": portfolio.Balance("BTC", { 431 self.assertEqual(1, ticker["bid"])
466 "total": "0.65", 432 self.assertEqual(3, ticker["ask"])
467 "exchange_total":"0.65", 433 self.assertEqual(2, ticker["average"])
468 "exchange_free": "0.35", 434 self.assertFalse(ticker["inverted"])
469 "exchange_used": "0.30"}),
470 "ETH": portfolio.Balance("ETH", {
471 "total": 3,
472 "exchange_total": 3,
473 "exchange_free": 3,
474 "exchange_used": 0}),
475 }
476 self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies()))
477 435
478 @mock.patch.object(portfolio.market, "fetch_all_balances") 436 ticker = helper.get_ticker("ETH", "XVG", market)
479 def test_fetch_balances(self, fetch_all_balances): 437 self.assertEqual(0.0625, ticker["average"])
480 fetch_all_balances.return_value = self.fetch_balance 438 self.assertTrue(ticker["inverted"])
439 self.assertIn("original", ticker)
440 self.assertEqual(10, ticker["original"]["bid"])
481 441
482 portfolio.Balance.fetch_balances(portfolio.market) 442 ticker = helper.get_ticker("XVG", "XMR", market)
483 self.assertNotIn("ETC", portfolio.Balance.currencies()) 443 self.assertIsNone(ticker)
484 self.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio.Balance.currencies()))
485 444
486 portfolio.Balance.known_balances["ETC"] = portfolio.Balance("ETC", { 445 market.fetch_ticker.assert_has_calls([
487 "exchange_total": "1", "exchange_free": "0", 446 mock.call("ETH/ETC"),
488 "exchange_used": "1" }) 447 mock.call("ETH/XVG"),
489 portfolio.Balance.fetch_balances(portfolio.market) 448 mock.call("XVG/ETH"),
490 self.assertEqual(0, portfolio.Balance.known_balances["ETC"].total) 449 mock.call("XVG/XMR"),
491 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.Balance.currencies())) 450 mock.call("XMR/XVG"),
451 ])
492 452
493 @mock.patch.object(portfolio.Portfolio, "repartition") 453 market2 = mock.Mock()
494 @mock.patch.object(portfolio.market, "fetch_all_balances") 454 market2.fetch_ticker.side_effect = [
495 def test_dispatch_assets(self, fetch_all_balances, repartition): 455 { "bid": 1, "ask": 3 },
496 fetch_all_balances.return_value = self.fetch_balance 456 { "bid": 1.2, "ask": 3.5 },
497 portfolio.Balance.fetch_balances(portfolio.market) 457 ]
458 ticker1 = helper.get_ticker("ETH", "ETC", market2)
459 ticker2 = helper.get_ticker("ETH", "ETC", market2)
460 ticker3 = helper.get_ticker("ETC", "ETH", market2)
461 market2.fetch_ticker.assert_called_once_with("ETH/ETC")
462 self.assertEqual(1, ticker1["bid"])
463 self.assertDictEqual(ticker1, ticker2)
464 self.assertDictEqual(ticker1, ticker3["original"])
498 465
499 self.assertNotIn("XEM", portfolio.Balance.currencies()) 466 ticker4 = helper.get_ticker("ETH", "ETC", market2, refresh=True)
467 ticker5 = helper.get_ticker("ETH", "ETC", market2)
468 self.assertEqual(1.2, ticker4["bid"])
469 self.assertDictEqual(ticker4, ticker5)
500 470
501 repartition.return_value = { 471 market3 = mock.Mock()
502 "XEM": (D("0.75"), "long"), 472 market3.fetch_ticker.side_effect = [
503 "BTC": (D("0.26"), "long"), 473 { "bid": 1, "ask": 3 },
504 } 474 { "bid": 1.2, "ask": 3.5 },
475 ]
476 ticker6 = helper.get_ticker("ETH", "ETC", market3)
477 helper.ticker_cache_timestamp -= 4
478 ticker7 = helper.get_ticker("ETH", "ETC", market3)
479 helper.ticker_cache_timestamp -= 2
480 ticker8 = helper.get_ticker("ETH", "ETC", market3)
481 self.assertDictEqual(ticker6, ticker7)
482 self.assertEqual(1.2, ticker8["bid"])
505 483
506 amounts = portfolio.Balance.dispatch_assets(portfolio.Amount("BTC", "10.1")) 484 def test_fetch_fees(self):
507 self.assertIn("XEM", portfolio.Balance.currencies()) 485 market = mock.Mock()
508 self.assertEqual(D("2.6"), amounts["BTC"].value) 486 market.fetch_fees.return_value = "Foo"
509 self.assertEqual(D("7.5"), amounts["XEM"].value) 487 self.assertEqual("Foo", helper.fetch_fees(market))
488 market.fetch_fees.assert_called_once()
489 self.assertEqual("Foo", helper.fetch_fees(market))
490 market.fetch_fees.assert_called_once()
510 491
511 @mock.patch.object(portfolio.Portfolio, "repartition") 492 @mock.patch.object(portfolio.Portfolio, "repartition")
512 @mock.patch.object(portfolio.Trade, "get_ticker") 493 @mock.patch.object(helper, "get_ticker")
513 @mock.patch.object(portfolio.Trade, "compute_trades") 494 @mock.patch.object(portfolio.TradeStore, "compute_trades")
514 def test_prepare_trades(self, compute_trades, get_ticker, repartition): 495 def test_prepare_trades(self, compute_trades, get_ticker, repartition):
515 repartition.return_value = { 496 repartition.return_value = {
516 "XEM": (D("0.75"), "long"), 497 "XEM": (D("0.75"), "long"),
@@ -541,7 +522,7 @@ class BalanceTest(WebMockTestCase):
541 "total": D("10000.0") 522 "total": D("10000.0")
542 }, 523 },
543 } 524 }
544 portfolio.Balance.prepare_trades(market) 525 helper.prepare_trades(market)
545 compute_trades.assert_called() 526 compute_trades.assert_called()
546 527
547 call = compute_trades.call_args 528 call = compute_trades.call_args
@@ -552,8 +533,8 @@ class BalanceTest(WebMockTestCase):
552 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) 533 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
553 534
554 @mock.patch.object(portfolio.Portfolio, "repartition") 535 @mock.patch.object(portfolio.Portfolio, "repartition")
555 @mock.patch.object(portfolio.Trade, "get_ticker") 536 @mock.patch.object(helper, "get_ticker")
556 @mock.patch.object(portfolio.Trade, "compute_trades") 537 @mock.patch.object(portfolio.TradeStore, "compute_trades")
557 def test_update_trades(self, compute_trades, get_ticker, repartition): 538 def test_update_trades(self, compute_trades, get_ticker, repartition):
558 repartition.return_value = { 539 repartition.return_value = {
559 "XEM": (D("0.75"), "long"), 540 "XEM": (D("0.75"), "long"),
@@ -584,7 +565,7 @@ class BalanceTest(WebMockTestCase):
584 "total": D("10000.0") 565 "total": D("10000.0")
585 }, 566 },
586 } 567 }
587 portfolio.Balance.update_trades(market) 568 helper.update_trades(market)
588 compute_trades.assert_called() 569 compute_trades.assert_called()
589 570
590 call = compute_trades.call_args 571 call = compute_trades.call_args
@@ -595,8 +576,8 @@ class BalanceTest(WebMockTestCase):
595 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) 576 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
596 577
597 @mock.patch.object(portfolio.Portfolio, "repartition") 578 @mock.patch.object(portfolio.Portfolio, "repartition")
598 @mock.patch.object(portfolio.Trade, "get_ticker") 579 @mock.patch.object(helper, "get_ticker")
599 @mock.patch.object(portfolio.Trade, "compute_trades") 580 @mock.patch.object(portfolio.TradeStore, "compute_trades")
600 def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition): 581 def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition):
601 def _get_ticker(c1, c2, market): 582 def _get_ticker(c1, c2, market):
602 if c1 == "USDT" and c2 == "BTC": 583 if c1 == "USDT" and c2 == "BTC":
@@ -621,7 +602,7 @@ class BalanceTest(WebMockTestCase):
621 "total": D("10000.0") 602 "total": D("10000.0")
622 }, 603 },
623 } 604 }
624 portfolio.Balance.prepare_trades_to_sell_all(market) 605 helper.prepare_trades_to_sell_all(market)
625 repartition.assert_not_called() 606 repartition.assert_not_called()
626 compute_trades.assert_called() 607 compute_trades.assert_called()
627 608
@@ -631,89 +612,243 @@ class BalanceTest(WebMockTestCase):
631 self.assertEqual(D("0.01"), call[0][0]["XVG"].value) 612 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
632 self.assertEqual(D("1.01"), call[0][1]["BTC"].value) 613 self.assertEqual(D("1.01"), call[0][1]["BTC"].value)
633 614
634 def test__repr(self): 615 @unittest.skip("TODO")
635 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX])", 616 def test_follow_orders(self):
636 repr(portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 }))) 617 pass
637 balance = portfolio.Balance("BTX", { "exchange_total": 3,
638 "exchange_used": 1, "exchange_free": 2 })
639 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance))
640 618
641 balance = portfolio.Balance("BTX", { "margin_total": 3,
642 "margin_borrowed": 1, "margin_free": 2 })
643 self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance))
644 619
645 balance = portfolio.Balance("BTX", { "margin_total": -3, 620class TradeStoreTest(WebMockTestCase):
646 "margin_borrowed_base_price": D("0.1"), 621 @unittest.skip("TODO")
647 "margin_borrowed_base_currency": "BTC", 622 def test_compute_trades(self):
648 "margin_lending_fees": D("0.002") }) 623 pass
649 self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
650 624
651class TradeTest(WebMockTestCase): 625 def test_prepare_orders(self):
626 trade_mock1 = mock.Mock()
627 trade_mock2 = mock.Mock()
652 628
653 def test_get_ticker(self): 629 portfolio.TradeStore.all.append(trade_mock1)
630 portfolio.TradeStore.all.append(trade_mock2)
631
632 portfolio.TradeStore.prepare_orders()
633 trade_mock1.prepare_order.assert_called_with(compute_value="default")
634 trade_mock2.prepare_order.assert_called_with(compute_value="default")
635
636 portfolio.TradeStore.prepare_orders(compute_value="bla")
637 trade_mock1.prepare_order.assert_called_with(compute_value="bla")
638 trade_mock2.prepare_order.assert_called_with(compute_value="bla")
639
640 trade_mock1.prepare_order.reset_mock()
641 trade_mock2.prepare_order.reset_mock()
642
643 trade_mock1.action = "foo"
644 trade_mock2.action = "bar"
645 portfolio.TradeStore.prepare_orders(only="bar")
646 trade_mock1.prepare_order.assert_not_called()
647 trade_mock2.prepare_order.assert_called_with(compute_value="default")
648
649 def test_print_all_with_order(self):
650 trade_mock1 = mock.Mock()
651 trade_mock2 = mock.Mock()
652 trade_mock3 = mock.Mock()
653 portfolio.TradeStore.all = [trade_mock1, trade_mock2, trade_mock3]
654
655 portfolio.TradeStore.print_all_with_order()
656
657 trade_mock1.print_with_order.assert_called()
658 trade_mock2.print_with_order.assert_called()
659 trade_mock3.print_with_order.assert_called()
660
661 @mock.patch.object(portfolio.TradeStore, "all_orders")
662 def test_run_orders(self, all_orders):
663 order_mock1 = mock.Mock()
664 order_mock2 = mock.Mock()
665 order_mock3 = mock.Mock()
666 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
667 portfolio.TradeStore.run_orders()
668 all_orders.assert_called_with(state="pending")
669
670 order_mock1.run.assert_called()
671 order_mock2.run.assert_called()
672 order_mock3.run.assert_called()
673
674 def test_all_orders(self):
675 trade_mock1 = mock.Mock()
676 trade_mock2 = mock.Mock()
677
678 order_mock1 = mock.Mock()
679 order_mock2 = mock.Mock()
680 order_mock3 = mock.Mock()
681
682 trade_mock1.orders = [order_mock1, order_mock2]
683 trade_mock2.orders = [order_mock3]
684
685 order_mock1.status = "pending"
686 order_mock2.status = "open"
687 order_mock3.status = "open"
688
689 portfolio.TradeStore.all.append(trade_mock1)
690 portfolio.TradeStore.all.append(trade_mock2)
691
692 orders = portfolio.TradeStore.all_orders()
693 self.assertEqual(3, len(orders))
694
695 open_orders = portfolio.TradeStore.all_orders(state="open")
696 self.assertEqual(2, len(open_orders))
697 self.assertEqual([order_mock2, order_mock3], open_orders)
698
699 @mock.patch.object(portfolio.TradeStore, "all_orders")
700 def test_update_all_orders_status(self, all_orders):
701 order_mock1 = mock.Mock()
702 order_mock2 = mock.Mock()
703 order_mock3 = mock.Mock()
704 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
705 portfolio.TradeStore.update_all_orders_status()
706 all_orders.assert_called_with(state="open")
707
708 order_mock1.get_status.assert_called()
709 order_mock2.get_status.assert_called()
710 order_mock3.get_status.assert_called()
711
712
713class BalanceStoreTest(WebMockTestCase):
714 def setUp(self):
715 super(BalanceStoreTest, self).setUp()
716
717 self.fetch_balance = {
718 "ETC": {
719 "exchange_free": 0,
720 "exchange_used": 0,
721 "exchange_total": 0,
722 "margin_total": 0,
723 },
724 "USDT": {
725 "exchange_free": D("6.0"),
726 "exchange_used": D("1.2"),
727 "exchange_total": D("7.2"),
728 "margin_total": 0,
729 },
730 "XVG": {
731 "exchange_free": 16,
732 "exchange_used": 0,
733 "exchange_total": 16,
734 "margin_total": 0,
735 },
736 "XMR": {
737 "exchange_free": 0,
738 "exchange_used": 0,
739 "exchange_total": 0,
740 "margin_total": D("-1.0"),
741 "margin_free": 0,
742 },
743 }
744
745 @mock.patch.object(helper, "get_ticker")
746 def test_in_currency(self, get_ticker):
747 portfolio.BalanceStore.all = {
748 "BTC": portfolio.Balance("BTC", {
749 "total": "0.65",
750 "exchange_total":"0.65",
751 "exchange_free": "0.35",
752 "exchange_used": "0.30"}),
753 "ETH": portfolio.Balance("ETH", {
754 "total": 3,
755 "exchange_total": 3,
756 "exchange_free": 3,
757 "exchange_used": 0}),
758 }
654 market = mock.Mock() 759 market = mock.Mock()
655 market.fetch_ticker.side_effect = [ 760 get_ticker.return_value = {
656 { "bid": 1, "ask": 3 }, 761 "bid": D("0.09"),
657 portfolio.ExchangeError("foo"), 762 "ask": D("0.11"),
658 { "bid": 10, "ask": 40 }, 763 "average": D("0.1"),
659 portfolio.ExchangeError("foo"), 764 }
660 portfolio.ExchangeError("foo"),
661 ]
662 765
663 ticker = portfolio.Trade.get_ticker("ETH", "ETC", market) 766 amounts = portfolio.BalanceStore.in_currency("BTC", market)
664 market.fetch_ticker.assert_called_with("ETH/ETC") 767 self.assertEqual("BTC", amounts["ETH"].currency)
665 self.assertEqual(1, ticker["bid"]) 768 self.assertEqual(D("0.65"), amounts["BTC"].value)
666 self.assertEqual(3, ticker["ask"]) 769 self.assertEqual(D("0.30"), amounts["ETH"].value)
667 self.assertEqual(2, ticker["average"])
668 self.assertFalse(ticker["inverted"])
669 770
670 ticker = portfolio.Trade.get_ticker("ETH", "XVG", market) 771 amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid")
671 self.assertEqual(0.0625, ticker["average"]) 772 self.assertEqual(D("0.65"), amounts["BTC"].value)
672 self.assertTrue(ticker["inverted"]) 773 self.assertEqual(D("0.27"), amounts["ETH"].value)
673 self.assertIn("original", ticker)
674 self.assertEqual(10, ticker["original"]["bid"])
675 774
676 ticker = portfolio.Trade.get_ticker("XVG", "XMR", market) 775 amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid", type="exchange_used")
677 self.assertIsNone(ticker) 776 self.assertEqual(D("0.30"), amounts["BTC"].value)
777 self.assertEqual(0, amounts["ETH"].value)
678 778
679 market.fetch_ticker.assert_has_calls([ 779 def test_fetch_balances(self):
680 mock.call("ETH/ETC"), 780 market = mock.Mock()
681 mock.call("ETH/XVG"), 781 market.fetch_all_balances.return_value = self.fetch_balance
682 mock.call("XVG/ETH"),
683 mock.call("XVG/XMR"),
684 mock.call("XMR/XVG"),
685 ])
686 782
687 market2 = mock.Mock() 783 portfolio.BalanceStore.fetch_balances(market)
688 market2.fetch_ticker.side_effect = [ 784 self.assertNotIn("ETC", portfolio.BalanceStore.currencies())
689 { "bid": 1, "ask": 3 }, 785 self.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio.BalanceStore.currencies()))
690 { "bid": 1.2, "ask": 3.5 },
691 ]
692 ticker1 = portfolio.Trade.get_ticker("ETH", "ETC", market2)
693 ticker2 = portfolio.Trade.get_ticker("ETH", "ETC", market2)
694 ticker3 = portfolio.Trade.get_ticker("ETC", "ETH", market2)
695 market2.fetch_ticker.assert_called_once_with("ETH/ETC")
696 self.assertEqual(1, ticker1["bid"])
697 self.assertDictEqual(ticker1, ticker2)
698 self.assertDictEqual(ticker1, ticker3["original"])
699 786
700 ticker4 = portfolio.Trade.get_ticker("ETH", "ETC", market2, refresh=True) 787 portfolio.BalanceStore.all["ETC"] = portfolio.Balance("ETC", {
701 ticker5 = portfolio.Trade.get_ticker("ETH", "ETC", market2) 788 "exchange_total": "1", "exchange_free": "0",
702 self.assertEqual(1.2, ticker4["bid"]) 789 "exchange_used": "1" })
703 self.assertDictEqual(ticker4, ticker5) 790 portfolio.BalanceStore.fetch_balances(market)
791 self.assertEqual(0, portfolio.BalanceStore.all["ETC"].total)
792 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.BalanceStore.currencies()))
704 793
705 market3 = mock.Mock() 794 @mock.patch.object(portfolio.Portfolio, "repartition")
706 market3.fetch_ticker.side_effect = [ 795 def test_dispatch_assets(self, repartition):
707 { "bid": 1, "ask": 3 }, 796 market = mock.Mock()
708 { "bid": 1.2, "ask": 3.5 }, 797 market.fetch_all_balances.return_value = self.fetch_balance
709 ] 798 portfolio.BalanceStore.fetch_balances(market)
710 ticker6 = portfolio.Trade.get_ticker("ETH", "ETC", market3) 799
711 portfolio.Trade.ticker_cache_timestamp -= 4 800 self.assertNotIn("XEM", portfolio.BalanceStore.currencies())
712 ticker7 = portfolio.Trade.get_ticker("ETH", "ETC", market3) 801
713 portfolio.Trade.ticker_cache_timestamp -= 2 802 repartition.return_value = {
714 ticker8 = portfolio.Trade.get_ticker("ETH", "ETC", market3) 803 "XEM": (D("0.75"), "long"),
715 self.assertDictEqual(ticker6, ticker7) 804 "BTC": (D("0.26"), "long"),
716 self.assertEqual(1.2, ticker8["bid"]) 805 }
806
807 amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "10.1"))
808 self.assertIn("XEM", portfolio.BalanceStore.currencies())
809 self.assertEqual(D("2.6"), amounts["BTC"].value)
810 self.assertEqual(D("7.5"), amounts["XEM"].value)
811
812 def test_currencies(self):
813 portfolio.BalanceStore.all = {
814 "BTC": portfolio.Balance("BTC", {
815 "total": "0.65",
816 "exchange_total":"0.65",
817 "exchange_free": "0.35",
818 "exchange_used": "0.30"}),
819 "ETH": portfolio.Balance("ETH", {
820 "total": 3,
821 "exchange_total": 3,
822 "exchange_free": 3,
823 "exchange_used": 0}),
824 }
825 self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies()))
826
827class ComputationTest(WebMockTestCase):
828 def test_compute_value(self):
829 compute = mock.Mock()
830 portfolio.Computation.compute_value("foo", "buy", compute_value=compute)
831 compute.assert_called_with("foo", "ask")
832
833 compute.reset_mock()
834 portfolio.Computation.compute_value("foo", "sell", compute_value=compute)
835 compute.assert_called_with("foo", "bid")
836
837 compute.reset_mock()
838 portfolio.Computation.compute_value("foo", "ask", compute_value=compute)
839 compute.assert_called_with("foo", "ask")
840
841 compute.reset_mock()
842 portfolio.Computation.compute_value("foo", "bid", compute_value=compute)
843 compute.assert_called_with("foo", "bid")
844
845 compute.reset_mock()
846 portfolio.Computation.computations["test"] = compute
847 portfolio.Computation.compute_value("foo", "bid", compute_value="test")
848 compute.assert_called_with("foo", "bid")
849
850
851class TradeTest(WebMockTestCase):
717 852
718 def test_values_assertion(self): 853 def test_values_assertion(self):
719 value_from = portfolio.Amount("BTC", "1.0") 854 value_from = portfolio.Amount("BTC", "1.0")
@@ -736,14 +871,6 @@ class TradeTest(WebMockTestCase):
736 trade = portfolio.Trade(value_from, value_to, "ETH") 871 trade = portfolio.Trade(value_from, value_to, "ETH")
737 self.assertEqual(0, trade.value_from.linked_to) 872 self.assertEqual(0, trade.value_from.linked_to)
738 873
739 def test_fetch_fees(self):
740 market = mock.Mock()
741 market.fetch_fees.return_value = "Foo"
742 self.assertEqual("Foo", portfolio.Trade.fetch_fees(market))
743 market.fetch_fees.assert_called_once()
744 self.assertEqual("Foo", portfolio.Trade.fetch_fees(market))
745 market.fetch_fees.assert_called_once()
746
747 def test_action(self): 874 def test_action(self):
748 value_from = portfolio.Amount("BTC", "1.0") 875 value_from = portfolio.Amount("BTC", "1.0")
749 value_from.linked_to = portfolio.Amount("ETH", "10.0") 876 value_from.linked_to = portfolio.Amount("ETH", "10.0")
@@ -821,34 +948,6 @@ class TradeTest(WebMockTestCase):
821 948
822 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount) 949 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount)
823 950
824 def test_prepare_orders(self):
825 trade_mock1 = mock.Mock()
826 trade_mock2 = mock.Mock()
827
828 portfolio.Trade.trades.append(trade_mock1)
829 portfolio.Trade.trades.append(trade_mock2)
830
831 portfolio.Trade.prepare_orders()
832 trade_mock1.prepare_order.assert_called_with(compute_value="default")
833 trade_mock2.prepare_order.assert_called_with(compute_value="default")
834
835 portfolio.Trade.prepare_orders(compute_value="bla")
836 trade_mock1.prepare_order.assert_called_with(compute_value="bla")
837 trade_mock2.prepare_order.assert_called_with(compute_value="bla")
838
839 trade_mock1.prepare_order.reset_mock()
840 trade_mock2.prepare_order.reset_mock()
841
842 trade_mock1.action = "foo"
843 trade_mock2.action = "bar"
844 portfolio.Trade.prepare_orders(only="bar")
845 trade_mock1.prepare_order.assert_not_called()
846 trade_mock2.prepare_order.assert_called_with(compute_value="default")
847
848 @unittest.skip("TODO")
849 def test_compute_trades(self):
850 pass
851
852 @unittest.skip("TODO") 951 @unittest.skip("TODO")
853 def test_prepare_order(self): 952 def test_prepare_order(self):
854 pass 953 pass
@@ -857,77 +956,6 @@ class TradeTest(WebMockTestCase):
857 def test_update_order(self): 956 def test_update_order(self):
858 pass 957 pass
859 958
860 @unittest.skip("TODO")
861 def test_follow_orders(self):
862 pass
863
864 @unittest.skip("TODO")
865 def test_move_balances(self):
866 pass
867
868 def test_all_orders(self):
869 trade_mock1 = mock.Mock()
870 trade_mock2 = mock.Mock()
871
872 order_mock1 = mock.Mock()
873 order_mock2 = mock.Mock()
874 order_mock3 = mock.Mock()
875
876 trade_mock1.orders = [order_mock1, order_mock2]
877 trade_mock2.orders = [order_mock3]
878
879 order_mock1.status = "pending"
880 order_mock2.status = "open"
881 order_mock3.status = "open"
882
883 portfolio.Trade.trades.append(trade_mock1)
884 portfolio.Trade.trades.append(trade_mock2)
885
886 orders = portfolio.Trade.all_orders()
887 self.assertEqual(3, len(orders))
888
889 open_orders = portfolio.Trade.all_orders(state="open")
890 self.assertEqual(2, len(open_orders))
891 self.assertEqual([order_mock2, order_mock3], open_orders)
892
893 @mock.patch.object(portfolio.Trade, "all_orders")
894 def test_run_orders(self, all_orders):
895 order_mock1 = mock.Mock()
896 order_mock2 = mock.Mock()
897 order_mock3 = mock.Mock()
898 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
899 portfolio.Trade.run_orders()
900 all_orders.assert_called_with(state="pending")
901
902 order_mock1.run.assert_called()
903 order_mock2.run.assert_called()
904 order_mock3.run.assert_called()
905
906 @mock.patch.object(portfolio.Trade, "all_orders")
907 def test_update_all_orders_status(self, all_orders):
908 order_mock1 = mock.Mock()
909 order_mock2 = mock.Mock()
910 order_mock3 = mock.Mock()
911 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
912 portfolio.Trade.update_all_orders_status()
913 all_orders.assert_called_with(state="open")
914
915 order_mock1.get_status.assert_called()
916 order_mock2.get_status.assert_called()
917 order_mock3.get_status.assert_called()
918
919 def test_print_all_with_order(self):
920 trade_mock1 = mock.Mock()
921 trade_mock2 = mock.Mock()
922 trade_mock3 = mock.Mock()
923 portfolio.Trade.trades = [trade_mock1, trade_mock2, trade_mock3]
924
925 portfolio.Trade.print_all_with_order()
926
927 trade_mock1.print_with_order.assert_called()
928 trade_mock2.print_with_order.assert_called()
929 trade_mock3.print_with_order.assert_called()
930
931 @mock.patch('sys.stdout', new_callable=StringIO) 959 @mock.patch('sys.stdout', new_callable=StringIO)
932 def test_print_with_order(self, mock_stdout): 960 def test_print_with_order(self, mock_stdout):
933 value_from = portfolio.Amount("BTC", "0.5") 961 value_from = portfolio.Amount("BTC", "0.5")
@@ -951,28 +979,6 @@ class TradeTest(WebMockTestCase):
951 self.assertEqual("\tMock 1", out[1]) 979 self.assertEqual("\tMock 1", out[1])
952 self.assertEqual("\tMock 2", out[2]) 980 self.assertEqual("\tMock 2", out[2])
953 981
954 def test_compute_value(self):
955 compute = mock.Mock()
956 portfolio.Trade.compute_value("foo", "buy", compute_value=compute)
957 compute.assert_called_with("foo", "ask")
958
959 compute.reset_mock()
960 portfolio.Trade.compute_value("foo", "sell", compute_value=compute)
961 compute.assert_called_with("foo", "bid")
962
963 compute.reset_mock()
964 portfolio.Trade.compute_value("foo", "ask", compute_value=compute)
965 compute.assert_called_with("foo", "ask")
966
967 compute.reset_mock()
968 portfolio.Trade.compute_value("foo", "bid", compute_value=compute)
969 compute.assert_called_with("foo", "bid")
970
971 compute.reset_mock()
972 portfolio.Computation.computations["test"] = compute
973 portfolio.Trade.compute_value("foo", "bid", compute_value="test")
974 compute.assert_called_with("foo", "bid")
975
976 def test__repr(self): 982 def test__repr(self):
977 value_from = portfolio.Amount("BTC", "0.5") 983 value_from = portfolio.Amount("BTC", "0.5")
978 value_from.linked_to = portfolio.Amount("ETH", "10.0") 984 value_from.linked_to = portfolio.Amount("ETH", "10.0")
@@ -1045,7 +1051,7 @@ class AcceptanceTest(WebMockTestCase):
1045 "ask": D("0.0012") 1051 "ask": D("0.0012")
1046 } 1052 }
1047 if symbol == "USDT/BTC": 1053 if symbol == "USDT/BTC":
1048 raise portfolio.ExchangeError 1054 raise helper.ExchangeError
1049 if symbol == "BTC/USDT": 1055 if symbol == "BTC/USDT":
1050 return { 1056 return {
1051 "symbol": "BTC/USDT", 1057 "symbol": "BTC/USDT",
@@ -1059,15 +1065,15 @@ class AcceptanceTest(WebMockTestCase):
1059 market.fetch_ticker.side_effect = fetch_ticker 1065 market.fetch_ticker.side_effect = fetch_ticker
1060 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 1066 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition):
1061 # Action 1 1067 # Action 1
1062 portfolio.Balance.prepare_trades(market) 1068 helper.prepare_trades(market)
1063 1069
1064 balances = portfolio.Balance.known_balances 1070 balances = portfolio.BalanceStore.all
1065 self.assertEqual(portfolio.Amount("ETH", 1), balances["ETH"].total) 1071 self.assertEqual(portfolio.Amount("ETH", 1), balances["ETH"].total)
1066 self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total) 1072 self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total)
1067 self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total) 1073 self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total)
1068 1074
1069 1075
1070 trades = portfolio.Trade.trades 1076 trades = TradeStore.all
1071 self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) 1077 self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from)
1072 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) 1078 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to)
1073 self.assertEqual("dispose", trades[0].action) 1079 self.assertEqual("dispose", trades[0].action)
@@ -1123,7 +1129,7 @@ class AcceptanceTest(WebMockTestCase):
1123 market.order_precision.return_value = 8 1129 market.order_precision.return_value = 8
1124 1130
1125 # Action 3 1131 # Action 3
1126 portfolio.Trade.run_orders() 1132 portfolio.TradeStore.run_orders()
1127 1133
1128 self.assertEqual("open", all_orders[0].status) 1134 self.assertEqual("open", all_orders[0].status)
1129 self.assertEqual("open", all_orders[1].status) 1135 self.assertEqual("open", all_orders[1].status)
@@ -1131,7 +1137,7 @@ class AcceptanceTest(WebMockTestCase):
1131 market.fetch_order.return_value = { "status": "closed" } 1137 market.fetch_order.return_value = { "status": "closed" }
1132 with mock.patch.object(portfolio.time, "sleep") as sleep: 1138 with mock.patch.object(portfolio.time, "sleep") as sleep:
1133 # Action 4 1139 # Action 4
1134 portfolio.Trade.follow_orders(verbose=False) 1140 helper.follow_orders(verbose=False)
1135 1141
1136 sleep.assert_called_with(30) 1142 sleep.assert_called_with(30)
1137 1143
@@ -1164,16 +1170,16 @@ class AcceptanceTest(WebMockTestCase):
1164 1170
1165 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 1171 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition):
1166 # Action 5 1172 # Action 5
1167 portfolio.Balance.update_trades(market, only="buy", compute_value="average") 1173 helper.update_trades(market, only="buy", compute_value="average")
1168 1174
1169 balances = portfolio.Balance.known_balances 1175 balances = portfolio.BalanceStore.all
1170 self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total) 1176 self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total)
1171 self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total) 1177 self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total)
1172 self.assertEqual(portfolio.Amount("BTC", D("0.134")), balances["BTC"].total) 1178 self.assertEqual(portfolio.Amount("BTC", D("0.134")), balances["BTC"].total)
1173 self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total) 1179 self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total)
1174 1180
1175 1181
1176 trades = portfolio.Trade.trades 1182 trades = TradeStore.all
1177 self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from) 1183 self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from)
1178 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to) 1184 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to)
1179 self.assertEqual("sell", trades["ETH"].action) 1185 self.assertEqual("sell", trades["ETH"].action)
@@ -1232,11 +1238,11 @@ class AcceptanceTest(WebMockTestCase):
1232 1238
1233 # Action 7 1239 # Action 7
1234 # TODO 1240 # TODO
1235 # portfolio.Trade.run_orders() 1241 # portfolio.TradeStore.run_orders()
1236 1242
1237 with mock.patch.object(portfolio.time, "sleep") as sleep: 1243 with mock.patch.object(portfolio.time, "sleep") as sleep:
1238 # Action 8 1244 # Action 8
1239 portfolio.Trade.follow_orders(verbose=False) 1245 helper.follow_orders(verbose=False)
1240 1246
1241 sleep.assert_called_with(30) 1247 sleep.assert_called_with(30)
1242 1248