diff options
-rw-r--r-- | ccxt_wrapper.py | 131 | ||||
-rw-r--r-- | test.py | 81 |
2 files changed, 153 insertions, 59 deletions
diff --git a/ccxt_wrapper.py b/ccxt_wrapper.py index 903bfc4..d37c306 100644 --- a/ccxt_wrapper.py +++ b/ccxt_wrapper.py | |||
@@ -11,29 +11,6 @@ class poloniexE(poloniex): | |||
11 | def nanoseconds(): | 11 | def nanoseconds(): |
12 | return int(time.time() * 1000000000) | 12 | return int(time.time() * 1000000000) |
13 | 13 | ||
14 | def nonce(self): | ||
15 | return self.nanoseconds() | ||
16 | |||
17 | def fetch_balance(self, params={}): | ||
18 | self.load_markets() | ||
19 | balances = self.privatePostReturnCompleteBalances(self.extend({ | ||
20 | 'account': 'all', | ||
21 | }, params)) | ||
22 | result = {'info': balances} | ||
23 | currencies = list(balances.keys()) | ||
24 | for c in range(0, len(currencies)): | ||
25 | id = currencies[c] | ||
26 | balance = balances[id] | ||
27 | currency = self.common_currency_code(id) | ||
28 | account = { | ||
29 | 'free': decimal.Decimal(balance['available']), | ||
30 | 'used': decimal.Decimal(balance['onOrders']), | ||
31 | 'total': decimal.Decimal(0.0), | ||
32 | } | ||
33 | account['total'] = self.sum(account['free'], account['used']) | ||
34 | result[currency] = account | ||
35 | return self.parse_balance(result) | ||
36 | |||
37 | def fetch_margin_balance(self): | 14 | def fetch_margin_balance(self): |
38 | """ | 15 | """ |
39 | portfolio.market.privatePostGetMarginPosition({"currencyPair": "BTC_DASH"}) | 16 | portfolio.market.privatePostGetMarginPosition({"currencyPair": "BTC_DASH"}) |
@@ -200,31 +177,8 @@ class poloniexE(poloniex): | |||
200 | 177 | ||
201 | return all_balances | 178 | return all_balances |
202 | 179 | ||
203 | def parse_ticker(self, ticker, market=None): | 180 | def create_exchange_order(self, symbol, type, side, amount, price=None, params={}): |
204 | timestamp = self.milliseconds() | 181 | return super(poloniexE, self).create_order(symbol, type, side, amount, price=price, params=params) |
205 | symbol = None | ||
206 | if market: | ||
207 | symbol = market['symbol'] | ||
208 | return { | ||
209 | 'symbol': symbol, | ||
210 | 'timestamp': timestamp, | ||
211 | 'datetime': self.iso8601(timestamp), | ||
212 | 'high': decimal.Decimal(ticker['high24hr']), | ||
213 | 'low': decimal.Decimal(ticker['low24hr']), | ||
214 | 'bid': decimal.Decimal(ticker['highestBid']), | ||
215 | 'ask': decimal.Decimal(ticker['lowestAsk']), | ||
216 | 'vwap': None, | ||
217 | 'open': None, | ||
218 | 'close': None, | ||
219 | 'first': None, | ||
220 | 'last': decimal.Decimal(ticker['last']), | ||
221 | 'change': decimal.Decimal(ticker['percentChange']), | ||
222 | 'percentage': None, | ||
223 | 'average': None, | ||
224 | 'baseVolume': decimal.Decimal(ticker['quoteVolume']), | ||
225 | 'quoteVolume': decimal.Decimal(ticker['baseVolume']), | ||
226 | 'info': ticker, | ||
227 | } | ||
228 | 182 | ||
229 | def create_margin_order(self, symbol, type, side, amount, price=None, lending_rate=None, params={}): | 183 | def create_margin_order(self, symbol, type, side, amount, price=None, lending_rate=None, params={}): |
230 | if type == 'market': | 184 | if type == 'market': |
@@ -254,17 +208,6 @@ class poloniexE(poloniex): | |||
254 | self.orders[id] = order | 208 | self.orders[id] = order |
255 | return self.extend({'info': response}, order) | 209 | return self.extend({'info': response}, order) |
256 | 210 | ||
257 | def create_exchange_order(self, symbol, type, side, amount, price=None, params={}): | ||
258 | return super(poloniexE, self).create_order(symbol, type, side, amount, price=price, params=params) | ||
259 | |||
260 | def create_order(self, symbol, type, side, amount, price=None, account="exchange", lending_rate=None, params={}): | ||
261 | if account == "exchange": | ||
262 | return self.create_exchange_order(symbol, type, side, amount, price=price, params=params) | ||
263 | elif account == "margin": | ||
264 | return self.create_margin_order(symbol, type, side, amount, price=price, lending_rate=lending_rate, params=params) | ||
265 | else: | ||
266 | raise NotImplementedError | ||
267 | |||
268 | def order_precision(self, symbol): | 211 | def order_precision(self, symbol): |
269 | return 8 | 212 | return 8 |
270 | 213 | ||
@@ -324,3 +267,73 @@ class poloniexE(poloniex): | |||
324 | "total": decimal.Decimal(summary["totalValue"]), | 267 | "total": decimal.Decimal(summary["totalValue"]), |
325 | } | 268 | } |
326 | 269 | ||
270 | def nonce(self): | ||
271 | """ | ||
272 | Wrapped to allow nonce with other libraries | ||
273 | """ | ||
274 | return self.nanoseconds() | ||
275 | |||
276 | def fetch_balance(self, params={}): | ||
277 | """ | ||
278 | Wrapped to get decimals | ||
279 | """ | ||
280 | self.load_markets() | ||
281 | balances = self.privatePostReturnCompleteBalances(self.extend({ | ||
282 | 'account': 'all', | ||
283 | }, params)) | ||
284 | result = {'info': balances} | ||
285 | currencies = list(balances.keys()) | ||
286 | for c in range(0, len(currencies)): | ||
287 | id = currencies[c] | ||
288 | balance = balances[id] | ||
289 | currency = self.common_currency_code(id) | ||
290 | account = { | ||
291 | 'free': decimal.Decimal(balance['available']), | ||
292 | 'used': decimal.Decimal(balance['onOrders']), | ||
293 | 'total': decimal.Decimal(0.0), | ||
294 | } | ||
295 | account['total'] = self.sum(account['free'], account['used']) | ||
296 | result[currency] = account | ||
297 | return self.parse_balance(result) | ||
298 | |||
299 | def parse_ticker(self, ticker, market=None): | ||
300 | """ | ||
301 | Wrapped to get decimals | ||
302 | """ | ||
303 | timestamp = self.milliseconds() | ||
304 | symbol = None | ||
305 | if market: | ||
306 | symbol = market['symbol'] | ||
307 | return { | ||
308 | 'symbol': symbol, | ||
309 | 'timestamp': timestamp, | ||
310 | 'datetime': self.iso8601(timestamp), | ||
311 | 'high': decimal.Decimal(ticker['high24hr']), | ||
312 | 'low': decimal.Decimal(ticker['low24hr']), | ||
313 | 'bid': decimal.Decimal(ticker['highestBid']), | ||
314 | 'ask': decimal.Decimal(ticker['lowestAsk']), | ||
315 | 'vwap': None, | ||
316 | 'open': None, | ||
317 | 'close': None, | ||
318 | 'first': None, | ||
319 | 'last': decimal.Decimal(ticker['last']), | ||
320 | 'change': decimal.Decimal(ticker['percentChange']), | ||
321 | 'percentage': None, | ||
322 | 'average': None, | ||
323 | 'baseVolume': decimal.Decimal(ticker['quoteVolume']), | ||
324 | 'quoteVolume': decimal.Decimal(ticker['baseVolume']), | ||
325 | 'info': ticker, | ||
326 | } | ||
327 | |||
328 | def create_order(self, symbol, type, side, amount, price=None, account="exchange", lending_rate=None, params={}): | ||
329 | """ | ||
330 | Wrapped to handle margin and exchange accounts | ||
331 | """ | ||
332 | if account == "exchange": | ||
333 | return self.create_exchange_order(symbol, type, side, amount, price=price, params=params) | ||
334 | elif account == "margin": | ||
335 | return self.create_margin_order(symbol, type, side, amount, price=price, lending_rate=lending_rate, params=params) | ||
336 | else: | ||
337 | raise NotImplementedError | ||
338 | |||
339 | |||
@@ -46,6 +46,87 @@ class WebMockTestCase(unittest.TestCase): | |||
46 | super(WebMockTestCase, self).tearDown() | 46 | super(WebMockTestCase, self).tearDown() |
47 | 47 | ||
48 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 48 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
49 | class poloniexETest(unittest.TestCase): | ||
50 | def setUp(self): | ||
51 | super(poloniexETest, self).setUp() | ||
52 | self.wm = requests_mock.Mocker() | ||
53 | self.wm.start() | ||
54 | |||
55 | self.s = market.ccxt.poloniexE() | ||
56 | |||
57 | def tearDown(self): | ||
58 | self.wm.stop() | ||
59 | super(poloniexETest, self).tearDown() | ||
60 | |||
61 | def test_nanoseconds(self): | ||
62 | with mock.patch.object(market.ccxt.time, "time") as time: | ||
63 | time.return_value = 123456.7890123456 | ||
64 | self.assertEqual(123456789012345, self.s.nanoseconds()) | ||
65 | |||
66 | def test_nonce(self): | ||
67 | with mock.patch.object(market.ccxt.time, "time") as time: | ||
68 | time.return_value = 123456.7890123456 | ||
69 | self.assertEqual(123456789012345, self.s.nonce()) | ||
70 | |||
71 | def test_order_precision(self): | ||
72 | self.assertEqual(8, self.s.order_precision("FOO")) | ||
73 | |||
74 | def test_transfer_balance(self): | ||
75 | with self.subTest(success=True),\ | ||
76 | mock.patch.object(self.s, "privatePostTransferBalance") as t: | ||
77 | t.return_value = { "success": 1 } | ||
78 | result = self.s.transfer_balance("FOO", 12, "exchange", "margin") | ||
79 | t.assert_called_once_with({ | ||
80 | "currency": "FOO", | ||
81 | "amount": 12, | ||
82 | "fromAccount": "exchange", | ||
83 | "toAccount": "margin", | ||
84 | "confirmed": 1 | ||
85 | }) | ||
86 | self.assertTrue(result) | ||
87 | |||
88 | with self.subTest(success=False),\ | ||
89 | mock.patch.object(self.s, "privatePostTransferBalance") as t: | ||
90 | t.return_value = { "success": 0 } | ||
91 | self.assertFalse(self.s.transfer_balance("FOO", 12, "exchange", "margin")) | ||
92 | |||
93 | def test_close_margin_position(self): | ||
94 | with mock.patch.object(self.s, "privatePostCloseMarginPosition") as c: | ||
95 | self.s.close_margin_position("FOO", "BAR") | ||
96 | c.assert_called_with({"currencyPair": "BAR_FOO"}) | ||
97 | |||
98 | def test_tradable_balances(self): | ||
99 | with mock.patch.object(self.s, "privatePostReturnTradableBalances") as r: | ||
100 | r.return_value = { | ||
101 | "FOO": { "exchange": "12.1234", "margin": "0.0123" }, | ||
102 | "BAR": { "exchange": "1", "margin": "0" }, | ||
103 | } | ||
104 | balances = self.s.tradable_balances() | ||
105 | self.assertEqual(["FOO", "BAR"], list(balances.keys())) | ||
106 | self.assertEqual(["exchange", "margin"], list(balances["FOO"].keys())) | ||
107 | self.assertEqual(D("12.1234"), balances["FOO"]["exchange"]) | ||
108 | self.assertEqual(["exchange", "margin"], list(balances["BAR"].keys())) | ||
109 | |||
110 | def test_margin_summary(self): | ||
111 | with mock.patch.object(self.s, "privatePostReturnMarginAccountSummary") as r: | ||
112 | r.return_value = { | ||
113 | "currentMargin": "1.49680968", | ||
114 | "lendingFees": "0.0000001", | ||
115 | "pl": "0.00008254", | ||
116 | "totalBorrowedValue": "0.00673602", | ||
117 | "totalValue": "0.01000000", | ||
118 | "netValue": "0.01008254", | ||
119 | } | ||
120 | expected = { | ||
121 | 'current_margin': D('1.49680968'), | ||
122 | 'gains': D('0.00008254'), | ||
123 | 'lending_fees': D('0.0000001'), | ||
124 | 'total': D('0.01000000'), | ||
125 | 'total_borrowed': D('0.00673602') | ||
126 | } | ||
127 | self.assertEqual(expected, self.s.margin_summary()) | ||
128 | |||
129 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
49 | class PortfolioTest(WebMockTestCase): | 130 | class PortfolioTest(WebMockTestCase): |
50 | def fill_data(self): | 131 | def fill_data(self): |
51 | if self.json_response is not None: | 132 | if self.json_response is not None: |