aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-09 20:00:26 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-09 20:07:47 +0100
commitc51687d2b0cbad5460d8424f550014502d84696e (patch)
treea4fce092bdc31c28b5c6ea0fc40768b65da6550c
parent80cdd672da2f0a4997a792bd1a2de19d4f516e5b (diff)
downloadTrader-c51687d2b0cbad5460d8424f550014502d84696e.tar.gz
Trader-c51687d2b0cbad5460d8424f550014502d84696e.tar.zst
Trader-c51687d2b0cbad5460d8424f550014502d84696e.zip
Fix Amount.__sub__ not working with 0
Add more tests
-rw-r--r--portfolio.py4
-rw-r--r--test.py479
2 files changed, 393 insertions, 90 deletions
diff --git a/portfolio.py b/portfolio.py
index b3065b8..45fbef9 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -116,6 +116,8 @@ class Amount:
116 return self.__add__(other) 116 return self.__add__(other)
117 117
118 def __sub__(self, other): 118 def __sub__(self, other):
119 if other == 0:
120 return self
119 if other.currency != self.currency and other.value * self.value != 0: 121 if other.currency != self.currency and other.value * self.value != 0:
120 raise Exception("Summing amounts must be done with same currencies") 122 raise Exception("Summing amounts must be done with same currencies")
121 return Amount(self.currency, self.value - other.value) 123 return Amount(self.currency, self.value - other.value)
@@ -197,7 +199,7 @@ class Balance:
197 "margin_lending_fees", 199 "margin_lending_fees",
198 "margin_borrowed_base_price" 200 "margin_borrowed_base_price"
199 ]: 201 ]:
200 setattr(self, key, Amount(base_currency, hash_[key])) 202 setattr(self, key, Amount(base_currency, hash_.get(key, 0)))
201 203
202 @classmethod 204 @classmethod
203 def in_currency(cls, other_currency, market, compute_value="average", type="total"): 205 def in_currency(cls, other_currency, market, compute_value="average", type="total"):
diff --git a/test.py b/test.py
index 6e27475..b27eb74 100644
--- a/test.py
+++ b/test.py
@@ -4,6 +4,7 @@ from decimal import Decimal as D
4from unittest import mock 4from unittest import mock
5import requests 5import requests
6import requests_mock 6import requests_mock
7from io import StringIO
7 8
8class WebMockTestCase(unittest.TestCase): 9class WebMockTestCase(unittest.TestCase):
9 import time 10 import time
@@ -20,7 +21,8 @@ class WebMockTestCase(unittest.TestCase):
20 ticker_cache={}, 21 ticker_cache={},
21 ticker_cache_timestamp=self.time.time(), 22 ticker_cache_timestamp=self.time.time(),
22 fees_cache={}, 23 fees_cache={},
23 trades={}), 24 debug=False,
25 trades=[]),
24 mock.patch.multiple(portfolio.Computation, 26 mock.patch.multiple(portfolio.Computation,
25 computations=portfolio.Computation.computations) 27 computations=portfolio.Computation.computations)
26 ] 28 ]
@@ -363,29 +365,30 @@ class BalanceTest(WebMockTestCase):
363 super(BalanceTest, self).setUp() 365 super(BalanceTest, self).setUp()
364 366
365 self.fetch_balance = { 367 self.fetch_balance = {
366 "free": "foo",
367 "info": "bar",
368 "used": "baz",
369 "total": "bazz",
370 "ETC": { 368 "ETC": {
371 "free": 0.0, 369 "exchange_free": 0,
372 "used": 0.0, 370 "exchange_used": 0,
373 "total": 0.0 371 "exchange_total": 0,
372 "margin_total": 0,
374 }, 373 },
375 "USDT": { 374 "USDT": {
376 "free": 6.0, 375 "exchange_free": D("6.0"),
377 "used": 1.2, 376 "exchange_used": D("1.2"),
378 "total": 7.2 377 "exchange_total": D("7.2"),
378 "margin_total": 0,
379 }, 379 },
380 "XVG": { 380 "XVG": {
381 "free": 16, 381 "exchange_free": 16,
382 "used": 0.0, 382 "exchange_used": 0,
383 "total": 16 383 "exchange_total": 16,
384 "margin_total": 0,
384 }, 385 },
385 "XMR": { 386 "XMR": {
386 "free": 0.0, 387 "exchange_free": 0,
387 "used": 0.0, 388 "exchange_used": 0,
388 "total": 0.0 389 "exchange_total": 0,
390 "margin_total": D("-1.0"),
391 "margin_free": 0,
389 }, 392 },
390 } 393 }
391 394
@@ -472,25 +475,25 @@ class BalanceTest(WebMockTestCase):
472 } 475 }
473 self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies())) 476 self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies()))
474 477
475 @unittest.expectedFailure 478 @mock.patch.object(portfolio.market, "fetch_all_balances")
476 @mock.patch.object(portfolio.market, "fetch_balance") 479 def test_fetch_balances(self, fetch_all_balances):
477 def test_fetch_balances(self, fetch_balance): 480 fetch_all_balances.return_value = self.fetch_balance
478 fetch_balance.return_value = self.fetch_balance
479 481
480 portfolio.Balance.fetch_balances(portfolio.market) 482 portfolio.Balance.fetch_balances(portfolio.market)
481 self.assertNotIn("XMR", portfolio.Balance.currencies()) 483 self.assertNotIn("ETC", portfolio.Balance.currencies())
482 self.assertListEqual(["USDT", "XVG"], list(portfolio.Balance.currencies())) 484 self.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio.Balance.currencies()))
483 485
484 portfolio.Balance.known_balances["ETC"] = portfolio.Balance("ETC", "1", "0", "1") 486 portfolio.Balance.known_balances["ETC"] = portfolio.Balance("ETC", {
487 "exchange_total": "1", "exchange_free": "0",
488 "exchange_used": "1" })
485 portfolio.Balance.fetch_balances(portfolio.market) 489 portfolio.Balance.fetch_balances(portfolio.market)
486 self.assertEqual(0, portfolio.Balance.known_balances["ETC"].total) 490 self.assertEqual(0, portfolio.Balance.known_balances["ETC"].total)
487 self.assertListEqual(["USDT", "XVG", "ETC"], list(portfolio.Balance.currencies())) 491 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.Balance.currencies()))
488 492
489 @unittest.expectedFailure
490 @mock.patch.object(portfolio.Portfolio, "repartition") 493 @mock.patch.object(portfolio.Portfolio, "repartition")
491 @mock.patch.object(portfolio.market, "fetch_balance") 494 @mock.patch.object(portfolio.market, "fetch_all_balances")
492 def test_dispatch_assets(self, fetch_balance, repartition): 495 def test_dispatch_assets(self, fetch_all_balances, repartition):
493 fetch_balance.return_value = self.fetch_balance 496 fetch_all_balances.return_value = self.fetch_balance
494 portfolio.Balance.fetch_balances(portfolio.market) 497 portfolio.Balance.fetch_balances(portfolio.market)
495 498
496 self.assertNotIn("XEM", portfolio.Balance.currencies()) 499 self.assertNotIn("XEM", portfolio.Balance.currencies())
@@ -505,7 +508,6 @@ class BalanceTest(WebMockTestCase):
505 self.assertEqual(D("2.6"), amounts["BTC"].value) 508 self.assertEqual(D("2.6"), amounts["BTC"].value)
506 self.assertEqual(D("7.5"), amounts["XEM"].value) 509 self.assertEqual(D("7.5"), amounts["XEM"].value)
507 510
508 @unittest.expectedFailure
509 @mock.patch.object(portfolio.Portfolio, "repartition") 511 @mock.patch.object(portfolio.Portfolio, "repartition")
510 @mock.patch.object(portfolio.Trade, "get_ticker") 512 @mock.patch.object(portfolio.Trade, "get_ticker")
511 @mock.patch.object(portfolio.Trade, "compute_trades") 513 @mock.patch.object(portfolio.Trade, "compute_trades")
@@ -525,15 +527,17 @@ class BalanceTest(WebMockTestCase):
525 get_ticker.side_effect = _get_ticker 527 get_ticker.side_effect = _get_ticker
526 528
527 market = mock.Mock() 529 market = mock.Mock()
528 market.fetch_balance.return_value = { 530 market.fetch_all_balances.return_value = {
529 "USDT": { 531 "USDT": {
530 "free": D("10000.0"), 532 "exchange_free": D("10000.0"),
531 "used": D("0.0"), 533 "exchange_used": D("0.0"),
534 "exchange_total": D("10000.0"),
532 "total": D("10000.0") 535 "total": D("10000.0")
533 }, 536 },
534 "XVG": { 537 "XVG": {
535 "free": D("10000.0"), 538 "exchange_free": D("10000.0"),
536 "used": D("0.0"), 539 "exchange_used": D("0.0"),
540 "exchange_total": D("10000.0"),
537 "total": D("10000.0") 541 "total": D("10000.0")
538 }, 542 },
539 } 543 }
@@ -547,14 +551,102 @@ class BalanceTest(WebMockTestCase):
547 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) 551 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value)
548 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) 552 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
549 553
550 @unittest.skip("TODO") 554 @mock.patch.object(portfolio.Portfolio, "repartition")
551 def test_update_trades(self): 555 @mock.patch.object(portfolio.Trade, "get_ticker")
552 pass 556 @mock.patch.object(portfolio.Trade, "compute_trades")
557 def test_update_trades(self, compute_trades, get_ticker, repartition):
558 repartition.return_value = {
559 "XEM": (D("0.75"), "long"),
560 "BTC": (D("0.25"), "long"),
561 }
562 def _get_ticker(c1, c2, market):
563 if c1 == "USDT" and c2 == "BTC":
564 return { "average": D("0.0001") }
565 if c1 == "XVG" and c2 == "BTC":
566 return { "average": D("0.000001") }
567 if c1 == "XEM" and c2 == "BTC":
568 return { "average": D("0.001") }
569 self.fail("Should be called with {}, {}".format(c1, c2))
570 get_ticker.side_effect = _get_ticker
571
572 market = mock.Mock()
573 market.fetch_all_balances.return_value = {
574 "USDT": {
575 "exchange_free": D("10000.0"),
576 "exchange_used": D("0.0"),
577 "exchange_total": D("10000.0"),
578 "total": D("10000.0")
579 },
580 "XVG": {
581 "exchange_free": D("10000.0"),
582 "exchange_used": D("0.0"),
583 "exchange_total": D("10000.0"),
584 "total": D("10000.0")
585 },
586 }
587 portfolio.Balance.update_trades(market)
588 compute_trades.assert_called()
589
590 call = compute_trades.call_args
591 self.assertEqual(market, call[1]["market"])
592 self.assertEqual(1, call[0][0]["USDT"].value)
593 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
594 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value)
595 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
596
597 @mock.patch.object(portfolio.Portfolio, "repartition")
598 @mock.patch.object(portfolio.Trade, "get_ticker")
599 @mock.patch.object(portfolio.Trade, "compute_trades")
600 def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition):
601 def _get_ticker(c1, c2, market):
602 if c1 == "USDT" and c2 == "BTC":
603 return { "average": D("0.0001") }
604 if c1 == "XVG" and c2 == "BTC":
605 return { "average": D("0.000001") }
606 self.fail("Should be called with {}, {}".format(c1, c2))
607 get_ticker.side_effect = _get_ticker
608
609 market = mock.Mock()
610 market.fetch_all_balances.return_value = {
611 "USDT": {
612 "exchange_free": D("10000.0"),
613 "exchange_used": D("0.0"),
614 "exchange_total": D("10000.0"),
615 "total": D("10000.0")
616 },
617 "XVG": {
618 "exchange_free": D("10000.0"),
619 "exchange_used": D("0.0"),
620 "exchange_total": D("10000.0"),
621 "total": D("10000.0")
622 },
623 }
624 portfolio.Balance.prepare_trades_to_sell_all(market)
625 repartition.assert_not_called()
626 compute_trades.assert_called()
627
628 call = compute_trades.call_args
629 self.assertEqual(market, call[1]["market"])
630 self.assertEqual(1, call[0][0]["USDT"].value)
631 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
632 self.assertEqual(D("1.01"), call[0][1]["BTC"].value)
553 633
554 @unittest.expectedFailure
555 def test__repr(self): 634 def test__repr(self):
556 balance = portfolio.Balance("BTX", 3, 1, 2) 635 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX])",
557 self.assertEqual("Balance(BTX [1.00000000 BTX/2.00000000 BTX/3.00000000 BTX])", repr(balance)) 636 repr(portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 })))
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
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
645 balance = portfolio.Balance("BTX", { "margin_total": -3,
646 "margin_borrowed_base_price": D("0.1"),
647 "margin_borrowed_base_currency": "BTC",
648 "margin_lending_fees": D("0.002") })
649 self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
558 650
559class TradeTest(WebMockTestCase): 651class TradeTest(WebMockTestCase):
560 652
@@ -623,12 +715,11 @@ class TradeTest(WebMockTestCase):
623 self.assertDictEqual(ticker6, ticker7) 715 self.assertDictEqual(ticker6, ticker7)
624 self.assertEqual(1.2, ticker8["bid"]) 716 self.assertEqual(1.2, ticker8["bid"])
625 717
626 @unittest.skip("TODO")
627 def test_values_assertion(self): 718 def test_values_assertion(self):
628 value_from = Amount("BTC", "1.0") 719 value_from = portfolio.Amount("BTC", "1.0")
629 value_from.linked_to = Amount("ETH", "10.0") 720 value_from.linked_to = portfolio.Amount("ETH", "10.0")
630 value_to = Amount("BTC", "1.0") 721 value_to = portfolio.Amount("BTC", "1.0")
631 trade = portfolioTrade(value_from, value_to, "ETH") 722 trade = portfolio.Trade(value_from, value_to, "ETH")
632 self.assertEqual("BTC", trade.base_currency) 723 self.assertEqual("BTC", trade.base_currency)
633 self.assertEqual("ETH", trade.currency) 724 self.assertEqual("ETH", trade.currency)
634 725
@@ -641,63 +732,275 @@ class TradeTest(WebMockTestCase):
641 value_from.currency = "ETH" 732 value_from.currency = "ETH"
642 portfolio.Trade(value_from, value_to, "ETH") 733 portfolio.Trade(value_from, value_to, "ETH")
643 734
644 @unittest.skip("TODO") 735 value_from = portfolio.Amount("BTC", 0)
736 trade = portfolio.Trade(value_from, value_to, "ETH")
737 self.assertEqual(0, trade.value_from.linked_to)
738
645 def test_fetch_fees(self): 739 def test_fetch_fees(self):
646 pass 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):
748 value_from = portfolio.Amount("BTC", "1.0")
749 value_from.linked_to = portfolio.Amount("ETH", "10.0")
750 value_to = portfolio.Amount("BTC", "1.0")
751 trade = portfolio.Trade(value_from, value_to, "ETH")
752
753 self.assertIsNone(trade.action)
754
755 value_from = portfolio.Amount("BTC", "1.0")
756 value_from.linked_to = portfolio.Amount("BTC", "1.0")
757 value_to = portfolio.Amount("BTC", "1.0")
758 trade = portfolio.Trade(value_from, value_to, "BTC")
759
760 self.assertIsNone(trade.action)
761
762 value_from = portfolio.Amount("BTC", "0.5")
763 value_from.linked_to = portfolio.Amount("ETH", "10.0")
764 value_to = portfolio.Amount("BTC", "1.0")
765 trade = portfolio.Trade(value_from, value_to, "ETH")
766
767 self.assertEqual("acquire", trade.action)
768
769 value_from = portfolio.Amount("BTC", "0")
770 value_from.linked_to = portfolio.Amount("ETH", "0")
771 value_to = portfolio.Amount("BTC", "-1.0")
772 trade = portfolio.Trade(value_from, value_to, "ETH")
773
774 self.assertEqual("dispose", trade.action)
775
776 def test_order_action(self):
777 value_from = portfolio.Amount("BTC", "0.5")
778 value_from.linked_to = portfolio.Amount("ETH", "10.0")
779 value_to = portfolio.Amount("BTC", "1.0")
780 trade = portfolio.Trade(value_from, value_to, "ETH")
781
782 self.assertEqual("buy", trade.order_action(False))
783 self.assertEqual("sell", trade.order_action(True))
784
785 value_from = portfolio.Amount("BTC", "0")
786 value_from.linked_to = portfolio.Amount("ETH", "0")
787 value_to = portfolio.Amount("BTC", "-1.0")
788 trade = portfolio.Trade(value_from, value_to, "ETH")
789
790 self.assertEqual("sell", trade.order_action(False))
791 self.assertEqual("buy", trade.order_action(True))
792
793 def test_trade_type(self):
794 value_from = portfolio.Amount("BTC", "0.5")
795 value_from.linked_to = portfolio.Amount("ETH", "10.0")
796 value_to = portfolio.Amount("BTC", "1.0")
797 trade = portfolio.Trade(value_from, value_to, "ETH")
798
799 self.assertEqual("long", trade.trade_type)
800
801 value_from = portfolio.Amount("BTC", "0")
802 value_from.linked_to = portfolio.Amount("ETH", "0")
803 value_to = portfolio.Amount("BTC", "-1.0")
804 trade = portfolio.Trade(value_from, value_to, "ETH")
805
806 self.assertEqual("short", trade.trade_type)
807
808 def test_filled_amount(self):
809 value_from = portfolio.Amount("BTC", "0.5")
810 value_from.linked_to = portfolio.Amount("ETH", "10.0")
811 value_to = portfolio.Amount("BTC", "1.0")
812 trade = portfolio.Trade(value_from, value_to, "ETH")
813
814 order1 = mock.Mock()
815 order1.filled_amount = portfolio.Amount("ETH", "0.3")
816
817 order2 = mock.Mock()
818 order2.filled_amount = portfolio.Amount("ETH", "0.01")
819 trade.orders.append(order1)
820 trade.orders.append(order2)
821
822 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount)
823
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")
647 847
648 @unittest.skip("TODO") 848 @unittest.skip("TODO")
649 def test_compute_trades(self): 849 def test_compute_trades(self):
650 pass 850 pass
651 851
652 @unittest.skip("TODO") 852 @unittest.skip("TODO")
653 def test_action(self): 853 def test_prepare_order(self):
654 pass 854 pass
655 855
656 @unittest.skip("TODO") 856 @unittest.skip("TODO")
657 def test_action(self): 857 def test_update_order(self):
658 pass 858 pass
659 859
660 @unittest.skip("TODO") 860 @unittest.skip("TODO")
661 def test_order_action(self): 861 def test_follow_orders(self):
662 pass 862 pass
663 863
664 @unittest.skip("TODO") 864 @unittest.skip("TODO")
665 def test_prepare_order(self): 865 def test_move_balances(self):
666 pass 866 pass
667 867
668 @unittest.skip("TODO")
669 def test_all_orders(self): 868 def test_all_orders(self):
670 pass 869 trade_mock1 = mock.Mock()
870 trade_mock2 = mock.Mock()
671 871
672 @unittest.skip("TODO") 872 order_mock1 = mock.Mock()
673 def test_follow_orders(self): 873 order_mock2 = mock.Mock()
674 pass 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)
932 def test_print_with_order(self, mock_stdout):
933 value_from = portfolio.Amount("BTC", "0.5")
934 value_from.linked_to = portfolio.Amount("ETH", "10.0")
935 value_to = portfolio.Amount("BTC", "1.0")
936 trade = portfolio.Trade(value_from, value_to, "ETH")
937
938 order_mock1 = mock.Mock()
939 order_mock1.__repr__ = mock.Mock()
940 order_mock1.__repr__.return_value = "Mock 1"
941 order_mock2 = mock.Mock()
942 order_mock2.__repr__ = mock.Mock()
943 order_mock2.__repr__.return_value = "Mock 2"
944 trade.orders.append(order_mock1)
945 trade.orders.append(order_mock2)
946
947 trade.print_with_order()
948
949 out = mock_stdout.getvalue().split("\n")
950 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", out[0])
951 self.assertEqual("\tMock 1", out[1])
952 self.assertEqual("\tMock 2", out[2])
675 953
676 @unittest.skip("TODO")
677 def test_compute_value(self): 954 def test_compute_value(self):
678 pass 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")
679 975
680 @unittest.skip("TODO")
681 def test__repr(self): 976 def test__repr(self):
682 pass 977 value_from = portfolio.Amount("BTC", "0.5")
978 value_from.linked_to = portfolio.Amount("ETH", "10.0")
979 value_to = portfolio.Amount("BTC", "1.0")
980 trade = portfolio.Trade(value_from, value_to, "ETH")
981
982 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade))
683 983
684class AcceptanceTest(WebMockTestCase): 984class AcceptanceTest(WebMockTestCase):
685 @unittest.expectedFailure 985 @unittest.expectedFailure
686 def test_success_sell_only_necessary(self): 986 def test_success_sell_only_necessary(self):
687 fetch_balance = { 987 fetch_balance = {
688 "ETH": { 988 "ETH": {
689 "free": D("1.0"), 989 "exchange_free": D("1.0"),
690 "used": D("0.0"), 990 "exchange_used": D("0.0"),
991 "exchange_total": D("1.0"),
691 "total": D("1.0"), 992 "total": D("1.0"),
692 }, 993 },
693 "ETC": { 994 "ETC": {
694 "free": D("4.0"), 995 "exchange_free": D("4.0"),
695 "used": D("0.0"), 996 "exchange_used": D("0.0"),
997 "exchange_total": D("4.0"),
696 "total": D("4.0"), 998 "total": D("4.0"),
697 }, 999 },
698 "XVG": { 1000 "XVG": {
699 "free": D("1000.0"), 1001 "exchange_free": D("1000.0"),
700 "used": D("0.0"), 1002 "exchange_used": D("0.0"),
1003 "exchange_total": D("1000.0"),
701 "total": D("1000.0"), 1004 "total": D("1000.0"),
702 }, 1005 },
703 } 1006 }
@@ -752,7 +1055,7 @@ class AcceptanceTest(WebMockTestCase):
752 self.fail("Shouldn't have been called with {}".format(symbol)) 1055 self.fail("Shouldn't have been called with {}".format(symbol))
753 1056
754 market = mock.Mock() 1057 market = mock.Mock()
755 market.fetch_balance.return_value = fetch_balance 1058 market.fetch_all_balances.return_value = fetch_balance
756 market.fetch_ticker.side_effect = fetch_ticker 1059 market.fetch_ticker.side_effect = fetch_ticker
757 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 1060 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition):
758 # Action 1 1061 # Action 1
@@ -765,34 +1068,32 @@ class AcceptanceTest(WebMockTestCase):
765 1068
766 1069
767 trades = portfolio.Trade.trades 1070 trades = portfolio.Trade.trades
768 self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from) 1071 self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from)
769 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to) 1072 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to)
770 self.assertEqual("sell", trades["ETH"].action) 1073 self.assertEqual("dispose", trades[0].action)
771 1074
772 self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["ETC"].value_from) 1075 self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[1].value_from)
773 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETC"].value_to) 1076 self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[1].value_to)
774 self.assertEqual("buy", trades["ETC"].action) 1077 self.assertEqual("acquire", trades[1].action)
775
776 self.assertNotIn("BTC", trades)
777 1078
778 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["BTD"].value_from) 1079 self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades[2].value_from)
779 self.assertEqual(portfolio.Amount("BTC", D("0.002")), trades["BTD"].value_to) 1080 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[2].value_to)
780 self.assertEqual("buy", trades["BTD"].action) 1081 self.assertEqual("dispose", trades[2].action)
781 1082
782 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["B2X"].value_from) 1083 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from)
783 self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades["B2X"].value_to) 1084 self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to)
784 self.assertEqual("buy", trades["B2X"].action) 1085 self.assertEqual("dispose", trades[3].action)
785 1086
786 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["USDT"].value_from) 1087 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from)
787 self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["USDT"].value_to) 1088 self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to)
788 self.assertEqual("buy", trades["USDT"].action) 1089 self.assertEqual("acquire", trades[4].action)
789 1090
790 self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades["XVG"].value_from) 1091 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[5].value_from)
791 self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["XVG"].value_to) 1092 self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[5].value_to)
792 self.assertEqual("sell", trades["XVG"].action) 1093 self.assertEqual("acquire", trades[5].action)
793 1094
794 # Action 2 1095 # Action 2
795 portfolio.Trade.prepare_orders(only="sell", compute_value=lambda x, y: x["bid"] * D("1.001")) 1096 portfolio.Trade.prepare_orders(only="dispose", compute_value=lambda x, y: x["bid"] * D("1.001"))
796 1097
797 all_orders = portfolio.Trade.all_orders() 1098 all_orders = portfolio.Trade.all_orders()
798 self.assertEqual(2, len(all_orders)) 1099 self.assertEqual(2, len(all_orders))