aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-12 02:10:08 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-12 02:10:08 +0100
commit83c698c925db9dcb2d347c2a625de88d85cfeb21 (patch)
tree3d1d321c068c16551502469fbc31566620b16c6c
parent34eb08f759a440af0376727664d9422041dfbd18 (diff)
parentd8deb0e9a6b7b2805e61dc19a287d5474c271cc5 (diff)
downloadTrader-83c698c925db9dcb2d347c2a625de88d85cfeb21.tar.gz
Trader-83c698c925db9dcb2d347c2a625de88d85cfeb21.tar.zst
Trader-83c698c925db9dcb2d347c2a625de88d85cfeb21.zip
Merge branch 'night_fixes' into dev
-rw-r--r--portfolio.py26
-rw-r--r--test.py88
2 files changed, 76 insertions, 38 deletions
diff --git a/portfolio.py b/portfolio.py
index 554b34f..69e3755 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -214,6 +214,7 @@ class Trade:
214 self.orders = [] 214 self.orders = []
215 self.market = market 215 self.market = market
216 self.closed = False 216 self.closed = False
217 self.inverted = None
217 assert self.value_from.value * self.value_to.value >= 0 218 assert self.value_from.value * self.value_to.value >= 0
218 assert self.value_from.currency == self.value_to.currency 219 assert self.value_from.currency == self.value_to.currency
219 if self.value_from != 0: 220 if self.value_from != 0:
@@ -238,8 +239,8 @@ class Trade:
238 else: 239 else:
239 return "dispose" 240 return "dispose"
240 241
241 def order_action(self, inverted): 242 def order_action(self):
242 if (self.value_from < self.value_to) != inverted: 243 if (self.value_from < self.value_to) != self.inverted:
243 return "buy" 244 return "buy"
244 else: 245 else:
245 return "sell" 246 return "sell"
@@ -262,7 +263,7 @@ class Trade:
262 263
263 @property 264 @property
264 def is_fullfiled(self): 265 def is_fullfiled(self):
265 return abs(self.filled_amount(in_base_currency=True)) >= abs(self.delta) 266 return abs(self.filled_amount(in_base_currency=(not self.inverted))) >= abs(self.delta)
266 267
267 def filled_amount(self, in_base_currency=False): 268 def filled_amount(self, in_base_currency=False):
268 filled_amount = 0 269 filled_amount = 0
@@ -308,17 +309,17 @@ class Trade:
308 if self.action is None: 309 if self.action is None:
309 return None 310 return None
310 ticker = self.market.get_ticker(self.currency, self.base_currency) 311 ticker = self.market.get_ticker(self.currency, self.base_currency)
311 inverted = ticker["inverted"] 312 self.inverted = ticker["inverted"]
312 if inverted: 313 if self.inverted:
313 ticker = ticker["original"] 314 ticker = ticker["original"]
314 rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value) 315 rate = Computation.compute_value(ticker, self.order_action(), compute_value=compute_value)
315 316
316 # FIXME: Dust amount should be removed from there if they werent 317 # FIXME: Dust amount should be removed from there if they werent
317 # honored in other sales 318 # honored in other sales
318 delta_in_base = abs(self.delta) 319 delta_in_base = abs(self.delta)
319 # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case) 320 # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case)
320 321
321 if not inverted: 322 if not self.inverted:
322 base_currency = self.base_currency 323 base_currency = self.base_currency
323 # BTC 324 # BTC
324 if self.action == "dispose": 325 if self.action == "dispose":
@@ -376,7 +377,7 @@ class Trade:
376 self.market.report.log_error("prepare_order", message="Less to do than already filled: {}".format(delta)) 377 self.market.report.log_error("prepare_order", message="Less to do than already filled: {}".format(delta))
377 return None 378 return None
378 379
379 order = Order(self.order_action(inverted), 380 order = Order(self.order_action(),
380 delta, rate, base_currency, self.trade_type, 381 delta, rate, base_currency, self.trade_type,
381 self.market, self, close_if_possible=close_if_possible) 382 self.market, self, close_if_possible=close_if_possible)
382 self.orders.append(order) 383 self.orders.append(order)
@@ -472,7 +473,7 @@ class Order:
472 473
473 @property 474 @property
474 def finished(self): 475 def finished(self):
475 return self.status == "closed" or self.status == "canceled" or self.status == "error" 476 return self.status.startswith("closed") or self.status == "canceled" or self.status == "error"
476 477
477 @retry(InsufficientFunds) 478 @retry(InsufficientFunds)
478 def run(self): 479 def run(self):
@@ -516,15 +517,13 @@ class Order:
516 # other states are "closed" and "canceled" 517 # other states are "closed" and "canceled"
517 if not self.finished: 518 if not self.finished:
518 self.fetch() 519 self.fetch()
519 if self.finished:
520 self.mark_finished_order()
521 return self.status 520 return self.status
522 521
523 def mark_finished_order(self): 522 def mark_finished_order(self):
524 if self.market.debug: 523 if self.status.startswith("closed") and self.market.debug:
525 self.market.report.log_debug_action("Mark {} as finished".format(self)) 524 self.market.report.log_debug_action("Mark {} as finished".format(self))
526 return 525 return
527 if self.status == "closed": 526 if self.status.startswith("closed"):
528 if self.trade_type == "short" and self.action == "buy" and self.close_if_possible: 527 if self.trade_type == "short" and self.action == "buy" and self.close_if_possible:
529 self.market.ccxt.close_margin_position(self.amount.currency, self.base_currency) 528 self.market.ccxt.close_margin_position(self.amount.currency, self.base_currency)
530 529
@@ -543,6 +542,7 @@ class Order:
543 542
544 self.fetch_mouvements() 543 self.fetch_mouvements()
545 544
545 self.mark_finished_order()
546 # FIXME: consider open order with dust remaining as closed 546 # FIXME: consider open order with dust remaining as closed
547 547
548 def dust_amount_remaining(self): 548 def dust_amount_remaining(self):
diff --git a/test.py b/test.py
index f61e739..ac9a6cd 100644
--- a/test.py
+++ b/test.py
@@ -2007,16 +2007,20 @@ class TradeTest(WebMockTestCase):
2007 value_to = portfolio.Amount("BTC", "1.0") 2007 value_to = portfolio.Amount("BTC", "1.0")
2008 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 2008 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
2009 2009
2010 self.assertEqual("buy", trade.order_action(False)) 2010 trade.inverted = False
2011 self.assertEqual("sell", trade.order_action(True)) 2011 self.assertEqual("buy", trade.order_action())
2012 trade.inverted = True
2013 self.assertEqual("sell", trade.order_action())
2012 2014
2013 value_from = portfolio.Amount("BTC", "0") 2015 value_from = portfolio.Amount("BTC", "0")
2014 value_from.linked_to = portfolio.Amount("ETH", "0") 2016 value_from.linked_to = portfolio.Amount("ETH", "0")
2015 value_to = portfolio.Amount("BTC", "-1.0") 2017 value_to = portfolio.Amount("BTC", "-1.0")
2016 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 2018 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
2017 2019
2018 self.assertEqual("sell", trade.order_action(False)) 2020 trade.inverted = False
2019 self.assertEqual("buy", trade.order_action(True)) 2021 self.assertEqual("sell", trade.order_action())
2022 trade.inverted = True
2023 self.assertEqual("buy", trade.order_action())
2020 2024
2021 def test_trade_type(self): 2025 def test_trade_type(self):
2022 value_from = portfolio.Amount("BTC", "0.5") 2026 value_from = portfolio.Amount("BTC", "0.5")
@@ -2034,26 +2038,59 @@ class TradeTest(WebMockTestCase):
2034 self.assertEqual("short", trade.trade_type) 2038 self.assertEqual("short", trade.trade_type)
2035 2039
2036 def test_is_fullfiled(self): 2040 def test_is_fullfiled(self):
2037 value_from = portfolio.Amount("BTC", "0.5") 2041 with self.subTest(inverted=False):
2038 value_from.linked_to = portfolio.Amount("ETH", "10.0") 2042 value_from = portfolio.Amount("BTC", "0.5")
2039 value_to = portfolio.Amount("BTC", "1.0") 2043 value_from.linked_to = portfolio.Amount("ETH", "10.0")
2040 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 2044 value_to = portfolio.Amount("BTC", "1.0")
2045 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
2041 2046
2042 order1 = mock.Mock() 2047 order1 = mock.Mock()
2043 order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3") 2048 order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3")
2044 2049
2045 order2 = mock.Mock() 2050 order2 = mock.Mock()
2046 order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01") 2051 order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01")
2047 trade.orders.append(order1) 2052 trade.orders.append(order1)
2048 trade.orders.append(order2) 2053 trade.orders.append(order2)
2054
2055 self.assertFalse(trade.is_fullfiled)
2056
2057 order3 = mock.Mock()
2058 order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19")
2059 trade.orders.append(order3)
2060
2061 self.assertTrue(trade.is_fullfiled)
2062
2063 order1.filled_amount.assert_called_with(in_base_currency=True)
2064 order2.filled_amount.assert_called_with(in_base_currency=True)
2065 order3.filled_amount.assert_called_with(in_base_currency=True)
2049 2066
2050 self.assertFalse(trade.is_fullfiled) 2067 with self.subTest(inverted=True):
2068 value_from = portfolio.Amount("BTC", "0.5")
2069 value_from.linked_to = portfolio.Amount("USDT", "1000.0")
2070 value_to = portfolio.Amount("BTC", "1.0")
2071 trade = portfolio.Trade(value_from, value_to, "USDT", self.m)
2072 trade.inverted = True
2051 2073
2052 order3 = mock.Mock() 2074 order1 = mock.Mock()
2053 order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19") 2075 order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3")
2054 trade.orders.append(order3) 2076
2077 order2 = mock.Mock()
2078 order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01")
2079 trade.orders.append(order1)
2080 trade.orders.append(order2)
2081
2082 self.assertFalse(trade.is_fullfiled)
2083
2084 order3 = mock.Mock()
2085 order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19")
2086 trade.orders.append(order3)
2087
2088 self.assertTrue(trade.is_fullfiled)
2089
2090 order1.filled_amount.assert_called_with(in_base_currency=False)
2091 order2.filled_amount.assert_called_with(in_base_currency=False)
2092 order3.filled_amount.assert_called_with(in_base_currency=False)
2055 2093
2056 self.assertTrue(trade.is_fullfiled)
2057 2094
2058 def test_filled_amount(self): 2095 def test_filled_amount(self):
2059 value_from = portfolio.Amount("BTC", "0.5") 2096 value_from = portfolio.Amount("BTC", "0.5")
@@ -2713,7 +2750,8 @@ class OrderTest(WebMockTestCase):
2713 self.m.report.log_debug_action.assert_called_once() 2750 self.m.report.log_debug_action.assert_called_once()
2714 2751
2715 @mock.patch.object(portfolio.Order, "fetch_mouvements") 2752 @mock.patch.object(portfolio.Order, "fetch_mouvements")
2716 def test_fetch(self, fetch_mouvements): 2753 @mock.patch.object(portfolio.Order, "mark_finished_order")
2754 def test_fetch(self, mark_finished_order, fetch_mouvements):
2717 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2755 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2718 D("0.1"), "BTC", "long", self.m, "trade") 2756 D("0.1"), "BTC", "long", self.m, "trade")
2719 order.id = 45 2757 order.id = 45
@@ -2723,6 +2761,7 @@ class OrderTest(WebMockTestCase):
2723 self.m.report.log_debug_action.assert_called_once() 2761 self.m.report.log_debug_action.assert_called_once()
2724 self.m.report.log_debug_action.reset_mock() 2762 self.m.report.log_debug_action.reset_mock()
2725 self.m.ccxt.fetch_order.assert_not_called() 2763 self.m.ccxt.fetch_order.assert_not_called()
2764 mark_finished_order.assert_not_called()
2726 fetch_mouvements.assert_not_called() 2765 fetch_mouvements.assert_not_called()
2727 2766
2728 with self.subTest(debug=False): 2767 with self.subTest(debug=False):
@@ -2739,17 +2778,19 @@ class OrderTest(WebMockTestCase):
2739 self.assertEqual("timestamp", order.timestamp) 2778 self.assertEqual("timestamp", order.timestamp)
2740 self.assertEqual(1, len(order.results)) 2779 self.assertEqual(1, len(order.results))
2741 self.m.report.log_debug_action.assert_not_called() 2780 self.m.report.log_debug_action.assert_not_called()
2781 mark_finished_order.assert_called_once()
2742 2782
2783 mark_finished_order.reset_mock()
2743 with self.subTest(missing_order=True): 2784 with self.subTest(missing_order=True):
2744 self.m.ccxt.fetch_order.side_effect = [ 2785 self.m.ccxt.fetch_order.side_effect = [
2745 portfolio.OrderNotCached, 2786 portfolio.OrderNotCached,
2746 ] 2787 ]
2747 order.fetch() 2788 order.fetch()
2748 self.assertEqual("closed_unknown", order.status) 2789 self.assertEqual("closed_unknown", order.status)
2790 mark_finished_order.assert_called_once()
2749 2791
2750 @mock.patch.object(portfolio.Order, "fetch") 2792 @mock.patch.object(portfolio.Order, "fetch")
2751 @mock.patch.object(portfolio.Order, "mark_finished_order") 2793 def test_get_status(self, fetch):
2752 def test_get_status(self, mark_finished_order, fetch):
2753 with self.subTest(debug=True): 2794 with self.subTest(debug=True):
2754 self.m.debug = True 2795 self.m.debug = True
2755 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2796 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
@@ -2768,10 +2809,8 @@ class OrderTest(WebMockTestCase):
2768 return update_status 2809 return update_status
2769 fetch.side_effect = _fetch(order) 2810 fetch.side_effect = _fetch(order)
2770 self.assertEqual("open", order.get_status()) 2811 self.assertEqual("open", order.get_status())
2771 mark_finished_order.assert_not_called()
2772 fetch.assert_called_once() 2812 fetch.assert_called_once()
2773 2813
2774 mark_finished_order.reset_mock()
2775 fetch.reset_mock() 2814 fetch.reset_mock()
2776 with self.subTest(debug=False, finished=True): 2815 with self.subTest(debug=False, finished=True):
2777 self.m.debug = False 2816 self.m.debug = False
@@ -2783,7 +2822,6 @@ class OrderTest(WebMockTestCase):
2783 return update_status 2822 return update_status
2784 fetch.side_effect = _fetch(order) 2823 fetch.side_effect = _fetch(order)
2785 self.assertEqual("closed", order.get_status()) 2824 self.assertEqual("closed", order.get_status())
2786 mark_finished_order.assert_called_once()
2787 fetch.assert_called_once() 2825 fetch.assert_called_once()
2788 2826
2789 def test_run(self): 2827 def test_run(self):