ccxt 4.4.33__py2.py3-none-any.whl → 4.4.34__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. ccxt/__init__.py +3 -1
  2. ccxt/abstract/bingx.py +16 -0
  3. ccxt/abstract/bitbank.py +5 -0
  4. ccxt/abstract/bitfinex2.py +1 -0
  5. ccxt/abstract/ellipx.py +25 -0
  6. ccxt/alpaca.py +2 -0
  7. ccxt/async_support/__init__.py +3 -1
  8. ccxt/async_support/alpaca.py +2 -0
  9. ccxt/async_support/base/exchange.py +1 -1
  10. ccxt/async_support/binance.py +19 -15
  11. ccxt/async_support/bingx.py +155 -8
  12. ccxt/async_support/bitbank.py +5 -0
  13. ccxt/async_support/bitbns.py +2 -0
  14. ccxt/async_support/bitfinex2.py +1 -0
  15. ccxt/async_support/bitget.py +174 -40
  16. ccxt/async_support/bitmex.py +2 -0
  17. ccxt/async_support/bitopro.py +3 -0
  18. ccxt/async_support/bitrue.py +1 -0
  19. ccxt/async_support/btcmarkets.py +2 -0
  20. ccxt/async_support/bybit.py +13 -10
  21. ccxt/async_support/cex.py +13 -4
  22. ccxt/async_support/coinbase.py +3 -2
  23. ccxt/async_support/coinex.py +1 -0
  24. ccxt/async_support/coinone.py +7 -7
  25. ccxt/async_support/coinsph.py +7 -7
  26. ccxt/async_support/coinspot.py +39 -39
  27. ccxt/async_support/cryptocom.py +36 -34
  28. ccxt/async_support/ellipx.py +1828 -0
  29. ccxt/async_support/gate.py +1 -0
  30. ccxt/async_support/hyperliquid.py +2 -0
  31. ccxt/async_support/krakenfutures.py +3 -1
  32. ccxt/async_support/okcoin.py +2 -0
  33. ccxt/async_support/okx.py +14 -10
  34. ccxt/async_support/onetrading.py +20 -1
  35. ccxt/async_support/paradex.py +2 -0
  36. ccxt/async_support/phemex.py +16 -0
  37. ccxt/async_support/poloniex.py +3 -1
  38. ccxt/async_support/poloniexfutures.py +3 -1
  39. ccxt/async_support/vertex.py +2 -0
  40. ccxt/async_support/woo.py +69 -69
  41. ccxt/base/exchange.py +27 -7
  42. ccxt/binance.py +19 -15
  43. ccxt/bingx.py +155 -8
  44. ccxt/bitbank.py +5 -0
  45. ccxt/bitbns.py +2 -0
  46. ccxt/bitfinex2.py +1 -0
  47. ccxt/bitget.py +174 -40
  48. ccxt/bitmex.py +2 -0
  49. ccxt/bitopro.py +3 -0
  50. ccxt/bitrue.py +1 -0
  51. ccxt/btcmarkets.py +2 -0
  52. ccxt/bybit.py +13 -10
  53. ccxt/cex.py +13 -4
  54. ccxt/coinbase.py +3 -2
  55. ccxt/coinex.py +1 -0
  56. ccxt/coinone.py +7 -7
  57. ccxt/coinsph.py +7 -7
  58. ccxt/coinspot.py +39 -39
  59. ccxt/cryptocom.py +36 -34
  60. ccxt/ellipx.py +1828 -0
  61. ccxt/gate.py +1 -0
  62. ccxt/hyperliquid.py +2 -0
  63. ccxt/krakenfutures.py +3 -1
  64. ccxt/okcoin.py +2 -0
  65. ccxt/okx.py +14 -10
  66. ccxt/onetrading.py +20 -1
  67. ccxt/paradex.py +2 -0
  68. ccxt/phemex.py +16 -0
  69. ccxt/poloniex.py +3 -1
  70. ccxt/poloniexfutures.py +3 -1
  71. ccxt/pro/__init__.py +1 -1
  72. ccxt/pro/idex.py +15 -0
  73. ccxt/pro/probit.py +4 -2
  74. ccxt/pro/woo.py +15 -15
  75. ccxt/test/tests_helpers.py +0 -2
  76. ccxt/vertex.py +2 -0
  77. ccxt/woo.py +69 -69
  78. {ccxt-4.4.33.dist-info → ccxt-4.4.34.dist-info}/METADATA +9 -8
  79. {ccxt-4.4.33.dist-info → ccxt-4.4.34.dist-info}/RECORD +82 -79
  80. {ccxt-4.4.33.dist-info → ccxt-4.4.34.dist-info}/LICENSE.txt +0 -0
  81. {ccxt-4.4.33.dist-info → ccxt-4.4.34.dist-info}/WHEEL +0 -0
  82. {ccxt-4.4.33.dist-info → ccxt-4.4.34.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1828 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ from ccxt.async_support.base.exchange import Exchange
7
+ from ccxt.abstract.ellipx import ImplicitAPI
8
+ import math
9
+ from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Ticker, Trade, TradingFeeInterface, Transaction
10
+ from typing import List
11
+ from ccxt.base.errors import ExchangeError
12
+ from ccxt.base.errors import AuthenticationError
13
+ from ccxt.base.errors import PermissionDenied
14
+ from ccxt.base.errors import ArgumentsRequired
15
+ from ccxt.base.errors import BadRequest
16
+ from ccxt.base.errors import NotSupported
17
+ from ccxt.base.errors import DDoSProtection
18
+ from ccxt.base.decimal_to_precision import TICK_SIZE
19
+ from ccxt.base.precise import Precise
20
+
21
+
22
+ class ellipx(Exchange, ImplicitAPI):
23
+
24
+ def describe(self):
25
+ return self.deep_extend(super(ellipx, self).describe(), {
26
+ 'id': 'ellipx',
27
+ 'name': 'Ellipx',
28
+ 'countries': ['PL'],
29
+ 'rateLimit': 200, # todo check
30
+ 'version': 'v1',
31
+ 'certified': False,
32
+ 'pro': False,
33
+ 'has': {
34
+ 'CORS': None,
35
+ 'spot': True,
36
+ 'margin': False,
37
+ 'swap': False,
38
+ 'future': False,
39
+ 'option': False,
40
+ 'addMargin': False,
41
+ 'cancelAllOrders': False,
42
+ 'cancelAllOrdersAfter': False,
43
+ 'cancelOrder': True,
44
+ 'cancelOrders': False,
45
+ 'cancelWithdraw': False,
46
+ 'closePosition': False,
47
+ 'createConvertTrade': False,
48
+ 'createDepositAddress': False,
49
+ 'createMarketBuyOrderWithCost': False,
50
+ 'createMarketOrder': False,
51
+ 'createMarketOrderWithCost': False,
52
+ 'createMarketSellOrderWithCost': False,
53
+ 'createOrder': True,
54
+ 'createOrderWithTakeProfitAndStopLoss': False,
55
+ 'createReduceOnlyOrder': False,
56
+ 'createStopLimitOrder': False,
57
+ 'createStopLossOrder': False,
58
+ 'createStopMarketOrder': False,
59
+ 'createStopOrder': False,
60
+ 'createTakeProfitOrder': False,
61
+ 'createTrailingAmountOrder': False,
62
+ 'createTrailingPercentOrder': False,
63
+ 'createTriggerOrder': False,
64
+ 'fetchAccounts': False,
65
+ 'fetchBalance': True,
66
+ 'fetchCanceledAndClosedOrders': False,
67
+ 'fetchCanceledOrders': False,
68
+ 'fetchClosedOrder': False,
69
+ 'fetchClosedOrders': False,
70
+ 'fetchConvertCurrencies': False,
71
+ 'fetchConvertQuote': False,
72
+ 'fetchConvertTrade': False,
73
+ 'fetchConvertTradeHistory': False,
74
+ 'fetchCurrencies': True,
75
+ 'fetchDepositAddress': True,
76
+ 'fetchDeposits': False,
77
+ 'fetchDepositsWithdrawals': False,
78
+ 'fetchFundingHistory': False,
79
+ 'fetchFundingRate': False,
80
+ 'fetchFundingRateHistory': False,
81
+ 'fetchFundingRates': False,
82
+ 'fetchIndexOHLCV': False,
83
+ 'fetchLedger': False,
84
+ 'fetchLeverage': False,
85
+ 'fetchLeverageTiers': False,
86
+ 'fetchMarginAdjustmentHistory': False,
87
+ 'fetchMarginMode': False,
88
+ 'fetchMarkets': True,
89
+ 'fetchMarkOHLCV': False,
90
+ 'fetchMyTrades': False,
91
+ 'fetchOHLCV': True,
92
+ 'fetchOpenInterestHistory': False,
93
+ 'fetchOpenOrder': False,
94
+ 'fetchOpenOrders': True,
95
+ 'fetchOrder': True,
96
+ 'fetchOrderBook': True,
97
+ 'fetchOrders': True,
98
+ 'fetchOrderTrades': True,
99
+ 'fetchPosition': False,
100
+ 'fetchPositionHistory': False,
101
+ 'fetchPositionMode': False,
102
+ 'fetchPositions': False,
103
+ 'fetchPositionsForSymbol': False,
104
+ 'fetchPositionsHistory': False,
105
+ 'fetchPremiumIndexOHLCV': False,
106
+ 'fetchStatus': False,
107
+ 'fetchTicker': True,
108
+ 'fetchTickers': False,
109
+ 'fetchTime': False,
110
+ 'fetchTrades': True,
111
+ 'fetchTradingFee': True,
112
+ 'fetchTradingFees': False,
113
+ 'fetchTransactions': False,
114
+ 'fetchTransfers': False,
115
+ 'fetchWithdrawals': False,
116
+ 'reduceMargin': False,
117
+ 'sandbox': False,
118
+ 'setLeverage': False,
119
+ 'setMargin': False,
120
+ 'setPositionMode': False,
121
+ 'transfer': False,
122
+ 'withdraw': True,
123
+ },
124
+ 'timeframes': {
125
+ '1m': '1m',
126
+ '5m': '5m',
127
+ '10m': '10m',
128
+ '1h': '1h',
129
+ '6h': '6h',
130
+ '12h': '12h',
131
+ '1d': '1d',
132
+ },
133
+ 'urls': {
134
+ 'logo': 'https://github.com/user-attachments/assets/e07c3f40-281c-4cdf-bacf-fa1c58218a2c',
135
+ 'api': {
136
+ 'public': 'https://data.ellipx.com',
137
+ 'private': 'https://app.ellipx.com/_rest',
138
+ '_rest': 'https://app.ellipx.com/_rest',
139
+ },
140
+ 'www': 'https://www.ellipx.com',
141
+ 'doc': 'https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM',
142
+ 'fees': 'https://www.ellipx.com/pages/pricing',
143
+ 'referral': '', # todo
144
+ },
145
+ 'api': {
146
+ '_rest': {
147
+ 'get': {
148
+ 'Market': 1,
149
+ 'Market/{currencyPair}': 1,
150
+ 'Crypto/Token/Info': 1,
151
+ },
152
+ },
153
+ 'public': {
154
+ 'get': {
155
+ 'Market/{currencyPair}:getDepth': 1,
156
+ 'Market/{currencyPair}:ticker': 1,
157
+ 'Market/{currencyPair}:getTrades': 1,
158
+ 'Market/{currencyPair}:getGraph': 1,
159
+ 'CMC:summary': 1,
160
+ 'CMC/{currencyPair}:ticker': 1,
161
+ },
162
+ },
163
+ 'private': {
164
+ 'get': {
165
+ 'User/Wallet': 1,
166
+ 'Market/{currencyPair}/Order': 1,
167
+ 'Market/Order/{orderUuid}': 1,
168
+ 'Market/{currencyPair}/Trade': 1,
169
+ 'Market/TradeFee:query': 1,
170
+ 'Unit/{currency}': 1,
171
+ 'Crypto/Token/{currency}': 1,
172
+ 'Crypto/Token/{currency}:chains': 1,
173
+ },
174
+ 'post': {
175
+ 'Market/{currencyPair}/Order': 1,
176
+ 'Crypto/Address:fetch': 1,
177
+ 'Crypto/Disbursement:withdraw': 1,
178
+ },
179
+ 'delete': {
180
+ 'Market/Order/{orderUuid}': 1,
181
+ },
182
+ },
183
+ },
184
+ 'fees': {
185
+ 'trading': {
186
+ 'tierBased': True,
187
+ 'feeSide': 'get',
188
+ 'percentage': True,
189
+ 'maker': self.parse_number('0.0025'), # default 25bps
190
+ 'taker': self.parse_number('0.0030'), # default 30bps
191
+ 'tiers': {
192
+ # volume in USDT
193
+ 'maker': [
194
+ [self.parse_number('0'), self.parse_number('0.0025')], # 0-10k: 25bps
195
+ [self.parse_number('10000'), self.parse_number('0.0020')], # 10k-50k: 20bps
196
+ [self.parse_number('50000'), self.parse_number('0.0015')], # 50k-100k: 15bps
197
+ [self.parse_number('100000'), self.parse_number('0.0010')], # 100k-1M: 10bps
198
+ [self.parse_number('1000000'), self.parse_number('0.0008')], # 1M-5M: 8bps
199
+ [self.parse_number('5000000'), self.parse_number('0.0003')], # 5M-15M: 3bps
200
+ [self.parse_number('15000000'), self.parse_number('0.0000')], # 15M-75M: 0bps
201
+ [self.parse_number('75000000'), self.parse_number('0.0000')], # 75M-100M: 0bps
202
+ [self.parse_number('100000000'), self.parse_number('0.0000')], # 100M+: 0bps
203
+ ],
204
+ 'taker': [
205
+ [self.parse_number('0'), self.parse_number('0.0030')], # 0-10k: 30bps
206
+ [self.parse_number('10000'), self.parse_number('0.0025')], # 10k-50k: 25bps
207
+ [self.parse_number('50000'), self.parse_number('0.0020')], # 50k-100k: 20bps
208
+ [self.parse_number('100000'), self.parse_number('0.0015')], # 100k-1M: 15bps
209
+ [self.parse_number('1000000'), self.parse_number('0.0012')], # 1M-5M: 12bps
210
+ [self.parse_number('5000000'), self.parse_number('0.0010')], # 5M-15M: 10bps
211
+ [self.parse_number('15000000'), self.parse_number('0.0008')], # 15M-75M: 8bps
212
+ [self.parse_number('75000000'), self.parse_number('0.0005')], # 75M-100M: 5bps
213
+ [self.parse_number('100000000'), self.parse_number('0.0003')], # 100M+: 3bps
214
+ ],
215
+ },
216
+ },
217
+ 'stablecoin': {
218
+ 'tierBased': False,
219
+ 'percentage': True,
220
+ 'maker': self.parse_number('0.0000'), # 0%
221
+ 'taker': self.parse_number('0.000015'), # 0.0015%
222
+ },
223
+ },
224
+ 'options': {
225
+ 'defaultType': 'spot',
226
+ 'recvWindow': 5 * 1000,
227
+ 'broker': 'CCXT',
228
+ 'networks': {
229
+ 'Bitcoin': 'Bitcoin',
230
+ 'Ethereum': 'ERC20',
231
+ },
232
+ 'defaultNetwork': 'defaultNetwork',
233
+ 'defaultNetworkCodeReplacements': {
234
+ 'BTC': 'Bitcoin',
235
+ 'ETH': 'Ethereum',
236
+ },
237
+ },
238
+ 'commonCurrencies': {},
239
+ 'exceptions': {
240
+ 'exact': {
241
+ # todo
242
+ '400': BadRequest,
243
+ '401': AuthenticationError,
244
+ '403': PermissionDenied,
245
+ '404': BadRequest,
246
+ '429': DDoSProtection,
247
+ '418': PermissionDenied,
248
+ '500': ExchangeError,
249
+ '504': ExchangeError,
250
+ },
251
+ 'broad': {},
252
+ },
253
+ 'precisionMode': TICK_SIZE,
254
+ })
255
+
256
+ def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
257
+ path = self.implode_params(path, params)
258
+ url = self.urls['api'][api] + '/' + path
259
+ if api == 'private':
260
+ self.check_required_credentials()
261
+ nonce = self.uuid()
262
+ timestamp = str(self.seconds())
263
+ if method == 'GET':
264
+ body = ''
265
+ else:
266
+ body = self.json(params)
267
+ params = self.extend({
268
+ '_key': self.apiKey,
269
+ '_time': timestamp,
270
+ '_nonce': nonce,
271
+ }, params)
272
+ query = self.urlencode(params)
273
+ bodyHash = self.hash(self.encode(body), 'sha256')
274
+ # Create sign string components
275
+ bodyHashBytes = self.base16_to_binary(bodyHash)
276
+ nulByte = self.number_to_be(0, 1)
277
+ components = [
278
+ self.encode(method),
279
+ nulByte,
280
+ self.encode(path),
281
+ nulByte,
282
+ self.encode(query),
283
+ nulByte,
284
+ bodyHashBytes,
285
+ ]
286
+ # Join with null byte separator using encode
287
+ signString = self.binary_concat_array(components)
288
+ sec = self.secret
289
+ remainder = self.calculate_mod(len(sec), 4)
290
+ paddingLength = 4 - remainder if remainder else 0
291
+ secretWithPadding = self.secret.replace('-', '+')
292
+ secretWithPadding = secretWithPadding.replace('_', '/')
293
+ secretWithPadding = secretWithPadding.ljust(len(self.secret) + paddingLength, '=')
294
+ secretBytes = self.base64_to_binary(secretWithPadding)
295
+ seed = self.array_slice(secretBytes, 0, 32) # Extract first 32 bytes
296
+ signature = self.eddsa(signString, seed, 'ed25519')
297
+ params['_sign'] = signature
298
+ if params:
299
+ url += '?' + self.urlencode(params)
300
+ if method == 'GET':
301
+ body = None
302
+ else:
303
+ headers = {
304
+ 'Content-Type': 'application/json',
305
+ }
306
+ return {'url': url, 'method': method, 'body': body, 'headers': headers}
307
+
308
+ def calculate_mod(self, a, b):
309
+ # trick to fix php transpiling error
310
+ return a % b
311
+
312
+ async def fetch_markets(self, params={}) -> List[Market]:
313
+ """
314
+ Fetches market information from the exchange.
315
+
316
+ https://docs.ccxt.com/en/latest/manual.html#markets
317
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.1a1t05wpgfof
318
+
319
+ :param dict [params]: - Extra parameters specific to the exchange API endpoint
320
+ :returns Promise<Market[]>: An array of market structures.
321
+ """
322
+ response = await self._restGetMarket(params)
323
+ # {
324
+ # Market__: "mkt-lrnp2e-eaor-eobj-ua73-75j6sjxe",
325
+ # Primary_Unit__: "unit-aebkye-u35b-e5zm-zt22-2qvwhsqa",
326
+ # Secondary_Unit__: "unit-jcevlk-soxf-fepb-yjwm-b32q5bom",
327
+ # Primary_Step: null,
328
+ # Secondary_Step: null,
329
+ # Status: "active",
330
+ # Default_Scale: "5",
331
+ # Priority: "100",
332
+ # Created: {
333
+ # unix: "1728113809",
334
+ # us: "0",
335
+ # iso: "2024-10-05 07:36:49.000000",
336
+ # tz: "UTC",
337
+ # full: "1728113809000000",
338
+ # unixms: "1728113809000",
339
+ # },
340
+ # Start: {
341
+ # unix: "1728295200",
342
+ # us: "0",
343
+ # iso: "2024-10-07 10:00:00.000000",
344
+ # tz: "UTC",
345
+ # full: "1728295200000000",
346
+ # unixms: "1728295200000",
347
+ # },
348
+ # Key: "BTC_USDC",
349
+ # Primary: {
350
+ # Unit__: "unit-aebkye-u35b-e5zm-zt22-2qvwhsqa",
351
+ # Currency__: "BTC",
352
+ # Crypto_Token__: "crtok-c5v3mh-grfn-hl5d-lmel-fvggbf4i",
353
+ # Key: "BTC",
354
+ # Symbol: "BTC",
355
+ # Symbol_Position: "after",
356
+ # Name: "Bitcoin",
357
+ # Decimals: "8",
358
+ # Display_Decimals: "8",
359
+ # Legacy_Decimals: null,
360
+ # Type: "crypto_token",
361
+ # Visible: "Y",
362
+ # Created: {
363
+ # unix: "1495247415",
364
+ # us: "0",
365
+ # iso: "2017-05-20 02:30:15.000000",
366
+ # tz: "UTC",
367
+ # full: "1495247415000000",
368
+ # unixms: "1495247415000",
369
+ # },
370
+ # },
371
+ # Secondary: {
372
+ # Unit__: "unit-jcevlk-soxf-fepb-yjwm-b32q5bom",
373
+ # Currency__: null,
374
+ # Crypto_Token__: "crtok-ptabkh-ra4r-anbd-cqra-bqfbtnba",
375
+ # Key: "USDC",
376
+ # Symbol: null,
377
+ # Symbol_Position: "before",
378
+ # Name: "Circle USD",
379
+ # Decimals: "6",
380
+ # Display_Decimals: "6",
381
+ # Legacy_Decimals: null,
382
+ # Type: "crypto_token",
383
+ # Visible: "Y",
384
+ # Created: {
385
+ # unix: "1694859829",
386
+ # us: "0",
387
+ # iso: "2023-09-16 10:23:49.000000",
388
+ # tz: "UTC",
389
+ # full: "1694859829000000",
390
+ # unixms: "1694859829000",
391
+ # },
392
+ # },
393
+ # }
394
+ markets = self.safe_value(response, 'data', [])
395
+ return self.parse_markets(markets)
396
+
397
+ def parse_market(self, market: dict) -> Market:
398
+ id = self.safe_string(market, 'Key')
399
+ base = self.safe_string(market['Primary'], 'Key')
400
+ quote = self.safe_string(market['Secondary'], 'Key')
401
+ baseId = self.safe_string(market['Primary'], 'Crypto_Token__')
402
+ quoteId = self.safe_string(market['Secondary'], 'Crypto_Token__')
403
+ status = self.safe_string(market, 'Status') == 'active'
404
+ created = self.safe_timestamp(market['Created'], 'unix')
405
+ amountPrecision = self.parse_number(self.parse_precision(self.safe_string(market['Primary'], 'Decimals')))
406
+ pricePrecision = self.parse_number(self.parse_precision(self.safe_string(market['Secondary'], 'Decimals')))
407
+ fees = self.fees # should use fetchTradingFees
408
+ return self.safe_market_structure({
409
+ 'id': id,
410
+ 'symbol': base + '/' + quote,
411
+ 'base': base,
412
+ 'quote': quote,
413
+ 'settle': None,
414
+ 'baseId': baseId,
415
+ 'quoteId': quoteId,
416
+ 'settleId': None,
417
+ 'type': 'spot',
418
+ 'spot': True,
419
+ 'margin': False,
420
+ 'swap': False,
421
+ 'future': False,
422
+ 'option': False,
423
+ 'active': status,
424
+ 'contract': False,
425
+ 'linear': None,
426
+ 'inverse': None,
427
+ 'taker': fees['trading']['taker'],
428
+ 'maker': fees['trading']['maker'],
429
+ 'contractSize': None,
430
+ 'expiry': None,
431
+ 'expiryDatetime': None,
432
+ 'strike': None,
433
+ 'optionType': None,
434
+ 'precision': {
435
+ 'amount': amountPrecision,
436
+ 'price': pricePrecision,
437
+ },
438
+ 'limits': {
439
+ 'amount': {
440
+ 'min': None,
441
+ 'max': None,
442
+ },
443
+ 'price': {
444
+ 'min': None,
445
+ 'max': None,
446
+ },
447
+ 'cost': {
448
+ 'min': None,
449
+ 'max': None,
450
+ },
451
+ },
452
+ 'info': market,
453
+ 'created': created,
454
+ })
455
+
456
+ async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
457
+ """
458
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
459
+
460
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.d2jylz4u6pmu
461
+
462
+ :param str symbol: unified symbol of the market to fetch the ticker for
463
+ :param dict [params]: extra parameters specific to the exchange API endpoint
464
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
465
+ """
466
+ await self.load_markets()
467
+ market = self.market(symbol)
468
+ marketId = market['id']
469
+ request = {
470
+ 'currencyPair': marketId,
471
+ }
472
+ response = await self.publicGetMarketCurrencyPairTicker(self.extend(request, params))
473
+ #
474
+ # {
475
+ # "data": {
476
+ # "market": "BTC_USDC",
477
+ # "ticker": {
478
+ # "time": 1730814600,
479
+ # "count": 2135,
480
+ # "high": {
481
+ # "v": "74766990000",
482
+ # "e": 6,
483
+ # "f": 74766.99
484
+ # },
485
+ # "low": {
486
+ # "v": "68734020000",
487
+ # "e": 6,
488
+ # "f": 68734.02
489
+ # },
490
+ # "avg": {
491
+ # "v": "72347941430",
492
+ # "e": 6,
493
+ # "f": 72347.94143
494
+ # },
495
+ # "vwap": {
496
+ # "v": "73050064447",
497
+ # "e": 6,
498
+ # "f": 73050.064447
499
+ # },
500
+ # "vol": {
501
+ # "v": "4885361",
502
+ # "e": 8,
503
+ # "f": 0.04885361
504
+ # },
505
+ # "secvol": {
506
+ # "v": "3568759346",
507
+ # "e": 6,
508
+ # "f": 3568.759346
509
+ # },
510
+ # "open": {
511
+ # "v": "68784020000",
512
+ # "e": 6,
513
+ # "f": 68784.02
514
+ # },
515
+ # "close": {
516
+ # "v": "73955570000",
517
+ # "e": 6,
518
+ # "f": 73955.57
519
+ # }
520
+ # }
521
+ # },
522
+ # "request_id": "cbf183e0-7a62-4674-838c-6693031fa240",
523
+ # "result": "success",
524
+ # "time": 0.015463566
525
+ # }
526
+ #
527
+ ticker = self.safe_value(response['data'], 'ticker', {})
528
+ return self.parse_ticker(ticker, market)
529
+
530
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
531
+ timestamp = self.safe_integer_product(ticker, 'time', 1000)
532
+ open = self.parse_amount(self.safe_value(ticker, 'open'))
533
+ high = self.parse_amount(self.safe_value(ticker, 'high'))
534
+ low = self.parse_amount(self.safe_value(ticker, 'low'))
535
+ close = self.parse_amount(self.safe_value(ticker, 'close'))
536
+ avg = self.parse_amount(self.safe_value(ticker, 'avg'))
537
+ vwap = self.parse_amount(self.safe_value(ticker, 'vwap'))
538
+ baseVolume = self.parse_amount(self.safe_value(ticker, 'vol'))
539
+ quoteVolume = self.parse_amount(self.safe_value(ticker, 'secvol'))
540
+ # count = self.safe_integer(ticker, 'count'); not used
541
+ return self.safe_ticker({
542
+ 'symbol': self.safe_symbol(None, market),
543
+ 'timestamp': timestamp,
544
+ 'datetime': self.iso8601(timestamp),
545
+ 'high': high,
546
+ 'low': low,
547
+ 'bid': None,
548
+ 'bidVolume': None,
549
+ 'ask': None,
550
+ 'askVolume': None,
551
+ 'vwap': vwap,
552
+ 'open': open,
553
+ 'close': close,
554
+ 'last': close,
555
+ 'previousClose': None,
556
+ 'change': None,
557
+ 'percentage': None,
558
+ 'average': avg,
559
+ 'baseVolume': baseVolume,
560
+ 'quoteVolume': quoteVolume,
561
+ 'info': ticker,
562
+ }, market)
563
+
564
+ async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
565
+ """
566
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
567
+
568
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.bqmucewhkpdz
569
+
570
+ :param str symbol: unified symbol of the market to fetch the order book for
571
+ :param int [limit]: the maximum amount of order book entries to return the exchange not supported yet.
572
+ :param dict [params]: extra parameters specific to the exchange API endpoint
573
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
574
+ """
575
+ await self.load_markets()
576
+ market = self.market(symbol)
577
+ marketId = market['id']
578
+ request = {
579
+ 'currencyPair': marketId,
580
+ }
581
+ response = await self.publicGetMarketCurrencyPairGetDepth(self.extend(request, params))
582
+ # {
583
+ # "data": {
584
+ # "asks": [
585
+ # {
586
+ # "price": {
587
+ # "v": "74941875231",
588
+ # "e": 6,
589
+ # "f": 74941.875231
590
+ # },
591
+ # "amount": {
592
+ # "v": "149",
593
+ # "e": 8,
594
+ # "f": 0.00000149
595
+ # }
596
+ # },
597
+ # {
598
+ # "price": {
599
+ # "v": "75063426037",
600
+ # "e": 6,
601
+ # "f": 75063.426037
602
+ # },
603
+ # "amount": {
604
+ # "v": "335",
605
+ # "e": 8,
606
+ # "f": 0.00000335
607
+ # }
608
+ # }
609
+ # ],
610
+ # "bids": [
611
+ # {
612
+ # "price": {
613
+ # "v": "64518711040",
614
+ # "e": 6,
615
+ # "f": 64518.71104
616
+ # },
617
+ # "amount": {
618
+ # "v": "132",
619
+ # "e": 8,
620
+ # "f": 0.00000132
621
+ # }
622
+ # },
623
+ # {
624
+ # "price": {
625
+ # "v": "64263569273",
626
+ # "e": 6,
627
+ # "f": 64263.569273
628
+ # },
629
+ # "amount": {
630
+ # "v": "210",
631
+ # "e": 8,
632
+ # "f": 0.0000021
633
+ # }
634
+ # }
635
+ # ],
636
+ # "market": "BTC_USDC"
637
+ # },
638
+ # "request_id": "71b7dffc-3120-4e46-a0bb-49ece5aea7e1",
639
+ # "result": "success",
640
+ # "time": 0.000074661
641
+ # }
642
+ data = self.safe_value(response, 'data', {}) # exchange specific v e f params
643
+ timestamp = self.milliseconds() # the exchange does not provide timestamp for self.
644
+ dataBidsLength = len(data['bids'])
645
+ dataAsksLength = len(data['asks'])
646
+ for i in range(0, dataBidsLength):
647
+ data['bids'][i]['price'] = self.parse_amount(data['bids'][i]['price'])
648
+ data['bids'][i]['amount'] = self.parse_amount(data['bids'][i]['amount'])
649
+ for i in range(0, dataAsksLength):
650
+ data['asks'][i]['price'] = self.parse_amount(data['asks'][i]['price'])
651
+ data['asks'][i]['amount'] = self.parse_amount(data['asks'][i]['amount'])
652
+ return self.parse_order_book(data, symbol, timestamp, 'bids', 'asks', 'price', 'amount')
653
+
654
+ async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
655
+ """
656
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market, default will return the last 24h period.
657
+
658
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.w65baeuhxwt8
659
+
660
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
661
+ :param str timeframe: the length of time each candle represents
662
+ :param int [since]: timestamp in ms of the earliest candle to fetch
663
+ :param int [limit]: the maximum amount of candles to fetch
664
+ :param dict [params]: extra parameters specific to the API endpoint
665
+ :returns OHLCV[]: A list of candles ordered, open, high, low, close, volume
666
+ """
667
+ await self.load_markets()
668
+ methodName = 'fetchOHLCV'
669
+ paginate = False
670
+ paginate, params = self.handle_option_and_params(params, methodName, 'paginate')
671
+ if paginate:
672
+ return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1000)
673
+ market = self.market(symbol)
674
+ marketId = market['id']
675
+ time_frame = self.safe_string(self.timeframes, timeframe, None)
676
+ request: dict = {
677
+ 'currencyPair': marketId,
678
+ 'interval': time_frame,
679
+ }
680
+ if since is not None:
681
+ request['start'] = int(math.floor(since / 1000))
682
+ until: Int = None
683
+ until, params = self.handle_option_and_params(params, methodName, 'until')
684
+ if until is not None:
685
+ request['end'] = until
686
+ # {
687
+ # "data": {
688
+ # "market": "BTC_USDC",
689
+ # "real_end": 1730970780,
690
+ # "requested_end": 1730970784,
691
+ # "start": 1730884200,
692
+ # "stats": [
693
+ # {
694
+ # "time": 1730884200,
695
+ # "count": 48,
696
+ # "high": {"v": "73898950000", "e": 6, "f": 73898.95},
697
+ # "low": {"v": "73642930000", "e": 6, "f": 73642.93},
698
+ # "open": {"v": "73830990000", "e": 6, "f": 73830.99},
699
+ # "close": {"v": "73682510000", "e": 6, "f": 73682.51},
700
+ # "vol": {"v": "88159", "e": 8, "f": 0.00088159}
701
+ # }
702
+ # ]
703
+ # }
704
+ # }
705
+ # No limit parameter supported by the API
706
+ response = await self.publicGetMarketCurrencyPairGetGraph(self.extend(request, params))
707
+ data = self.safe_dict(response, 'data', {})
708
+ ohlcv = self.safe_list(data, 'stats', [])
709
+ return self.parse_ohlcvs(ohlcv, market, timeframe, since, limit)
710
+
711
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
712
+ return [
713
+ self.safe_integer(ohlcv, 'time') * 1000, # timestamp
714
+ self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'open'))), # open
715
+ self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'high'))), # high
716
+ self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'low'))), # low
717
+ self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'close'))), # close
718
+ self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'vol'))), # volume
719
+ ]
720
+
721
+ async def fetch_currencies(self, params={}) -> Currencies:
722
+ """
723
+ fetches information on all currencies from the exchange, including deposit/withdrawal details and available chains
724
+
725
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.x65f9s9j74jf
726
+
727
+ :param dict [params]: extra parameters specific to the ellipx API endpoint
728
+ :param str [params.Can_Deposit]: filter currencies by deposit availability, Y for available
729
+ :param number [params.results_per_page]: number of results per page, default 100
730
+ :param str [params._expand]: additional fields to expand in response, default '/Crypto_Token,/Crypto_Chain'
731
+ :returns Promise<Currencies>: An object of currency structures indexed by currency codes
732
+ """
733
+ response = await self._restGetCryptoTokenInfo(self.extend({
734
+ 'Can_Deposit': 'Y',
735
+ 'results_per_page': 100,
736
+ '_expand': '/Crypto_Token,/Crypto_Chain',
737
+ }, params))
738
+ currencies = {}
739
+ data = self.safe_value(response, 'data', [])
740
+ for i in range(0, len(data)):
741
+ currency = self.parse_currency(data[i])
742
+ code = self.safe_string(currency, 'code')
743
+ if code is not None:
744
+ currencies[code] = currency
745
+ return currencies
746
+
747
+ def parse_currency(self, currency) -> Currency:
748
+ id = self.safe_string(currency, 'Crypto_Token__')
749
+ token = self.safe_value(currency, 'Crypto_Token', {})
750
+ code = self.safe_currency_code(self.safe_string(token, 'Symbol'))
751
+ name = self.safe_string(token, 'Name')
752
+ active = self.safe_string(currency, 'Status') == 'valid'
753
+ deposit = self.safe_string(currency, 'Can_Deposit') == 'Y'
754
+ withdraw = self.safe_string(currency, 'Status') == 'valid'
755
+ fee = None
756
+ if currency['Withdraw_Fee'] is not None:
757
+ fee = self.parse_number(self.parse_amount(currency['Withdraw_Fee']))
758
+ precision = self.parse_number(self.parse_precision(self.safe_string(token, 'Decimals')))
759
+ minDeposit = None
760
+ if currency['Minimum_Deposit'] is not None:
761
+ minDeposit = self.parse_amount(currency['Minimum_Deposit'])
762
+ minWithdraw = None
763
+ if currency['Minimum_Withdraw'] is not None:
764
+ minWithdraw = self.parse_amount(currency['Minimum_Withdraw'])
765
+ networkId = self.safe_string(currency, 'Crypto_Chain__')
766
+ networkData = self.safe_value(currency, 'Crypto_Chain', {})
767
+ networkCode = self.safe_string(networkData, 'Type', 'default')
768
+ networks = {
769
+ 'string': None,
770
+ 'info': networkCode == {} if 'default' else networkData,
771
+ 'id': networkId or id or '',
772
+ 'network': networkCode,
773
+ 'active': active,
774
+ 'deposit': deposit,
775
+ 'withdraw': withdraw,
776
+ 'fee': fee,
777
+ 'precision': precision,
778
+ 'limits': {
779
+ 'deposit': {
780
+ 'min': minDeposit,
781
+ 'max': None,
782
+ },
783
+ 'withdraw': {
784
+ 'min': minWithdraw,
785
+ 'max': None,
786
+ },
787
+ },
788
+ }
789
+ result: Currency = {
790
+ 'info': currency,
791
+ 'id': id,
792
+ 'code': code,
793
+ 'name': name,
794
+ 'active': active,
795
+ 'deposit': deposit,
796
+ 'withdraw': withdraw,
797
+ 'fee': fee,
798
+ 'precision': precision,
799
+ 'type': None,
800
+ 'limits': {
801
+ 'amount': {
802
+ 'min': None,
803
+ 'max': None,
804
+ },
805
+ 'withdraw': {
806
+ 'min': minWithdraw,
807
+ 'max': None,
808
+ },
809
+ },
810
+ 'networks': networks,
811
+ }
812
+ return result
813
+
814
+ async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
815
+ """
816
+ fetches all completed trades for a particular market/symbol
817
+ :param str symbol: unified market symbol(e.g. 'BTC/USDT')
818
+ :param int [since]: timestamp in ms of the earliest trade to fetch
819
+ :param int [limit]: the maximum amount of trades to fetch
820
+ :param dict [params]: extra parameters specific to the EllipX API endpoint
821
+ :param str [params.before]: get trades before the given trade ID
822
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
823
+ """
824
+ await self.load_markets()
825
+ market = self.market(symbol)
826
+ marketId = market['id']
827
+ request = {
828
+ 'currencyPair': marketId,
829
+ }
830
+ # endpoint support before trade id.
831
+ # The actual endpoint URL will be: https://data.ellipx.com/Market/{currencyPair}:getTrades
832
+ # {
833
+ # "id": "BTC_USDC:1731053859:914141972:0",
834
+ # "pair": [
835
+ # "BTC",
836
+ # "USDC"
837
+ # ],
838
+ # "bid": {
839
+ # "id": "mktor-swishf-uv6n-hrzj-63ye-bdqnk33q",
840
+ # "iss": "ellipx:beta",
841
+ # "uniq": "order:1731053859:914141972:0"
842
+ # },
843
+ # "ask": {
844
+ # "id": "mktor-p3ozvt-qurz-gmzo-bf5n-g4rcuy6u",
845
+ # "iss": "ellipx:beta",
846
+ # "uniq": "order:1731053859:874659786:0"
847
+ # },
848
+ # "type": "bid",
849
+ # "amount": {
850
+ # "v": "412",
851
+ # "e": 8,
852
+ # "f": 0.00000412
853
+ # },
854
+ # "price": {
855
+ # "v": "75878090000",
856
+ # "e": 6,
857
+ # "f": 75878.09
858
+ # },
859
+ # "date": "2024-11-08T08:17:39.914141972Z"
860
+ # }
861
+ response = await self.publicGetMarketCurrencyPairGetTrades(self.extend(request, params))
862
+ data = self.safe_dict(response, 'data', {})
863
+ trades = self.safe_list(data, 'trades', [])
864
+ return self.parse_trades(trades, market, since, limit)
865
+
866
+ def parse_trade(self, trade, market=None) -> Trade:
867
+ # Format of trade ID: "BTC_USDC:1731053859:914141972:0"
868
+ id = self.safe_string(trade, 'id')
869
+ # fetchTrades and fetchMyTrades return different trade structures
870
+ date = self.safe_dict(trade, 'date')
871
+ timestamp = None
872
+ if date is None:
873
+ timestamp = self.parse8601(self.safe_string(trade, 'date'))
874
+ else:
875
+ timestamp = self.safe_integer(date, 'unixms')
876
+ type = self.safe_string(trade, 'type')
877
+ side = 'buy' if (type == 'bid') else 'sell'
878
+ amount = self.safe_dict(trade, 'amount')
879
+ price = self.safe_dict(trade, 'price')
880
+ amountFloat = self.parse_amount(amount)
881
+ priceFloat = self.parse_amount(price)
882
+ # fetchTrades and fetchMyTrades return different trade structures
883
+ pair = self.safe_list(trade, 'pair')
884
+ marketSymbol = None
885
+ if pair is None:
886
+ symbol = self.safe_string(trade, 'pair')
887
+ base, quote = symbol.split('_')
888
+ marketSymbol = base + '/' + quote
889
+ else:
890
+ marketSymbol = self.safe_string(pair, 0) + '/' + self.safe_string(pair, 1)
891
+ bidOrder = self.safe_dict(trade, 'bid')
892
+ askOrder = self.safe_dict(trade, 'ask')
893
+ isBuy = (side == 'buy')
894
+ orderId = self.safe_string(bidOrder, 'id') if isBuy else self.safe_string(askOrder, 'id')
895
+ return self.safe_trade({
896
+ 'id': id,
897
+ 'info': trade,
898
+ 'timestamp': timestamp,
899
+ 'datetime': self.iso8601(timestamp),
900
+ 'symbol': marketSymbol,
901
+ 'type': None,
902
+ 'side': side,
903
+ 'order': orderId,
904
+ 'takerOrMaker': None,
905
+ 'price': priceFloat,
906
+ 'amount': amountFloat,
907
+ 'cost': None,
908
+ 'fee': None,
909
+ })
910
+
911
+ async def fetch_balance(self, params={}) -> Balances:
912
+ """
913
+ query for balance and get the amount of funds available for trading or funds locked in orders
914
+
915
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.ihrjov144txg
916
+
917
+ :param dict [params]: extra parameters specific to the exchange API endpoint
918
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
919
+ """
920
+ await self.load_markets()
921
+ response = await self.privateGetUserWallet(params)
922
+ # {
923
+ # "User_Wallet__": "usw-vv7hzo-qel5-gupk-neqi-7f3wz5pq",
924
+ # "User__": "usr-...",
925
+ # "Realm__": "usrr-cb3c7n-qvxv-fdrb-uc2q-gpja2foi",
926
+ # "Unit__": "unit-aebkye-u35b-e5zm-zt22-2qvwhsqa",
927
+ # "Balance": {
928
+ # "value": "0.00006394",
929
+ # "value_int": "6394",
930
+ # "value_disp": "0.00006394",
931
+ # "value_xint": {
932
+ # "v": "6394",
933
+ # "e": 8,
934
+ # "f": 0.00006394
935
+ # },
936
+ # "display": "0.00006394BTC",
937
+ # "display_short": "0.00006394BTC",
938
+ # "currency": "BTC",
939
+ # "unit": "BTC",
940
+ # "has_vat": False,
941
+ # "tax_profile": null
942
+ # },
943
+ # "Balance_Date": {
944
+ # "unix": 1731128270,
945
+ # "us": 426208,
946
+ # "iso": "2024-11-09 04:57:50.426208",
947
+ # "tz": "UTC",
948
+ # "full": "1731128270426208",
949
+ # "unixms": "1731128270426"
950
+ # },
951
+ # "Liabilities": {
952
+ # "value": "0.00000000",
953
+ # "value_int": "0",
954
+ # "value_disp": "0.00000000",
955
+ # "value_xint": {
956
+ # "v": "0",
957
+ # "e": 8,
958
+ # "f": 0
959
+ # },
960
+ # "display": "0.00000000BTC",
961
+ # "display_short": "0.00000000BTC",
962
+ # "currency": "BTC",
963
+ # "unit": "BTC",
964
+ # "has_vat": False,
965
+ # "tax_profile": null
966
+ # },
967
+ # "Index": "5",
968
+ # "Backend": "virtual",
969
+ # "Disable_Limits": "N",
970
+ # "Unencumbered_Balance": {
971
+ # "value": "0.00006394",
972
+ # "value_int": "6394",
973
+ # "value_disp": "0.00006394",
974
+ # "value_xint": {
975
+ # "v": "6394",
976
+ # "e": 8,
977
+ # "f": 0.00006394
978
+ # },
979
+ # "display": "0.00006394BTC",
980
+ # "display_short": "0.00006394BTC",
981
+ # "currency": "BTC",
982
+ # "unit": "BTC",
983
+ # "has_vat": False,
984
+ # "tax_profile": null
985
+ # }
986
+ # }
987
+ result: dict = {
988
+ 'info': response,
989
+ 'timestamp': None,
990
+ 'datetime': None,
991
+ }
992
+ dataArray = self.safe_list(response, 'data', [])
993
+ # Use first item's timestamp if available
994
+ dataArrayLength = len(dataArray)
995
+ if dataArrayLength > 0:
996
+ firstItem = dataArray[0]
997
+ balanceDate = self.safe_dict(firstItem, 'Balance_Date', {})
998
+ result['timestamp'] = self.safe_integer(balanceDate, 'unixms')
999
+ result['datetime'] = self.iso8601(result['timestamp'])
1000
+ # Process each balance entry
1001
+ for i in range(0, len(dataArray)):
1002
+ entry = dataArray[i]
1003
+ balance = self.safe_dict(entry, 'Balance', {})
1004
+ currency = self.safe_string(balance, 'currency')
1005
+ if currency is not None:
1006
+ account = {
1007
+ 'free': self.parse_amount(entry['Unencumbered_Balance']['value_xint']),
1008
+ 'used': self.parse_amount(entry['Liabilities']['value_xint']),
1009
+ 'total': self.parse_amount(balance['value_xint']),
1010
+ }
1011
+ result[currency] = account
1012
+ return self.safe_balance(result)
1013
+
1014
+ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1015
+ """
1016
+ create a new order in a market
1017
+
1018
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.yzfak2n2bwpo
1019
+
1020
+ :param str symbol: unified market symbol(e.g. 'BTC/USDT')
1021
+ :param str type: order type - the exchange automatically sets type to 'limit' if price defined, 'market' if None
1022
+ :param str side: 'buy' or 'sell'
1023
+ :param float [amount]: amount of base currency to trade(can be None if using Spend_Limit)
1024
+ :param float [price]: price per unit of base currency for limit orders
1025
+ :param dict [params]: extra parameters specific to the EllipX API endpoint
1026
+ :param float [params.cost]: maximum amount to spend in quote currency(required for market orders if amount None)
1027
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1028
+ """
1029
+ await self.load_markets()
1030
+ market = self.market(symbol)
1031
+ # the exchange automatically sets the type to 'limit' if the price is defined and to 'market' if it is not
1032
+ marketId = market['id']
1033
+ orderType = 'bid'
1034
+ if side == 'buy':
1035
+ orderType = 'bid'
1036
+ else:
1037
+ orderType = 'ask'
1038
+ request: Any = {
1039
+ 'currencyPair': marketId,
1040
+ 'Type': orderType,
1041
+ }
1042
+ if amount is not None:
1043
+ request['Amount'] = self.amount_to_precision(symbol, amount)
1044
+ if price is not None:
1045
+ request['Price'] = self.price_to_precision(symbol, price)
1046
+ cost = self.safe_string(params, 'cost')
1047
+ if cost is not None:
1048
+ params = self.omit(params, 'cost')
1049
+ request['Spend_Limit'] = self.price_to_precision(symbol, cost)
1050
+ response = await self.privatePostMarketCurrencyPairOrder(self.extend(request, params))
1051
+ # {
1052
+ # "result": "success",
1053
+ # "data": {
1054
+ # "Market_Order__": "mktor-x2grmu-zwo5-fyxc-4gue-vd4ouvsa",
1055
+ # "Market__": "mkt-lrnp2e-eaor-eobj-ua73-75j6sjxe",
1056
+ # "User__": "usr-...",
1057
+ # "Uniq": "order:1728719021:583795548:0",
1058
+ # "Type": "bid",
1059
+ # "Status": "pending",
1060
+ # "Flags": {},
1061
+ # "Amount": {
1062
+ # "v": "100000000",
1063
+ # "e": 8,
1064
+ # "f": 1
1065
+ # },
1066
+ # "Price": null,
1067
+ # "Spend_Limit": {
1068
+ # "v": "1000000",
1069
+ # "e": 6,
1070
+ # "f": 1
1071
+ # },
1072
+ # "Executed": {
1073
+ # "v": "0",
1074
+ # "e": 0,
1075
+ # "f": 0
1076
+ # },
1077
+ # "Secured": {
1078
+ # "v": "1000000",
1079
+ # "e": 6,
1080
+ # "f": 1
1081
+ # },
1082
+ # "Version": "0",
1083
+ # "Created": {
1084
+ # "unix": 1728719020,
1085
+ # "us": 315195,
1086
+ # "iso": "2024-10-12 07:43:40.315195",
1087
+ # "tz": "UTC",
1088
+ # "full": "1728719020315195",
1089
+ # "unixms": "1728719020315"
1090
+ # },
1091
+ # "Updated": {
1092
+ # "unix": 1728719020,
1093
+ # "us": 315195,
1094
+ # "iso": "2024-10-12 07:43:40.315195",
1095
+ # "tz": "UTC",
1096
+ # "full": "1728719020315195",
1097
+ # "unixms": "1728719020315"
1098
+ # }
1099
+ # }
1100
+ # }
1101
+ order = self.safe_dict(response, 'data', {})
1102
+ return self.parse_order(order, market)
1103
+
1104
+ async def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
1105
+ """
1106
+ fetches information on an order made by the user
1107
+ :param str id: the order ID by createOrder or fetchOrders
1108
+ :param str|None symbol: not used by ellipx.fetchOrder
1109
+ :param dict [params]: extra parameters specific to the EllipX API endpoint
1110
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1111
+ """
1112
+ await self.load_markets()
1113
+ request = {
1114
+ 'orderUuid': id,
1115
+ }
1116
+ response = await self.privateGetMarketOrderOrderUuid(self.extend(request, params))
1117
+ data = self.safe_dict(response, 'data', {})
1118
+ return self.parse_order(data, None)
1119
+
1120
+ async def fetch_orders_by_status(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1121
+ """
1122
+ fetches a list of orders placed on the exchange
1123
+
1124
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.5z2nh2b5s81n
1125
+
1126
+ :param str status: 'open' or 'closed', omit for all orders
1127
+ :param str symbol: unified market symbol
1128
+ :param int [since]: timestamp in ms of the earliest order
1129
+ :param int [limit]: the maximum amount of orders to fetch
1130
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1131
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1132
+ """
1133
+ await self.load_markets()
1134
+ market = None
1135
+ request: Any = {}
1136
+ if symbol is not None:
1137
+ market = self.market(symbol)
1138
+ marketId = market['id']
1139
+ request['currencyPair'] = marketId
1140
+ if status is not None:
1141
+ request['Status'] = status
1142
+ response = await self.privateGetMarketCurrencyPairOrder(self.extend(request, params))
1143
+ # {
1144
+ # "result": "success",
1145
+ # "data": [
1146
+ # {
1147
+ # "Market_Order__": "mktor-aglvd2-iy5v-enbj-nwrb-scqsnosa",
1148
+ # "Market__": "mkt-lrnp2e-eaor-eobj-ua73-75j6sjxe",
1149
+ # "User__": "usr-...",
1150
+ # "Uniq": "order:1728712511:964332600:0",
1151
+ # "Type": "ask",
1152
+ # "Status": "open",
1153
+ # "Flags": {},
1154
+ # "Amount": {
1155
+ # "v": "1",
1156
+ # "e": 8,
1157
+ # "f": 1.0e-8
1158
+ # },
1159
+ # "Price": {
1160
+ # "v": "63041306872",
1161
+ # "e": 6,
1162
+ # "f": 63041.306872
1163
+ # },
1164
+ # "Spend_Limit": null,
1165
+ # "Executed": {
1166
+ # "v": "892",
1167
+ # "e": 8,
1168
+ # "f": 8.92e-6
1169
+ # },
1170
+ # "Secured": null,
1171
+ # "Version": "3",
1172
+ # "Created": {
1173
+ # "unix": 1728712510,
1174
+ # "us": 669096,
1175
+ # "iso": "2024-10-12 05:55:10.669096",
1176
+ # "tz": "UTC",
1177
+ # "full": "1728712510669096",
1178
+ # "unixms": "1728712510669"
1179
+ # },
1180
+ # "Updated": {
1181
+ # "unix": 1728712510,
1182
+ # "us": 669096,
1183
+ # "iso": "2024-10-12 05:55:10.669096",
1184
+ # "tz": "UTC",
1185
+ # "full": "1728712510669096",
1186
+ # "unixms": "1728712510669"
1187
+ # }
1188
+ # }
1189
+ # ],
1190
+ # "paging": {
1191
+ # "page_no": 1,
1192
+ # "count": "1",
1193
+ # "page_max": 1,
1194
+ # "results_per_page": 20
1195
+ # }
1196
+ # }
1197
+ data = self.safe_value(response, 'data', [])
1198
+ return self.parse_orders(data, market, since, limit)
1199
+
1200
+ async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1201
+ """
1202
+ fetches information on multiple orders made by the user
1203
+
1204
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.5z2nh2b5s81n
1205
+
1206
+ :param str symbol: unified market symbol of the market orders were made in
1207
+ :param int|None since: timestamp in ms of the earliest order
1208
+ :param int|None limit: the maximum amount of orders to fetch
1209
+ :param dict params: extra parameters specific to the exchange API endpoint
1210
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1211
+ """
1212
+ if symbol is None:
1213
+ raise ArgumentsRequired(self.id + ' fetchOrders requires a symbol parameter')
1214
+ return await self.fetch_orders_by_status(None, symbol, since, limit, params)
1215
+
1216
+ async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1217
+ """
1218
+ fetches information on open orders made by the user
1219
+
1220
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.5z2nh2b5s81n
1221
+
1222
+ :param str symbol: unified market symbol of the market orders were made in
1223
+ :param int|None since: timestamp in ms of the earliest order
1224
+ :param int|None limit: the maximum amount of orders to fetch
1225
+ :param dict params: extra parameters specific to the exchange API endpoint
1226
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1227
+ """
1228
+ if symbol is None:
1229
+ raise ArgumentsRequired(self.id + ' fetchOpenOrders requires a symbol parameter')
1230
+ return await self.fetch_orders_by_status('open', symbol, since, limit, params)
1231
+
1232
+ def parse_order(self, order, market=None) -> Order:
1233
+ id = self.safe_string(order, 'Market_Order__')
1234
+ timestamp = self.safe_integer(self.safe_dict(order, 'Created'), 'unixms')
1235
+ orderType = self.safe_string(order, 'Type')
1236
+ side = 'sell'
1237
+ if orderType == 'bid':
1238
+ side = 'buy'
1239
+ status = self.parse_order_status(self.safe_string(order, 'Status'))
1240
+ amount = self.parse_number(self.parse_amount(self.safe_dict(order, 'Amount')))
1241
+ price = self.parse_number(self.parse_amount(self.safe_dict(order, 'Price')))
1242
+ type = 'market' if (price is None) else 'limit'
1243
+ executed = self.parse_number(self.parse_amount(self.safe_dict(order, 'Executed')))
1244
+ filled = executed
1245
+ remaining = self.parse_number(self.parse_amount(self.safe_dict(order, 'Secured')))
1246
+ cost = self.parse_number(self.parse_amount(self.safe_dict(order, 'Total_Spent')))
1247
+ symbol = market['symbol'] if market else None
1248
+ clientOrderId = None
1249
+ timeInForce = 'GTC' # default to Good Till Cancelled
1250
+ postOnly = False
1251
+ updated = self.safe_dict(order, 'Updated', {})
1252
+ lastTradeTimestamp = self.safe_integer(updated, 'unixms', None)
1253
+ return self.safe_order({
1254
+ 'id': id,
1255
+ 'clientOrderId': clientOrderId,
1256
+ 'info': order,
1257
+ 'timestamp': timestamp,
1258
+ 'datetime': self.iso8601(timestamp),
1259
+ 'lastTradeTimestamp': lastTradeTimestamp,
1260
+ 'status': self.parse_order_status(status),
1261
+ 'symbol': symbol,
1262
+ 'type': type,
1263
+ 'timeInForce': timeInForce,
1264
+ 'postOnly': postOnly,
1265
+ 'side': side,
1266
+ 'price': price,
1267
+ 'stopPrice': None,
1268
+ 'triggerPrice': None,
1269
+ 'average': None,
1270
+ 'cost': cost,
1271
+ 'amount': amount,
1272
+ 'filled': filled,
1273
+ 'remaining': remaining,
1274
+ 'fee': None,
1275
+ 'trades': None,
1276
+ }, market)
1277
+
1278
+ async def cancel_order(self, id: str, symbol: Str = None, params={}) -> Order:
1279
+ """
1280
+ Cancels an open order on the exchange
1281
+
1282
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.f1qu1pb1rebn
1283
+
1284
+ :param str id: - The order ID to cancel(format: mktor-xxxxx-xxxx-xxxx-xxxx-xxxxxxxx)
1285
+ :param str [symbol]: - ellipx.cancelOrder does not use the symbol parameter
1286
+ :param dict [params]: - Extra parameters specific to the exchange API
1287
+ :returns Promise<dict>: A Promise that resolves to the canceled order info
1288
+ """
1289
+ await self.load_markets()
1290
+ request = {
1291
+ 'orderUuid': id,
1292
+ }
1293
+ response = await self.privateDeleteMarketOrderOrderUuid(self.extend(request, params))
1294
+ # {
1295
+ # result: "success",
1296
+ # request_id: "887dba33-d11b-43f0-8034-dd7890882cc5",
1297
+ # time: "0.8975801467895508",
1298
+ # data: True,
1299
+ # access: {
1300
+ # "mktor-rf5k5b-5fhf-dmde-wxqj-3y23jeii": {
1301
+ # required: "A",
1302
+ # available: "O",
1303
+ # },
1304
+ # },
1305
+ # }
1306
+ # self endpoint always returns True and a warning message if the order cancelled before.
1307
+ warningResponse = self.safe_value(response, 'warning', None)
1308
+ statusResponse = self.safe_bool(response, 'data')
1309
+ status = 'canceled'
1310
+ if statusResponse is not True or warningResponse is not None:
1311
+ status = 'closed'
1312
+ return self.safe_order({
1313
+ 'id': id,
1314
+ 'clientOrderId': None,
1315
+ 'info': self.json(response), # original response
1316
+ 'timestamp': None,
1317
+ 'datetime': None,
1318
+ 'lastTradeTimestamp': None,
1319
+ 'status': status,
1320
+ 'symbol': None,
1321
+ 'type': None,
1322
+ 'timeInForce': None,
1323
+ 'postOnly': None,
1324
+ 'side': None,
1325
+ 'price': None,
1326
+ 'stopPrice': None,
1327
+ 'triggerPrice': None,
1328
+ 'average': None,
1329
+ 'cost': None,
1330
+ 'amount': None,
1331
+ 'filled': None,
1332
+ 'remaining': None,
1333
+ 'fee': None,
1334
+ 'trades': None,
1335
+ }, None)
1336
+
1337
+ async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1338
+ """
1339
+ fetch all the trades made from a single order
1340
+ :param str id: order id
1341
+ :param str symbol: unified market symbol
1342
+ :param int [since]: the earliest time in ms to fetch trades for
1343
+ :param int [limit]: the maximum number of trades to retrieve
1344
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1345
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1346
+ """
1347
+ if symbol is None:
1348
+ raise ArgumentsRequired('fetchMyTrades requires a symbol parameter')
1349
+ await self.load_markets()
1350
+ market = self.market(symbol)
1351
+ currencyPair = market['id']
1352
+ request = {
1353
+ 'Market_Order__': id,
1354
+ 'currencyPair': currencyPair,
1355
+ }
1356
+ response = await self.privateGetMarketCurrencyPairTrade(self.extend(request, params))
1357
+ # {
1358
+ # "result": "success",
1359
+ # "request_id": "fc5be99d-d085-46f8-9228-e46d0996f112",
1360
+ # "time": 0.030913114547729492,
1361
+ # "data": [
1362
+ # {
1363
+ # "id": "DOGE_USDC:1731505789:911642994:0",
1364
+ # "pair": "DOGE_USDC",
1365
+ # "bid": {
1366
+ # "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
1367
+ # },
1368
+ # "ask": {
1369
+ # "id": "mktor-oxmac4-mtkf-gi3o-mamg-u2cboqe4"
1370
+ # },
1371
+ # "type": "bid",
1372
+ # "amount": {
1373
+ # "v": "334609419",
1374
+ # "e": 8,
1375
+ # "f": 3.34609419
1376
+ # },
1377
+ # "price": {
1378
+ # "v": "410673",
1379
+ # "e": 6,
1380
+ # "f": 0.410673
1381
+ # },
1382
+ # "date": {
1383
+ # "unix": 1731505789,
1384
+ # "us": 911642,
1385
+ # "iso": "2024-11-13 13:49:49.911642",
1386
+ # "tz": "UTC",
1387
+ # "full": "1731505789911642",
1388
+ # "unixms": "1731505789911"
1389
+ # }
1390
+ # },
1391
+ # {
1392
+ # "id": "DOGE_USDC:1731505789:911642994:4",
1393
+ # "pair": "DOGE_USDC",
1394
+ # "bid": {
1395
+ # "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
1396
+ # },
1397
+ # "ask": {
1398
+ # "id": "mktor-cmtztk-3z3n-gupp-uqdg-74g4wjfq"
1399
+ # },
1400
+ # "type": "bid",
1401
+ # "amount": {
1402
+ # "v": "145453950",
1403
+ # "e": 8,
1404
+ # "f": 1.4545395
1405
+ # },
1406
+ # "price": {
1407
+ # "v": "412589",
1408
+ # "e": 6,
1409
+ # "f": 0.412589
1410
+ # },
1411
+ # "date": {
1412
+ # "unix": 1731505789,
1413
+ # "us": 911642,
1414
+ # "iso": "2024-11-13 13:49:49.911642",
1415
+ # "tz": "UTC",
1416
+ # "full": "1731505789911642",
1417
+ # "unixms": "1731505789911"
1418
+ # }
1419
+ # },
1420
+ # {
1421
+ # "id": "DOGE_USDC:1731505789:911642994:2",
1422
+ # "pair": "DOGE_USDC",
1423
+ # "bid": {
1424
+ # "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
1425
+ # },
1426
+ # "ask": {
1427
+ # "id": "mktor-6tyslh-b33b-flnm-2ata-acjkco4y"
1428
+ # },
1429
+ # "type": "bid",
1430
+ # "amount": {
1431
+ # "v": "587627076",
1432
+ # "e": 8,
1433
+ # "f": 5.87627076
1434
+ # },
1435
+ # "price": {
1436
+ # "v": "411005",
1437
+ # "e": 6,
1438
+ # "f": 0.411005
1439
+ # },
1440
+ # "date": {
1441
+ # "unix": 1731505789,
1442
+ # "us": 911642,
1443
+ # "iso": "2024-11-13 13:49:49.911642",
1444
+ # "tz": "UTC",
1445
+ # "full": "1731505789911642",
1446
+ # "unixms": "1731505789911"
1447
+ # }
1448
+ # },
1449
+ # {
1450
+ # "id": "DOGE_USDC:1731505789:911642994:1",
1451
+ # "pair": "DOGE_USDC",
1452
+ # "bid": {
1453
+ # "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
1454
+ # },
1455
+ # "ask": {
1456
+ # "id": "mktor-ihpjlj-5ufj-dm5l-fmud-oftkqcgu"
1457
+ # },
1458
+ # "type": "bid",
1459
+ # "amount": {
1460
+ # "v": "475845734",
1461
+ # "e": 8,
1462
+ # "f": 4.75845734
1463
+ # },
1464
+ # "price": {
1465
+ # "v": "410830",
1466
+ # "e": 6,
1467
+ # "f": 0.41083
1468
+ # },
1469
+ # "date": {
1470
+ # "unix": 1731505789,
1471
+ # "us": 911642,
1472
+ # "iso": "2024-11-13 13:49:49.911642",
1473
+ # "tz": "UTC",
1474
+ # "full": "1731505789911642",
1475
+ # "unixms": "1731505789911"
1476
+ # }
1477
+ # },
1478
+ # {
1479
+ # "id": "DOGE_USDC:1731505789:911642994:3",
1480
+ # "pair": "DOGE_USDC",
1481
+ # "bid": {
1482
+ # "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
1483
+ # },
1484
+ # "ask": {
1485
+ # "id": "mktor-d2uyb3-nzsj-aevn-dikr-tq3sxhre"
1486
+ # },
1487
+ # "type": "bid",
1488
+ # "amount": {
1489
+ # "v": "641013461",
1490
+ # "e": 8,
1491
+ # "f": 6.41013461
1492
+ # },
1493
+ # "price": {
1494
+ # "v": "411846",
1495
+ # "e": 6,
1496
+ # "f": 0.411846
1497
+ # },
1498
+ # "date": {
1499
+ # "unix": 1731505789,
1500
+ # "us": 911642,
1501
+ # "iso": "2024-11-13 13:49:49.911642",
1502
+ # "tz": "UTC",
1503
+ # "full": "1731505789911642",
1504
+ # "unixms": "1731505789911"
1505
+ # }
1506
+ # }
1507
+ # ],
1508
+ # "access": {
1509
+ # "mkt-xrkg5l-akjz-cxxl-3a2e-mul5gfo4": {
1510
+ # "required": "r",
1511
+ # "available": "?"
1512
+ # },
1513
+ # "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4": {
1514
+ # "required": "R",
1515
+ # "available": "O"
1516
+ # }
1517
+ # },
1518
+ # "paging": {
1519
+ # "page_no": 1,
1520
+ # "count": "5",
1521
+ # "page_max": 1,
1522
+ # "results_per_page": 20
1523
+ # }
1524
+ # }
1525
+ data = self.safe_list(response, 'data')
1526
+ return self.parse_trades(data, market, since, limit)
1527
+
1528
+ async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
1529
+ """
1530
+ fetches a crypto deposit address for a specific currency
1531
+
1532
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.k7qe5aricayh
1533
+
1534
+ :param str code: unified currency code(e.g. "BTC", "ETH", "USDT")
1535
+ :param dict [params]: extra parameters specific to the EllipX API endpoint
1536
+ :returns dict: an address structure {
1537
+ 'currency': string, # unified currency code
1538
+ 'address': string, # the address for deposits
1539
+ 'tag': string|None, # tag/memo for deposits if needed
1540
+ 'network': object, # network object from currency info
1541
+ 'info': object # raw response from exchange
1542
+ }
1543
+ :throws ExchangeError if: currency does not support deposits
1544
+ """
1545
+ await self.load_markets()
1546
+ currency = self.currency(code)
1547
+ network = self.safe_value(currency['info'], 'Crypto_Chain', None)
1548
+ request = {
1549
+ 'Crypto_Token__': self.safe_string(network, 'Crypto_Token__'),
1550
+ 'Crypto_Chain__': self.safe_string(network, 'Crypto_Chain__'),
1551
+ }
1552
+ response = await self.privatePostCryptoAddressFetch(self.extend(request, params))
1553
+ data = self.safe_value(response, 'data', {})
1554
+ address = self.safe_string(data, 'Address')
1555
+ tag = self.safe_string(data, 'memo')
1556
+ self.check_address(address)
1557
+ return {
1558
+ 'currency': code,
1559
+ 'address': address,
1560
+ 'tag': tag,
1561
+ 'network': network,
1562
+ 'info': response,
1563
+ }
1564
+
1565
+ async def fetch_trading_fee(self, symbol: str = None, params={}) -> TradingFeeInterface:
1566
+ """
1567
+ Fetches the current trading fees(maker and taker) applicable to the user.
1568
+
1569
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.kki5jay2c8it
1570
+
1571
+ :param str [symbol]: Not used by EllipX are not symbol-specific.
1572
+ :param dict [params]: Extra parameters specific to the EllipX API endpoint.
1573
+ :returns Promise<dict>: A promise resolving to a unified trading fee structure:
1574
+ {
1575
+ 'info': object, # the raw response from the exchange
1576
+ 'symbol': None, # symbol is not used for self exchange
1577
+ 'maker': number, # maker fee rate in decimal form
1578
+ 'taker': number, # taker fee rate in decimal form
1579
+ 'percentage': True, # indicates fees are in percentage
1580
+ 'tierBased': False, # indicates fees do not vary by volume tiers
1581
+ }
1582
+ """
1583
+ await self.load_markets()
1584
+ response = await self.privateGetMarketTradeFeeQuery(params)
1585
+ #
1586
+ # Example response:
1587
+ # {
1588
+ # "result": "success",
1589
+ # "data": {
1590
+ # "maker": 15.0, # in basis points
1591
+ # "taker": 25.0, # in basis points
1592
+ # "volume": 123456.78,
1593
+ # "promo": {
1594
+ # # promotional discounts if any
1595
+ # }
1596
+ # }
1597
+ # }
1598
+ #
1599
+ data = self.safe_value(response, 'data', {})
1600
+ maker = self.safe_number(data, 'maker') # in basis points
1601
+ taker = self.safe_number(data, 'taker') # in basis points
1602
+ makerFee = maker / 10000 if (maker is not None) else None
1603
+ takerFee = taker / 10000 if (taker is not None) else None
1604
+ return {
1605
+ 'info': response,
1606
+ 'symbol': None, # the exchange only have separate fees for stablecoin pairs
1607
+ 'maker': makerFee,
1608
+ 'taker': takerFee,
1609
+ 'percentage': True, # fees are expressed in percentages
1610
+ 'tierBased': True, # fees can vary based on volume tiers
1611
+ }
1612
+
1613
+ async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
1614
+ """
1615
+ Make a withdrawal request
1616
+
1617
+ https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.zegupoa8g4t9
1618
+
1619
+ :param str code: Currency code
1620
+ :param number amount: Amount to withdraw
1621
+ :param str address: Destination wallet address
1622
+ :param str [tag]: Additional tag/memo for currencies that require it
1623
+ :param dict params: Extra parameters specific to the EllipX API endpoint(Crypto_Chain__, Unit__)
1624
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1625
+ """
1626
+ self.check_address(address)
1627
+ await self.load_markets()
1628
+ currency = self.currency(code)
1629
+ networks = self.safe_value(currency, 'networks')
1630
+ if networks is None:
1631
+ raise NotSupported(self.id + ' withdraw() for ' + code + ' is not supported')
1632
+ chainsResponse = await self.privateGetUnitCurrency({'currency': currency['code']}) # fetch Unit__ params for currency
1633
+ chainsData = self.safe_value(chainsResponse, 'data', [])
1634
+ unit = self.safe_string(chainsData, 'Unit__')
1635
+ # check params again and omit params
1636
+ self.omit(params, 'Unit__')
1637
+ self.omit(params, 'Crypto_Chain__')
1638
+ amountString = str(amount)
1639
+ request = {
1640
+ 'Unit__': unit,
1641
+ 'amount': amountString,
1642
+ 'address': address,
1643
+ 'Crypto_Chain__': networks['id'],
1644
+ }
1645
+ if tag is not None:
1646
+ request['memo'] = tag
1647
+ response = await self.privatePostCryptoDisbursementWithdraw(self.extend(request, params))
1648
+ # {
1649
+ # Crypto_Disbursement__: "crdsb-4pw3kg-ipn5-amvb-da4n-6xncy4r4",
1650
+ # Crypto_Token__: "crtok-dnehz4-wbgv-bunf-iyd3-m7gtsz2q",
1651
+ # Crypto_Chain__: "chain-kjfvwn-l2xn-eclc-ul5d-mb6fu5hm",
1652
+ # User__: "usr-5oint6-ozpr-alfp-2wxi-zgbm4osy",
1653
+ # Value: {
1654
+ # v: "1000000000",
1655
+ # e: "8",
1656
+ # f: "10",
1657
+ # },
1658
+ # Value_USD: "4.08723",
1659
+ # Address: "D6z62LUwyNBi3QbPkzW8C4m7VDAgu9wb2Z",
1660
+ # Status: "pending",
1661
+ # Transaction: null,
1662
+ # Requested: {
1663
+ # unix: "1731570982",
1664
+ # us: "203569",
1665
+ # iso: "2024-11-14 07:56:22.203569",
1666
+ # tz: "UTC",
1667
+ # full: "1731570982203569",
1668
+ # unixms: "1731570982203",
1669
+ # },
1670
+ # Scheduled: null,
1671
+ # Processed: null,
1672
+ # Amount: {
1673
+ # value: "10.00000000",
1674
+ # value_int: "1000000000",
1675
+ # value_disp: "10.00000000",
1676
+ # value_xint: {
1677
+ # v: "1000000000",
1678
+ # e: "8",
1679
+ # f: "10",
1680
+ # },
1681
+ # display: "10.00000000DOGE",
1682
+ # display_short: "10.00000000DOGE",
1683
+ # currency: "DOGE",
1684
+ # unit: "DOGE",
1685
+ # has_vat: False,
1686
+ # tax_profile: null,
1687
+ # raw: {
1688
+ # value: "10.00000000",
1689
+ # value_int: "1000000000",
1690
+ # value_disp: "10.00000000",
1691
+ # value_xint: {
1692
+ # v: "1000000000",
1693
+ # e: "8",
1694
+ # f: "10",
1695
+ # },
1696
+ # display: "10.00000000DOGE",
1697
+ # display_short: "10.00000000DOGE",
1698
+ # currency: "DOGE",
1699
+ # unit: "DOGE",
1700
+ # has_vat: False,
1701
+ # tax_profile: null,
1702
+ # },
1703
+ # tax: {
1704
+ # value: "10.00000000",
1705
+ # value_int: "1000000000",
1706
+ # value_disp: "10.00000000",
1707
+ # value_xint: {
1708
+ # v: "1000000000",
1709
+ # e: "8",
1710
+ # f: "10",
1711
+ # },
1712
+ # display: "10.00000000DOGE",
1713
+ # display_short: "10.00000000DOGE",
1714
+ # currency: "DOGE",
1715
+ # unit: "DOGE",
1716
+ # has_vat: True,
1717
+ # tax_profile: null,
1718
+ # },
1719
+ # tax_only: {
1720
+ # value: "0.000",
1721
+ # value_int: "0",
1722
+ # value_disp: "0",
1723
+ # value_xint: {
1724
+ # v: "0",
1725
+ # e: "3",
1726
+ # f: "0",
1727
+ # },
1728
+ # display: "¥0",
1729
+ # display_short: "¥0",
1730
+ # currency: "JPY",
1731
+ # unit: "JPY",
1732
+ # has_vat: False,
1733
+ # tax_profile: null,
1734
+ # },
1735
+ # tax_rate: "0",
1736
+ # },
1737
+ # }
1738
+ data = self.safe_dict(response, 'data')
1739
+ amountResponse = self.safe_dict(data, 'Amount')
1740
+ requested = self.safe_dict(data, 'Requested')
1741
+ processed = self.safe_dict(data, 'Processed')
1742
+ withdrawId = self.safe_string(data, 'Crypto_Disbursement__')
1743
+ timestamp = self.safe_integer(requested, 'unixms')
1744
+ return {
1745
+ 'info': response,
1746
+ 'id': withdrawId,
1747
+ 'txid': None,
1748
+ 'timestamp': timestamp,
1749
+ 'datetime': self.iso8601(timestamp),
1750
+ 'network': self.safe_string(data, 'Crypto_Chain__'),
1751
+ 'address': self.safe_string(data, 'Address'),
1752
+ 'addressTo': self.safe_string(data, 'Address'),
1753
+ 'addressFrom': None,
1754
+ 'tag': tag,
1755
+ 'tagTo': tag,
1756
+ 'tagFrom': None,
1757
+ 'type': 'withdrawal',
1758
+ 'amount': self.safe_number(amountResponse, 'value'),
1759
+ 'currency': code,
1760
+ 'status': self.parse_transaction_status(self.safe_string(data, 'Status')),
1761
+ 'updated': self.safe_timestamp(processed, 'unix'),
1762
+ 'internal': False,
1763
+ 'comment': None,
1764
+ 'fee': {
1765
+ 'currency': code,
1766
+ 'cost': None, # Fee information not provided in response
1767
+ 'rate': None,
1768
+ },
1769
+ }
1770
+
1771
+ def parse_transaction_status(self, status: str) -> str:
1772
+ statuses = {
1773
+ 'pending': 'pending',
1774
+ 'completed': 'ok',
1775
+ 'failed': 'failed',
1776
+ 'cancelled': 'canceled',
1777
+ }
1778
+ return self.safe_string(statuses, status, status)
1779
+
1780
+ def parse_order_status(self, status):
1781
+ statuses = {
1782
+ 'pending': 'open', # starting state of all orders
1783
+ 'running': 'open', # when order is being executed
1784
+ 'post-pending': 'open', # post-only order waiting to be placed
1785
+ 'open': 'open', # active order in the orderbook
1786
+ 'stop': 'open', # when stop order not yet triggered
1787
+ 'invalid': 'rejected', # order rejected
1788
+ 'done': 'closed', # order fully executed
1789
+ 'cancel': 'canceled', # order canceled by user
1790
+ 'canceled': 'canceled', # alternative spelling
1791
+ }
1792
+ return self.safe_string(statuses, status, status)
1793
+
1794
+ def parse_amount(self, amount) -> Str:
1795
+ v = self.safe_string(amount, 'v', None)
1796
+ e = self.safe_integer(amount, 'e', None)
1797
+ if v is None or e is None:
1798
+ return None
1799
+ preciseAmount = Precise(v)
1800
+ preciseAmount.decimals = e
1801
+ preciseAmount.reduce()
1802
+ return str(preciseAmount)
1803
+
1804
+ def to_amount(self, amount: float, precision: float) -> dict:
1805
+ v = str(amount)
1806
+ e = precision
1807
+ return {
1808
+ 'v': v,
1809
+ 'e': e,
1810
+ }
1811
+
1812
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
1813
+ # {
1814
+ # "code": 404,
1815
+ # "error": "Not Found: Crypto\\Token(US)",
1816
+ # "exception": "Exception\\NotFound",
1817
+ # "message": "[I18N:error_not_found]",
1818
+ # "request": "cc83738a-2438-4f53-ae44-f15306c07f32",
1819
+ # "result": "error",
1820
+ # "time": 0.0089569091796875,
1821
+ # "token": "error_not_found"
1822
+ # }
1823
+ errorCode = self.safe_string(response, 'code')
1824
+ message = self.safe_string(response, 'message')
1825
+ if errorCode is not None:
1826
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, message)
1827
+ raise ExchangeError(self.id + ' ' + message)
1828
+ return None