diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-02-24 19:53:58 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-02-24 19:53:58 +0100 |
commit | 3d0247f944d7510943dfaa64eeb0e15a43b6c989 (patch) | |
tree | 2695181c77e444d52bca5de47b6c99b9b6c1ed32 /test.py | |
parent | c31df868c655612b8387a25111e69882f0fe6344 (diff) | |
download | Trader-3d0247f944d7510943dfaa64eeb0e15a43b6c989.tar.gz Trader-3d0247f944d7510943dfaa64eeb0e15a43b6c989.tar.zst Trader-3d0247f944d7510943dfaa64eeb0e15a43b6c989.zip |
Add report store to store messages and logs
Diffstat (limited to 'test.py')
-rw-r--r-- | test.py | 850 |
1 files changed, 714 insertions, 136 deletions
@@ -27,6 +27,8 @@ class WebMockTestCase(unittest.TestCase): | |||
27 | self.wm.start() | 27 | self.wm.start() |
28 | 28 | ||
29 | self.patchers = [ | 29 | self.patchers = [ |
30 | mock.patch.multiple(portfolio.ReportStore, | ||
31 | logs=[], verbose_print=True), | ||
30 | mock.patch.multiple(portfolio.BalanceStore, | 32 | mock.patch.multiple(portfolio.BalanceStore, |
31 | all={},), | 33 | all={},), |
32 | mock.patch.multiple(portfolio.TradeStore, | 34 | mock.patch.multiple(portfolio.TradeStore, |
@@ -63,7 +65,8 @@ class PortfolioTest(WebMockTestCase): | |||
63 | 65 | ||
64 | self.wm.get(portfolio.Portfolio.URL, text=self.json_response) | 66 | self.wm.get(portfolio.Portfolio.URL, text=self.json_response) |
65 | 67 | ||
66 | def test_get_cryptoportfolio(self): | 68 | @mock.patch("portfolio.ReportStore") |
69 | def test_get_cryptoportfolio(self, report_store): | ||
67 | self.wm.get(portfolio.Portfolio.URL, [ | 70 | self.wm.get(portfolio.Portfolio.URL, [ |
68 | {"text":'{ "foo": "bar" }', "status_code": 200}, | 71 | {"text":'{ "foo": "bar" }', "status_code": 200}, |
69 | {"text": "System Error", "status_code": 500}, | 72 | {"text": "System Error", "status_code": 500}, |
@@ -74,17 +77,28 @@ class PortfolioTest(WebMockTestCase): | |||
74 | self.assertEqual("bar", portfolio.Portfolio.data["foo"]) | 77 | self.assertEqual("bar", portfolio.Portfolio.data["foo"]) |
75 | self.assertTrue(self.wm.called) | 78 | self.assertTrue(self.wm.called) |
76 | self.assertEqual(1, self.wm.call_count) | 79 | self.assertEqual(1, self.wm.call_count) |
80 | report_store.log_error.assert_not_called() | ||
81 | report_store.log_http_request.assert_called_once() | ||
82 | report_store.log_http_request.reset_mock() | ||
77 | 83 | ||
78 | portfolio.Portfolio.get_cryptoportfolio() | 84 | portfolio.Portfolio.get_cryptoportfolio() |
79 | self.assertIsNone(portfolio.Portfolio.data) | 85 | self.assertIsNone(portfolio.Portfolio.data) |
80 | self.assertEqual(2, self.wm.call_count) | 86 | self.assertEqual(2, self.wm.call_count) |
87 | report_store.log_error.assert_not_called() | ||
88 | report_store.log_http_request.assert_called_once() | ||
89 | report_store.log_http_request.reset_mock() | ||
90 | |||
81 | 91 | ||
82 | portfolio.Portfolio.data = "Foo" | 92 | portfolio.Portfolio.data = "Foo" |
83 | portfolio.Portfolio.get_cryptoportfolio() | 93 | portfolio.Portfolio.get_cryptoportfolio() |
84 | self.assertEqual("Foo", portfolio.Portfolio.data) | 94 | self.assertEqual("Foo", portfolio.Portfolio.data) |
85 | self.assertEqual(3, self.wm.call_count) | 95 | self.assertEqual(3, self.wm.call_count) |
96 | report_store.log_error.assert_called_once_with("get_cryptoportfolio", | ||
97 | exception=mock.ANY) | ||
98 | report_store.log_http_request.assert_not_called() | ||
86 | 99 | ||
87 | def test_parse_cryptoportfolio(self): | 100 | @mock.patch("portfolio.ReportStore") |
101 | def test_parse_cryptoportfolio(self, report_store): | ||
88 | portfolio.Portfolio.parse_cryptoportfolio() | 102 | portfolio.Portfolio.parse_cryptoportfolio() |
89 | 103 | ||
90 | self.assertListEqual( | 104 | self.assertListEqual( |
@@ -121,15 +135,22 @@ class PortfolioTest(WebMockTestCase): | |||
121 | self.assertDictEqual(expected, liquidities["medium"][date]) | 135 | self.assertDictEqual(expected, liquidities["medium"][date]) |
122 | self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date) | 136 | self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date) |
123 | 137 | ||
138 | report_store.log_http_request.assert_called_once_with("GET", | ||
139 | portfolio.Portfolio.URL, None, mock.ANY, mock.ANY) | ||
140 | report_store.log_http_request.reset_mock() | ||
141 | |||
124 | # It doesn't refetch the data when available | 142 | # It doesn't refetch the data when available |
125 | portfolio.Portfolio.parse_cryptoportfolio() | 143 | portfolio.Portfolio.parse_cryptoportfolio() |
144 | report_store.log_http_request.assert_not_called() | ||
126 | 145 | ||
127 | self.assertEqual(1, self.wm.call_count) | 146 | self.assertEqual(1, self.wm.call_count) |
128 | 147 | ||
129 | portfolio.Portfolio.parse_cryptoportfolio(refetch=True) | 148 | portfolio.Portfolio.parse_cryptoportfolio(refetch=True) |
130 | self.assertEqual(2, self.wm.call_count) | 149 | self.assertEqual(2, self.wm.call_count) |
150 | report_store.log_http_request.assert_called_once() | ||
131 | 151 | ||
132 | def test_repartition(self): | 152 | @mock.patch("portfolio.ReportStore") |
153 | def test_repartition(self, report_store): | ||
133 | expected_medium = { | 154 | expected_medium = { |
134 | 'BTC': (D("1.1102e-16"), "long"), | 155 | 'BTC': (D("1.1102e-16"), "long"), |
135 | 'USDT': (D("0.1"), "long"), | 156 | 'USDT': (D("0.1"), "long"), |
@@ -163,6 +184,8 @@ class PortfolioTest(WebMockTestCase): | |||
163 | 184 | ||
164 | portfolio.Portfolio.repartition(refetch=True) | 185 | portfolio.Portfolio.repartition(refetch=True) |
165 | self.assertEqual(2, self.wm.call_count) | 186 | self.assertEqual(2, self.wm.call_count) |
187 | report_store.log_http_request.assert_called() | ||
188 | self.assertEqual(2, report_store.log_http_request.call_count) | ||
166 | 189 | ||
167 | @mock.patch.object(portfolio.time, "sleep") | 190 | @mock.patch.object(portfolio.time, "sleep") |
168 | @mock.patch.object(portfolio.Portfolio, "repartition") | 191 | @mock.patch.object(portfolio.Portfolio, "repartition") |
@@ -434,6 +457,17 @@ class AmountTest(WebMockTestCase): | |||
434 | amount2.linked_to = amount3 | 457 | amount2.linked_to = amount3 |
435 | self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1)) | 458 | self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1)) |
436 | 459 | ||
460 | def test_as_json(self): | ||
461 | amount = portfolio.Amount("BTX", 32) | ||
462 | self.assertEqual({"currency": "BTX", "value": D("32")}, amount.as_json()) | ||
463 | |||
464 | amount = portfolio.Amount("BTX", "1E-10") | ||
465 | self.assertEqual({"currency": "BTX", "value": D("0")}, amount.as_json()) | ||
466 | |||
467 | amount = portfolio.Amount("BTX", "1E-5") | ||
468 | self.assertEqual({"currency": "BTX", "value": D("0.00001")}, amount.as_json()) | ||
469 | self.assertEqual("0.00001", str(amount.as_json()["value"])) | ||
470 | |||
437 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 471 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
438 | class BalanceTest(WebMockTestCase): | 472 | class BalanceTest(WebMockTestCase): |
439 | def test_values(self): | 473 | def test_values(self): |
@@ -497,6 +531,18 @@ class BalanceTest(WebMockTestCase): | |||
497 | "margin_borrowed": 1, "exchange_free": 2, "exchange_total": 2}) | 531 | "margin_borrowed": 1, "exchange_free": 2, "exchange_total": 2}) |
498 | self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [borrowed 1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance)) | 532 | self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [borrowed 1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance)) |
499 | 533 | ||
534 | def test_as_json(self): | ||
535 | balance = portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 }) | ||
536 | as_json = balance.as_json() | ||
537 | self.assertEqual(set(portfolio.Balance.base_keys), set(as_json.keys())) | ||
538 | self.assertEqual(D(0), as_json["total"]) | ||
539 | self.assertEqual(D(2), as_json["exchange_total"]) | ||
540 | self.assertEqual(D(2), as_json["exchange_free"]) | ||
541 | self.assertEqual(D(0), as_json["exchange_used"]) | ||
542 | self.assertEqual(D(0), as_json["margin_total"]) | ||
543 | self.assertEqual(D(0), as_json["margin_free"]) | ||
544 | self.assertEqual(D(0), as_json["margin_borrowed"]) | ||
545 | |||
500 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 546 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
501 | class HelperTest(WebMockTestCase): | 547 | class HelperTest(WebMockTestCase): |
502 | def test_get_ticker(self): | 548 | def test_get_ticker(self): |
@@ -575,7 +621,9 @@ class HelperTest(WebMockTestCase): | |||
575 | @mock.patch.object(portfolio.Portfolio, "repartition") | 621 | @mock.patch.object(portfolio.Portfolio, "repartition") |
576 | @mock.patch.object(helper, "get_ticker") | 622 | @mock.patch.object(helper, "get_ticker") |
577 | @mock.patch.object(portfolio.TradeStore, "compute_trades") | 623 | @mock.patch.object(portfolio.TradeStore, "compute_trades") |
578 | def test_prepare_trades(self, compute_trades, get_ticker, repartition): | 624 | @mock.patch("store.ReportStore") |
625 | @mock.patch("helper.ReportStore") | ||
626 | def test_prepare_trades(self, report_store_h, report_store, compute_trades, get_ticker, repartition): | ||
579 | repartition.return_value = { | 627 | repartition.return_value = { |
580 | "XEM": (D("0.75"), "long"), | 628 | "XEM": (D("0.75"), "long"), |
581 | "BTC": (D("0.25"), "long"), | 629 | "BTC": (D("0.25"), "long"), |
@@ -614,11 +662,15 @@ class HelperTest(WebMockTestCase): | |||
614 | self.assertEqual(D("0.01"), call[0][0]["XVG"].value) | 662 | self.assertEqual(D("0.01"), call[0][0]["XVG"].value) |
615 | self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) | 663 | self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) |
616 | self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) | 664 | self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) |
665 | report_store_h.log_stage.assert_called_once_with("prepare_trades") | ||
666 | report_store.log_balances.assert_called_once_with(market) | ||
617 | 667 | ||
618 | @mock.patch.object(portfolio.Portfolio, "repartition") | 668 | @mock.patch.object(portfolio.Portfolio, "repartition") |
619 | @mock.patch.object(helper, "get_ticker") | 669 | @mock.patch.object(helper, "get_ticker") |
620 | @mock.patch.object(portfolio.TradeStore, "compute_trades") | 670 | @mock.patch.object(portfolio.TradeStore, "compute_trades") |
621 | def test_update_trades(self, compute_trades, get_ticker, repartition): | 671 | @mock.patch("store.ReportStore") |
672 | @mock.patch("helper.ReportStore") | ||
673 | def test_update_trades(self, report_store_h, report_store, compute_trades, get_ticker, repartition): | ||
622 | repartition.return_value = { | 674 | repartition.return_value = { |
623 | "XEM": (D("0.75"), "long"), | 675 | "XEM": (D("0.75"), "long"), |
624 | "BTC": (D("0.25"), "long"), | 676 | "BTC": (D("0.25"), "long"), |
@@ -657,11 +709,15 @@ class HelperTest(WebMockTestCase): | |||
657 | self.assertEqual(D("0.01"), call[0][0]["XVG"].value) | 709 | self.assertEqual(D("0.01"), call[0][0]["XVG"].value) |
658 | self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) | 710 | self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) |
659 | self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) | 711 | self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) |
712 | report_store_h.log_stage.assert_called_once_with("update_trades") | ||
713 | report_store.log_balances.assert_called_once_with(market) | ||
660 | 714 | ||
661 | @mock.patch.object(portfolio.Portfolio, "repartition") | 715 | @mock.patch.object(portfolio.Portfolio, "repartition") |
662 | @mock.patch.object(helper, "get_ticker") | 716 | @mock.patch.object(helper, "get_ticker") |
663 | @mock.patch.object(portfolio.TradeStore, "compute_trades") | 717 | @mock.patch.object(portfolio.TradeStore, "compute_trades") |
664 | def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition): | 718 | @mock.patch("store.ReportStore") |
719 | @mock.patch("helper.ReportStore") | ||
720 | def test_prepare_trades_to_sell_all(self, report_store_h, report_store, compute_trades, get_ticker, repartition): | ||
665 | def _get_ticker(c1, c2, market): | 721 | def _get_ticker(c1, c2, market): |
666 | if c1 == "USDT" and c2 == "BTC": | 722 | if c1 == "USDT" and c2 == "BTC": |
667 | return { "average": D("0.0001") } | 723 | return { "average": D("0.0001") } |
@@ -694,16 +750,17 @@ class HelperTest(WebMockTestCase): | |||
694 | self.assertEqual(1, call[0][0]["USDT"].value) | 750 | self.assertEqual(1, call[0][0]["USDT"].value) |
695 | self.assertEqual(D("0.01"), call[0][0]["XVG"].value) | 751 | self.assertEqual(D("0.01"), call[0][0]["XVG"].value) |
696 | self.assertEqual(D("1.01"), call[0][1]["BTC"].value) | 752 | self.assertEqual(D("1.01"), call[0][1]["BTC"].value) |
753 | report_store_h.log_stage.assert_called_once_with("prepare_trades_to_sell_all") | ||
754 | report_store.log_balances.assert_called_once_with(market) | ||
697 | 755 | ||
698 | @mock.patch.object(portfolio.time, "sleep") | 756 | @mock.patch.object(portfolio.time, "sleep") |
699 | @mock.patch.object(portfolio.TradeStore, "all_orders") | 757 | @mock.patch.object(portfolio.TradeStore, "all_orders") |
700 | def test_follow_orders(self, all_orders, time_mock): | 758 | def test_follow_orders(self, all_orders, time_mock): |
701 | for verbose, debug, sleep in [ | 759 | for debug, sleep in [ |
702 | (True, False, None), (False, False, None), | 760 | (False, None), (True, None), |
703 | (True, True, None), (True, False, 12), | 761 | (False, 12), (True, 12)]: |
704 | (True, True, 12)]: | 762 | with self.subTest(sleep=sleep, debug=debug), \ |
705 | with self.subTest(sleep=sleep, debug=debug, verbose=verbose), \ | 763 | mock.patch("helper.ReportStore") as report_store: |
706 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
707 | portfolio.TradeStore.debug = debug | 764 | portfolio.TradeStore.debug = debug |
708 | order_mock1 = mock.Mock() | 765 | order_mock1 = mock.Mock() |
709 | order_mock2 = mock.Mock() | 766 | order_mock2 = mock.Mock() |
@@ -729,7 +786,7 @@ class HelperTest(WebMockTestCase): | |||
729 | order_mock2.trade = mock.Mock() | 786 | order_mock2.trade = mock.Mock() |
730 | order_mock3.trade = mock.Mock() | 787 | order_mock3.trade = mock.Mock() |
731 | 788 | ||
732 | helper.follow_orders(verbose=verbose, sleep=sleep) | 789 | helper.follow_orders(sleep=sleep) |
733 | 790 | ||
734 | order_mock1.trade.update_order.assert_any_call(order_mock1, 1) | 791 | order_mock1.trade.update_order.assert_any_call(order_mock1, 1) |
735 | order_mock1.trade.update_order.assert_any_call(order_mock1, 2) | 792 | order_mock1.trade.update_order.assert_any_call(order_mock1, 2) |
@@ -743,25 +800,43 @@ class HelperTest(WebMockTestCase): | |||
743 | order_mock3.trade.update_order.assert_any_call(order_mock3, 2) | 800 | order_mock3.trade.update_order.assert_any_call(order_mock3, 2) |
744 | self.assertEqual(1, order_mock3.trade.update_order.call_count) | 801 | self.assertEqual(1, order_mock3.trade.update_order.call_count) |
745 | self.assertEqual(2, order_mock3.get_status.call_count) | 802 | self.assertEqual(2, order_mock3.get_status.call_count) |
803 | report_store.log_stage.assert_called() | ||
804 | calls = [ | ||
805 | mock.call("follow_orders_begin"), | ||
806 | mock.call("follow_orders_tick_1"), | ||
807 | mock.call("follow_orders_tick_2"), | ||
808 | mock.call("follow_orders_tick_3"), | ||
809 | mock.call("follow_orders_end"), | ||
810 | ] | ||
811 | report_store.log_stage.assert_has_calls(calls) | ||
812 | report_store.log_orders.assert_called() | ||
813 | self.assertEqual(3, report_store.log_orders.call_count) | ||
814 | calls = [ | ||
815 | mock.call([order_mock1, order_mock2], tick=1), | ||
816 | mock.call([order_mock1, order_mock3], tick=2), | ||
817 | mock.call([order_mock1, order_mock3], tick=3), | ||
818 | ] | ||
819 | report_store.log_orders.assert_has_calls(calls) | ||
820 | calls = [ | ||
821 | mock.call(order_mock1, 3, finished=True), | ||
822 | mock.call(order_mock3, 3, finished=True), | ||
823 | ] | ||
824 | report_store.log_order.assert_has_calls(calls) | ||
746 | 825 | ||
747 | if sleep is None: | 826 | if sleep is None: |
748 | if debug: | 827 | if debug: |
828 | report_store.log_debug_action.assert_called_with("Set follow_orders tick to 7s") | ||
749 | time_mock.assert_called_with(7) | 829 | time_mock.assert_called_with(7) |
750 | else: | 830 | else: |
751 | time_mock.assert_called_with(30) | 831 | time_mock.assert_called_with(30) |
752 | else: | 832 | else: |
753 | time_mock.assert_called_with(sleep) | 833 | time_mock.assert_called_with(sleep) |
754 | 834 | ||
755 | if verbose: | ||
756 | self.assertNotEqual("", stdout_mock.getvalue()) | ||
757 | else: | ||
758 | self.assertEqual("", stdout_mock.getvalue()) | ||
759 | |||
760 | @mock.patch.object(portfolio.BalanceStore, "fetch_balances") | 835 | @mock.patch.object(portfolio.BalanceStore, "fetch_balances") |
761 | def test_move_balance(self, fetch_balances): | 836 | def test_move_balance(self, fetch_balances): |
762 | for debug in [True, False]: | 837 | for debug in [True, False]: |
763 | with self.subTest(debug=debug),\ | 838 | with self.subTest(debug=debug),\ |
764 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 839 | mock.patch("helper.ReportStore") as report_store: |
765 | value_from = portfolio.Amount("BTC", "1.0") | 840 | value_from = portfolio.Amount("BTC", "1.0") |
766 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | 841 | value_from.linked_to = portfolio.Amount("ETH", "10.0") |
767 | value_to = portfolio.Amount("BTC", "10.0") | 842 | value_to = portfolio.Amount("BTC", "10.0") |
@@ -788,8 +863,11 @@ class HelperTest(WebMockTestCase): | |||
788 | helper.move_balances(market, debug=debug) | 863 | helper.move_balances(market, debug=debug) |
789 | 864 | ||
790 | fetch_balances.assert_called_with(market) | 865 | fetch_balances.assert_called_with(market) |
866 | report_store.log_move_balances.assert_called_once() | ||
867 | |||
791 | if debug: | 868 | if debug: |
792 | self.assertRegex(stdout_mock.getvalue(), "market.transfer_balance") | 869 | report_store.log_debug_action.assert_called() |
870 | self.assertEqual(3, report_store.log_debug_action.call_count) | ||
793 | else: | 871 | else: |
794 | market.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") | 872 | market.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") |
795 | market.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange") | 873 | market.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange") |
@@ -797,9 +875,8 @@ class HelperTest(WebMockTestCase): | |||
797 | 875 | ||
798 | @mock.patch.object(helper, "prepare_trades") | 876 | @mock.patch.object(helper, "prepare_trades") |
799 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") | 877 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") |
800 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | 878 | @mock.patch.object(portfolio.ReportStore, "log_stage") |
801 | @mock.patch('sys.stdout', new_callable=StringIO) | 879 | def test_print_orders(self, log_stage, prepare_orders, prepare_trades): |
802 | def test_print_orders(self, stdout_mock, print_all_with_order, prepare_orders, prepare_trades): | ||
803 | market = mock.Mock() | 880 | market = mock.Mock() |
804 | portfolio.BalanceStore.all = { | 881 | portfolio.BalanceStore.all = { |
805 | "BTC": portfolio.Balance("BTC", { | 882 | "BTC": portfolio.Balance("BTC", { |
@@ -817,13 +894,12 @@ class HelperTest(WebMockTestCase): | |||
817 | prepare_trades.assert_called_with(market, base_currency="BTC", | 894 | prepare_trades.assert_called_with(market, base_currency="BTC", |
818 | compute_value="average", debug=True) | 895 | compute_value="average", debug=True) |
819 | prepare_orders.assert_called_with(compute_value="average") | 896 | prepare_orders.assert_called_with(compute_value="average") |
820 | print_all_with_order.assert_called() | 897 | log_stage.assert_called_with("print_orders") |
821 | self.assertRegex(stdout_mock.getvalue(), "Balance") | ||
822 | 898 | ||
823 | @mock.patch.object(portfolio.BalanceStore, "fetch_balances") | 899 | @mock.patch.object(portfolio.BalanceStore, "fetch_balances") |
824 | @mock.patch.object(portfolio.BalanceStore, "in_currency") | 900 | @mock.patch.object(portfolio.BalanceStore, "in_currency") |
825 | @mock.patch('sys.stdout', new_callable=StringIO) | 901 | @mock.patch.object(helper.ReportStore, "print_log") |
826 | def test_print_balances(self, stdout_mock, in_currency, fetch_balances): | 902 | def test_print_balances(self, print_log, in_currency, fetch_balances): |
827 | market = mock.Mock() | 903 | market = mock.Mock() |
828 | portfolio.BalanceStore.all = { | 904 | portfolio.BalanceStore.all = { |
829 | "BTC": portfolio.Balance("BTC", { | 905 | "BTC": portfolio.Balance("BTC", { |
@@ -843,18 +919,18 @@ class HelperTest(WebMockTestCase): | |||
843 | } | 919 | } |
844 | helper.print_balances(market) | 920 | helper.print_balances(market) |
845 | fetch_balances.assert_called_with(market) | 921 | fetch_balances.assert_called_with(market) |
846 | self.assertRegex(stdout_mock.getvalue(), "Balance") | 922 | print_log.assert_has_calls([ |
847 | self.assertRegex(stdout_mock.getvalue(), "0.95000000 BTC") | 923 | mock.call("total:"), |
924 | mock.call(portfolio.Amount("BTC", "0.95")), | ||
925 | ]) | ||
848 | 926 | ||
849 | @mock.patch.object(helper, "prepare_trades") | 927 | @mock.patch.object(helper, "prepare_trades") |
850 | @mock.patch.object(helper, "follow_orders") | 928 | @mock.patch.object(helper, "follow_orders") |
851 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") | 929 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") |
852 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | ||
853 | @mock.patch.object(portfolio.TradeStore, "run_orders") | 930 | @mock.patch.object(portfolio.TradeStore, "run_orders") |
854 | @mock.patch('sys.stdout', new_callable=StringIO) | 931 | @mock.patch.object(portfolio.ReportStore, "log_stage") |
855 | def test_process_sell_needed__1_sell(self, stdout_mock, run_orders, | 932 | def test_process_sell_needed__1_sell(self, log_stage, run_orders, |
856 | print_all_with_order, prepare_orders, follow_orders, | 933 | prepare_orders, follow_orders, prepare_trades): |
857 | prepare_trades): | ||
858 | market = mock.Mock() | 934 | market = mock.Mock() |
859 | portfolio.BalanceStore.all = { | 935 | portfolio.BalanceStore.all = { |
860 | "BTC": portfolio.Balance("BTC", { | 936 | "BTC": portfolio.Balance("BTC", { |
@@ -873,20 +949,18 @@ class HelperTest(WebMockTestCase): | |||
873 | liquidity="medium", debug=False) | 949 | liquidity="medium", debug=False) |
874 | prepare_orders.assert_called_with(compute_value="average", | 950 | prepare_orders.assert_called_with(compute_value="average", |
875 | only="dispose") | 951 | only="dispose") |
876 | print_all_with_order.assert_called() | ||
877 | run_orders.assert_called() | 952 | run_orders.assert_called() |
878 | follow_orders.assert_called() | 953 | follow_orders.assert_called() |
879 | self.assertRegex(stdout_mock.getvalue(), "Balance") | 954 | log_stage.assert_called_with("process_sell_needed__1_sell_end") |
880 | 955 | ||
881 | @mock.patch.object(helper, "update_trades") | 956 | @mock.patch.object(helper, "update_trades") |
882 | @mock.patch.object(helper, "follow_orders") | 957 | @mock.patch.object(helper, "follow_orders") |
883 | @mock.patch.object(helper, "move_balances") | 958 | @mock.patch.object(helper, "move_balances") |
884 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") | 959 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") |
885 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | ||
886 | @mock.patch.object(portfolio.TradeStore, "run_orders") | 960 | @mock.patch.object(portfolio.TradeStore, "run_orders") |
887 | @mock.patch('sys.stdout', new_callable=StringIO) | 961 | @mock.patch.object(portfolio.ReportStore, "log_stage") |
888 | def test_process_sell_needed__2_buy(self, stdout_mock, run_orders, | 962 | def test_process_sell_needed__2_buy(self, log_stage, run_orders, |
889 | print_all_with_order, prepare_orders, move_balances, | 963 | prepare_orders, move_balances, |
890 | follow_orders, update_trades): | 964 | follow_orders, update_trades): |
891 | market = mock.Mock() | 965 | market = mock.Mock() |
892 | portfolio.BalanceStore.all = { | 966 | portfolio.BalanceStore.all = { |
@@ -906,21 +980,18 @@ class HelperTest(WebMockTestCase): | |||
906 | debug=False, liquidity="medium", only="acquire") | 980 | debug=False, liquidity="medium", only="acquire") |
907 | prepare_orders.assert_called_with(compute_value="average", | 981 | prepare_orders.assert_called_with(compute_value="average", |
908 | only="acquire") | 982 | only="acquire") |
909 | print_all_with_order.assert_called() | ||
910 | move_balances.assert_called_with(market, debug=False) | 983 | move_balances.assert_called_with(market, debug=False) |
911 | run_orders.assert_called() | 984 | run_orders.assert_called() |
912 | follow_orders.assert_called() | 985 | follow_orders.assert_called() |
913 | self.assertRegex(stdout_mock.getvalue(), "Balance") | 986 | log_stage.assert_called_with("process_sell_needed__2_buy_end") |
914 | 987 | ||
915 | @mock.patch.object(helper, "prepare_trades_to_sell_all") | 988 | @mock.patch.object(helper, "prepare_trades_to_sell_all") |
916 | @mock.patch.object(helper, "follow_orders") | 989 | @mock.patch.object(helper, "follow_orders") |
917 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") | 990 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") |
918 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | ||
919 | @mock.patch.object(portfolio.TradeStore, "run_orders") | 991 | @mock.patch.object(portfolio.TradeStore, "run_orders") |
920 | @mock.patch('sys.stdout', new_callable=StringIO) | 992 | @mock.patch.object(portfolio.ReportStore, "log_stage") |
921 | def test_process_sell_all__1_sell(self, stdout_mock, run_orders, | 993 | def test_process_sell_all__1_sell(self, log_stage, run_orders, |
922 | print_all_with_order, prepare_orders, follow_orders, | 994 | prepare_orders, follow_orders, prepare_trades_to_sell_all): |
923 | prepare_trades_to_sell_all): | ||
924 | market = mock.Mock() | 995 | market = mock.Mock() |
925 | portfolio.BalanceStore.all = { | 996 | portfolio.BalanceStore.all = { |
926 | "BTC": portfolio.Balance("BTC", { | 997 | "BTC": portfolio.Balance("BTC", { |
@@ -938,21 +1009,19 @@ class HelperTest(WebMockTestCase): | |||
938 | prepare_trades_to_sell_all.assert_called_with(market, base_currency="BTC", | 1009 | prepare_trades_to_sell_all.assert_called_with(market, base_currency="BTC", |
939 | debug=False) | 1010 | debug=False) |
940 | prepare_orders.assert_called_with(compute_value="average") | 1011 | prepare_orders.assert_called_with(compute_value="average") |
941 | print_all_with_order.assert_called() | ||
942 | run_orders.assert_called() | 1012 | run_orders.assert_called() |
943 | follow_orders.assert_called() | 1013 | follow_orders.assert_called() |
944 | self.assertRegex(stdout_mock.getvalue(), "Balance") | 1014 | log_stage.assert_called_with("process_sell_all__1_all_sell_end") |
945 | 1015 | ||
946 | @mock.patch.object(helper, "prepare_trades") | 1016 | @mock.patch.object(helper, "prepare_trades") |
947 | @mock.patch.object(helper, "follow_orders") | 1017 | @mock.patch.object(helper, "follow_orders") |
948 | @mock.patch.object(helper, "move_balances") | 1018 | @mock.patch.object(helper, "move_balances") |
949 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") | 1019 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") |
950 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | ||
951 | @mock.patch.object(portfolio.TradeStore, "run_orders") | 1020 | @mock.patch.object(portfolio.TradeStore, "run_orders") |
952 | @mock.patch('sys.stdout', new_callable=StringIO) | 1021 | @mock.patch.object(portfolio.ReportStore, "log_stage") |
953 | def test_process_sell_all__2_all_buy(self, stdout_mock, run_orders, | 1022 | def test_process_sell_all__2_all_buy(self, log_stage, run_orders, |
954 | print_all_with_order, prepare_orders, move_balances, | 1023 | prepare_orders, move_balances, follow_orders, |
955 | follow_orders, prepare_trades): | 1024 | prepare_trades): |
956 | market = mock.Mock() | 1025 | market = mock.Mock() |
957 | portfolio.BalanceStore.all = { | 1026 | portfolio.BalanceStore.all = { |
958 | "BTC": portfolio.Balance("BTC", { | 1027 | "BTC": portfolio.Balance("BTC", { |
@@ -970,18 +1039,18 @@ class HelperTest(WebMockTestCase): | |||
970 | prepare_trades.assert_called_with(market, base_currency="BTC", | 1039 | prepare_trades.assert_called_with(market, base_currency="BTC", |
971 | liquidity="medium", debug=False) | 1040 | liquidity="medium", debug=False) |
972 | prepare_orders.assert_called_with(compute_value="average") | 1041 | prepare_orders.assert_called_with(compute_value="average") |
973 | print_all_with_order.assert_called() | ||
974 | move_balances.assert_called_with(market, debug=False) | 1042 | move_balances.assert_called_with(market, debug=False) |
975 | run_orders.assert_called() | 1043 | run_orders.assert_called() |
976 | follow_orders.assert_called() | 1044 | follow_orders.assert_called() |
977 | self.assertRegex(stdout_mock.getvalue(), "Balance") | 1045 | log_stage.assert_called_with("process_sell_all__2_all_buy_end") |
978 | 1046 | ||
979 | 1047 | ||
980 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 1048 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
981 | class TradeStoreTest(WebMockTestCase): | 1049 | class TradeStoreTest(WebMockTestCase): |
982 | @mock.patch.object(portfolio.BalanceStore, "currencies") | 1050 | @mock.patch.object(portfolio.BalanceStore, "currencies") |
983 | @mock.patch.object(portfolio.TradeStore, "add_trade_if_matching") | 1051 | @mock.patch.object(portfolio.TradeStore, "trade_if_matching") |
984 | def test_compute_trades(self, add_trade_if_matching, currencies): | 1052 | @mock.patch.object(portfolio.ReportStore, "log_trades") |
1053 | def test_compute_trades(self, log_trades, trade_if_matching, currencies): | ||
985 | currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"] | 1054 | currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"] |
986 | 1055 | ||
987 | values_in_base = { | 1056 | values_in_base = { |
@@ -996,96 +1065,89 @@ class TradeStoreTest(WebMockTestCase): | |||
996 | "BTC": portfolio.Amount("BTC", D("0.4")), | 1065 | "BTC": portfolio.Amount("BTC", D("0.4")), |
997 | "ETH": portfolio.Amount("BTC", D("0.3")), | 1066 | "ETH": portfolio.Amount("BTC", D("0.3")), |
998 | } | 1067 | } |
1068 | side_effect = [ | ||
1069 | (True, 1), | ||
1070 | (False, 2), | ||
1071 | (False, 3), | ||
1072 | (True, 4), | ||
1073 | (True, 5) | ||
1074 | ] | ||
1075 | trade_if_matching.side_effect = side_effect | ||
999 | 1076 | ||
1000 | portfolio.TradeStore.compute_trades(values_in_base, | 1077 | portfolio.TradeStore.compute_trades(values_in_base, |
1001 | new_repartition, only="only", market="market") | 1078 | new_repartition, only="only", market="market") |
1002 | 1079 | ||
1003 | self.assertEqual(5, add_trade_if_matching.call_count) | 1080 | self.assertEqual(5, trade_if_matching.call_count) |
1004 | add_trade_if_matching.assert_any_call( | 1081 | self.assertEqual(3, len(portfolio.TradeStore.all)) |
1005 | portfolio.Amount("BTC", D("0.9")), | 1082 | self.assertEqual([1, 4, 5], portfolio.TradeStore.all) |
1006 | portfolio.Amount("BTC", 0), | 1083 | log_trades.assert_called_with(side_effect, "only", False) |
1007 | "XMR", only="only", market="market" | ||
1008 | ) | ||
1009 | add_trade_if_matching.assert_any_call( | ||
1010 | portfolio.Amount("BTC", D("0.4")), | ||
1011 | portfolio.Amount("BTC", D("0.5")), | ||
1012 | "DASH", only="only", market="market" | ||
1013 | ) | ||
1014 | add_trade_if_matching.assert_any_call( | ||
1015 | portfolio.Amount("BTC", D("-0.5")), | ||
1016 | portfolio.Amount("BTC", D("0")), | ||
1017 | "XVG", only="only", market="market" | ||
1018 | ) | ||
1019 | add_trade_if_matching.assert_any_call( | ||
1020 | portfolio.Amount("BTC", D("0")), | ||
1021 | portfolio.Amount("BTC", D("0.1")), | ||
1022 | "XVG", only="only", market="market" | ||
1023 | ) | ||
1024 | add_trade_if_matching.assert_any_call( | ||
1025 | portfolio.Amount("BTC", D("0")), | ||
1026 | portfolio.Amount("BTC", D("0.3")), | ||
1027 | "ETH", only="only", market="market" | ||
1028 | ) | ||
1029 | 1084 | ||
1030 | def test_add_trade_if_matching(self): | 1085 | def test_trade_if_matching(self): |
1031 | result = portfolio.TradeStore.add_trade_if_matching( | 1086 | result = portfolio.TradeStore.trade_if_matching( |
1032 | portfolio.Amount("BTC", D("0")), | 1087 | portfolio.Amount("BTC", D("0")), |
1033 | portfolio.Amount("BTC", D("0.3")), | 1088 | portfolio.Amount("BTC", D("0.3")), |
1034 | "ETH", only="nope", market="market" | 1089 | "ETH", only="nope", market="market" |
1035 | ) | 1090 | ) |
1036 | self.assertEqual(0, len(portfolio.TradeStore.all)) | 1091 | self.assertEqual(False, result[0]) |
1037 | self.assertEqual(False, result) | 1092 | self.assertIsInstance(result[1], portfolio.Trade) |
1038 | 1093 | ||
1039 | portfolio.TradeStore.all = [] | 1094 | portfolio.TradeStore.all = [] |
1040 | result = portfolio.TradeStore.add_trade_if_matching( | 1095 | result = portfolio.TradeStore.trade_if_matching( |
1041 | portfolio.Amount("BTC", D("0")), | 1096 | portfolio.Amount("BTC", D("0")), |
1042 | portfolio.Amount("BTC", D("0.3")), | 1097 | portfolio.Amount("BTC", D("0.3")), |
1043 | "ETH", only=None, market="market" | 1098 | "ETH", only=None, market="market" |
1044 | ) | 1099 | ) |
1045 | self.assertEqual(1, len(portfolio.TradeStore.all)) | 1100 | self.assertEqual(True, result[0]) |
1046 | self.assertEqual(True, result) | ||
1047 | 1101 | ||
1048 | portfolio.TradeStore.all = [] | 1102 | portfolio.TradeStore.all = [] |
1049 | result = portfolio.TradeStore.add_trade_if_matching( | 1103 | result = portfolio.TradeStore.trade_if_matching( |
1050 | portfolio.Amount("BTC", D("0")), | 1104 | portfolio.Amount("BTC", D("0")), |
1051 | portfolio.Amount("BTC", D("0.3")), | 1105 | portfolio.Amount("BTC", D("0.3")), |
1052 | "ETH", only="acquire", market="market" | 1106 | "ETH", only="acquire", market="market" |
1053 | ) | 1107 | ) |
1054 | self.assertEqual(1, len(portfolio.TradeStore.all)) | 1108 | self.assertEqual(True, result[0]) |
1055 | self.assertEqual(True, result) | ||
1056 | 1109 | ||
1057 | portfolio.TradeStore.all = [] | 1110 | portfolio.TradeStore.all = [] |
1058 | result = portfolio.TradeStore.add_trade_if_matching( | 1111 | result = portfolio.TradeStore.trade_if_matching( |
1059 | portfolio.Amount("BTC", D("0")), | 1112 | portfolio.Amount("BTC", D("0")), |
1060 | portfolio.Amount("BTC", D("0.3")), | 1113 | portfolio.Amount("BTC", D("0.3")), |
1061 | "ETH", only="dispose", market="market" | 1114 | "ETH", only="dispose", market="market" |
1062 | ) | 1115 | ) |
1063 | self.assertEqual(0, len(portfolio.TradeStore.all)) | 1116 | self.assertEqual(False, result[0]) |
1064 | self.assertEqual(False, result) | ||
1065 | 1117 | ||
1066 | def test_prepare_orders(self): | 1118 | @mock.patch.object(portfolio.ReportStore, "log_orders") |
1119 | def test_prepare_orders(self, log_orders): | ||
1067 | trade_mock1 = mock.Mock() | 1120 | trade_mock1 = mock.Mock() |
1068 | trade_mock2 = mock.Mock() | 1121 | trade_mock2 = mock.Mock() |
1069 | 1122 | ||
1123 | trade_mock1.prepare_order.return_value = 1 | ||
1124 | trade_mock2.prepare_order.return_value = 2 | ||
1125 | |||
1070 | portfolio.TradeStore.all.append(trade_mock1) | 1126 | portfolio.TradeStore.all.append(trade_mock1) |
1071 | portfolio.TradeStore.all.append(trade_mock2) | 1127 | portfolio.TradeStore.all.append(trade_mock2) |
1072 | 1128 | ||
1073 | portfolio.TradeStore.prepare_orders() | 1129 | portfolio.TradeStore.prepare_orders() |
1074 | trade_mock1.prepare_order.assert_called_with(compute_value="default") | 1130 | trade_mock1.prepare_order.assert_called_with(compute_value="default") |
1075 | trade_mock2.prepare_order.assert_called_with(compute_value="default") | 1131 | trade_mock2.prepare_order.assert_called_with(compute_value="default") |
1132 | log_orders.assert_called_once_with([1, 2], None, "default") | ||
1133 | |||
1134 | log_orders.reset_mock() | ||
1076 | 1135 | ||
1077 | portfolio.TradeStore.prepare_orders(compute_value="bla") | 1136 | portfolio.TradeStore.prepare_orders(compute_value="bla") |
1078 | trade_mock1.prepare_order.assert_called_with(compute_value="bla") | 1137 | trade_mock1.prepare_order.assert_called_with(compute_value="bla") |
1079 | trade_mock2.prepare_order.assert_called_with(compute_value="bla") | 1138 | trade_mock2.prepare_order.assert_called_with(compute_value="bla") |
1139 | log_orders.assert_called_once_with([1, 2], None, "bla") | ||
1080 | 1140 | ||
1081 | trade_mock1.prepare_order.reset_mock() | 1141 | trade_mock1.prepare_order.reset_mock() |
1082 | trade_mock2.prepare_order.reset_mock() | 1142 | trade_mock2.prepare_order.reset_mock() |
1143 | log_orders.reset_mock() | ||
1083 | 1144 | ||
1084 | trade_mock1.action = "foo" | 1145 | trade_mock1.action = "foo" |
1085 | trade_mock2.action = "bar" | 1146 | trade_mock2.action = "bar" |
1086 | portfolio.TradeStore.prepare_orders(only="bar") | 1147 | portfolio.TradeStore.prepare_orders(only="bar") |
1087 | trade_mock1.prepare_order.assert_not_called() | 1148 | trade_mock1.prepare_order.assert_not_called() |
1088 | trade_mock2.prepare_order.assert_called_with(compute_value="default") | 1149 | trade_mock2.prepare_order.assert_called_with(compute_value="default") |
1150 | log_orders.assert_called_once_with([2], "bar", "default") | ||
1089 | 1151 | ||
1090 | def test_print_all_with_order(self): | 1152 | def test_print_all_with_order(self): |
1091 | trade_mock1 = mock.Mock() | 1153 | trade_mock1 = mock.Mock() |
@@ -1099,8 +1161,10 @@ class TradeStoreTest(WebMockTestCase): | |||
1099 | trade_mock2.print_with_order.assert_called() | 1161 | trade_mock2.print_with_order.assert_called() |
1100 | trade_mock3.print_with_order.assert_called() | 1162 | trade_mock3.print_with_order.assert_called() |
1101 | 1163 | ||
1164 | @mock.patch.object(portfolio.ReportStore, "log_stage") | ||
1165 | @mock.patch.object(portfolio.ReportStore, "log_orders") | ||
1102 | @mock.patch.object(portfolio.TradeStore, "all_orders") | 1166 | @mock.patch.object(portfolio.TradeStore, "all_orders") |
1103 | def test_run_orders(self, all_orders): | 1167 | def test_run_orders(self, all_orders, log_orders, log_stage): |
1104 | order_mock1 = mock.Mock() | 1168 | order_mock1 = mock.Mock() |
1105 | order_mock2 = mock.Mock() | 1169 | order_mock2 = mock.Mock() |
1106 | order_mock3 = mock.Mock() | 1170 | order_mock3 = mock.Mock() |
@@ -1112,6 +1176,10 @@ class TradeStoreTest(WebMockTestCase): | |||
1112 | order_mock2.run.assert_called() | 1176 | order_mock2.run.assert_called() |
1113 | order_mock3.run.assert_called() | 1177 | order_mock3.run.assert_called() |
1114 | 1178 | ||
1179 | log_stage.assert_called_with("run_orders") | ||
1180 | log_orders.assert_called_with([order_mock1, order_mock2, | ||
1181 | order_mock3]) | ||
1182 | |||
1115 | def test_all_orders(self): | 1183 | def test_all_orders(self): |
1116 | trade_mock1 = mock.Mock() | 1184 | trade_mock1 = mock.Mock() |
1117 | trade_mock2 = mock.Mock() | 1185 | trade_mock2 = mock.Mock() |
@@ -1150,6 +1218,7 @@ class TradeStoreTest(WebMockTestCase): | |||
1150 | order_mock2.get_status.assert_called() | 1218 | order_mock2.get_status.assert_called() |
1151 | order_mock3.get_status.assert_called() | 1219 | order_mock3.get_status.assert_called() |
1152 | 1220 | ||
1221 | |||
1153 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 1222 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
1154 | class BalanceStoreTest(WebMockTestCase): | 1223 | class BalanceStoreTest(WebMockTestCase): |
1155 | def setUp(self): | 1224 | def setUp(self): |
@@ -1184,7 +1253,8 @@ class BalanceStoreTest(WebMockTestCase): | |||
1184 | } | 1253 | } |
1185 | 1254 | ||
1186 | @mock.patch.object(helper, "get_ticker") | 1255 | @mock.patch.object(helper, "get_ticker") |
1187 | def test_in_currency(self, get_ticker): | 1256 | @mock.patch("portfolio.ReportStore.log_tickers") |
1257 | def test_in_currency(self, log_tickers, get_ticker): | ||
1188 | portfolio.BalanceStore.all = { | 1258 | portfolio.BalanceStore.all = { |
1189 | "BTC": portfolio.Balance("BTC", { | 1259 | "BTC": portfolio.Balance("BTC", { |
1190 | "total": "0.65", | 1260 | "total": "0.65", |
@@ -1208,16 +1278,26 @@ class BalanceStoreTest(WebMockTestCase): | |||
1208 | self.assertEqual("BTC", amounts["ETH"].currency) | 1278 | self.assertEqual("BTC", amounts["ETH"].currency) |
1209 | self.assertEqual(D("0.65"), amounts["BTC"].value) | 1279 | self.assertEqual(D("0.65"), amounts["BTC"].value) |
1210 | self.assertEqual(D("0.30"), amounts["ETH"].value) | 1280 | self.assertEqual(D("0.30"), amounts["ETH"].value) |
1281 | log_tickers.assert_called_once_with(market, amounts, "BTC", | ||
1282 | "average", "total") | ||
1283 | log_tickers.reset_mock() | ||
1211 | 1284 | ||
1212 | amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid") | 1285 | amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid") |
1213 | self.assertEqual(D("0.65"), amounts["BTC"].value) | 1286 | self.assertEqual(D("0.65"), amounts["BTC"].value) |
1214 | self.assertEqual(D("0.27"), amounts["ETH"].value) | 1287 | self.assertEqual(D("0.27"), amounts["ETH"].value) |
1288 | log_tickers.assert_called_once_with(market, amounts, "BTC", | ||
1289 | "bid", "total") | ||
1290 | log_tickers.reset_mock() | ||
1215 | 1291 | ||
1216 | amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid", type="exchange_used") | 1292 | amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid", type="exchange_used") |
1217 | self.assertEqual(D("0.30"), amounts["BTC"].value) | 1293 | self.assertEqual(D("0.30"), amounts["BTC"].value) |
1218 | self.assertEqual(0, amounts["ETH"].value) | 1294 | self.assertEqual(0, amounts["ETH"].value) |
1295 | log_tickers.assert_called_once_with(market, amounts, "BTC", | ||
1296 | "bid", "exchange_used") | ||
1297 | log_tickers.reset_mock() | ||
1219 | 1298 | ||
1220 | def test_fetch_balances(self): | 1299 | @mock.patch.object(portfolio.ReportStore, "log_balances") |
1300 | def test_fetch_balances(self, log_balances): | ||
1221 | market = mock.Mock() | 1301 | market = mock.Mock() |
1222 | market.fetch_all_balances.return_value = self.fetch_balance | 1302 | market.fetch_all_balances.return_value = self.fetch_balance |
1223 | 1303 | ||
@@ -1231,20 +1311,24 @@ class BalanceStoreTest(WebMockTestCase): | |||
1231 | portfolio.BalanceStore.fetch_balances(market) | 1311 | portfolio.BalanceStore.fetch_balances(market) |
1232 | self.assertEqual(0, portfolio.BalanceStore.all["ETC"].total) | 1312 | self.assertEqual(0, portfolio.BalanceStore.all["ETC"].total) |
1233 | self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.BalanceStore.currencies())) | 1313 | self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.BalanceStore.currencies())) |
1314 | log_balances.assert_called_with(market) | ||
1234 | 1315 | ||
1235 | @mock.patch.object(portfolio.Portfolio, "repartition") | 1316 | @mock.patch.object(portfolio.Portfolio, "repartition") |
1236 | def test_dispatch_assets(self, repartition): | 1317 | @mock.patch.object(portfolio.ReportStore, "log_balances") |
1318 | @mock.patch("store.ReportStore.log_dispatch") | ||
1319 | def test_dispatch_assets(self, log_dispatch, log_balances, repartition): | ||
1237 | market = mock.Mock() | 1320 | market = mock.Mock() |
1238 | market.fetch_all_balances.return_value = self.fetch_balance | 1321 | market.fetch_all_balances.return_value = self.fetch_balance |
1239 | portfolio.BalanceStore.fetch_balances(market) | 1322 | portfolio.BalanceStore.fetch_balances(market) |
1240 | 1323 | ||
1241 | self.assertNotIn("XEM", portfolio.BalanceStore.currencies()) | 1324 | self.assertNotIn("XEM", portfolio.BalanceStore.currencies()) |
1242 | 1325 | ||
1243 | repartition.return_value = { | 1326 | repartition_hash = { |
1244 | "XEM": (D("0.75"), "long"), | 1327 | "XEM": (D("0.75"), "long"), |
1245 | "BTC": (D("0.26"), "long"), | 1328 | "BTC": (D("0.26"), "long"), |
1246 | "DASH": (D("0.10"), "short"), | 1329 | "DASH": (D("0.10"), "short"), |
1247 | } | 1330 | } |
1331 | repartition.return_value = repartition_hash | ||
1248 | 1332 | ||
1249 | amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "11.1")) | 1333 | amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "11.1")) |
1250 | repartition.assert_called_with(liquidity="medium") | 1334 | repartition.assert_called_with(liquidity="medium") |
@@ -1252,6 +1336,9 @@ class BalanceStoreTest(WebMockTestCase): | |||
1252 | self.assertEqual(D("2.6"), amounts["BTC"].value) | 1336 | self.assertEqual(D("2.6"), amounts["BTC"].value) |
1253 | self.assertEqual(D("7.5"), amounts["XEM"].value) | 1337 | self.assertEqual(D("7.5"), amounts["XEM"].value) |
1254 | self.assertEqual(D("-1.0"), amounts["DASH"].value) | 1338 | self.assertEqual(D("-1.0"), amounts["DASH"].value) |
1339 | log_balances.assert_called_with(market) | ||
1340 | log_dispatch.assert_called_once_with(portfolio.Amount("BTC", | ||
1341 | "11.1"), amounts, "medium", repartition_hash) | ||
1255 | 1342 | ||
1256 | def test_currencies(self): | 1343 | def test_currencies(self): |
1257 | portfolio.BalanceStore.all = { | 1344 | portfolio.BalanceStore.all = { |
@@ -1268,6 +1355,23 @@ class BalanceStoreTest(WebMockTestCase): | |||
1268 | } | 1355 | } |
1269 | self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies())) | 1356 | self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies())) |
1270 | 1357 | ||
1358 | def test_as_json(self): | ||
1359 | balance_mock1 = mock.Mock() | ||
1360 | balance_mock1.as_json.return_value = 1 | ||
1361 | |||
1362 | balance_mock2 = mock.Mock() | ||
1363 | balance_mock2.as_json.return_value = 2 | ||
1364 | |||
1365 | portfolio.BalanceStore.all = { | ||
1366 | "BTC": balance_mock1, | ||
1367 | "ETH": balance_mock2, | ||
1368 | } | ||
1369 | |||
1370 | as_json = portfolio.BalanceStore.as_json() | ||
1371 | self.assertEqual(1, as_json["BTC"]) | ||
1372 | self.assertEqual(2, as_json["ETH"]) | ||
1373 | |||
1374 | |||
1271 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 1375 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
1272 | class ComputationTest(WebMockTestCase): | 1376 | class ComputationTest(WebMockTestCase): |
1273 | def test_compute_value(self): | 1377 | def test_compute_value(self): |
@@ -1426,7 +1530,8 @@ class TradeTest(WebMockTestCase): | |||
1426 | Order.assert_not_called() | 1530 | Order.assert_not_called() |
1427 | 1531 | ||
1428 | get_ticker.return_value = { "inverted": False } | 1532 | get_ticker.return_value = { "inverted": False } |
1429 | with self.subTest(desc="Already filled"), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1533 | with self.subTest(desc="Already filled"),\ |
1534 | mock.patch("portfolio.ReportStore") as report_store: | ||
1430 | filled_amount.return_value = portfolio.Amount("FOO", "100") | 1535 | filled_amount.return_value = portfolio.Amount("FOO", "100") |
1431 | compute_value.return_value = D("0.125") | 1536 | compute_value.return_value = D("0.125") |
1432 | 1537 | ||
@@ -1441,7 +1546,7 @@ class TradeTest(WebMockTestCase): | |||
1441 | filled_amount.assert_called_with(in_base_currency=False) | 1546 | filled_amount.assert_called_with(in_base_currency=False) |
1442 | compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") | 1547 | compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") |
1443 | self.assertEqual(0, len(trade.orders)) | 1548 | self.assertEqual(0, len(trade.orders)) |
1444 | self.assertRegex(stdout_mock.getvalue(), "Less to do than already filled: ") | 1549 | report_store.log_error.assert_called_with("prepare_order", message=mock.ANY) |
1445 | Order.assert_not_called() | 1550 | Order.assert_not_called() |
1446 | 1551 | ||
1447 | with self.subTest(action="dispose", inverted=False): | 1552 | with self.subTest(action="dispose", inverted=False): |
@@ -1554,77 +1659,110 @@ class TradeTest(WebMockTestCase): | |||
1554 | prepare_order.return_value = new_order_mock | 1659 | prepare_order.return_value = new_order_mock |
1555 | 1660 | ||
1556 | for i in [0, 1, 3, 4, 6]: | 1661 | for i in [0, 1, 3, 4, 6]: |
1557 | with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1662 | with self.subTest(tick=i),\ |
1663 | mock.patch.object(portfolio.ReportStore, "log_order") as log_order: | ||
1558 | trade.update_order(order_mock, i) | 1664 | trade.update_order(order_mock, i) |
1559 | order_mock.cancel.assert_not_called() | 1665 | order_mock.cancel.assert_not_called() |
1560 | new_order_mock.run.assert_not_called() | 1666 | new_order_mock.run.assert_not_called() |
1561 | self.assertRegex(stdout_mock.getvalue(), "tick {}, waiting".format(i)) | 1667 | log_order.assert_called_once_with(order_mock, i, |
1668 | update="waiting", compute_value=None, new_order=None) | ||
1562 | 1669 | ||
1563 | order_mock.reset_mock() | 1670 | order_mock.reset_mock() |
1564 | new_order_mock.reset_mock() | 1671 | new_order_mock.reset_mock() |
1565 | trade.orders = [] | 1672 | trade.orders = [] |
1566 | 1673 | ||
1567 | with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1674 | with mock.patch.object(portfolio.ReportStore, "log_order") as log_order: |
1568 | trade.update_order(order_mock, 2) | 1675 | trade.update_order(order_mock, 2) |
1569 | order_mock.cancel.assert_called() | 1676 | order_mock.cancel.assert_called() |
1570 | new_order_mock.run.assert_called() | 1677 | new_order_mock.run.assert_called() |
1571 | prepare_order.assert_called() | 1678 | prepare_order.assert_called() |
1572 | self.assertRegex(stdout_mock.getvalue(), "tick 2, cancelling and adjusting") | 1679 | log_order.assert_called() |
1680 | self.assertEqual(2, log_order.call_count) | ||
1681 | calls = [ | ||
1682 | mock.call(order_mock, 2, update="adjusting", | ||
1683 | compute_value='lambda x, y: (x[y] + x["average"]) / 2', | ||
1684 | new_order=new_order_mock), | ||
1685 | mock.call(order_mock, 2, new_order=new_order_mock), | ||
1686 | ] | ||
1687 | log_order.assert_has_calls(calls) | ||
1573 | 1688 | ||
1574 | order_mock.reset_mock() | 1689 | order_mock.reset_mock() |
1575 | new_order_mock.reset_mock() | 1690 | new_order_mock.reset_mock() |
1576 | trade.orders = [] | 1691 | trade.orders = [] |
1577 | 1692 | ||
1578 | with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1693 | with mock.patch.object(portfolio.ReportStore, "log_order") as log_order: |
1579 | trade.update_order(order_mock, 5) | 1694 | trade.update_order(order_mock, 5) |
1580 | order_mock.cancel.assert_called() | 1695 | order_mock.cancel.assert_called() |
1581 | new_order_mock.run.assert_called() | 1696 | new_order_mock.run.assert_called() |
1582 | prepare_order.assert_called() | 1697 | prepare_order.assert_called() |
1583 | self.assertRegex(stdout_mock.getvalue(), "tick 5, cancelling and adjusting") | 1698 | self.assertEqual(2, log_order.call_count) |
1699 | log_order.assert_called() | ||
1700 | calls = [ | ||
1701 | mock.call(order_mock, 5, update="adjusting", | ||
1702 | compute_value='lambda x, y: (x[y]*2 + x["average"]) / 3', | ||
1703 | new_order=new_order_mock), | ||
1704 | mock.call(order_mock, 5, new_order=new_order_mock), | ||
1705 | ] | ||
1706 | log_order.assert_has_calls(calls) | ||
1584 | 1707 | ||
1585 | order_mock.reset_mock() | 1708 | order_mock.reset_mock() |
1586 | new_order_mock.reset_mock() | 1709 | new_order_mock.reset_mock() |
1587 | trade.orders = [] | 1710 | trade.orders = [] |
1588 | 1711 | ||
1589 | with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1712 | with mock.patch.object(portfolio.ReportStore, "log_order") as log_order: |
1590 | trade.update_order(order_mock, 7) | 1713 | trade.update_order(order_mock, 7) |
1591 | order_mock.cancel.assert_called() | 1714 | order_mock.cancel.assert_called() |
1592 | new_order_mock.run.assert_called() | 1715 | new_order_mock.run.assert_called() |
1593 | prepare_order.assert_called_with(compute_value="default") | 1716 | prepare_order.assert_called_with(compute_value="default") |
1594 | self.assertRegex(stdout_mock.getvalue(), "tick 7, fallbacking to market value") | 1717 | log_order.assert_called() |
1595 | self.assertRegex(stdout_mock.getvalue(), "tick 7, market value, cancelling and adjusting to") | 1718 | self.assertEqual(2, log_order.call_count) |
1719 | calls = [ | ||
1720 | mock.call(order_mock, 7, update="market_fallback", | ||
1721 | compute_value='default', | ||
1722 | new_order=new_order_mock), | ||
1723 | mock.call(order_mock, 7, new_order=new_order_mock), | ||
1724 | ] | ||
1725 | log_order.assert_has_calls(calls) | ||
1596 | 1726 | ||
1597 | order_mock.reset_mock() | 1727 | order_mock.reset_mock() |
1598 | new_order_mock.reset_mock() | 1728 | new_order_mock.reset_mock() |
1599 | trade.orders = [] | 1729 | trade.orders = [] |
1600 | 1730 | ||
1601 | for i in [10, 13, 16]: | 1731 | for i in [10, 13, 16]: |
1602 | with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1732 | with self.subTest(tick=i), mock.patch.object(portfolio.ReportStore, "log_order") as log_order: |
1603 | trade.update_order(order_mock, i) | 1733 | trade.update_order(order_mock, i) |
1604 | order_mock.cancel.assert_called() | 1734 | order_mock.cancel.assert_called() |
1605 | new_order_mock.run.assert_called() | 1735 | new_order_mock.run.assert_called() |
1606 | prepare_order.assert_called_with(compute_value="default") | 1736 | prepare_order.assert_called_with(compute_value="default") |
1607 | self.assertNotRegex(stdout_mock.getvalue(), "tick {}, fallbacking to market value".format(i)) | 1737 | log_order.assert_called() |
1608 | self.assertRegex(stdout_mock.getvalue(), "tick {}, market value, cancelling and adjusting to".format(i)) | 1738 | self.assertEqual(2, log_order.call_count) |
1739 | calls = [ | ||
1740 | mock.call(order_mock, i, update="market_adjust", | ||
1741 | compute_value='default', | ||
1742 | new_order=new_order_mock), | ||
1743 | mock.call(order_mock, i, new_order=new_order_mock), | ||
1744 | ] | ||
1745 | log_order.assert_has_calls(calls) | ||
1609 | 1746 | ||
1610 | order_mock.reset_mock() | 1747 | order_mock.reset_mock() |
1611 | new_order_mock.reset_mock() | 1748 | new_order_mock.reset_mock() |
1612 | trade.orders = [] | 1749 | trade.orders = [] |
1613 | 1750 | ||
1614 | for i in [8, 9, 11, 12]: | 1751 | for i in [8, 9, 11, 12]: |
1615 | with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1752 | with self.subTest(tick=i), mock.patch.object(portfolio.ReportStore, "log_order") as log_order: |
1616 | trade.update_order(order_mock, i) | 1753 | trade.update_order(order_mock, i) |
1617 | order_mock.cancel.assert_not_called() | 1754 | order_mock.cancel.assert_not_called() |
1618 | new_order_mock.run.assert_not_called() | 1755 | new_order_mock.run.assert_not_called() |
1619 | self.assertEqual("", stdout_mock.getvalue()) | 1756 | log_order.assert_called_once_with(order_mock, i, update="waiting", |
1757 | compute_value=None, new_order=None) | ||
1620 | 1758 | ||
1621 | order_mock.reset_mock() | 1759 | order_mock.reset_mock() |
1622 | new_order_mock.reset_mock() | 1760 | new_order_mock.reset_mock() |
1623 | trade.orders = [] | 1761 | trade.orders = [] |
1624 | 1762 | ||
1625 | 1763 | ||
1626 | @mock.patch('sys.stdout', new_callable=StringIO) | 1764 | @mock.patch.object(portfolio.ReportStore, "print_log") |
1627 | def test_print_with_order(self, mock_stdout): | 1765 | def test_print_with_order(self, print_log): |
1628 | value_from = portfolio.Amount("BTC", "0.5") | 1766 | value_from = portfolio.Amount("BTC", "0.5") |
1629 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | 1767 | value_from.linked_to = portfolio.Amount("ETH", "10.0") |
1630 | value_to = portfolio.Amount("BTC", "1.0") | 1768 | value_to = portfolio.Amount("BTC", "1.0") |
@@ -1651,12 +1789,13 @@ class TradeTest(WebMockTestCase): | |||
1651 | 1789 | ||
1652 | trade.print_with_order() | 1790 | trade.print_with_order() |
1653 | 1791 | ||
1654 | out = mock_stdout.getvalue().split("\n") | 1792 | print_log.assert_called() |
1655 | self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", out[0]) | 1793 | calls = print_log.mock_calls |
1656 | self.assertEqual("\tMock 1", out[1]) | 1794 | self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls[0][1][0])) |
1657 | self.assertEqual("\tMock 2", out[2]) | 1795 | self.assertEqual("\tMock 1", str(calls[1][1][0])) |
1658 | self.assertEqual("\t\tMouvement 1", out[3]) | 1796 | self.assertEqual("\tMock 2", str(calls[2][1][0])) |
1659 | self.assertEqual("\t\tMouvement 2", out[4]) | 1797 | self.assertEqual("\t\tMouvement 1", str(calls[3][1][0])) |
1798 | self.assertEqual("\t\tMouvement 2", str(calls[4][1][0])) | ||
1660 | 1799 | ||
1661 | def test__repr(self): | 1800 | def test__repr(self): |
1662 | value_from = portfolio.Amount("BTC", "0.5") | 1801 | value_from = portfolio.Amount("BTC", "0.5") |
@@ -1666,6 +1805,19 @@ class TradeTest(WebMockTestCase): | |||
1666 | 1805 | ||
1667 | self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) | 1806 | self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) |
1668 | 1807 | ||
1808 | def test_as_json(self): | ||
1809 | value_from = portfolio.Amount("BTC", "0.5") | ||
1810 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | ||
1811 | value_to = portfolio.Amount("BTC", "1.0") | ||
1812 | trade = portfolio.Trade(value_from, value_to, "ETH") | ||
1813 | |||
1814 | as_json = trade.as_json() | ||
1815 | self.assertEqual("acquire", as_json["action"]) | ||
1816 | self.assertEqual(D("0.5"), as_json["from"]) | ||
1817 | self.assertEqual(D("1.0"), as_json["to"]) | ||
1818 | self.assertEqual("ETH", as_json["currency"]) | ||
1819 | self.assertEqual("BTC", as_json["base_currency"]) | ||
1820 | |||
1669 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 1821 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
1670 | class OrderTest(WebMockTestCase): | 1822 | class OrderTest(WebMockTestCase): |
1671 | def test_values(self): | 1823 | def test_values(self): |
@@ -1693,6 +1845,27 @@ class OrderTest(WebMockTestCase): | |||
1693 | close_if_possible=True) | 1845 | close_if_possible=True) |
1694 | self.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending] ✂)", repr(order)) | 1846 | self.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending] ✂)", repr(order)) |
1695 | 1847 | ||
1848 | def test_as_json(self): | ||
1849 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1850 | D("0.1"), "BTC", "long", "market", "trade") | ||
1851 | mouvement_mock1 = mock.Mock() | ||
1852 | mouvement_mock1.as_json.return_value = 1 | ||
1853 | mouvement_mock2 = mock.Mock() | ||
1854 | mouvement_mock2.as_json.return_value = 2 | ||
1855 | |||
1856 | order.mouvements = [mouvement_mock1, mouvement_mock2] | ||
1857 | as_json = order.as_json() | ||
1858 | self.assertEqual("buy", as_json["action"]) | ||
1859 | self.assertEqual("long", as_json["trade_type"]) | ||
1860 | self.assertEqual(10, as_json["amount"]) | ||
1861 | self.assertEqual("ETH", as_json["currency"]) | ||
1862 | self.assertEqual("BTC", as_json["base_currency"]) | ||
1863 | self.assertEqual(D("0.1"), as_json["rate"]) | ||
1864 | self.assertEqual("pending", as_json["status"]) | ||
1865 | self.assertEqual(False, as_json["close_if_possible"]) | ||
1866 | self.assertIsNone(as_json["id"]) | ||
1867 | self.assertEqual([1, 2], as_json["mouvements"]) | ||
1868 | |||
1696 | def test_account(self): | 1869 | def test_account(self): |
1697 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 1870 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
1698 | D("0.1"), "BTC", "long", "market", "trade") | 1871 | D("0.1"), "BTC", "long", "market", "trade") |
@@ -1728,7 +1901,8 @@ class OrderTest(WebMockTestCase): | |||
1728 | self.assertTrue(order.finished) | 1901 | self.assertTrue(order.finished) |
1729 | 1902 | ||
1730 | @mock.patch.object(portfolio.Order, "fetch") | 1903 | @mock.patch.object(portfolio.Order, "fetch") |
1731 | def test_cancel(self, fetch): | 1904 | @mock.patch("portfolio.ReportStore") |
1905 | def test_cancel(self, report_store, fetch): | ||
1732 | market = mock.Mock() | 1906 | market = mock.Mock() |
1733 | portfolio.TradeStore.debug = True | 1907 | portfolio.TradeStore.debug = True |
1734 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 1908 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
@@ -1737,6 +1911,8 @@ class OrderTest(WebMockTestCase): | |||
1737 | 1911 | ||
1738 | order.cancel() | 1912 | order.cancel() |
1739 | market.cancel_order.assert_not_called() | 1913 | market.cancel_order.assert_not_called() |
1914 | report_store.log_debug_action.assert_called_once() | ||
1915 | report_store.log_debug_action.reset_mock() | ||
1740 | self.assertEqual("canceled", order.status) | 1916 | self.assertEqual("canceled", order.status) |
1741 | 1917 | ||
1742 | portfolio.TradeStore.debug = False | 1918 | portfolio.TradeStore.debug = False |
@@ -1748,6 +1924,7 @@ class OrderTest(WebMockTestCase): | |||
1748 | order.cancel() | 1924 | order.cancel() |
1749 | market.cancel_order.assert_called_with(42) | 1925 | market.cancel_order.assert_called_with(42) |
1750 | fetch.assert_called_once() | 1926 | fetch.assert_called_once() |
1927 | report_store.log_debug_action.assert_not_called() | ||
1751 | 1928 | ||
1752 | def test_dust_amount_remaining(self): | 1929 | def test_dust_amount_remaining(self): |
1753 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 1930 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
@@ -1824,7 +2001,8 @@ class OrderTest(WebMockTestCase): | |||
1824 | order.fetch_mouvements() | 2001 | order.fetch_mouvements() |
1825 | self.assertEqual(0, len(order.mouvements)) | 2002 | self.assertEqual(0, len(order.mouvements)) |
1826 | 2003 | ||
1827 | def test_mark_finished_order(self): | 2004 | @mock.patch("portfolio.ReportStore") |
2005 | def test_mark_finished_order(self, report_store): | ||
1828 | market = mock.Mock() | 2006 | market = mock.Mock() |
1829 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 2007 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
1830 | D("0.1"), "BTC", "short", market, "trade", | 2008 | D("0.1"), "BTC", "short", market, "trade", |
@@ -1870,9 +2048,11 @@ class OrderTest(WebMockTestCase): | |||
1870 | 2048 | ||
1871 | order.mark_finished_order() | 2049 | order.mark_finished_order() |
1872 | market.close_margin_position.assert_not_called() | 2050 | market.close_margin_position.assert_not_called() |
2051 | report_store.log_debug_action.assert_called_once() | ||
1873 | 2052 | ||
1874 | @mock.patch.object(portfolio.Order, "fetch_mouvements") | 2053 | @mock.patch.object(portfolio.Order, "fetch_mouvements") |
1875 | def test_fetch(self, fetch_mouvements): | 2054 | @mock.patch("portfolio.ReportStore") |
2055 | def test_fetch(self, report_store, fetch_mouvements): | ||
1876 | time = self.time.time() | 2056 | time = self.time.time() |
1877 | with mock.patch.object(portfolio.time, "time") as time_mock: | 2057 | with mock.patch.object(portfolio.time, "time") as time_mock: |
1878 | market = mock.Mock() | 2058 | market = mock.Mock() |
@@ -1883,10 +2063,14 @@ class OrderTest(WebMockTestCase): | |||
1883 | portfolio.TradeStore.debug = True | 2063 | portfolio.TradeStore.debug = True |
1884 | order.fetch() | 2064 | order.fetch() |
1885 | time_mock.assert_not_called() | 2065 | time_mock.assert_not_called() |
2066 | report_store.log_debug_action.assert_called_once() | ||
2067 | report_store.log_debug_action.reset_mock() | ||
1886 | order.fetch(force=True) | 2068 | order.fetch(force=True) |
1887 | time_mock.assert_not_called() | 2069 | time_mock.assert_not_called() |
1888 | market.fetch_order.assert_not_called() | 2070 | market.fetch_order.assert_not_called() |
1889 | fetch_mouvements.assert_not_called() | 2071 | fetch_mouvements.assert_not_called() |
2072 | report_store.log_debug_action.assert_called_once() | ||
2073 | report_store.log_debug_action.reset_mock() | ||
1890 | self.assertIsNone(order.fetch_cache_timestamp) | 2074 | self.assertIsNone(order.fetch_cache_timestamp) |
1891 | 2075 | ||
1892 | with self.subTest(debug=False): | 2076 | with self.subTest(debug=False): |
@@ -1924,16 +2108,19 @@ class OrderTest(WebMockTestCase): | |||
1924 | order.fetch() | 2108 | order.fetch() |
1925 | market.fetch_order.assert_called_once() | 2109 | market.fetch_order.assert_called_once() |
1926 | fetch_mouvements.assert_called_once() | 2110 | fetch_mouvements.assert_called_once() |
2111 | report_store.log_debug_action.assert_not_called() | ||
1927 | 2112 | ||
1928 | @mock.patch.object(portfolio.Order, "fetch") | 2113 | @mock.patch.object(portfolio.Order, "fetch") |
1929 | @mock.patch.object(portfolio.Order, "mark_finished_order") | 2114 | @mock.patch.object(portfolio.Order, "mark_finished_order") |
1930 | def test_get_status(self, mark_finished_order, fetch): | 2115 | @mock.patch("portfolio.ReportStore") |
2116 | def test_get_status(self, report_store, mark_finished_order, fetch): | ||
1931 | with self.subTest(debug=True): | 2117 | with self.subTest(debug=True): |
1932 | portfolio.TradeStore.debug = True | 2118 | portfolio.TradeStore.debug = True |
1933 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 2119 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
1934 | D("0.1"), "BTC", "long", "market", "trade") | 2120 | D("0.1"), "BTC", "long", "market", "trade") |
1935 | self.assertEqual("pending", order.get_status()) | 2121 | self.assertEqual("pending", order.get_status()) |
1936 | fetch.assert_not_called() | 2122 | fetch.assert_not_called() |
2123 | report_store.log_debug_action.assert_called_once() | ||
1937 | 2124 | ||
1938 | with self.subTest(debug=False, finished=False): | 2125 | with self.subTest(debug=False, finished=False): |
1939 | portfolio.TradeStore.debug = False | 2126 | portfolio.TradeStore.debug = False |
@@ -1968,13 +2155,13 @@ class OrderTest(WebMockTestCase): | |||
1968 | 2155 | ||
1969 | market.order_precision.return_value = 4 | 2156 | market.order_precision.return_value = 4 |
1970 | with self.subTest(debug=True),\ | 2157 | with self.subTest(debug=True),\ |
1971 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 2158 | mock.patch('portfolio.ReportStore') as report_store: |
1972 | portfolio.TradeStore.debug = True | 2159 | portfolio.TradeStore.debug = True |
1973 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 2160 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
1974 | D("0.1"), "BTC", "long", market, "trade") | 2161 | D("0.1"), "BTC", "long", market, "trade") |
1975 | order.run() | 2162 | order.run() |
1976 | market.create_order.assert_not_called() | 2163 | market.create_order.assert_not_called() |
1977 | self.assertEqual("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)\n", stdout_mock.getvalue()) | 2164 | report_store.log_debug_action.assert_called_with("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)") |
1978 | self.assertEqual("open", order.status) | 2165 | self.assertEqual("open", order.status) |
1979 | self.assertEqual(1, len(order.results)) | 2166 | self.assertEqual(1, len(order.results)) |
1980 | self.assertEqual(-1, order.id) | 2167 | self.assertEqual(-1, order.id) |
@@ -1992,7 +2179,7 @@ class OrderTest(WebMockTestCase): | |||
1992 | 2179 | ||
1993 | market.create_order.reset_mock() | 2180 | market.create_order.reset_mock() |
1994 | with self.subTest(exception=True),\ | 2181 | with self.subTest(exception=True),\ |
1995 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 2182 | mock.patch('portfolio.ReportStore') as report_store: |
1996 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 2183 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
1997 | D("0.1"), "BTC", "long", market, "trade") | 2184 | D("0.1"), "BTC", "long", market, "trade") |
1998 | market.create_order.side_effect = Exception("bouh") | 2185 | market.create_order.side_effect = Exception("bouh") |
@@ -2000,8 +2187,7 @@ class OrderTest(WebMockTestCase): | |||
2000 | market.create_order.assert_called_once() | 2187 | market.create_order.assert_called_once() |
2001 | self.assertEqual(0, len(order.results)) | 2188 | self.assertEqual(0, len(order.results)) |
2002 | self.assertEqual("error", order.status) | 2189 | self.assertEqual("error", order.status) |
2003 | self.assertRegex(stdout_mock.getvalue(), "error when running market.create_order") | 2190 | report_store.log_error.assert_called_once() |
2004 | self.assertRegex(stdout_mock.getvalue(), "Exception: bouh") | ||
2005 | 2191 | ||
2006 | market.create_order.reset_mock() | 2192 | market.create_order.reset_mock() |
2007 | with self.subTest(dust_amount_exception=True),\ | 2193 | with self.subTest(dust_amount_exception=True),\ |
@@ -2050,6 +2236,7 @@ class MouvementTest(WebMockTestCase): | |||
2050 | "amount": "10", "total": "1" | 2236 | "amount": "10", "total": "1" |
2051 | }) | 2237 | }) |
2052 | self.assertEqual("Mouvement(2017-12-30 12:00:12 ; buy 10.00000000 ETH (1.00000000 BTC) fee: 0.1500%)", repr(mouvement)) | 2238 | self.assertEqual("Mouvement(2017-12-30 12:00:12 ; buy 10.00000000 ETH (1.00000000 BTC) fee: 0.1500%)", repr(mouvement)) |
2239 | |||
2053 | mouvement = portfolio.Mouvement("ETH", "BTC", { | 2240 | mouvement = portfolio.Mouvement("ETH", "BTC", { |
2054 | "tradeID": 42, "type": "buy", | 2241 | "tradeID": 42, "type": "buy", |
2055 | "date": "garbage", "rate": "0.1", | 2242 | "date": "garbage", "rate": "0.1", |
@@ -2057,10 +2244,401 @@ class MouvementTest(WebMockTestCase): | |||
2057 | }) | 2244 | }) |
2058 | self.assertEqual("Mouvement(No date ; buy 10.00000000 ETH (1.00000000 BTC))", repr(mouvement)) | 2245 | self.assertEqual("Mouvement(No date ; buy 10.00000000 ETH (1.00000000 BTC))", repr(mouvement)) |
2059 | 2246 | ||
2247 | def test_as_json(self): | ||
2248 | mouvement = portfolio.Mouvement("ETH", "BTC", { | ||
2249 | "tradeID": 42, "type": "buy", "fee": "0.0015", | ||
2250 | "date": "2017-12-30 12:00:12", "rate": "0.1", | ||
2251 | "amount": "10", "total": "1" | ||
2252 | }) | ||
2253 | as_json = mouvement.as_json() | ||
2254 | |||
2255 | self.assertEqual(D("0.0015"), as_json["fee_rate"]) | ||
2256 | self.assertEqual(portfolio.datetime(2017, 12, 30, 12, 0, 12), as_json["date"]) | ||
2257 | self.assertEqual("buy", as_json["action"]) | ||
2258 | self.assertEqual(D("10"), as_json["total"]) | ||
2259 | self.assertEqual(D("1"), as_json["total_in_base"]) | ||
2260 | self.assertEqual("BTC", as_json["base_currency"]) | ||
2261 | self.assertEqual("ETH", as_json["currency"]) | ||
2262 | |||
2263 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
2264 | class ReportStoreTest(WebMockTestCase): | ||
2265 | def test_add_log(self): | ||
2266 | portfolio.ReportStore.add_log({"foo": "bar"}) | ||
2267 | |||
2268 | self.assertEqual({"foo": "bar", "date": mock.ANY}, portfolio.ReportStore.logs[0]) | ||
2269 | |||
2270 | def test_set_verbose(self): | ||
2271 | with self.subTest(verbose=True): | ||
2272 | portfolio.ReportStore.set_verbose(True) | ||
2273 | self.assertTrue(portfolio.ReportStore.verbose_print) | ||
2274 | |||
2275 | with self.subTest(verbose=False): | ||
2276 | portfolio.ReportStore.set_verbose(False) | ||
2277 | self.assertFalse(portfolio.ReportStore.verbose_print) | ||
2278 | |||
2279 | def test_print_log(self): | ||
2280 | with self.subTest(verbose=True),\ | ||
2281 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
2282 | portfolio.ReportStore.set_verbose(True) | ||
2283 | portfolio.ReportStore.print_log("Coucou") | ||
2284 | portfolio.ReportStore.print_log(portfolio.Amount("BTC", 1)) | ||
2285 | self.assertEqual(stdout_mock.getvalue(), "Coucou\n1.00000000 BTC\n") | ||
2286 | |||
2287 | with self.subTest(verbose=False),\ | ||
2288 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
2289 | portfolio.ReportStore.set_verbose(False) | ||
2290 | portfolio.ReportStore.print_log("Coucou") | ||
2291 | portfolio.ReportStore.print_log(portfolio.Amount("BTC", 1)) | ||
2292 | self.assertEqual(stdout_mock.getvalue(), "") | ||
2293 | |||
2294 | def test_to_json(self): | ||
2295 | portfolio.ReportStore.logs.append({"foo": "bar"}) | ||
2296 | self.assertEqual('[{"foo": "bar"}]', portfolio.ReportStore.to_json()) | ||
2297 | portfolio.ReportStore.logs.append({"date": portfolio.datetime(2018, 2, 24)}) | ||
2298 | self.assertEqual('[{"foo": "bar"}, {"date": "2018-02-24T00:00:00"}]', portfolio.ReportStore.to_json()) | ||
2299 | portfolio.ReportStore.logs.append({"amount": portfolio.Amount("BTC", 1)}) | ||
2300 | with self.assertRaises(TypeError): | ||
2301 | portfolio.ReportStore.to_json() | ||
2302 | |||
2303 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2304 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2305 | def test_log_stage(self, add_log, print_log): | ||
2306 | portfolio.ReportStore.log_stage("foo") | ||
2307 | print_log.assert_has_calls([ | ||
2308 | mock.call("-----------"), | ||
2309 | mock.call("[Stage] foo"), | ||
2310 | ]) | ||
2311 | add_log.assert_called_once_with({'type': 'stage', 'stage': 'foo'}) | ||
2312 | |||
2313 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2314 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2315 | @mock.patch("store.BalanceStore") | ||
2316 | def test_log_balances(self, balance_store, add_log, print_log): | ||
2317 | balance_store.as_json.return_value = "json" | ||
2318 | balance_store.all = { "FOO": "bar", "BAR": "baz" } | ||
2319 | |||
2320 | portfolio.ReportStore.log_balances("market") | ||
2321 | print_log.assert_has_calls([ | ||
2322 | mock.call("[Balance]"), | ||
2323 | mock.call("\tbar"), | ||
2324 | mock.call("\tbaz"), | ||
2325 | ]) | ||
2326 | add_log.assert_called_once_with({'type': 'balance', 'balances': 'json'}) | ||
2327 | |||
2328 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2329 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2330 | def test_log_tickers(self, add_log, print_log): | ||
2331 | market = mock.Mock() | ||
2332 | amounts = { | ||
2333 | "BTC": portfolio.Amount("BTC", 10), | ||
2334 | "ETH": portfolio.Amount("BTC", D("0.3")) | ||
2335 | } | ||
2336 | amounts["ETH"].rate = D("0.1") | ||
2337 | |||
2338 | portfolio.ReportStore.log_tickers(market, amounts, "BTC", "default", "total") | ||
2339 | print_log.assert_not_called() | ||
2340 | add_log.assert_called_once_with({ | ||
2341 | 'type': 'tickers', | ||
2342 | 'compute_value': 'default', | ||
2343 | 'balance_type': 'total', | ||
2344 | 'currency': 'BTC', | ||
2345 | 'balances': { | ||
2346 | 'BTC': D('10'), | ||
2347 | 'ETH': D('0.3') | ||
2348 | }, | ||
2349 | 'rates': { | ||
2350 | 'BTC': None, | ||
2351 | 'ETH': D('0.1') | ||
2352 | }, | ||
2353 | 'total': D('10.3') | ||
2354 | }) | ||
2355 | |||
2356 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2357 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2358 | def test_log_dispatch(self, add_log, print_log): | ||
2359 | amount = portfolio.Amount("BTC", "10.3") | ||
2360 | amounts = { | ||
2361 | "BTC": portfolio.Amount("BTC", 10), | ||
2362 | "ETH": portfolio.Amount("BTC", D("0.3")) | ||
2363 | } | ||
2364 | portfolio.ReportStore.log_dispatch(amount, amounts, "medium", "repartition") | ||
2365 | print_log.assert_not_called() | ||
2366 | add_log.assert_called_once_with({ | ||
2367 | 'type': 'dispatch', | ||
2368 | 'liquidity': 'medium', | ||
2369 | 'repartition_ratio': 'repartition', | ||
2370 | 'total_amount': { | ||
2371 | 'currency': 'BTC', | ||
2372 | 'value': D('10.3') | ||
2373 | }, | ||
2374 | 'repartition': { | ||
2375 | 'BTC': D('10'), | ||
2376 | 'ETH': D('0.3') | ||
2377 | } | ||
2378 | }) | ||
2379 | |||
2380 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2381 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2382 | def test_log_trades(self, add_log, print_log): | ||
2383 | trade_mock1 = mock.Mock() | ||
2384 | trade_mock2 = mock.Mock() | ||
2385 | trade_mock1.as_json.return_value = { "trade": "1" } | ||
2386 | trade_mock2.as_json.return_value = { "trade": "2" } | ||
2387 | |||
2388 | matching_and_trades = [ | ||
2389 | (True, trade_mock1), | ||
2390 | (False, trade_mock2), | ||
2391 | ] | ||
2392 | portfolio.ReportStore.log_trades(matching_and_trades, "only", "debug") | ||
2393 | |||
2394 | print_log.assert_not_called() | ||
2395 | add_log.assert_called_with({ | ||
2396 | 'type': 'trades', | ||
2397 | 'only': 'only', | ||
2398 | 'debug': 'debug', | ||
2399 | 'trades': [ | ||
2400 | {'trade': '1', 'skipped': False}, | ||
2401 | {'trade': '2', 'skipped': True} | ||
2402 | ] | ||
2403 | }) | ||
2404 | |||
2405 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2406 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2407 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | ||
2408 | def test_log_orders(self, print_all_with_order, add_log, print_log): | ||
2409 | order_mock1 = mock.Mock() | ||
2410 | order_mock2 = mock.Mock() | ||
2411 | |||
2412 | order_mock1.as_json.return_value = "order1" | ||
2413 | order_mock2.as_json.return_value = "order2" | ||
2414 | |||
2415 | orders = [order_mock1, order_mock2] | ||
2416 | |||
2417 | portfolio.ReportStore.log_orders(orders, tick="tick", | ||
2418 | only="only", compute_value="compute_value") | ||
2419 | |||
2420 | print_log.assert_called_once_with("[Orders]") | ||
2421 | print_all_with_order.assert_called_once_with(ind="\t") | ||
2422 | |||
2423 | add_log.assert_called_with({ | ||
2424 | 'type': 'orders', | ||
2425 | 'only': 'only', | ||
2426 | 'compute_value': 'compute_value', | ||
2427 | 'tick': 'tick', | ||
2428 | 'orders': ['order1', 'order2'] | ||
2429 | }) | ||
2430 | |||
2431 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2432 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2433 | def test_log_order(self, add_log, print_log): | ||
2434 | order_mock = mock.Mock() | ||
2435 | order_mock.as_json.return_value = "order" | ||
2436 | new_order_mock = mock.Mock() | ||
2437 | new_order_mock.as_json.return_value = "new_order" | ||
2438 | order_mock.__repr__ = mock.Mock() | ||
2439 | order_mock.__repr__.return_value = "Order Mock" | ||
2440 | new_order_mock.__repr__ = mock.Mock() | ||
2441 | new_order_mock.__repr__.return_value = "New order Mock" | ||
2442 | |||
2443 | with self.subTest(finished=True): | ||
2444 | portfolio.ReportStore.log_order(order_mock, 1, finished=True) | ||
2445 | print_log.assert_called_once_with("[Order] Finished Order Mock") | ||
2446 | add_log.assert_called_once_with({ | ||
2447 | 'type': 'order', | ||
2448 | 'tick': 1, | ||
2449 | 'update': None, | ||
2450 | 'order': 'order', | ||
2451 | 'compute_value': None, | ||
2452 | 'new_order': None | ||
2453 | }) | ||
2454 | |||
2455 | add_log.reset_mock() | ||
2456 | print_log.reset_mock() | ||
2457 | |||
2458 | with self.subTest(update="waiting"): | ||
2459 | portfolio.ReportStore.log_order(order_mock, 1, update="waiting") | ||
2460 | print_log.assert_called_once_with("[Order] Order Mock, tick 1, waiting") | ||
2461 | add_log.assert_called_once_with({ | ||
2462 | 'type': 'order', | ||
2463 | 'tick': 1, | ||
2464 | 'update': 'waiting', | ||
2465 | 'order': 'order', | ||
2466 | 'compute_value': None, | ||
2467 | 'new_order': None | ||
2468 | }) | ||
2469 | |||
2470 | add_log.reset_mock() | ||
2471 | print_log.reset_mock() | ||
2472 | with self.subTest(update="adjusting"): | ||
2473 | portfolio.ReportStore.log_order(order_mock, 3, | ||
2474 | update="adjusting", new_order=new_order_mock, | ||
2475 | compute_value="default") | ||
2476 | print_log.assert_called_once_with("[Order] Order Mock, tick 3, cancelling and adjusting to New order Mock") | ||
2477 | add_log.assert_called_once_with({ | ||
2478 | 'type': 'order', | ||
2479 | 'tick': 3, | ||
2480 | 'update': 'adjusting', | ||
2481 | 'order': 'order', | ||
2482 | 'compute_value': "default", | ||
2483 | 'new_order': 'new_order' | ||
2484 | }) | ||
2485 | |||
2486 | add_log.reset_mock() | ||
2487 | print_log.reset_mock() | ||
2488 | with self.subTest(update="market_fallback"): | ||
2489 | portfolio.ReportStore.log_order(order_mock, 7, | ||
2490 | update="market_fallback", new_order=new_order_mock) | ||
2491 | print_log.assert_called_once_with("[Order] Order Mock, tick 7, fallbacking to market value") | ||
2492 | add_log.assert_called_once_with({ | ||
2493 | 'type': 'order', | ||
2494 | 'tick': 7, | ||
2495 | 'update': 'market_fallback', | ||
2496 | 'order': 'order', | ||
2497 | 'compute_value': None, | ||
2498 | 'new_order': 'new_order' | ||
2499 | }) | ||
2500 | |||
2501 | add_log.reset_mock() | ||
2502 | print_log.reset_mock() | ||
2503 | with self.subTest(update="market_adjusting"): | ||
2504 | portfolio.ReportStore.log_order(order_mock, 17, | ||
2505 | update="market_adjust", new_order=new_order_mock) | ||
2506 | print_log.assert_called_once_with("[Order] Order Mock, tick 17, market value, cancelling and adjusting to New order Mock") | ||
2507 | add_log.assert_called_once_with({ | ||
2508 | 'type': 'order', | ||
2509 | 'tick': 17, | ||
2510 | 'update': 'market_adjust', | ||
2511 | 'order': 'order', | ||
2512 | 'compute_value': None, | ||
2513 | 'new_order': 'new_order' | ||
2514 | }) | ||
2515 | |||
2516 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2517 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2518 | def test_log_move_balances(self, add_log, print_log): | ||
2519 | needed = { | ||
2520 | "BTC": portfolio.Amount("BTC", 10), | ||
2521 | "USDT": 1 | ||
2522 | } | ||
2523 | moving = { | ||
2524 | "BTC": portfolio.Amount("BTC", 3), | ||
2525 | "USDT": -2 | ||
2526 | } | ||
2527 | portfolio.ReportStore.log_move_balances(needed, moving, True) | ||
2528 | print_log.assert_not_called() | ||
2529 | add_log.assert_called_once_with({ | ||
2530 | 'type': 'move_balances', | ||
2531 | 'debug': True, | ||
2532 | 'needed': { | ||
2533 | 'BTC': D('10'), | ||
2534 | 'USDT': 1 | ||
2535 | }, | ||
2536 | 'moving': { | ||
2537 | 'BTC': D('3'), | ||
2538 | 'USDT': -2 | ||
2539 | } | ||
2540 | }) | ||
2541 | |||
2542 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2543 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2544 | def test_log_http_request(self, add_log, print_log): | ||
2545 | response = mock.Mock() | ||
2546 | response.status_code = 200 | ||
2547 | response.text = "Hey" | ||
2548 | |||
2549 | portfolio.ReportStore.log_http_request("method", "url", "body", | ||
2550 | "headers", response) | ||
2551 | print_log.assert_not_called() | ||
2552 | add_log.assert_called_once_with({ | ||
2553 | 'type': 'http_request', | ||
2554 | 'method': 'method', | ||
2555 | 'url': 'url', | ||
2556 | 'body': 'body', | ||
2557 | 'headers': 'headers', | ||
2558 | 'status': 200, | ||
2559 | 'response': 'Hey' | ||
2560 | }) | ||
2561 | |||
2562 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2563 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2564 | def test_log_error(self, add_log, print_log): | ||
2565 | with self.subTest(message=None, exception=None): | ||
2566 | portfolio.ReportStore.log_error("action") | ||
2567 | print_log.assert_called_once_with("[Error] action") | ||
2568 | add_log.assert_called_once_with({ | ||
2569 | 'type': 'error', | ||
2570 | 'action': 'action', | ||
2571 | 'exception_class': None, | ||
2572 | 'exception_message': None, | ||
2573 | 'message': None | ||
2574 | }) | ||
2575 | |||
2576 | print_log.reset_mock() | ||
2577 | add_log.reset_mock() | ||
2578 | with self.subTest(message="Hey", exception=None): | ||
2579 | portfolio.ReportStore.log_error("action", message="Hey") | ||
2580 | print_log.assert_has_calls([ | ||
2581 | mock.call("[Error] action"), | ||
2582 | mock.call("\tHey") | ||
2583 | ]) | ||
2584 | add_log.assert_called_once_with({ | ||
2585 | 'type': 'error', | ||
2586 | 'action': 'action', | ||
2587 | 'exception_class': None, | ||
2588 | 'exception_message': None, | ||
2589 | 'message': "Hey" | ||
2590 | }) | ||
2591 | |||
2592 | print_log.reset_mock() | ||
2593 | add_log.reset_mock() | ||
2594 | with self.subTest(message=None, exception=Exception("bouh")): | ||
2595 | portfolio.ReportStore.log_error("action", exception=Exception("bouh")) | ||
2596 | print_log.assert_has_calls([ | ||
2597 | mock.call("[Error] action"), | ||
2598 | mock.call("\tException: bouh") | ||
2599 | ]) | ||
2600 | add_log.assert_called_once_with({ | ||
2601 | 'type': 'error', | ||
2602 | 'action': 'action', | ||
2603 | 'exception_class': "Exception", | ||
2604 | 'exception_message': "bouh", | ||
2605 | 'message': None | ||
2606 | }) | ||
2607 | |||
2608 | print_log.reset_mock() | ||
2609 | add_log.reset_mock() | ||
2610 | with self.subTest(message="Hey", exception=Exception("bouh")): | ||
2611 | portfolio.ReportStore.log_error("action", message="Hey", exception=Exception("bouh")) | ||
2612 | print_log.assert_has_calls([ | ||
2613 | mock.call("[Error] action"), | ||
2614 | mock.call("\tException: bouh"), | ||
2615 | mock.call("\tHey") | ||
2616 | ]) | ||
2617 | add_log.assert_called_once_with({ | ||
2618 | 'type': 'error', | ||
2619 | 'action': 'action', | ||
2620 | 'exception_class': "Exception", | ||
2621 | 'exception_message': "bouh", | ||
2622 | 'message': "Hey" | ||
2623 | }) | ||
2624 | |||
2625 | @mock.patch.object(portfolio.ReportStore, "print_log") | ||
2626 | @mock.patch.object(portfolio.ReportStore, "add_log") | ||
2627 | def test_log_debug_action(self, add_log, print_log): | ||
2628 | portfolio.ReportStore.log_debug_action("Hey") | ||
2629 | |||
2630 | print_log.assert_called_once_with("[Debug] Hey") | ||
2631 | add_log.assert_called_once_with({ | ||
2632 | 'type': 'debug_action', | ||
2633 | 'action': 'Hey' | ||
2634 | }) | ||
2635 | |||
2060 | @unittest.skipUnless("acceptance" in limits, "Acceptance skipped") | 2636 | @unittest.skipUnless("acceptance" in limits, "Acceptance skipped") |
2061 | class AcceptanceTest(WebMockTestCase): | 2637 | class AcceptanceTest(WebMockTestCase): |
2062 | @unittest.expectedFailure | 2638 | @unittest.expectedFailure |
2063 | def test_success_sell_only_necessary(self): | 2639 | def test_success_sell_only_necessary(self): |
2640 | # FIXME: catch stdout | ||
2641 | portfolio.ReportStore.verbose_print = False | ||
2064 | fetch_balance = { | 2642 | fetch_balance = { |
2065 | "ETH": { | 2643 | "ETH": { |
2066 | "exchange_free": D("1.0"), | 2644 | "exchange_free": D("1.0"), |