1 from .helper
import limits
, unittest
, mock
, D
5 @unittest.skipUnless("unit" in limits
, "Unit skipped")
6 class poloniexETest(unittest
.TestCase
):
9 self
.wm
= requests_mock
.Mocker()
12 self
.s
= market
.ccxt
.poloniexE()
19 with self
.subTest("Nominal case"), \
20 mock
.patch("market.ccxt.poloniexE.session") as session
:
21 session
.request
.return_value
= "response"
22 ccxt
= market
.ccxt
.poloniexE()
23 ccxt
._market
= mock
.Mock
24 ccxt
._market
.report
= mock
.Mock()
25 ccxt
._market
.market_id
= 3
26 ccxt
._market
.user_id
= 3
28 ccxt
.session
.request("GET", "URL", data
="data",
30 ccxt
._market
.report
.log_http_request
.assert_called_with('GET', 'URL', 'data',
31 {'X-market-id': '3', 'X-user-id': '3'}
, 'response')
33 with self
.subTest("Raising"),\
34 mock
.patch("market.ccxt.poloniexE.session") as session
:
35 session
.request
.side_effect
= market
.ccxt
.RequestException("Boo")
37 ccxt
= market
.ccxt
.poloniexE()
38 ccxt
._market
= mock
.Mock
39 ccxt
._market
.report
= mock
.Mock()
40 ccxt
._market
.market_id
= 3
41 ccxt
._market
.user_id
= 3
43 with self
.assertRaises(market
.ccxt
.RequestException
, msg
="Boo") as cm
:
44 ccxt
.session
.request("GET", "URL", data
="data",
46 ccxt
._market
.report
.log_http_request
.assert_called_with('GET', 'URL', 'data',
47 {'X-market-id': '3', 'X-user-id': '3'}
, cm
.exception
)
50 def test_nanoseconds(self
):
51 with mock
.patch
.object(market
.ccxt
.time
, "time") as time
:
52 time
.return_value
= 123456.7890123456
53 self
.assertEqual(123456789012345, self
.s
.nanoseconds())
56 with mock
.patch
.object(market
.ccxt
.time
, "time") as time
:
57 time
.return_value
= 123456.7890123456
58 self
.assertEqual(123456789012345, self
.s
.nonce())
60 def test_request(self
):
61 with mock
.patch
.object(market
.ccxt
.poloniex
, "request") as request
,\
62 mock
.patch("market.ccxt.retry_call") as retry_call
:
63 with self
.subTest(wrapped
=True):
64 with self
.subTest(desc
="public"):
66 retry_call
.assert_called_with(request
,
67 delay
=1, tries
=10, fargs
=["foo"],
68 fkwargs
={'api': 'public', 'method': 'GET', 'params': {}
, 'headers': None, 'body': None},
69 exceptions
=(market
.ccxt
.RequestTimeout
, market
.ccxt
.InvalidNonce
))
70 request
.assert_not_called()
72 with self
.subTest(desc
="private GET"):
73 self
.s
.request("foo", api
="private")
74 retry_call
.assert_called_with(request
,
75 delay
=1, tries
=10, fargs
=["foo"],
76 fkwargs
={'api': 'private', 'method': 'GET', 'params': {}
, 'headers': None, 'body': None},
77 exceptions
=(market
.ccxt
.RequestTimeout
, market
.ccxt
.InvalidNonce
))
78 request
.assert_not_called()
80 with self
.subTest(desc
="private POST regexp"):
81 self
.s
.request("returnFoo", api
="private", method
="POST")
82 retry_call
.assert_called_with(request
,
83 delay
=1, tries
=10, fargs
=["returnFoo"],
84 fkwargs
={'api': 'private', 'method': 'POST', 'params': {}
, 'headers': None, 'body': None},
85 exceptions
=(market
.ccxt
.RequestTimeout
, market
.ccxt
.InvalidNonce
))
86 request
.assert_not_called()
88 with self
.subTest(desc
="private POST non-regexp"):
89 self
.s
.request("getMarginPosition", api
="private", method
="POST")
90 retry_call
.assert_called_with(request
,
91 delay
=1, tries
=10, fargs
=["getMarginPosition"],
92 fkwargs
={'api': 'private', 'method': 'POST', 'params': {}
, 'headers': None, 'body': None},
93 exceptions
=(market
.ccxt
.RequestTimeout
, market
.ccxt
.InvalidNonce
))
94 request
.assert_not_called()
95 retry_call
.reset_mock()
97 with self
.subTest(wrapped
=False):
98 with self
.subTest(desc
="private POST non-matching regexp"):
99 self
.s
.request("marginBuy", api
="private", method
="POST")
100 request
.assert_called_with("marginBuy",
101 api
="private", method
="POST", params
={},
102 headers
=None, body
=None)
103 retry_call
.assert_not_called()
105 with self
.subTest(desc
="private POST non-matching non-regexp"):
106 self
.s
.request("closeMarginPositionOther", api
="private", method
="POST")
107 request
.assert_called_with("closeMarginPositionOther",
108 api
="private", method
="POST", params
={},
109 headers
=None, body
=None)
110 retry_call
.assert_not_called()
112 def test_order_precision(self
):
127 with mock
.patch
.object(self
.s
, "load_markets") as load_markets
:
128 self
.assertEqual(5, self
.s
.order_precision("FOO"))
129 load_markets
.assert_called_once()
131 def test_transfer_balance(self
):
132 with self
.subTest(success
=True),\
133 mock
.patch
.object(self
.s
, "privatePostTransferBalance") as t
:
134 t
.return_value
= { "success": 1 }
135 result
= self
.s
.transfer_balance("FOO", 12, "exchange", "margin")
136 t
.assert_called_once_with({
139 "fromAccount": "exchange",
140 "toAccount": "margin",
143 self
.assertTrue(result
)
145 with self
.subTest(success
=False),\
146 mock
.patch
.object(self
.s
, "privatePostTransferBalance") as t
:
147 t
.return_value
= { "success": 0 }
148 self
.assertFalse(self
.s
.transfer_balance("FOO", 12, "exchange", "margin"))
150 def test_close_margin_position(self
):
151 with mock
.patch
.object(self
.s
, "privatePostCloseMarginPosition") as c
:
152 self
.s
.close_margin_position("FOO", "BAR")
153 c
.assert_called_with({"currencyPair": "BAR_FOO"}
)
155 def test_tradable_balances(self
):
156 with mock
.patch
.object(self
.s
, "privatePostReturnTradableBalances") as r
:
158 "FOO": { "exchange": "12.1234", "margin": "0.0123" }
,
159 "BAR": { "exchange": "1", "margin": "0" }
,
161 balances
= self
.s
.tradable_balances()
162 self
.assertEqual(["FOO", "BAR"], list(balances
.keys()))
163 self
.assertEqual(["exchange", "margin"], list(balances
["FOO"].keys()))
164 self
.assertEqual(D("12.1234"), balances
["FOO"]["exchange"])
165 self
.assertEqual(["exchange", "margin"], list(balances
["BAR"].keys()))
167 def test_margin_summary(self
):
168 with mock
.patch
.object(self
.s
, "privatePostReturnMarginAccountSummary") as r
:
170 "currentMargin": "1.49680968",
171 "lendingFees": "0.0000001",
173 "totalBorrowedValue": "0.00673602",
174 "totalValue": "0.01000000",
175 "netValue": "0.01008254",
178 'current_margin': D('1.49680968'),
179 'gains': D('0.00008254'),
180 'lending_fees': D('0.0000001'),
181 'total': D('0.01000000'),
182 'total_borrowed': D('0.00673602')
184 self
.assertEqual(expected
, self
.s
.margin_summary())
186 def test_parse_ticker(self
):
190 "highestBid": "10.5",
193 "percentChange": "0.1",
200 with mock
.patch
.object(self
.s
, "milliseconds") as ms
:
201 ms
.return_value
= 1520292715123
202 result
= self
.s
.parse_ticker(ticker
, market
)
206 "timestamp": 1520292715123,
207 "datetime": "2018-03-05T23:31:55.123Z",
220 "baseVolume": D("10"),
221 "quoteVolume": D("20"),
224 self
.assertEqual(expected
, result
)
226 def test_fetch_margin_balance(self
):
227 with mock
.patch
.object(self
.s
, "privatePostGetMarginPosition") as get_margin_position
:
228 get_margin_position
.return_value
= {
231 "basePrice": "0.06818560",
232 "lendingFees": "0.00000001",
233 "liquidationPrice": "0.15107132",
235 "total": "0.00681856",
241 "lendingFees": "0.00000001",
242 "liquidationPrice": "0.6",
251 "liquidationPrice": "-1",
257 balances
= self
.s
.fetch_margin_balance()
258 self
.assertEqual(2, len(balances
))
262 "borrowedPrice": D("0.06818560"),
263 "lendingFees": D("1E-8"),
264 "pl": D("-0.00000371"),
265 "liquidationPrice": D("0.15107132"),
267 "total": D("0.00681856"),
268 "baseCurrency": "BTC"
272 "borrowedPrice": D("0.1"),
273 "lendingFees": D("1E-8"),
274 "pl": D("0.00000371"),
275 "liquidationPrice": D("0.6"),
278 "baseCurrency": "BTC"
281 self
.assertEqual(expected
, balances
)
284 self
.assertEqual(D("1.1"), self
.s
.sum(D("1"), D("0.1")))
286 def test_fetch_balance(self
):
287 with mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
288 mock
.patch
.object(self
.s
, "privatePostReturnCompleteBalances") as balances
,\
289 mock
.patch
.object(self
.s
, "common_currency_code") as ccc
:
290 ccc
.side_effect
= ["ETH", "BTC", "DASH"]
291 balances
.return_value
= {
308 "ETH": {"available": "10", "onOrders": "1"}
,
309 "BTC": {"available": "1", "onOrders": "0"}
,
310 "DASH": {"available": "0", "onOrders": "3"}
312 "ETH": {"free": D("10"), "used": D("1"), "total": D("11")}
,
313 "BTC": {"free": D("1"), "used": D("0"), "total": D("1")}
,
314 "DASH": {"free": D("0"), "used": D("3"), "total": D("3")}
,
315 "free": {"ETH": D("10"), "BTC": D("1"), "DASH": D("0")}
,
316 "used": {"ETH": D("1"), "BTC": D("0"), "DASH": D("3")}
,
317 "total": {"ETH": D("11"), "BTC": D("1"), "DASH": D("3")}
319 result
= self
.s
.fetch_balance()
320 load_markets
.assert_called_once()
321 self
.assertEqual(expected
, result
)
323 def test_fetch_balance_per_type(self
):
324 with mock
.patch
.object(self
.s
, "privatePostReturnAvailableAccountBalances") as balances
:
325 balances
.return_value
= {
327 "BLK": "159.83673869",
329 "USDT": "0.00002625",
339 "BLK": "159.83673869",
341 "USDT": "0.00002625",
349 "BLK": D("159.83673869"),
350 "BTC": D("0.00005959"),
351 "USDT": D("0.00002625"),
352 "XMR": D("0.18719303")
354 "margin": {"BTC": D("0.03019227")}
,
355 "BLK": {"exchange": D("159.83673869")}
,
356 "BTC": {"exchange": D("0.00005959"), "margin": D("0.03019227")}
,
357 "USDT": {"exchange": D("0.00002625")}
,
358 "XMR": {"exchange": D("0.18719303")}
360 result
= self
.s
.fetch_balance_per_type()
361 self
.assertEqual(expected
, result
)
363 def test_fetch_all_balances(self
):
365 with mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
366 mock
.patch
.object(self
.s
, "privatePostGetMarginPosition") as margin_balance
,\
367 mock
.patch
.object(self
.s
, "privatePostReturnCompleteBalances") as balance
,\
368 mock
.patch
.object(self
.s
, "privatePostReturnAvailableAccountBalances") as balance_per_type
:
370 with open("test_samples/poloniexETest.test_fetch_all_balances.1.json") as f
:
371 balance
.return_value
= json
.load(f
)
372 with open("test_samples/poloniexETest.test_fetch_all_balances.2.json") as f
:
373 margin_balance
.return_value
= json
.load(f
)
374 with open("test_samples/poloniexETest.test_fetch_all_balances.3.json") as f
:
375 balance_per_type
.return_value
= json
.load(f
)
377 result
= self
.s
.fetch_all_balances()
379 "total": D("-12779.79821852"),
380 "exchange_used": D("0E-8"),
381 "exchange_total": D("0E-8"),
382 "exchange_free": D("0E-8"),
383 "margin_available": 0,
384 "margin_in_position": 0,
385 "margin_borrowed": D("12779.79821852"),
386 "margin_total": D("-12779.79821852"),
387 "margin_pending_gain": 0,
388 "margin_lending_fees": D("-9E-8"),
389 "margin_pending_base_gain": D("0.00024059"),
390 "margin_position_type": "short",
391 "margin_liquidation_price": D("0.00000246"),
392 "margin_borrowed_base_price": D("0.00599149"),
393 "margin_borrowed_base_currency": "BTC"
395 expected_btc
= {"total": D("0.05432165"),
396 "exchange_used": D("0E-8"),
397 "exchange_total": D("0.00005959"),
398 "exchange_free": D("0.00005959"),
399 "margin_available": D("0.03019227"),
400 "margin_in_position": D("0.02406979"),
401 "margin_borrowed": 0,
402 "margin_total": D("0.05426206"),
403 "margin_pending_gain": D("0.00093955"),
404 "margin_lending_fees": 0,
405 "margin_pending_base_gain": 0,
406 "margin_position_type": None,
407 "margin_liquidation_price": 0,
408 "margin_borrowed_base_price": 0,
409 "margin_borrowed_base_currency": None
411 expected_xmr
= {"total": D("0.18719303"),
412 "exchange_used": D("0E-8"),
413 "exchange_total": D("0.18719303"),
414 "exchange_free": D("0.18719303"),
415 "margin_available": 0,
416 "margin_in_position": 0,
417 "margin_borrowed": 0,
419 "margin_pending_gain": 0,
420 "margin_lending_fees": 0,
421 "margin_pending_base_gain": 0,
422 "margin_position_type": None,
423 "margin_liquidation_price": 0,
424 "margin_borrowed_base_price": 0,
425 "margin_borrowed_base_currency": None
427 self
.assertEqual(expected_xmr
, result
["XMR"])
428 self
.assertEqual(expected_doge
, result
["DOGE"])
429 self
.assertEqual(expected_btc
, result
["BTC"])
431 def test_create_order(self
):
432 with self
.subTest(type="market"),\
433 self
.assertRaises(market
.ExchangeError
):
434 self
.s
.create_order("FOO", "market", "buy", "10")
436 with self
.subTest(type="limit", account
="margin"),\
437 mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
438 mock
.patch
.object(self
.s
, "privatePostMarginBuy") as margin_buy
,\
439 mock
.patch
.object(self
.s
, "privatePostMarginSell") as margin_sell
,\
440 mock
.patch
.object(self
.s
, "market") as market_mock
,\
441 mock
.patch
.object(self
.s
, "price_to_precision") as ptp
,\
442 mock
.patch
.object(self
.s
, "amount_to_precision") as atp
:
444 margin_buy
.return_value
= {
447 margin_sell
.return_value
= {
450 market_mock
.return_value
= { "id": "BTC_ETC", "symbol": "BTC_ETC" }
451 ptp
.return_value
= D("0.1")
452 atp
.return_value
= D("12")
454 order
= self
.s
.create_order("BTC_ETC", "limit", "buy", "12",
455 account
="margin", price
="0.1")
456 self
.assertEqual(123, order
["id"])
457 margin_buy
.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12")}
)
458 margin_sell
.assert_not_called()
459 margin_buy
.reset_mock()
460 margin_sell
.reset_mock()
462 order
= self
.s
.create_order("BTC_ETC", "limit", "sell",
463 "12", account
="margin", lending_rate
="0.01", price
="0.1")
464 self
.assertEqual(456, order
["id"])
465 margin_sell
.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12"), "lendingRate": "0.01"}
)
466 margin_buy
.assert_not_called()
468 with self
.subTest(type="limit", account
="exchange"),\
469 mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
470 mock
.patch
.object(self
.s
, "privatePostBuy") as exchange_buy
,\
471 mock
.patch
.object(self
.s
, "privatePostSell") as exchange_sell
,\
472 mock
.patch
.object(self
.s
, "market") as market_mock
,\
473 mock
.patch
.object(self
.s
, "price_to_precision") as ptp
,\
474 mock
.patch
.object(self
.s
, "amount_to_precision") as atp
:
476 exchange_buy
.return_value
= {
479 exchange_sell
.return_value
= {
482 market_mock
.return_value
= { "id": "BTC_ETC", "symbol": "BTC_ETC" }
483 ptp
.return_value
= D("0.1")
484 atp
.return_value
= D("12")
486 order
= self
.s
.create_order("BTC_ETC", "limit", "buy", "12",
487 account
="exchange", price
="0.1")
488 self
.assertEqual(123, order
["id"])
489 exchange_buy
.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12")}
)
490 exchange_sell
.assert_not_called()
491 exchange_buy
.reset_mock()
492 exchange_sell
.reset_mock()
494 order
= self
.s
.create_order("BTC_ETC", "limit", "sell",
495 "12", account
="exchange", lending_rate
="0.01", price
="0.1")
496 self
.assertEqual(456, order
["id"])
497 exchange_sell
.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12")}
)
498 exchange_buy
.assert_not_called()
500 with self
.subTest(account
="unknown"), self
.assertRaises(NotImplementedError),\
501 mock
.patch
.object(self
.s
, "load_markets") as load_markets
:
502 self
.s
.create_order("symbol", "type", "side", "amount", account
="unknown")
504 def test_common_currency_code(self
):
505 self
.assertEqual("FOO", self
.s
.common_currency_code("FOO"))
507 def test_currency_id(self
):
508 self
.assertEqual("FOO", self
.s
.currency_id("FOO"))
510 def test_amount_to_precision(self
):
525 self
.assertEqual("0.0001", self
.s
.amount_to_precision("FOO", D("0.0001")))
526 self
.assertEqual("0.0000001", self
.s
.amount_to_precision("BAR", D("0.0000001")))
527 self
.assertEqual("0.000001", self
.s
.amount_to_precision("FOO", D("0.000001")))
529 def test_price_to_precision(self
):
544 self
.assertEqual("0.0001", self
.s
.price_to_precision("FOO", D("0.0001")))
545 self
.assertEqual("0.0000001", self
.s
.price_to_precision("BAR", D("0.0000001")))
546 self
.assertEqual("0", self
.s
.price_to_precision("FOO", D("0.000001")))
548 def test_is_dust_trade(self
):
549 self
.assertTrue(self
.s
.is_dust_trade(D("0.0000009"), D("1000")))
550 self
.assertTrue(self
.s
.is_dust_trade(D("0.000001"), D("10")))
551 self
.assertFalse(self
.s
.is_dust_trade(D("0.000001"), D("100")))