X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=test.py;h=d58b2035af902c63609daf0857a2aaea2b42827f;hb=7eb9cb36c2ef01097fe8e99d40dd17308abdfe37;hp=890381541368f5d683d942584152e0dfbd5324f0;hpb=5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git diff --git a/test.py b/test.py index 8903815..d58b203 100644 --- a/test.py +++ b/test.py @@ -32,7 +32,7 @@ class WebMockTestCase(unittest.TestCase): mock.patch.multiple(portfolio.TradeStore, all=[], debug=False), - mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={}), + mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}), mock.patch.multiple(portfolio.Computation, computations=portfolio.Computation.computations), mock.patch.multiple(helper, @@ -102,7 +102,8 @@ class PortfolioTest(WebMockTestCase): 'SC': (D("0.0623"), "long"), 'ZEC': (D("0.3701"), "long"), } - self.assertDictEqual(expected, liquidities["high"]['2018-01-08']) + date = portfolio.datetime(2018, 1, 8) + self.assertDictEqual(expected, liquidities["high"][date]) expected = { 'BTC': (D("1.1102e-16"), "long"), @@ -117,13 +118,17 @@ class PortfolioTest(WebMockTestCase): 'VIA': (D("0.1"), "long"), 'XCP': (D("0.1"), "long"), } - self.assertDictEqual(expected, liquidities["medium"]['2018-01-08']) + self.assertDictEqual(expected, liquidities["medium"][date]) + self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date) # It doesn't refetch the data when available portfolio.Portfolio.parse_cryptoportfolio() self.assertEqual(1, self.wm.call_count) + portfolio.Portfolio.parse_cryptoportfolio(refetch=True) + self.assertEqual(2, self.wm.call_count) + def test_repartition(self): expected_medium = { 'BTC': (D("1.1102e-16"), "long"), @@ -151,6 +156,48 @@ class PortfolioTest(WebMockTestCase): self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium")) self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high")) + self.assertEqual(1, self.wm.call_count) + + portfolio.Portfolio.repartition() + self.assertEqual(1, self.wm.call_count) + + portfolio.Portfolio.repartition(refetch=True) + self.assertEqual(2, self.wm.call_count) + + @mock.patch.object(portfolio.time, "sleep") + @mock.patch.object(portfolio.Portfolio, "repartition") + def test_wait_for_recent(self, repartition, sleep): + self.call_count = 0 + def _repartition(refetch): + self.assertTrue(refetch) + self.call_count += 1 + portfolio.Portfolio.last_date = portfolio.datetime.now()\ + - portfolio.timedelta(10)\ + + portfolio.timedelta(self.call_count) + repartition.side_effect = _repartition + + portfolio.Portfolio.wait_for_recent() + sleep.assert_called_with(30) + self.assertEqual(6, sleep.call_count) + self.assertEqual(7, repartition.call_count) + + sleep.reset_mock() + repartition.reset_mock() + portfolio.Portfolio.last_date = None + self.call_count = 0 + portfolio.Portfolio.wait_for_recent(delta=15) + sleep.assert_not_called() + self.assertEqual(1, repartition.call_count) + + sleep.reset_mock() + repartition.reset_mock() + portfolio.Portfolio.last_date = None + self.call_count = 0 + portfolio.Portfolio.wait_for_recent(delta=1) + sleep.assert_called_with(30) + self.assertEqual(9, sleep.call_count) + self.assertEqual(10, repartition.call_count) + @unittest.skipUnless("unit" in limits, "Unit skipped") class AmountTest(WebMockTestCase): def test_values(self): @@ -220,6 +267,8 @@ class AmountTest(WebMockTestCase): amount4 = portfolio.Amount("ETH", 0.0) self.assertEqual(amount1, amount1 + amount4) + self.assertEqual(amount1, amount1 + 0) + def test__radd(self): amount = portfolio.Amount("XVG", "12.9") @@ -241,6 +290,13 @@ class AmountTest(WebMockTestCase): amount4 = portfolio.Amount("ETH", 0.0) self.assertEqual(amount1, amount1 - amount4) + def test__rsub(self): + amount = portfolio.Amount("ETH", "1.6") + with self.assertRaises(Exception): + 3 - amount + + self.assertEqual(portfolio.Amount("ETH", "-1.6"), -amount) + def test__mul(self): amount = portfolio.Amount("XEM", 11) @@ -724,7 +780,8 @@ class HelperTest(WebMockTestCase): portfolio.TradeStore.all = [trade1, trade2, trade3] balance1 = portfolio.Balance("BTC", { "margin_free": "0" }) balance2 = portfolio.Balance("USDT", { "margin_free": "100" }) - portfolio.BalanceStore.all = {"BTC": balance1, "USDT": balance2} + balance3 = portfolio.Balance("ETC", { "margin_free": "10" }) + portfolio.BalanceStore.all = {"BTC": balance1, "USDT": balance2, "ETC": balance3} market = mock.Mock() @@ -736,6 +793,7 @@ class HelperTest(WebMockTestCase): else: market.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") market.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange") + market.transfer_balance.assert_any_call("ETC", 10, "margin", "exchange") @mock.patch.object(helper, "prepare_trades") @mock.patch.object(portfolio.TradeStore, "prepare_orders") @@ -757,11 +815,167 @@ class HelperTest(WebMockTestCase): } helper.print_orders(market) prepare_trades.assert_called_with(market, base_currency="BTC", - compute_value="average") + compute_value="average", debug=True) prepare_orders.assert_called_with(compute_value="average") print_all_with_order.assert_called() self.assertRegex(stdout_mock.getvalue(), "Balance") + @mock.patch.object(portfolio.BalanceStore, "fetch_balances") + @mock.patch.object(portfolio.BalanceStore, "in_currency") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_print_balances(self, stdout_mock, in_currency, fetch_balances): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + in_currency.return_value = { + "BTC": portfolio.Amount("BTC", "0.65"), + "ETH": portfolio.Amount("BTC", "0.3"), + } + helper.print_balances(market) + fetch_balances.assert_called_with(market) + self.assertRegex(stdout_mock.getvalue(), "Balance") + self.assertRegex(stdout_mock.getvalue(), "0.95000000 BTC") + + @mock.patch.object(helper, "prepare_trades") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_needed__1_sell(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, follow_orders, + prepare_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_needed__1_sell(market) + prepare_trades.assert_called_with(market, base_currency="BTC", + debug=False) + prepare_orders.assert_called_with(compute_value="average", + only="dispose") + print_all_with_order.assert_called() + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "update_trades") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(helper, "move_balances") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_needed__2_buy(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, move_balances, + follow_orders, update_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_needed__2_buy(market) + update_trades.assert_called_with(market, base_currency="BTC", + debug=False, only="acquire") + prepare_orders.assert_called_with(compute_value="average", + only="acquire") + print_all_with_order.assert_called() + move_balances.assert_called_with(market, debug=False) + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "prepare_trades_to_sell_all") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_all__1_sell(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, follow_orders, + prepare_trades_to_sell_all): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_all__1_all_sell(market) + prepare_trades_to_sell_all.assert_called_with(market, base_currency="BTC", + debug=False) + prepare_orders.assert_called_with(compute_value="average") + print_all_with_order.assert_called() + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "prepare_trades") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(helper, "move_balances") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_all__2_all_buy(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, move_balances, + follow_orders, prepare_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_all__2_all_buy(market) + prepare_trades.assert_called_with(market, base_currency="BTC", + debug=False) + prepare_orders.assert_called_with() + print_all_with_order.assert_called() + move_balances.assert_called_with(market, debug=False) + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + @unittest.skipUnless("unit" in limits, "Unit skipped") class TradeStoreTest(WebMockTestCase): @@ -1033,6 +1247,7 @@ class BalanceStoreTest(WebMockTestCase): } amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "11.1")) + repartition.assert_called_with(liquidity="medium") self.assertIn("XEM", portfolio.BalanceStore.currencies()) self.assertEqual(D("2.6"), amounts["BTC"].value) self.assertEqual(D("7.5"), amounts["XEM"].value) @@ -1549,12 +1764,12 @@ class OrderTest(WebMockTestCase): order = portfolio.Order("buy", portfolio.Amount("ETH", 10), D("0.1"), "BTC", "long", "market", "trade") order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { - "id": 42, "type": "buy", "fee": "0.0015", + "tradeID": 42, "type": "buy", "fee": "0.0015", "date": "2017-12-30 12:00:12", "rate": "0.1", "amount": "3", "total": "0.3" })) order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { - "id": 43, "type": "buy", "fee": "0.0015", + "tradeID": 43, "type": "buy", "fee": "0.0015", "date": "2017-12-30 13:00:12", "rate": "0.2", "amount": "2", "total": "0.4" })) @@ -1569,12 +1784,12 @@ class OrderTest(WebMockTestCase): market = mock.Mock() market.privatePostReturnOrderTrades.return_value = [ { - "id": 42, "type": "buy", "fee": "0.0015", + "tradeID": 42, "type": "buy", "fee": "0.0015", "date": "2017-12-30 12:00:12", "rate": "0.1", "amount": "3", "total": "0.3" }, { - "id": 43, "type": "buy", "fee": "0.0015", + "tradeID": 43, "type": "buy", "fee": "0.0015", "date": "2017-12-30 13:00:12", "rate": "0.2", "amount": "2", "total": "0.4" } @@ -1591,6 +1806,12 @@ class OrderTest(WebMockTestCase): self.assertEqual(42, order.mouvements[0].id) self.assertEqual(43, order.mouvements[1].id) + market.privatePostReturnOrderTrades.side_effect = portfolio.ExchangeError + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.fetch_mouvements() + self.assertEqual(0, len(order.mouvements)) + def test_mark_finished_order(self): market = mock.Mock() order = portfolio.Order("buy", portfolio.Amount("ETH", 10), @@ -1770,11 +1991,24 @@ class OrderTest(WebMockTestCase): self.assertRegex(stdout_mock.getvalue(), "error when running market.create_order") self.assertRegex(stdout_mock.getvalue(), "Exception: bouh") + market.create_order.reset_mock() + with self.subTest(dust_amount_exception=True),\ + mock.patch.object(portfolio.Order, "mark_finished_order") as mark_finished_order: + order = portfolio.Order("buy", portfolio.Amount("ETH", 0.001), + D("0.1"), "BTC", "long", market, "trade") + market.create_order.side_effect = portfolio.ExchangeNotAvailable + order.run() + market.create_order.assert_called_once() + self.assertEqual(0, len(order.results)) + self.assertEqual("closed", order.status) + mark_finished_order.assert_called_once() + + @unittest.skipUnless("unit" in limits, "Unit skipped") class MouvementTest(WebMockTestCase): def test_values(self): mouvement = portfolio.Mouvement("ETH", "BTC", { - "id": 42, "type": "buy", "fee": "0.0015", + "tradeID": 42, "type": "buy", "fee": "0.0015", "date": "2017-12-30 12:00:12", "rate": "0.1", "amount": "10", "total": "1" }) @@ -1788,6 +2022,15 @@ class MouvementTest(WebMockTestCase): self.assertEqual(portfolio.Amount("ETH", "10"), mouvement.total) self.assertEqual(portfolio.Amount("BTC", "1"), mouvement.total_in_base) + mouvement = portfolio.Mouvement("ETH", "BTC", { "foo": "bar" }) + self.assertIsNone(mouvement.date) + self.assertIsNone(mouvement.id) + self.assertIsNone(mouvement.action) + self.assertEqual(-1, mouvement.fee_rate) + self.assertEqual(0, mouvement.rate) + self.assertEqual(portfolio.Amount("ETH", 0), mouvement.total) + self.assertEqual(portfolio.Amount("BTC", 0), mouvement.total_in_base) + @unittest.skipUnless("acceptance" in limits, "Acceptance skipped") class AcceptanceTest(WebMockTestCase): @unittest.expectedFailure @@ -1939,7 +2182,7 @@ class AcceptanceTest(WebMockTestCase): market.fetch_order.return_value = { "status": "closed", "datetime": "2018-01-20 13:40:00" } market.privatePostReturnOrderTrades.return_value = [ { - "id": 42, "type": "buy", "fee": "0.0015", + "tradeID": 42, "type": "buy", "fee": "0.0015", "date": "2017-12-30 12:00:12", "rate": "0.1", "amount": "10", "total": "1" }