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
):
113 self
.assertEqual(8, self
.s
.order_precision("FOO"))
115 def test_transfer_balance(self
):
116 with self
.subTest(success
=True),\
117 mock
.patch
.object(self
.s
, "privatePostTransferBalance") as t
:
118 t
.return_value
= { "success": 1 }
119 result
= self
.s
.transfer_balance("FOO", 12, "exchange", "margin")
120 t
.assert_called_once_with({
123 "fromAccount": "exchange",
124 "toAccount": "margin",
127 self
.assertTrue(result
)
129 with self
.subTest(success
=False),\
130 mock
.patch
.object(self
.s
, "privatePostTransferBalance") as t
:
131 t
.return_value
= { "success": 0 }
132 self
.assertFalse(self
.s
.transfer_balance("FOO", 12, "exchange", "margin"))
134 def test_close_margin_position(self
):
135 with mock
.patch
.object(self
.s
, "privatePostCloseMarginPosition") as c
:
136 self
.s
.close_margin_position("FOO", "BAR")
137 c
.assert_called_with({"currencyPair": "BAR_FOO"}
)
139 def test_tradable_balances(self
):
140 with mock
.patch
.object(self
.s
, "privatePostReturnTradableBalances") as r
:
142 "FOO": { "exchange": "12.1234", "margin": "0.0123" }
,
143 "BAR": { "exchange": "1", "margin": "0" }
,
145 balances
= self
.s
.tradable_balances()
146 self
.assertEqual(["FOO", "BAR"], list(balances
.keys()))
147 self
.assertEqual(["exchange", "margin"], list(balances
["FOO"].keys()))
148 self
.assertEqual(D("12.1234"), balances
["FOO"]["exchange"])
149 self
.assertEqual(["exchange", "margin"], list(balances
["BAR"].keys()))
151 def test_margin_summary(self
):
152 with mock
.patch
.object(self
.s
, "privatePostReturnMarginAccountSummary") as r
:
154 "currentMargin": "1.49680968",
155 "lendingFees": "0.0000001",
157 "totalBorrowedValue": "0.00673602",
158 "totalValue": "0.01000000",
159 "netValue": "0.01008254",
162 'current_margin': D('1.49680968'),
163 'gains': D('0.00008254'),
164 'lending_fees': D('0.0000001'),
165 'total': D('0.01000000'),
166 'total_borrowed': D('0.00673602')
168 self
.assertEqual(expected
, self
.s
.margin_summary())
170 def test_create_order(self
):
171 with mock
.patch
.object(self
.s
, "create_exchange_order") as exchange
,\
172 mock
.patch
.object(self
.s
, "create_margin_order") as margin
:
173 with self
.subTest(account
="unspecified"):
174 self
.s
.create_order("symbol", "type", "side", "amount", price
="price", lending_rate
="lending_rate", params
="params")
175 exchange
.assert_called_once_with("symbol", "type", "side", "amount", price
="price", params
="params")
176 margin
.assert_not_called()
177 exchange
.reset_mock()
180 with self
.subTest(account
="exchange"):
181 self
.s
.create_order("symbol", "type", "side", "amount", account
="exchange", price
="price", lending_rate
="lending_rate", params
="params")
182 exchange
.assert_called_once_with("symbol", "type", "side", "amount", price
="price", params
="params")
183 margin
.assert_not_called()
184 exchange
.reset_mock()
187 with self
.subTest(account
="margin"):
188 self
.s
.create_order("symbol", "type", "side", "amount", account
="margin", price
="price", lending_rate
="lending_rate", params
="params")
189 margin
.assert_called_once_with("symbol", "type", "side", "amount", lending_rate
="lending_rate", price
="price", params
="params")
190 exchange
.assert_not_called()
191 exchange
.reset_mock()
194 with self
.subTest(account
="unknown"), self
.assertRaises(NotImplementedError):
195 self
.s
.create_order("symbol", "type", "side", "amount", account
="unknown")
197 def test_parse_ticker(self
):
201 "highestBid": "10.5",
204 "percentChange": "0.1",
211 with mock
.patch
.object(self
.s
, "milliseconds") as ms
:
212 ms
.return_value
= 1520292715123
213 result
= self
.s
.parse_ticker(ticker
, market
)
217 "timestamp": 1520292715123,
218 "datetime": "2018-03-05T23:31:55.123Z",
231 "baseVolume": D("10"),
232 "quoteVolume": D("20"),
235 self
.assertEqual(expected
, result
)
237 def test_fetch_margin_balance(self
):
238 with mock
.patch
.object(self
.s
, "privatePostGetMarginPosition") as get_margin_position
:
239 get_margin_position
.return_value
= {
242 "basePrice": "0.06818560",
243 "lendingFees": "0.00000001",
244 "liquidationPrice": "0.15107132",
246 "total": "0.00681856",
252 "lendingFees": "0.00000001",
253 "liquidationPrice": "0.6",
262 "liquidationPrice": "-1",
268 balances
= self
.s
.fetch_margin_balance()
269 self
.assertEqual(2, len(balances
))
273 "borrowedPrice": D("0.06818560"),
274 "lendingFees": D("1E-8"),
275 "pl": D("-0.00000371"),
276 "liquidationPrice": D("0.15107132"),
278 "total": D("0.00681856"),
279 "baseCurrency": "BTC"
283 "borrowedPrice": D("0.1"),
284 "lendingFees": D("1E-8"),
285 "pl": D("0.00000371"),
286 "liquidationPrice": D("0.6"),
289 "baseCurrency": "BTC"
292 self
.assertEqual(expected
, balances
)
295 self
.assertEqual(D("1.1"), self
.s
.sum(D("1"), D("0.1")))
297 def test_fetch_balance(self
):
298 with mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
299 mock
.patch
.object(self
.s
, "privatePostReturnCompleteBalances") as balances
,\
300 mock
.patch
.object(self
.s
, "common_currency_code") as ccc
:
301 ccc
.side_effect
= ["ETH", "BTC", "DASH"]
302 balances
.return_value
= {
319 "ETH": {"available": "10", "onOrders": "1"}
,
320 "BTC": {"available": "1", "onOrders": "0"}
,
321 "DASH": {"available": "0", "onOrders": "3"}
323 "ETH": {"free": D("10"), "used": D("1"), "total": D("11")}
,
324 "BTC": {"free": D("1"), "used": D("0"), "total": D("1")}
,
325 "DASH": {"free": D("0"), "used": D("3"), "total": D("3")}
,
326 "free": {"ETH": D("10"), "BTC": D("1"), "DASH": D("0")}
,
327 "used": {"ETH": D("1"), "BTC": D("0"), "DASH": D("3")}
,
328 "total": {"ETH": D("11"), "BTC": D("1"), "DASH": D("3")}
330 result
= self
.s
.fetch_balance()
331 load_markets
.assert_called_once()
332 self
.assertEqual(expected
, result
)
334 def test_fetch_balance_per_type(self
):
335 with mock
.patch
.object(self
.s
, "privatePostReturnAvailableAccountBalances") as balances
:
336 balances
.return_value
= {
338 "BLK": "159.83673869",
340 "USDT": "0.00002625",
350 "BLK": "159.83673869",
352 "USDT": "0.00002625",
360 "BLK": D("159.83673869"),
361 "BTC": D("0.00005959"),
362 "USDT": D("0.00002625"),
363 "XMR": D("0.18719303")
365 "margin": {"BTC": D("0.03019227")}
,
366 "BLK": {"exchange": D("159.83673869")}
,
367 "BTC": {"exchange": D("0.00005959"), "margin": D("0.03019227")}
,
368 "USDT": {"exchange": D("0.00002625")}
,
369 "XMR": {"exchange": D("0.18719303")}
371 result
= self
.s
.fetch_balance_per_type()
372 self
.assertEqual(expected
, result
)
374 def test_fetch_all_balances(self
):
376 with mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
377 mock
.patch
.object(self
.s
, "privatePostGetMarginPosition") as margin_balance
,\
378 mock
.patch
.object(self
.s
, "privatePostReturnCompleteBalances") as balance
,\
379 mock
.patch
.object(self
.s
, "privatePostReturnAvailableAccountBalances") as balance_per_type
:
381 with open("test_samples/poloniexETest.test_fetch_all_balances.1.json") as f
:
382 balance
.return_value
= json
.load(f
)
383 with open("test_samples/poloniexETest.test_fetch_all_balances.2.json") as f
:
384 margin_balance
.return_value
= json
.load(f
)
385 with open("test_samples/poloniexETest.test_fetch_all_balances.3.json") as f
:
386 balance_per_type
.return_value
= json
.load(f
)
388 result
= self
.s
.fetch_all_balances()
390 "total": D("-12779.79821852"),
391 "exchange_used": D("0E-8"),
392 "exchange_total": D("0E-8"),
393 "exchange_free": D("0E-8"),
394 "margin_available": 0,
395 "margin_in_position": 0,
396 "margin_borrowed": D("12779.79821852"),
397 "margin_total": D("-12779.79821852"),
398 "margin_pending_gain": 0,
399 "margin_lending_fees": D("-9E-8"),
400 "margin_pending_base_gain": D("0.00024059"),
401 "margin_position_type": "short",
402 "margin_liquidation_price": D("0.00000246"),
403 "margin_borrowed_base_price": D("0.00599149"),
404 "margin_borrowed_base_currency": "BTC"
406 expected_btc
= {"total": D("0.05432165"),
407 "exchange_used": D("0E-8"),
408 "exchange_total": D("0.00005959"),
409 "exchange_free": D("0.00005959"),
410 "margin_available": D("0.03019227"),
411 "margin_in_position": D("0.02406979"),
412 "margin_borrowed": 0,
413 "margin_total": D("0.05426206"),
414 "margin_pending_gain": D("0.00093955"),
415 "margin_lending_fees": 0,
416 "margin_pending_base_gain": 0,
417 "margin_position_type": None,
418 "margin_liquidation_price": 0,
419 "margin_borrowed_base_price": 0,
420 "margin_borrowed_base_currency": None
422 expected_xmr
= {"total": D("0.18719303"),
423 "exchange_used": D("0E-8"),
424 "exchange_total": D("0.18719303"),
425 "exchange_free": D("0.18719303"),
426 "margin_available": 0,
427 "margin_in_position": 0,
428 "margin_borrowed": 0,
430 "margin_pending_gain": 0,
431 "margin_lending_fees": 0,
432 "margin_pending_base_gain": 0,
433 "margin_position_type": None,
434 "margin_liquidation_price": 0,
435 "margin_borrowed_base_price": 0,
436 "margin_borrowed_base_currency": None
438 self
.assertEqual(expected_xmr
, result
["XMR"])
439 self
.assertEqual(expected_doge
, result
["DOGE"])
440 self
.assertEqual(expected_btc
, result
["BTC"])
442 def test_create_margin_order(self
):
443 with self
.assertRaises(market
.ExchangeError
):
444 self
.s
.create_margin_order("FOO", "market", "buy", "10")
446 with mock
.patch
.object(self
.s
, "load_markets") as load_markets
,\
447 mock
.patch
.object(self
.s
, "privatePostMarginBuy") as margin_buy
,\
448 mock
.patch
.object(self
.s
, "privatePostMarginSell") as margin_sell
,\
449 mock
.patch
.object(self
.s
, "market") as market_mock
,\
450 mock
.patch
.object(self
.s
, "price_to_precision") as ptp
,\
451 mock
.patch
.object(self
.s
, "amount_to_precision") as atp
:
453 margin_buy
.return_value
= {
456 margin_sell
.return_value
= {
459 market_mock
.return_value
= { "id": "BTC_ETC", "symbol": "BTC_ETC" }
460 ptp
.return_value
= D("0.1")
461 atp
.return_value
= D("12")
463 order
= self
.s
.create_margin_order("BTC_ETC", "margin", "buy", "12", price
="0.1")
464 self
.assertEqual(123, order
["id"])
465 margin_buy
.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12")}
)
466 margin_sell
.assert_not_called()
467 margin_buy
.reset_mock()
468 margin_sell
.reset_mock()
470 order
= self
.s
.create_margin_order("BTC_ETC", "margin", "sell", "12", lending_rate
="0.01", price
="0.1")
471 self
.assertEqual(456, order
["id"])
472 margin_sell
.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12"), "lendingRate": "0.01"}
)
473 margin_buy
.assert_not_called()
475 def test_create_exchange_order(self
):
476 with mock
.patch
.object(market
.ccxt
.poloniex
, "create_order") as create_order
:
477 self
.s
.create_order("symbol", "type", "side", "amount", price
="price", params
="params")
479 create_order
.assert_called_once_with("symbol", "type", "side", "amount", price
="price", params
="params")
481 def test_common_currency_code(self
):
482 self
.assertEqual("FOO", self
.s
.common_currency_code("FOO"))
484 def test_currency_id(self
):
485 self
.assertEqual("FOO", self
.s
.currency_id("FOO"))