From 5542e9e31a0074f4ed3b91cadce643ad60083cde Mon Sep 17 00:00:00 2001 From: =?utf8?q?Isma=C3=ABl=20Bouya?= Date: Mon, 2 Apr 2018 19:18:49 +0200 Subject: [PATCH] Fix vanishing orders Fixes https://git.immae.eu/mantisbt/view.php?id=60 --- portfolio.py | 19 +++++++++ test.py | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/portfolio.py b/portfolio.py index 9dae23e..9e98c43 100644 --- a/portfolio.py +++ b/portfolio.py @@ -381,6 +381,14 @@ class Trade: self.orders.append(order) return order + def reopen_same_order(self, order): + new_order = Order(order.action, order.amount, order.rate, + order.base_currency, order.trade_type, self.market, + self, close_if_possible=order.close_if_possible) + self.orders.append(new_order) + new_order.run() + return new_order + def as_json(self): return { "action": self.action, @@ -542,6 +550,15 @@ class Order: self.fetch() return self.status + def fix_disappeared_order(self): + if self.status.startswith("closed") and \ + len(self.mouvements) == 1 and \ + self.mouvements[0].total_in_base == 0: + self.status = "error_disappeared" + new_order = self.trade.reopen_same_order(self) + self.market.report.log_error("fetch", + message="Order {} disappeared, recreating it as {}".format(self, new_order)) + def mark_finished_order(self): if self.status.startswith("closed") and self.market.debug: self.market.report.log_debug_action("Mark {} as finished".format(self)) @@ -565,6 +582,8 @@ class Order: self.fetch_mouvements() + self.fix_disappeared_order() + self.mark_finished_order() # FIXME: consider open order with dust remaining as closed diff --git a/test.py b/test.py index 9ea9df4..7b17f9e 100644 --- a/test.py +++ b/test.py @@ -2406,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") @@ -3038,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 @@ -3050,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): @@ -3067,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): @@ -3077,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): -- 2.41.0