aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-04 23:35:16 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-04 23:35:16 +0100
commitf9226903cb53a9b303a26de562e321159349f8df (patch)
tree671e139c9cadaf4070287f50d1f71c0d26d6ba80
parentc5a7f2863f5c041ff906b2407a74971e2178a729 (diff)
downloadTrader-f9226903cb53a9b303a26de562e321159349f8df.tar.gz
Trader-f9226903cb53a9b303a26de562e321159349f8df.tar.zst
Trader-f9226903cb53a9b303a26de562e321159349f8df.zip
Fixes after night runv0.5
- Currency pair doesn’t work when fetching orders - Store error in main to report. - Cancel orders when closing trade - Print closed status of trade to repr - Don’t try to cancel not cancellable orders
-rw-r--r--main.py5
-rw-r--r--portfolio.py26
-rw-r--r--test.py105
3 files changed, 102 insertions, 34 deletions
diff --git a/main.py b/main.py
index 17cf58c..d4bab02 100644
--- a/main.py
+++ b/main.py
@@ -10,6 +10,9 @@ for market_config, user_id in helper.main_fetch_markets(pg_config, args.user):
10 user_market = market.Market.from_config(market_config, debug=args.debug) 10 user_market = market.Market.from_config(market_config, debug=args.debug)
11 helper.main_process_market(user_market, args.action, before=args.before, after=args.after) 11 helper.main_process_market(user_market, args.action, before=args.before, after=args.after)
12 except Exception as e: 12 except Exception as e:
13 print("{}: {}".format(e.__class__.__name__, e)) 13 try:
14 user_market.report.log_error("main", exception=e)
15 except:
16 print("{}: {}".format(e.__class__.__name__, e))
14 finally: 17 finally:
15 helper.main_store_report(report_path, user_id, user_market) 18 helper.main_store_report(report_path, user_id, user_market)
diff --git a/portfolio.py b/portfolio.py
index f27e84f..0f2c011 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -3,7 +3,7 @@ from datetime import datetime, timedelta
3from decimal import Decimal as D, ROUND_DOWN 3from decimal import Decimal as D, ROUND_DOWN
4from json import JSONDecodeError 4from json import JSONDecodeError
5from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError 5from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError
6from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached 6from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached, OrderNotFound
7from retry import retry 7from retry import retry
8import requests 8import requests
9 9
@@ -333,6 +333,8 @@ class Trade:
333 return not (self.is_fullfiled or self.closed) 333 return not (self.is_fullfiled or self.closed)
334 334
335 def close(self): 335 def close(self):
336 for order in self.orders:
337 order.cancel()
336 self.closed = True 338 self.closed = True
337 339
338 @property 340 @property
@@ -467,11 +469,19 @@ class Trade:
467 } 469 }
468 470
469 def __repr__(self): 471 def __repr__(self):
470 return "Trade({} -> {} in {}, {})".format( 472 if self.closed and not self.is_fullfiled:
473 closed = " ❌"
474 elif self.is_fullfiled:
475 closed = " ✔"
476 else:
477 closed = ""
478
479 return "Trade({} -> {} in {}, {}{})".format(
471 self.value_from, 480 self.value_from,
472 self.value_to, 481 self.value_to,
473 self.currency, 482 self.currency,
474 self.action) 483 self.action,
484 closed)
475 485
476 def print_with_order(self, ind=""): 486 def print_with_order(self, ind=""):
477 self.market.report.print_log("{}{}".format(ind, self)) 487 self.market.report.print_log("{}{}".format(ind, self))
@@ -600,7 +610,7 @@ class Order:
600 self.market.report.log_debug_action("Fetching {}".format(self)) 610 self.market.report.log_debug_action("Fetching {}".format(self))
601 return 611 return
602 try: 612 try:
603 result = self.market.ccxt.fetch_order(self.id, symbol=self.amount.currency) 613 result = self.market.ccxt.fetch_order(self.id)
604 self.results.append(result) 614 self.results.append(result)
605 self.status = result["status"] 615 self.status = result["status"]
606 # Time at which the order started 616 # Time at which the order started
@@ -645,8 +655,12 @@ class Order:
645 self.market.report.log_debug_action("Mark {} as cancelled".format(self)) 655 self.market.report.log_debug_action("Mark {} as cancelled".format(self))
646 self.status = "canceled" 656 self.status = "canceled"
647 return 657 return
648 self.market.ccxt.cancel_order(self.id) 658 if self.open and self.id is not None:
649 self.fetch() 659 try:
660 self.market.ccxt.cancel_order(self.id)
661 except OrderNotFound as e: # Closed inbetween
662 self.market.report.log_error("cancel_order", message="Already cancelled order", exception=e)
663 self.fetch()
650 664
651class Mouvement: 665class Mouvement:
652 def __init__(self, currency, base_currency, hash_): 666 def __init__(self, currency, base_currency, hash_):
diff --git a/test.py b/test.py
index 0bfa2b7..a45010b 100644
--- a/test.py
+++ b/test.py
@@ -1746,25 +1746,46 @@ class TradeTest(WebMockTestCase):
1746 trade.orders.append(order_mock1) 1746 trade.orders.append(order_mock1)
1747 trade.orders.append(order_mock2) 1747 trade.orders.append(order_mock2)
1748 1748
1749 trade.print_with_order() 1749 with mock.patch.object(trade, "filled_amount") as filled:
1750 filled.return_value = portfolio.Amount("BTC", "0.1")
1750 1751
1751 self.m.report.print_log.assert_called() 1752 trade.print_with_order()
1752 calls = self.m.report.print_log.mock_calls 1753
1753 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls[0][1][0])) 1754 self.m.report.print_log.assert_called()
1754 self.assertEqual("\tMock 1", str(calls[1][1][0])) 1755 calls = self.m.report.print_log.mock_calls
1755 self.assertEqual("\tMock 2", str(calls[2][1][0])) 1756 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls[0][1][0]))
1756 self.assertEqual("\t\tMouvement 1", str(calls[3][1][0])) 1757 self.assertEqual("\tMock 1", str(calls[1][1][0]))
1757 self.assertEqual("\t\tMouvement 2", str(calls[4][1][0])) 1758 self.assertEqual("\tMock 2", str(calls[2][1][0]))
1759 self.assertEqual("\t\tMouvement 1", str(calls[3][1][0]))
1760 self.assertEqual("\t\tMouvement 2", str(calls[4][1][0]))
1761
1762 self.m.report.print_log.reset_mock()
1763
1764 filled.return_value = portfolio.Amount("BTC", "0.5")
1765 trade.print_with_order()
1766 calls = self.m.report.print_log.mock_calls
1767 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire ✔)", str(calls[0][1][0]))
1768
1769 self.m.report.print_log.reset_mock()
1770
1771 filled.return_value = portfolio.Amount("BTC", "0.1")
1772 trade.closed = True
1773 trade.print_with_order()
1774 calls = self.m.report.print_log.mock_calls
1775 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire ❌)", str(calls[0][1][0]))
1758 1776
1759 def test_close(self): 1777 def test_close(self):
1760 value_from = portfolio.Amount("BTC", "0.5") 1778 value_from = portfolio.Amount("BTC", "0.5")
1761 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1779 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1762 value_to = portfolio.Amount("BTC", "1.0") 1780 value_to = portfolio.Amount("BTC", "1.0")
1763 trade = portfolio.Trade(value_from, value_to, "ETH", self.m) 1781 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1782 order1 = mock.Mock()
1783 trade.orders.append(order1)
1764 1784
1765 trade.close() 1785 trade.close()
1766 1786
1767 self.assertEqual(True, trade.closed) 1787 self.assertEqual(True, trade.closed)
1788 order1.cancel.assert_called_once_with()
1768 1789
1769 def test_pending(self): 1790 def test_pending(self):
1770 value_from = portfolio.Amount("BTC", "0.5") 1791 value_from = portfolio.Amount("BTC", "0.5")
@@ -1888,27 +1909,57 @@ class OrderTest(WebMockTestCase):
1888 1909
1889 @mock.patch.object(portfolio.Order, "fetch") 1910 @mock.patch.object(portfolio.Order, "fetch")
1890 def test_cancel(self, fetch): 1911 def test_cancel(self, fetch):
1891 self.m.debug = True 1912 with self.subTest(debug=True):
1892 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1913 self.m.debug = True
1893 D("0.1"), "BTC", "long", self.m, "trade") 1914 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1894 order.status = "open" 1915 D("0.1"), "BTC", "long", self.m, "trade")
1916 order.status = "open"
1895 1917
1896 order.cancel() 1918 order.cancel()
1897 self.m.ccxt.cancel_order.assert_not_called() 1919 self.m.ccxt.cancel_order.assert_not_called()
1898 self.m.report.log_debug_action.assert_called_once() 1920 self.m.report.log_debug_action.assert_called_once()
1899 self.m.report.log_debug_action.reset_mock() 1921 self.m.report.log_debug_action.reset_mock()
1900 self.assertEqual("canceled", order.status) 1922 self.assertEqual("canceled", order.status)
1901 1923
1902 self.m.debug = False 1924 with self.subTest(desc="Nominal case"):
1903 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1925 self.m.debug = False
1904 D("0.1"), "BTC", "long", self.m, "trade") 1926 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1905 order.status = "open" 1927 D("0.1"), "BTC", "long", self.m, "trade")
1906 order.id = 42 1928 order.status = "open"
1929 order.id = 42
1907 1930
1908 order.cancel() 1931 order.cancel()
1909 self.m.ccxt.cancel_order.assert_called_with(42) 1932 self.m.ccxt.cancel_order.assert_called_with(42)
1910 fetch.assert_called_once_with() 1933 fetch.assert_called_once_with()
1911 self.m.report.log_debug_action.assert_not_called() 1934 self.m.report.log_debug_action.assert_not_called()
1935
1936 with self.subTest(exception=True):
1937 self.m.ccxt.cancel_order.side_effect = portfolio.OrderNotFound
1938 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1939 D("0.1"), "BTC", "long", self.m, "trade")
1940 order.status = "open"
1941 order.id = 42
1942 order.cancel()
1943 self.m.ccxt.cancel_order.assert_called_with(42)
1944 self.m.report.log_error.assert_called_once()
1945
1946 self.m.reset_mock()
1947 with self.subTest(id=None):
1948 self.m.ccxt.cancel_order.side_effect = portfolio.OrderNotFound
1949 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1950 D("0.1"), "BTC", "long", self.m, "trade")
1951 order.status = "open"
1952 order.cancel()
1953 self.m.ccxt.cancel_order.assert_not_called()
1954
1955 self.m.reset_mock()
1956 with self.subTest(open=False):
1957 self.m.ccxt.cancel_order.side_effect = portfolio.OrderNotFound
1958 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1959 D("0.1"), "BTC", "long", self.m, "trade")
1960 order.status = "closed"
1961 order.cancel()
1962 self.m.ccxt.cancel_order.assert_not_called()
1912 1963
1913 def test_dust_amount_remaining(self): 1964 def test_dust_amount_remaining(self):
1914 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1965 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
@@ -2050,7 +2101,7 @@ class OrderTest(WebMockTestCase):
2050 } 2101 }
2051 order.fetch() 2102 order.fetch()
2052 2103
2053 self.m.ccxt.fetch_order.assert_called_once_with(45, symbol="ETH") 2104 self.m.ccxt.fetch_order.assert_called_once_with(45)
2054 fetch_mouvements.assert_called_once() 2105 fetch_mouvements.assert_called_once()
2055 self.assertEqual("foo", order.status) 2106 self.assertEqual("foo", order.status)
2056 self.assertEqual("timestamp", order.timestamp) 2107 self.assertEqual("timestamp", order.timestamp)