aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-12 02:06:42 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-12 02:06:42 +0100
commit3faa0d739e599daafacf948534d2e66292387d02 (patch)
treef56ae35251cfeb60c563c023f7f48e59f8dbee7e
parentf9226903cb53a9b303a26de562e321159349f8df (diff)
parentd8deb0e9a6b7b2805e61dc19a287d5474c271cc5 (diff)
downloadTrader-3faa0d739e599daafacf948534d2e66292387d02.tar.gz
Trader-3faa0d739e599daafacf948534d2e66292387d02.tar.zst
Trader-3faa0d739e599daafacf948534d2e66292387d02.zip
Merge branch 'night_fixes'v0.5.1
-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 0f2c011..ed50b57 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -291,6 +291,7 @@ class Trade:
291 self.orders = [] 291 self.orders = []
292 self.market = market 292 self.market = market
293 self.closed = False 293 self.closed = False
294 self.inverted = None
294 assert self.value_from.value * self.value_to.value >= 0 295 assert self.value_from.value * self.value_to.value >= 0
295 assert self.value_from.currency == self.value_to.currency 296 assert self.value_from.currency == self.value_to.currency
296 if self.value_from != 0: 297 if self.value_from != 0:
@@ -315,8 +316,8 @@ class Trade:
315 else: 316 else:
316 return "dispose" 317 return "dispose"
317 318
318 def order_action(self, inverted): 319 def order_action(self):
319 if (self.value_from < self.value_to) != inverted: 320 if (self.value_from < self.value_to) != self.inverted:
320 return "buy" 321 return "buy"
321 else: 322 else:
322 return "sell" 323 return "sell"
@@ -339,7 +340,7 @@ class Trade:
339 340
340 @property 341 @property
341 def is_fullfiled(self): 342 def is_fullfiled(self):
342 return abs(self.filled_amount(in_base_currency=True)) >= abs(self.delta) 343 return abs(self.filled_amount(in_base_currency=(not self.inverted))) >= abs(self.delta)
343 344
344 def filled_amount(self, in_base_currency=False): 345 def filled_amount(self, in_base_currency=False):
345 filled_amount = 0 346 filled_amount = 0
@@ -385,17 +386,17 @@ class Trade:
385 if self.action is None: 386 if self.action is None:
386 return None 387 return None
387 ticker = self.market.get_ticker(self.currency, self.base_currency) 388 ticker = self.market.get_ticker(self.currency, self.base_currency)
388 inverted = ticker["inverted"] 389 self.inverted = ticker["inverted"]
389 if inverted: 390 if self.inverted:
390 ticker = ticker["original"] 391 ticker = ticker["original"]
391 rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value) 392 rate = Computation.compute_value(ticker, self.order_action(), compute_value=compute_value)
392 393
393 # FIXME: Dust amount should be removed from there if they werent 394 # FIXME: Dust amount should be removed from there if they werent
394 # honored in other sales 395 # honored in other sales
395 delta_in_base = abs(self.delta) 396 delta_in_base = abs(self.delta)
396 # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case) 397 # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case)
397 398
398 if not inverted: 399 if not self.inverted:
399 base_currency = self.base_currency 400 base_currency = self.base_currency
400 # BTC 401 # BTC
401 if self.action == "dispose": 402 if self.action == "dispose":
@@ -453,7 +454,7 @@ class Trade:
453 self.market.report.log_error("prepare_order", message="Less to do than already filled: {}".format(delta)) 454 self.market.report.log_error("prepare_order", message="Less to do than already filled: {}".format(delta))
454 return None 455 return None
455 456
456 order = Order(self.order_action(inverted), 457 order = Order(self.order_action(),
457 delta, rate, base_currency, self.trade_type, 458 delta, rate, base_currency, self.trade_type,
458 self.market, self, close_if_possible=close_if_possible) 459 self.market, self, close_if_possible=close_if_possible)
459 self.orders.append(order) 460 self.orders.append(order)
@@ -549,7 +550,7 @@ class Order:
549 550
550 @property 551 @property
551 def finished(self): 552 def finished(self):
552 return self.status == "closed" or self.status == "canceled" or self.status == "error" 553 return self.status.startswith("closed") or self.status == "canceled" or self.status == "error"
553 554
554 @retry(InsufficientFunds) 555 @retry(InsufficientFunds)
555 def run(self): 556 def run(self):
@@ -593,15 +594,13 @@ class Order:
593 # other states are "closed" and "canceled" 594 # other states are "closed" and "canceled"
594 if not self.finished: 595 if not self.finished:
595 self.fetch() 596 self.fetch()
596 if self.finished:
597 self.mark_finished_order()
598 return self.status 597 return self.status
599 598
600 def mark_finished_order(self): 599 def mark_finished_order(self):
601 if self.market.debug: 600 if self.status.startswith("closed") and self.market.debug:
602 self.market.report.log_debug_action("Mark {} as finished".format(self)) 601 self.market.report.log_debug_action("Mark {} as finished".format(self))
603 return 602 return
604 if self.status == "closed": 603 if self.status.startswith("closed"):
605 if self.trade_type == "short" and self.action == "buy" and self.close_if_possible: 604 if self.trade_type == "short" and self.action == "buy" and self.close_if_possible:
606 self.market.ccxt.close_margin_position(self.amount.currency, self.base_currency) 605 self.market.ccxt.close_margin_position(self.amount.currency, self.base_currency)
607 606
@@ -620,6 +619,7 @@ class Order:
620 619
621 self.fetch_mouvements() 620 self.fetch_mouvements()
622 621
622 self.mark_finished_order()
623 # FIXME: consider open order with dust remaining as closed 623 # FIXME: consider open order with dust remaining as closed
624 624
625 def dust_amount_remaining(self): 625 def dust_amount_remaining(self):
diff --git a/test.py b/test.py
index a45010b..921af9f 100644
--- a/test.py
+++ b/test.py
@@ -1375,16 +1375,20 @@ class TradeTest(WebMockTestCase):
1375 value_to = portfolio.Amount("BTC", "1.0") 1375 value_to = portfolio.Amount("BTC", "1.0")
1376 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 1376 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1377 1377
1378 self.assertEqual("buy", trade.order_action(False)) 1378 trade.inverted = False
1379 self.assertEqual("sell", trade.order_action(True)) 1379 self.assertEqual("buy", trade.order_action())
1380 trade.inverted = True
1381 self.assertEqual("sell", trade.order_action())
1380 1382
1381 value_from = portfolio.Amount("BTC", "0") 1383 value_from = portfolio.Amount("BTC", "0")
1382 value_from.linked_to = portfolio.Amount("ETH", "0") 1384 value_from.linked_to = portfolio.Amount("ETH", "0")
1383 value_to = portfolio.Amount("BTC", "-1.0") 1385 value_to = portfolio.Amount("BTC", "-1.0")
1384 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 1386 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1385 1387
1386 self.assertEqual("sell", trade.order_action(False)) 1388 trade.inverted = False
1387 self.assertEqual("buy", trade.order_action(True)) 1389 self.assertEqual("sell", trade.order_action())
1390 trade.inverted = True
1391 self.assertEqual("buy", trade.order_action())
1388 1392
1389 def test_trade_type(self): 1393 def test_trade_type(self):
1390 value_from = portfolio.Amount("BTC", "0.5") 1394 value_from = portfolio.Amount("BTC", "0.5")
@@ -1402,26 +1406,59 @@ class TradeTest(WebMockTestCase):
1402 self.assertEqual("short", trade.trade_type) 1406 self.assertEqual("short", trade.trade_type)
1403 1407
1404 def test_is_fullfiled(self): 1408 def test_is_fullfiled(self):
1405 value_from = portfolio.Amount("BTC", "0.5") 1409 with self.subTest(inverted=False):
1406 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1410 value_from = portfolio.Amount("BTC", "0.5")
1407 value_to = portfolio.Amount("BTC", "1.0") 1411 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1408 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 1412 value_to = portfolio.Amount("BTC", "1.0")
1413 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1409 1414
1410 order1 = mock.Mock() 1415 order1 = mock.Mock()
1411 order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3") 1416 order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3")
1412 1417
1413 order2 = mock.Mock() 1418 order2 = mock.Mock()
1414 order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01") 1419 order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01")
1415 trade.orders.append(order1) 1420 trade.orders.append(order1)
1416 trade.orders.append(order2) 1421 trade.orders.append(order2)
1422
1423 self.assertFalse(trade.is_fullfiled)
1424
1425 order3 = mock.Mock()
1426 order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19")
1427 trade.orders.append(order3)
1428
1429 self.assertTrue(trade.is_fullfiled)
1430
1431 order1.filled_amount.assert_called_with(in_base_currency=True)
1432 order2.filled_amount.assert_called_with(in_base_currency=True)
1433 order3.filled_amount.assert_called_with(in_base_currency=True)
1417 1434
1418 self.assertFalse(trade.is_fullfiled) 1435 with self.subTest(inverted=True):
1436 value_from = portfolio.Amount("BTC", "0.5")
1437 value_from.linked_to = portfolio.Amount("USDT", "1000.0")
1438 value_to = portfolio.Amount("BTC", "1.0")
1439 trade = portfolio.Trade(value_from, value_to, "USDT", self.m)
1440 trade.inverted = True
1419 1441
1420 order3 = mock.Mock() 1442 order1 = mock.Mock()
1421 order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19") 1443 order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3")
1422 trade.orders.append(order3) 1444
1445 order2 = mock.Mock()
1446 order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01")
1447 trade.orders.append(order1)
1448 trade.orders.append(order2)
1449
1450 self.assertFalse(trade.is_fullfiled)
1451
1452 order3 = mock.Mock()
1453 order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19")
1454 trade.orders.append(order3)
1455
1456 self.assertTrue(trade.is_fullfiled)
1457
1458 order1.filled_amount.assert_called_with(in_base_currency=False)
1459 order2.filled_amount.assert_called_with(in_base_currency=False)
1460 order3.filled_amount.assert_called_with(in_base_currency=False)
1423 1461
1424 self.assertTrue(trade.is_fullfiled)
1425 1462
1426 def test_filled_amount(self): 1463 def test_filled_amount(self):
1427 value_from = portfolio.Amount("BTC", "0.5") 1464 value_from = portfolio.Amount("BTC", "0.5")
@@ -2081,7 +2118,8 @@ class OrderTest(WebMockTestCase):
2081 self.m.report.log_debug_action.assert_called_once() 2118 self.m.report.log_debug_action.assert_called_once()
2082 2119
2083 @mock.patch.object(portfolio.Order, "fetch_mouvements") 2120 @mock.patch.object(portfolio.Order, "fetch_mouvements")
2084 def test_fetch(self, fetch_mouvements): 2121 @mock.patch.object(portfolio.Order, "mark_finished_order")
2122 def test_fetch(self, mark_finished_order, fetch_mouvements):
2085 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2123 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2086 D("0.1"), "BTC", "long", self.m, "trade") 2124 D("0.1"), "BTC", "long", self.m, "trade")
2087 order.id = 45 2125 order.id = 45
@@ -2091,6 +2129,7 @@ class OrderTest(WebMockTestCase):
2091 self.m.report.log_debug_action.assert_called_once() 2129 self.m.report.log_debug_action.assert_called_once()
2092 self.m.report.log_debug_action.reset_mock() 2130 self.m.report.log_debug_action.reset_mock()
2093 self.m.ccxt.fetch_order.assert_not_called() 2131 self.m.ccxt.fetch_order.assert_not_called()
2132 mark_finished_order.assert_not_called()
2094 fetch_mouvements.assert_not_called() 2133 fetch_mouvements.assert_not_called()
2095 2134
2096 with self.subTest(debug=False): 2135 with self.subTest(debug=False):
@@ -2107,17 +2146,19 @@ class OrderTest(WebMockTestCase):
2107 self.assertEqual("timestamp", order.timestamp) 2146 self.assertEqual("timestamp", order.timestamp)
2108 self.assertEqual(1, len(order.results)) 2147 self.assertEqual(1, len(order.results))
2109 self.m.report.log_debug_action.assert_not_called() 2148 self.m.report.log_debug_action.assert_not_called()
2149 mark_finished_order.assert_called_once()
2110 2150
2151 mark_finished_order.reset_mock()
2111 with self.subTest(missing_order=True): 2152 with self.subTest(missing_order=True):
2112 self.m.ccxt.fetch_order.side_effect = [ 2153 self.m.ccxt.fetch_order.side_effect = [
2113 portfolio.OrderNotCached, 2154 portfolio.OrderNotCached,
2114 ] 2155 ]
2115 order.fetch() 2156 order.fetch()
2116 self.assertEqual("closed_unknown", order.status) 2157 self.assertEqual("closed_unknown", order.status)
2158 mark_finished_order.assert_called_once()
2117 2159
2118 @mock.patch.object(portfolio.Order, "fetch") 2160 @mock.patch.object(portfolio.Order, "fetch")
2119 @mock.patch.object(portfolio.Order, "mark_finished_order") 2161 def test_get_status(self, fetch):
2120 def test_get_status(self, mark_finished_order, fetch):
2121 with self.subTest(debug=True): 2162 with self.subTest(debug=True):
2122 self.m.debug = True 2163 self.m.debug = True
2123 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2164 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
@@ -2136,10 +2177,8 @@ class OrderTest(WebMockTestCase):
2136 return update_status 2177 return update_status
2137 fetch.side_effect = _fetch(order) 2178 fetch.side_effect = _fetch(order)
2138 self.assertEqual("open", order.get_status()) 2179 self.assertEqual("open", order.get_status())
2139 mark_finished_order.assert_not_called()
2140 fetch.assert_called_once() 2180 fetch.assert_called_once()
2141 2181
2142 mark_finished_order.reset_mock()
2143 fetch.reset_mock() 2182 fetch.reset_mock()
2144 with self.subTest(debug=False, finished=True): 2183 with self.subTest(debug=False, finished=True):
2145 self.m.debug = False 2184 self.m.debug = False
@@ -2151,7 +2190,6 @@ class OrderTest(WebMockTestCase):
2151 return update_status 2190 return update_status
2152 fetch.side_effect = _fetch(order) 2191 fetch.side_effect = _fetch(order)
2153 self.assertEqual("closed", order.get_status()) 2192 self.assertEqual("closed", order.get_status())
2154 mark_finished_order.assert_called_once()
2155 fetch.assert_called_once() 2193 fetch.assert_called_once()
2156 2194
2157 def test_run(self): 2195 def test_run(self):