aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-24 15:18:31 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-24 16:05:26 +0100
commitc7c1e0b26821fdd5622f81fb456f1028d4c9ab09 (patch)
treef3115a444aa3472a98678193e09b21f3c3455332
parent472787b6360221588423d03fe3e73d92c09a7c9d (diff)
downloadTrader-c7c1e0b26821fdd5622f81fb456f1028d4c9ab09.tar.gz
Trader-c7c1e0b26821fdd5622f81fb456f1028d4c9ab09.tar.zst
Trader-c7c1e0b26821fdd5622f81fb456f1028d4c9ab09.zip
Add retry facility for api call timeouts
Fixes https://git.immae.eu/mantisbt/view.php?id=40
-rw-r--r--ccxt_wrapper.py30
-rw-r--r--test.py52
2 files changed, 82 insertions, 0 deletions
diff --git a/ccxt_wrapper.py b/ccxt_wrapper.py
index d37c306..c500659 100644
--- a/ccxt_wrapper.py
+++ b/ccxt_wrapper.py
@@ -1,12 +1,42 @@
1from ccxt import * 1from ccxt import *
2import decimal 2import decimal
3import time 3import time
4from retry.api import retry_call
5import re
4 6
5def _cw_exchange_sum(self, *args): 7def _cw_exchange_sum(self, *args):
6 return sum([arg for arg in args if isinstance(arg, (float, int, decimal.Decimal))]) 8 return sum([arg for arg in args if isinstance(arg, (float, int, decimal.Decimal))])
7Exchange.sum = _cw_exchange_sum 9Exchange.sum = _cw_exchange_sum
8 10
9class poloniexE(poloniex): 11class poloniexE(poloniex):
12 RETRIABLE_CALLS = [
13 re.compile(r"^return"),
14 re.compile(r"^cancel"),
15 re.compile(r"^closeMarginPosition$"),
16 re.compile(r"^getMarginPosition$"),
17 ]
18
19 def request(self, path, api='public', method='GET', params={}, headers=None, body=None):
20 """
21 Wrapped to allow retry of non-posting requests"
22 """
23
24 origin_request = super(poloniexE, self).request
25 kwargs = {
26 "api": api,
27 "method": method,
28 "params": params,
29 "headers": headers,
30 "body": body
31 }
32
33 retriable = any(re.match(call, path) for call in self.RETRIABLE_CALLS)
34 if api == "public" or method == "GET" or retriable:
35 return retry_call(origin_request, fargs=[path], fkwargs=kwargs,
36 tries=10, delay=1, exceptions=(RequestTimeout,))
37 else:
38 return origin_request(path, **kwargs)
39
10 @staticmethod 40 @staticmethod
11 def nanoseconds(): 41 def nanoseconds():
12 return int(time.time() * 1000000000) 42 return int(time.time() * 1000000000)
diff --git a/test.py b/test.py
index 637a305..40c64a9 100644
--- a/test.py
+++ b/test.py
@@ -80,6 +80,58 @@ class poloniexETest(unittest.TestCase):
80 time.return_value = 123456.7890123456 80 time.return_value = 123456.7890123456
81 self.assertEqual(123456789012345, self.s.nonce()) 81 self.assertEqual(123456789012345, self.s.nonce())
82 82
83 def test_request(self):
84 with mock.patch.object(market.ccxt.poloniex, "request") as request,\
85 mock.patch("market.ccxt.retry_call") as retry_call:
86 with self.subTest(wrapped=True):
87 with self.subTest(desc="public"):
88 self.s.request("foo")
89 retry_call.assert_called_with(request,
90 delay=1, tries=10, fargs=["foo"],
91 fkwargs={'api': 'public', 'method': 'GET', 'params': {}, 'headers': None, 'body': None},
92 exceptions=(market.ccxt.RequestTimeout,))
93 request.assert_not_called()
94
95 with self.subTest(desc="private GET"):
96 self.s.request("foo", api="private")
97 retry_call.assert_called_with(request,
98 delay=1, tries=10, fargs=["foo"],
99 fkwargs={'api': 'private', 'method': 'GET', 'params': {}, 'headers': None, 'body': None},
100 exceptions=(market.ccxt.RequestTimeout,))
101 request.assert_not_called()
102
103 with self.subTest(desc="private POST regexp"):
104 self.s.request("returnFoo", api="private", method="POST")
105 retry_call.assert_called_with(request,
106 delay=1, tries=10, fargs=["returnFoo"],
107 fkwargs={'api': 'private', 'method': 'POST', 'params': {}, 'headers': None, 'body': None},
108 exceptions=(market.ccxt.RequestTimeout,))
109 request.assert_not_called()
110
111 with self.subTest(desc="private POST non-regexp"):
112 self.s.request("getMarginPosition", api="private", method="POST")
113 retry_call.assert_called_with(request,
114 delay=1, tries=10, fargs=["getMarginPosition"],
115 fkwargs={'api': 'private', 'method': 'POST', 'params': {}, 'headers': None, 'body': None},
116 exceptions=(market.ccxt.RequestTimeout,))
117 request.assert_not_called()
118 retry_call.reset_mock()
119 request.reset_mock()
120 with self.subTest(wrapped=False):
121 with self.subTest(desc="private POST non-matching regexp"):
122 self.s.request("marginBuy", api="private", method="POST")
123 request.assert_called_with("marginBuy",
124 api="private", method="POST", params={},
125 headers=None, body=None)
126 retry_call.assert_not_called()
127
128 with self.subTest(desc="private POST non-matching non-regexp"):
129 self.s.request("closeMarginPositionOther", api="private", method="POST")
130 request.assert_called_with("closeMarginPositionOther",
131 api="private", method="POST", params={},
132 headers=None, body=None)
133 retry_call.assert_not_called()
134
83 def test_order_precision(self): 135 def test_order_precision(self):
84 self.assertEqual(8, self.s.order_precision("FOO")) 136 self.assertEqual(8, self.s.order_precision("FOO"))
85 137