diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-04-02 19:19:49 +0200 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-04-02 19:19:49 +0200 |
commit | 7a676e2b0db5a168f08b419701cb71c28d7c76ee (patch) | |
tree | b8216162ee14e27695e4b46cfb26825ccfdb7e25 | |
parent | b03f2a307dfdaafc1597035ef669b14da0d55f77 (diff) | |
parent | 5542e9e31a0074f4ed3b91cadce643ad60083cde (diff) | |
download | Trader-7a676e2b0db5a168f08b419701cb71c28d7c76ee.tar.gz Trader-7a676e2b0db5a168f08b419701cb71c28d7c76ee.tar.zst Trader-7a676e2b0db5a168f08b419701cb71c28d7c76ee.zip |
Merge branch 'retry_vanished' into dev
-rw-r--r-- | portfolio.py | 19 | ||||
-rw-r--r-- | test.py | 118 |
2 files changed, 136 insertions, 1 deletions
diff --git a/portfolio.py b/portfolio.py index 9dae23e..9e98c43 100644 --- a/portfolio.py +++ b/portfolio.py | |||
@@ -381,6 +381,14 @@ class Trade: | |||
381 | self.orders.append(order) | 381 | self.orders.append(order) |
382 | return order | 382 | return order |
383 | 383 | ||
384 | def reopen_same_order(self, order): | ||
385 | new_order = Order(order.action, order.amount, order.rate, | ||
386 | order.base_currency, order.trade_type, self.market, | ||
387 | self, close_if_possible=order.close_if_possible) | ||
388 | self.orders.append(new_order) | ||
389 | new_order.run() | ||
390 | return new_order | ||
391 | |||
384 | def as_json(self): | 392 | def as_json(self): |
385 | return { | 393 | return { |
386 | "action": self.action, | 394 | "action": self.action, |
@@ -542,6 +550,15 @@ class Order: | |||
542 | self.fetch() | 550 | self.fetch() |
543 | return self.status | 551 | return self.status |
544 | 552 | ||
553 | def fix_disappeared_order(self): | ||
554 | if self.status.startswith("closed") and \ | ||
555 | len(self.mouvements) == 1 and \ | ||
556 | self.mouvements[0].total_in_base == 0: | ||
557 | self.status = "error_disappeared" | ||
558 | new_order = self.trade.reopen_same_order(self) | ||
559 | self.market.report.log_error("fetch", | ||
560 | message="Order {} disappeared, recreating it as {}".format(self, new_order)) | ||
561 | |||
545 | def mark_finished_order(self): | 562 | def mark_finished_order(self): |
546 | if self.status.startswith("closed") and self.market.debug: | 563 | if self.status.startswith("closed") and self.market.debug: |
547 | self.market.report.log_debug_action("Mark {} as finished".format(self)) | 564 | self.market.report.log_debug_action("Mark {} as finished".format(self)) |
@@ -565,6 +582,8 @@ class Order: | |||
565 | 582 | ||
566 | self.fetch_mouvements() | 583 | self.fetch_mouvements() |
567 | 584 | ||
585 | self.fix_disappeared_order() | ||
586 | |||
568 | self.mark_finished_order() | 587 | self.mark_finished_order() |
569 | # FIXME: consider open order with dust remaining as closed | 588 | # FIXME: consider open order with dust remaining as closed |
570 | 589 | ||
@@ -2406,6 +2406,27 @@ class TradeTest(WebMockTestCase): | |||
2406 | order1.filled_amount.assert_called_with(in_base_currency=True) | 2406 | order1.filled_amount.assert_called_with(in_base_currency=True) |
2407 | order2.filled_amount.assert_called_with(in_base_currency=True) | 2407 | order2.filled_amount.assert_called_with(in_base_currency=True) |
2408 | 2408 | ||
2409 | def test_reopen_same_order(self): | ||
2410 | value_from = portfolio.Amount("BTC", "0.5") | ||
2411 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | ||
2412 | value_to = portfolio.Amount("BTC", "1.0") | ||
2413 | trade = portfolio.Trade(value_from, value_to, "ETH", self.m) | ||
2414 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
2415 | D("0.1"), "BTC", "long", self.m, "trade") | ||
2416 | with mock.patch("portfolio.Order.run") as run: | ||
2417 | new_order = trade.reopen_same_order(order) | ||
2418 | self.assertEqual("buy", new_order.action) | ||
2419 | self.assertEqual(portfolio.Amount("ETH", 10), new_order.amount) | ||
2420 | self.assertEqual(D("0.1"), new_order.rate) | ||
2421 | self.assertEqual("BTC", new_order.base_currency) | ||
2422 | self.assertEqual("long", new_order.trade_type) | ||
2423 | self.assertEqual(self.m, new_order.market) | ||
2424 | self.assertEqual(False, new_order.close_if_possible) | ||
2425 | self.assertEqual(trade, new_order.trade) | ||
2426 | run.assert_called_once() | ||
2427 | self.assertEqual(1, len(trade.orders)) | ||
2428 | self.assertEqual(new_order, trade.orders[0]) | ||
2429 | |||
2409 | @mock.patch.object(portfolio.Computation, "compute_value") | 2430 | @mock.patch.object(portfolio.Computation, "compute_value") |
2410 | @mock.patch.object(portfolio.Trade, "filled_amount") | 2431 | @mock.patch.object(portfolio.Trade, "filled_amount") |
2411 | @mock.patch.object(portfolio, "Order") | 2432 | @mock.patch.object(portfolio, "Order") |
@@ -3038,8 +3059,9 @@ class OrderTest(WebMockTestCase): | |||
3038 | self.m.report.log_debug_action.assert_called_once() | 3059 | self.m.report.log_debug_action.assert_called_once() |
3039 | 3060 | ||
3040 | @mock.patch.object(portfolio.Order, "fetch_mouvements") | 3061 | @mock.patch.object(portfolio.Order, "fetch_mouvements") |
3062 | @mock.patch.object(portfolio.Order, "fix_disappeared_order") | ||
3041 | @mock.patch.object(portfolio.Order, "mark_finished_order") | 3063 | @mock.patch.object(portfolio.Order, "mark_finished_order") |
3042 | def test_fetch(self, mark_finished_order, fetch_mouvements): | 3064 | def test_fetch(self, mark_finished_order, fix_disappeared_order, fetch_mouvements): |
3043 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 3065 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
3044 | D("0.1"), "BTC", "long", self.m, "trade") | 3066 | D("0.1"), "BTC", "long", self.m, "trade") |
3045 | order.id = 45 | 3067 | order.id = 45 |
@@ -3050,6 +3072,7 @@ class OrderTest(WebMockTestCase): | |||
3050 | self.m.report.log_debug_action.reset_mock() | 3072 | self.m.report.log_debug_action.reset_mock() |
3051 | self.m.ccxt.fetch_order.assert_not_called() | 3073 | self.m.ccxt.fetch_order.assert_not_called() |
3052 | mark_finished_order.assert_not_called() | 3074 | mark_finished_order.assert_not_called() |
3075 | fix_disappeared_order.assert_not_called() | ||
3053 | fetch_mouvements.assert_not_called() | 3076 | fetch_mouvements.assert_not_called() |
3054 | 3077 | ||
3055 | with self.subTest(debug=False): | 3078 | with self.subTest(debug=False): |
@@ -3067,6 +3090,7 @@ class OrderTest(WebMockTestCase): | |||
3067 | self.assertEqual(1, len(order.results)) | 3090 | self.assertEqual(1, len(order.results)) |
3068 | self.m.report.log_debug_action.assert_not_called() | 3091 | self.m.report.log_debug_action.assert_not_called() |
3069 | mark_finished_order.assert_called_once() | 3092 | mark_finished_order.assert_called_once() |
3093 | fix_disappeared_order.assert_called_once() | ||
3070 | 3094 | ||
3071 | mark_finished_order.reset_mock() | 3095 | mark_finished_order.reset_mock() |
3072 | with self.subTest(missing_order=True): | 3096 | with self.subTest(missing_order=True): |
@@ -3077,6 +3101,98 @@ class OrderTest(WebMockTestCase): | |||
3077 | self.assertEqual("closed_unknown", order.status) | 3101 | self.assertEqual("closed_unknown", order.status) |
3078 | mark_finished_order.assert_called_once() | 3102 | mark_finished_order.assert_called_once() |
3079 | 3103 | ||
3104 | def test_fix_disappeared_order(self): | ||
3105 | with self.subTest("Open order"): | ||
3106 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
3107 | D("0.1"), "BTC", "long", self.m, "trade") | ||
3108 | order.id = 45 | ||
3109 | order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { | ||
3110 | "tradeID":21336541, | ||
3111 | "currencyPair":"BTC_XRP", | ||
3112 | "type":"sell", | ||
3113 | "rate":"0.00007013", | ||
3114 | "amount":"0.00000222", | ||
3115 | "total":"0.00000000", | ||
3116 | "fee":"0.00150000", | ||
3117 | "date":"2018-04-02 00:09:13" | ||
3118 | })) | ||
3119 | with mock.patch.object(order, "trade") as trade: | ||
3120 | order.fix_disappeared_order() | ||
3121 | trade.reopen_same_order.assert_not_called() | ||
3122 | |||
3123 | with self.subTest("Non-zero amount"): | ||
3124 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
3125 | D("0.1"), "BTC", "long", self.m, "trade") | ||
3126 | order.id = 45 | ||
3127 | order.status = "closed" | ||
3128 | order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { | ||
3129 | "tradeID":21336541, | ||
3130 | "currencyPair":"BTC_XRP", | ||
3131 | "type":"sell", | ||
3132 | "rate":"0.00007013", | ||
3133 | "amount":"0.00000222", | ||
3134 | "total":"0.00000010", | ||
3135 | "fee":"0.00150000", | ||
3136 | "date":"2018-04-02 00:09:13" | ||
3137 | })) | ||
3138 | with mock.patch.object(order, "trade") as trade: | ||
3139 | order.fix_disappeared_order() | ||
3140 | self.assertEqual("closed", order.status) | ||
3141 | trade.reopen_same_order.assert_not_called() | ||
3142 | |||
3143 | with self.subTest("Other mouvements"): | ||
3144 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
3145 | D("0.1"), "BTC", "long", self.m, "trade") | ||
3146 | order.id = 45 | ||
3147 | order.status = "closed" | ||
3148 | order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { | ||
3149 | "tradeID":21336541, | ||
3150 | "currencyPair":"BTC_XRP", | ||
3151 | "type":"sell", | ||
3152 | "rate":"0.00007013", | ||
3153 | "amount":"0.00000222", | ||
3154 | "total":"0.00000001", | ||
3155 | "fee":"0.00150000", | ||
3156 | "date":"2018-04-02 00:09:13" | ||
3157 | })) | ||
3158 | order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { | ||
3159 | "tradeID":21336541, | ||
3160 | "currencyPair":"BTC_XRP", | ||
3161 | "type":"sell", | ||
3162 | "rate":"0.00007013", | ||
3163 | "amount":"0.00000222", | ||
3164 | "total":"0.00000000", | ||
3165 | "fee":"0.00150000", | ||
3166 | "date":"2018-04-02 00:09:13" | ||
3167 | })) | ||
3168 | with mock.patch.object(order, "trade") as trade: | ||
3169 | order.fix_disappeared_order() | ||
3170 | self.assertEqual("closed", order.status) | ||
3171 | trade.reopen_same_order.assert_not_called() | ||
3172 | |||
3173 | with self.subTest("Order disappeared"): | ||
3174 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
3175 | D("0.1"), "BTC", "long", self.m, "trade") | ||
3176 | order.id = 45 | ||
3177 | order.status = "closed" | ||
3178 | order.mouvements.append(portfolio.Mouvement("XRP", "BTC", { | ||
3179 | "tradeID":21336541, | ||
3180 | "currencyPair":"BTC_XRP", | ||
3181 | "type":"sell", | ||
3182 | "rate":"0.00007013", | ||
3183 | "amount":"0.00000222", | ||
3184 | "total":"0.00000000", | ||
3185 | "fee":"0.00150000", | ||
3186 | "date":"2018-04-02 00:09:13" | ||
3187 | })) | ||
3188 | with mock.patch.object(order, "trade") as trade: | ||
3189 | trade.reopen_same_order.return_value = "New order" | ||
3190 | order.fix_disappeared_order() | ||
3191 | self.assertEqual("error_disappeared", order.status) | ||
3192 | trade.reopen_same_order.assert_called_once_with(order) | ||
3193 | self.m.report.log_error.assert_called_once_with('fetch', | ||
3194 | message='Order Order(buy long 10.00000000 ETH at 0.1 BTC [error_disappeared]) disappeared, recreating it as New order') | ||
3195 | |||
3080 | @mock.patch.object(portfolio.Order, "fetch") | 3196 | @mock.patch.object(portfolio.Order, "fetch") |
3081 | def test_get_status(self, fetch): | 3197 | def test_get_status(self, fetch): |
3082 | with self.subTest(debug=True): | 3198 | with self.subTest(debug=True): |