X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=test.py;h=7b17f9ebc54cd3b68c3a777157a61a5ed60ccf83;hb=5542e9e31a0074f4ed3b91cadce643ad60083cde;hp=5b9e2b4244cf7dcca5c8d917c7856ded88ab75c3;hpb=d004a2a5e15a78991870dcb90cd6db63ab40a4e6;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git diff --git a/test.py b/test.py index 5b9e2b4..7b17f9e 100644 --- a/test.py +++ b/test.py @@ -27,7 +27,7 @@ class WebMockTestCase(unittest.TestCase): return type('Args', (object,), { "debug": debug, "quiet": quiet })() def setUp(self): - super(WebMockTestCase, self).setUp() + super().setUp() self.wm = requests_mock.Mocker() self.wm.start() @@ -55,12 +55,12 @@ class WebMockTestCase(unittest.TestCase): for patcher in self.patchers: patcher.stop() self.wm.stop() - super(WebMockTestCase, self).tearDown() + super().tearDown() @unittest.skipUnless("unit" in limits, "Unit skipped") class poloniexETest(unittest.TestCase): def setUp(self): - super(poloniexETest, self).setUp() + super().setUp() self.wm = requests_mock.Mocker() self.wm.start() @@ -68,7 +68,7 @@ class poloniexETest(unittest.TestCase): def tearDown(self): self.wm.stop() - super(poloniexETest, self).tearDown() + super().tearDown() def test__init(self): with mock.patch("market.ccxt.poloniexE.session") as session: @@ -101,7 +101,7 @@ class poloniexETest(unittest.TestCase): retry_call.assert_called_with(request, delay=1, tries=10, fargs=["foo"], fkwargs={'api': 'public', 'method': 'GET', 'params': {}, 'headers': None, 'body': None}, - exceptions=(market.ccxt.RequestTimeout,)) + exceptions=(market.ccxt.RequestTimeout, market.ccxt.InvalidNonce)) request.assert_not_called() with self.subTest(desc="private GET"): @@ -109,7 +109,7 @@ class poloniexETest(unittest.TestCase): retry_call.assert_called_with(request, delay=1, tries=10, fargs=["foo"], fkwargs={'api': 'private', 'method': 'GET', 'params': {}, 'headers': None, 'body': None}, - exceptions=(market.ccxt.RequestTimeout,)) + exceptions=(market.ccxt.RequestTimeout, market.ccxt.InvalidNonce)) request.assert_not_called() with self.subTest(desc="private POST regexp"): @@ -117,7 +117,7 @@ class poloniexETest(unittest.TestCase): retry_call.assert_called_with(request, delay=1, tries=10, fargs=["returnFoo"], fkwargs={'api': 'private', 'method': 'POST', 'params': {}, 'headers': None, 'body': None}, - exceptions=(market.ccxt.RequestTimeout,)) + exceptions=(market.ccxt.RequestTimeout, market.ccxt.InvalidNonce)) request.assert_not_called() with self.subTest(desc="private POST non-regexp"): @@ -125,7 +125,7 @@ class poloniexETest(unittest.TestCase): retry_call.assert_called_with(request, delay=1, tries=10, fargs=["getMarginPosition"], fkwargs={'api': 'private', 'method': 'POST', 'params': {}, 'headers': None, 'body': None}, - exceptions=(market.ccxt.RequestTimeout,)) + exceptions=(market.ccxt.RequestTimeout, market.ccxt.InvalidNonce)) request.assert_not_called() retry_call.reset_mock() request.reset_mock() @@ -609,7 +609,7 @@ class LockedVar(unittest.TestCase): @unittest.skipUnless("unit" in limits, "Unit skipped") class PortfolioTest(WebMockTestCase): def setUp(self): - super(PortfolioTest, self).setUp() + super().setUp() with open("test_samples/test_portfolio.json") as example: self.json_response = example.read() @@ -1154,7 +1154,7 @@ class BalanceTest(WebMockTestCase): @unittest.skipUnless("unit" in limits, "Unit skipped") class MarketTest(WebMockTestCase): def setUp(self): - super(MarketTest, self).setUp() + super().setUp() self.ccxt = mock.Mock(spec=market.ccxt.poloniexE) @@ -1461,16 +1461,18 @@ class MarketTest(WebMockTestCase): m.ccxt.transfer_balance.side_effect = [ market.ccxt.RequestTimeout, + market.ccxt.InvalidNonce, True ] m.move_balances() self.ccxt.transfer_balance.assert_has_calls([ + mock.call("BTC", 3, "exchange", "margin"), mock.call("BTC", 3, "exchange", "margin"), mock.call("BTC", 3, "exchange", "margin") ]) - self.assertEqual(2, fetch_balances.call_count) + self.assertEqual(3, fetch_balances.call_count) m.report.log_error.assert_called_with(mock.ANY, message="Retrying", exception=mock.ANY) - self.assertEqual(2, m.report.log_move_balances.call_count) + self.assertEqual(3, m.report.log_move_balances.call_count) self.ccxt.transfer_balance.reset_mock() m.report.reset_mock() @@ -2054,7 +2056,7 @@ class TradeStoreTest(WebMockTestCase): @unittest.skipUnless("unit" in limits, "Unit skipped") class BalanceStoreTest(WebMockTestCase): def setUp(self): - super(BalanceStoreTest, self).setUp() + super().setUp() self.fetch_balance = { "ETC": { @@ -2404,6 +2406,27 @@ class TradeTest(WebMockTestCase): order1.filled_amount.assert_called_with(in_base_currency=True) order2.filled_amount.assert_called_with(in_base_currency=True) + def test_reopen_same_order(self): + value_from = portfolio.Amount("BTC", "0.5") + value_from.linked_to = portfolio.Amount("ETH", "10.0") + value_to = portfolio.Amount("BTC", "1.0") + trade = portfolio.Trade(value_from, value_to, "ETH", self.m) + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", self.m, "trade") + with mock.patch("portfolio.Order.run") as run: + new_order = trade.reopen_same_order(order) + self.assertEqual("buy", new_order.action) + self.assertEqual(portfolio.Amount("ETH", 10), new_order.amount) + self.assertEqual(D("0.1"), new_order.rate) + self.assertEqual("BTC", new_order.base_currency) + self.assertEqual("long", new_order.trade_type) + self.assertEqual(self.m, new_order.market) + self.assertEqual(False, new_order.close_if_possible) + self.assertEqual(trade, new_order.trade) + run.assert_called_once() + self.assertEqual(1, len(trade.orders)) + self.assertEqual(new_order, trade.orders[0]) + @mock.patch.object(portfolio.Computation, "compute_value") @mock.patch.object(portfolio.Trade, "filled_amount") @mock.patch.object(portfolio, "Order") @@ -3036,8 +3059,9 @@ class OrderTest(WebMockTestCase): self.m.report.log_debug_action.assert_called_once() @mock.patch.object(portfolio.Order, "fetch_mouvements") + @mock.patch.object(portfolio.Order, "fix_disappeared_order") @mock.patch.object(portfolio.Order, "mark_finished_order") - def test_fetch(self, mark_finished_order, fetch_mouvements): + def test_fetch(self, mark_finished_order, fix_disappeared_order, fetch_mouvements): order = portfolio.Order("buy", portfolio.Amount("ETH", 10), D("0.1"), "BTC", "long", self.m, "trade") order.id = 45 @@ -3048,6 +3072,7 @@ class OrderTest(WebMockTestCase): self.m.report.log_debug_action.reset_mock() self.m.ccxt.fetch_order.assert_not_called() mark_finished_order.assert_not_called() + fix_disappeared_order.assert_not_called() fetch_mouvements.assert_not_called() with self.subTest(debug=False): @@ -3065,6 +3090,7 @@ class OrderTest(WebMockTestCase): self.assertEqual(1, len(order.results)) self.m.report.log_debug_action.assert_not_called() mark_finished_order.assert_called_once() + fix_disappeared_order.assert_called_once() mark_finished_order.reset_mock() with self.subTest(missing_order=True): @@ -3075,6 +3101,98 @@ class OrderTest(WebMockTestCase): self.assertEqual("closed_unknown", order.status) mark_finished_order.assert_called_once() + def test_fix_disappeared_order(self): + with self.subTest("Open order"): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", self.m, "trade") + order.id = 45 + order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { + "tradeID":21336541, + "currencyPair":"BTC_XRP", + "type":"sell", + "rate":"0.00007013", + "amount":"0.00000222", + "total":"0.00000000", + "fee":"0.00150000", + "date":"2018-04-02 00:09:13" + })) + with mock.patch.object(order, "trade") as trade: + order.fix_disappeared_order() + trade.reopen_same_order.assert_not_called() + + with self.subTest("Non-zero amount"): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", self.m, "trade") + order.id = 45 + order.status = "closed" + order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { + "tradeID":21336541, + "currencyPair":"BTC_XRP", + "type":"sell", + "rate":"0.00007013", + "amount":"0.00000222", + "total":"0.00000010", + "fee":"0.00150000", + "date":"2018-04-02 00:09:13" + })) + with mock.patch.object(order, "trade") as trade: + order.fix_disappeared_order() + self.assertEqual("closed", order.status) + trade.reopen_same_order.assert_not_called() + + with self.subTest("Other mouvements"): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", self.m, "trade") + order.id = 45 + order.status = "closed" + order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { + "tradeID":21336541, + "currencyPair":"BTC_XRP", + "type":"sell", + "rate":"0.00007013", + "amount":"0.00000222", + "total":"0.00000001", + "fee":"0.00150000", + "date":"2018-04-02 00:09:13" + })) + order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { + "tradeID":21336541, + "currencyPair":"BTC_XRP", + "type":"sell", + "rate":"0.00007013", + "amount":"0.00000222", + "total":"0.00000000", + "fee":"0.00150000", + "date":"2018-04-02 00:09:13" + })) + with mock.patch.object(order, "trade") as trade: + order.fix_disappeared_order() + self.assertEqual("closed", order.status) + trade.reopen_same_order.assert_not_called() + + with self.subTest("Order disappeared"): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", self.m, "trade") + order.id = 45 + order.status = "closed" + order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { + "tradeID":21336541, + "currencyPair":"BTC_XRP", + "type":"sell", + "rate":"0.00007013", + "amount":"0.00000222", + "total":"0.00000000", + "fee":"0.00150000", + "date":"2018-04-02 00:09:13" + })) + with mock.patch.object(order, "trade") as trade: + trade.reopen_same_order.return_value = "New order" + order.fix_disappeared_order() + self.assertEqual("error_disappeared", order.status) + trade.reopen_same_order.assert_called_once_with(order) + self.m.report.log_error.assert_called_once_with('fetch', + message='Order Order(buy long 10.00000000 ETH at 0.1 BTC [error_disappeared]) disappeared, recreating it as New order') + @mock.patch.object(portfolio.Order, "fetch") def test_get_status(self, fetch): with self.subTest(debug=True): @@ -3213,6 +3331,48 @@ class OrderTest(WebMockTestCase): self.assertEqual(5, self.m.report.log_error.call_count) self.m.report.log_error.assert_called_with(mock.ANY, message="Giving up Order(buy long 0.00096060 ETH at 0.1 BTC [pending])", exception=mock.ANY) + self.m.reset_mock() + with self.subTest(invalid_nonce=True): + with self.subTest(retry_success=True): + order = portfolio.Order("buy", portfolio.Amount("ETH", "0.001"), + D("0.1"), "BTC", "long", self.m, "trade") + self.m.ccxt.create_order.side_effect = [ + portfolio.InvalidNonce, + portfolio.InvalidNonce, + { "id": 123 }, + ] + order.run() + self.m.ccxt.create_order.assert_has_calls([ + mock.call('ETH/BTC', 'limit', 'buy', D('0.0010'), account='exchange', price=D('0.1')), + mock.call('ETH/BTC', 'limit', 'buy', D('0.0010'), account='exchange', price=D('0.1')), + mock.call('ETH/BTC', 'limit', 'buy', D('0.0010'), account='exchange', price=D('0.1')), + ]) + self.assertEqual(3, self.m.ccxt.create_order.call_count) + self.assertEqual(3, order.tries) + self.m.report.log_error.assert_called() + self.assertEqual(2, self.m.report.log_error.call_count) + self.m.report.log_error.assert_called_with(mock.ANY, message="Retrying after invalid nonce", exception=mock.ANY) + self.assertEqual(123, order.id) + + self.m.reset_mock() + with self.subTest(retry_success=False): + order = portfolio.Order("buy", portfolio.Amount("ETH", "0.001"), + D("0.1"), "BTC", "long", self.m, "trade") + self.m.ccxt.create_order.side_effect = [ + portfolio.InvalidNonce, + portfolio.InvalidNonce, + portfolio.InvalidNonce, + portfolio.InvalidNonce, + portfolio.InvalidNonce, + ] + order.run() + self.assertEqual(5, self.m.ccxt.create_order.call_count) + self.assertEqual(5, order.tries) + self.m.report.log_error.assert_called() + self.assertEqual(5, self.m.report.log_error.call_count) + self.m.report.log_error.assert_called_with(mock.ANY, message="Giving up Order(buy long 0.00100000 ETH at 0.1 BTC [pending]) after invalid nonce", exception=mock.ANY) + self.assertEqual("error", order.status) + self.m.reset_mock() with self.subTest(request_timeout=True): order = portfolio.Order("buy", portfolio.Amount("ETH", "0.001"),