ccxt 4.4.64__py2.py3-none-any.whl → 4.4.68__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 (55) hide show
  1. ccxt/__init__.py +5 -3
  2. ccxt/abstract/cryptomus.py +20 -0
  3. ccxt/abstract/derive.py +117 -0
  4. ccxt/abstract/tradeogre.py +1 -0
  5. ccxt/abstract/whitebit.py +16 -0
  6. ccxt/async_support/__init__.py +5 -3
  7. ccxt/async_support/base/exchange.py +6 -5
  8. ccxt/async_support/binance.py +5 -3
  9. ccxt/async_support/bitget.py +22 -12
  10. ccxt/async_support/bitrue.py +6 -3
  11. ccxt/async_support/bybit.py +1 -1
  12. ccxt/async_support/coinbase.py +73 -2
  13. ccxt/async_support/cryptocom.py +2 -0
  14. ccxt/async_support/cryptomus.py +1041 -0
  15. ccxt/async_support/derive.py +2530 -0
  16. ccxt/async_support/gate.py +5 -1
  17. ccxt/async_support/htx.py +19 -5
  18. ccxt/async_support/hyperliquid.py +108 -68
  19. ccxt/async_support/luno.py +113 -1
  20. ccxt/async_support/paradex.py +51 -12
  21. ccxt/async_support/tradeogre.py +132 -13
  22. ccxt/async_support/whitebit.py +276 -2
  23. ccxt/base/errors.py +0 -6
  24. ccxt/base/exchange.py +13 -4
  25. ccxt/binance.py +5 -3
  26. ccxt/bitget.py +22 -12
  27. ccxt/bitrue.py +6 -3
  28. ccxt/bybit.py +1 -1
  29. ccxt/coinbase.py +73 -2
  30. ccxt/cryptocom.py +2 -0
  31. ccxt/cryptomus.py +1041 -0
  32. ccxt/derive.py +2529 -0
  33. ccxt/gate.py +5 -1
  34. ccxt/htx.py +19 -5
  35. ccxt/hyperliquid.py +108 -68
  36. ccxt/luno.py +113 -1
  37. ccxt/paradex.py +51 -12
  38. ccxt/pro/__init__.py +3 -3
  39. ccxt/pro/bybit.py +3 -2
  40. ccxt/pro/derive.py +704 -0
  41. ccxt/pro/gate.py +5 -2
  42. ccxt/pro/hyperliquid.py +3 -3
  43. ccxt/test/tests_async.py +36 -3
  44. ccxt/test/tests_sync.py +36 -3
  45. ccxt/tradeogre.py +132 -13
  46. ccxt/whitebit.py +276 -2
  47. {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/METADATA +16 -12
  48. {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/RECORD +51 -48
  49. ccxt/abstract/currencycom.py +0 -68
  50. ccxt/async_support/currencycom.py +0 -2070
  51. ccxt/currencycom.py +0 -2070
  52. ccxt/pro/currencycom.py +0 -536
  53. {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/LICENSE.txt +0 -0
  54. {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/WHEEL +0 -0
  55. {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/top_level.txt +0 -0
@@ -1,2070 +0,0 @@
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.currencycom import ImplicitAPI
8
- import hashlib
9
- from ccxt.base.types import Account, Any, Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, Leverage, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, 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 ArgumentsRequired
14
- from ccxt.base.errors import BadRequest
15
- from ccxt.base.errors import BadSymbol
16
- from ccxt.base.errors import InsufficientFunds
17
- from ccxt.base.errors import InvalidOrder
18
- from ccxt.base.errors import OrderNotFound
19
- from ccxt.base.errors import NotSupported
20
- from ccxt.base.errors import DDoSProtection
21
- from ccxt.base.errors import ExchangeNotAvailable
22
- from ccxt.base.errors import InvalidNonce
23
- from ccxt.base.decimal_to_precision import TICK_SIZE
24
- from ccxt.base.precise import Precise
25
-
26
-
27
- class currencycom(Exchange, ImplicitAPI):
28
-
29
- def describe(self) -> Any:
30
- return self.deep_extend(super(currencycom, self).describe(), {
31
- 'id': 'currencycom',
32
- 'name': 'Currency.com',
33
- 'countries': ['BY'], # Belarus
34
- 'rateLimit': 100,
35
- 'certified': False,
36
- 'pro': True,
37
- 'version': 'v2',
38
- # new metainfo interface
39
- 'has': {
40
- 'CORS': None,
41
- 'spot': True,
42
- 'margin': True,
43
- 'swap': True,
44
- 'future': False,
45
- 'option': False,
46
- 'addMargin': None,
47
- 'cancelAllOrders': None,
48
- 'cancelOrder': True,
49
- 'cancelOrders': None,
50
- 'createDepositAddress': None,
51
- 'createLimitOrder': True,
52
- 'createMarketOrder': True,
53
- 'createOrder': True,
54
- 'createStopLimitOrder': True,
55
- 'createStopMarketOrder': True,
56
- 'createStopOrder': True,
57
- 'editOrder': 'emulated',
58
- 'fetchAccounts': True,
59
- 'fetchBalance': True,
60
- 'fetchBidsAsks': None,
61
- 'fetchBorrowRateHistory': None,
62
- 'fetchCanceledOrders': None,
63
- 'fetchClosedOrder': None,
64
- 'fetchClosedOrders': None,
65
- 'fetchCrossBorrowRate': False,
66
- 'fetchCrossBorrowRates': False,
67
- 'fetchCurrencies': True,
68
- 'fetchDeposit': None,
69
- 'fetchDepositAddress': True,
70
- 'fetchDepositAddresses': False,
71
- 'fetchDepositAddressesByNetwork': False,
72
- 'fetchDeposits': True,
73
- 'fetchDepositsWithdrawals': True,
74
- 'fetchFundingHistory': False,
75
- 'fetchFundingRate': False,
76
- 'fetchFundingRateHistory': False,
77
- 'fetchFundingRates': False,
78
- 'fetchIndexOHLCV': False,
79
- 'fetchIsolatedBorrowRate': False,
80
- 'fetchIsolatedBorrowRates': False,
81
- 'fetchL2OrderBook': True,
82
- 'fetchLedger': True,
83
- 'fetchLedgerEntry': False,
84
- 'fetchLeverage': True,
85
- 'fetchLeverageTiers': False,
86
- 'fetchMarginMode': False,
87
- 'fetchMarkets': True,
88
- 'fetchMarkOHLCV': False,
89
- 'fetchMyTrades': True,
90
- 'fetchOHLCV': True,
91
- 'fetchOpenOrder': None,
92
- 'fetchOpenOrders': True,
93
- 'fetchOrder': True,
94
- 'fetchOrderBook': True,
95
- 'fetchOrderBooks': None,
96
- 'fetchOrders': None,
97
- 'fetchOrderTrades': None,
98
- 'fetchPosition': None,
99
- 'fetchPositionMode': False,
100
- 'fetchPositions': True,
101
- 'fetchPositionsRisk': None,
102
- 'fetchPremiumIndexOHLCV': False,
103
- 'fetchTicker': True,
104
- 'fetchTickers': True,
105
- 'fetchTime': True,
106
- 'fetchTrades': True,
107
- 'fetchTradingFee': False,
108
- 'fetchTradingFees': True,
109
- 'fetchTradingLimits': None,
110
- 'fetchTransactionFee': None,
111
- 'fetchTransactionFees': None,
112
- 'fetchTransactions': 'emulated',
113
- 'fetchTransfers': None,
114
- 'fetchWithdrawal': None,
115
- 'fetchWithdrawals': True,
116
- 'reduceMargin': None,
117
- 'sandbox': True,
118
- 'setLeverage': None,
119
- 'setMarginMode': None,
120
- 'setPositionMode': None,
121
- 'signIn': None,
122
- 'transfer': None,
123
- 'withdraw': None,
124
- },
125
- 'timeframes': {
126
- '1m': '1m',
127
- '5m': '5m',
128
- '10m': '10m',
129
- '15m': '15m',
130
- '30m': '30m',
131
- '1h': '1h',
132
- '4h': '4h',
133
- '1d': '1d',
134
- '1w': '1w',
135
- },
136
- 'hostname': 'backend.currency.com',
137
- 'urls': {
138
- 'logo': 'https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg',
139
- 'api': {
140
- 'public': 'https://api-adapter.{hostname}/api',
141
- 'private': 'https://api-adapter.{hostname}/api',
142
- 'marketcap': 'https://marketcap.{hostname}/api',
143
- },
144
- 'test': {
145
- 'public': 'https://demo-api-adapter.{hostname}/api',
146
- 'private': 'https://demo-api-adapter.{hostname}/api',
147
- },
148
- 'www': 'https://www.currency.com',
149
- 'referral': 'https://currency.com/trading/signup?c=362jaimv&pid=referral',
150
- 'doc': [
151
- 'https://currency.com/api',
152
- ],
153
- 'fees': 'https://currency.com/fees-charges',
154
- },
155
- # rate-limits are described at: https://currency.com/api-get-started
156
- 'api': {
157
- 'public': {
158
- 'get': {
159
- 'v1/time': 1,
160
- 'v1/exchangeInfo': 1,
161
- 'v1/depth': 1,
162
- 'v1/aggTrades': 1,
163
- 'v1/klines': 1,
164
- 'v1/ticker/24hr': 1,
165
- 'v2/time': 1,
166
- 'v2/exchangeInfo': 1,
167
- 'v2/depth': 1,
168
- 'v2/aggTrades': 1,
169
- 'v2/klines': 1,
170
- 'v2/ticker/24hr': 1,
171
- },
172
- },
173
- 'marketcap': {
174
- 'get': {
175
- 'v1/assets': 1,
176
- 'v1/candles': 1,
177
- 'v1/orderbook': 1,
178
- 'v1/summary': 1,
179
- 'v1/ticker': 1,
180
- 'v1/token/assets': 1,
181
- 'v1/token/orderbook': 1,
182
- 'v1/token/summary': 1,
183
- 'v1/token/ticker': 1,
184
- 'v1/token/trades': 1,
185
- 'v1/token_crypto/OHLC': 1,
186
- 'v1/token_crypto/assets': 1,
187
- 'v1/token_crypto/orderbook': 1,
188
- 'v1/token_crypto/summary': 1,
189
- 'v1/token_crypto/ticker': 1,
190
- 'v1/token_crypto/trades': 1,
191
- 'v1/trades': 1,
192
- },
193
- },
194
- 'private': {
195
- 'get': {
196
- 'v1/account': 1,
197
- 'v1/currencies': 1,
198
- 'v1/deposits': 1,
199
- 'v1/depositAddress': 1,
200
- 'v1/ledger': 1,
201
- 'v1/leverageSettings': 1,
202
- 'v1/myTrades': 1,
203
- 'v1/openOrders': 1,
204
- 'v1/tradingPositions': 1,
205
- 'v1/tradingPositionsHistory': 1,
206
- 'v1/transactions': 1,
207
- 'v1/withdrawals': 1,
208
- 'v2/account': 1,
209
- 'v2/currencies': 1,
210
- 'v2/deposits': 1,
211
- 'v2/depositAddress': 1,
212
- 'v2/ledger': 1,
213
- 'v2/leverageSettings': 1,
214
- 'v2/myTrades': 1,
215
- 'v2/openOrders': 1,
216
- 'v2/tradingPositions': 1,
217
- 'v2/tradingPositionsHistory': 1,
218
- 'v2/transactions': 1,
219
- 'v2/withdrawals': 1,
220
- 'v2/fetchOrder': 1,
221
- },
222
- 'post': {
223
- 'v1/order': 1,
224
- 'v1/updateTradingPosition': 1,
225
- 'v1/updateTradingOrder': 1,
226
- 'v1/closeTradingPosition': 1,
227
- 'v2/order': 1,
228
- 'v2/updateTradingPosition': 1,
229
- 'v2/updateTradingOrder': 1,
230
- 'v2/closeTradingPosition': 1,
231
- },
232
- 'delete': {
233
- 'v1/order': 1,
234
- 'v2/order': 1,
235
- },
236
- },
237
- },
238
- 'fees': {
239
- 'trading': {
240
- 'feeSide': 'get',
241
- 'tierBased': False,
242
- 'percentage': True,
243
- 'taker': self.parse_number('0.002'),
244
- 'maker': self.parse_number('0.002'),
245
- },
246
- },
247
- 'precisionMode': TICK_SIZE,
248
- # exchange-specific options
249
- 'options': {
250
- 'defaultTimeInForce': 'GTC', # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel, 'FOK' = Fill Or Kill
251
- 'warnOnFetchOpenOrdersWithoutSymbol': True,
252
- 'recvWindow': 5 * 1000, # 5 sec, default
253
- 'timeDifference': 0, # the difference between system clock and Binance clock
254
- 'adjustForTimeDifference': False, # controls the adjustment logic upon instantiation
255
- 'parseOrderToPrecision': False, # force amounts and costs in parseOrder to precision
256
- 'newOrderRespType': {
257
- 'market': 'FULL', # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
258
- 'limit': 'RESULT', # we change it from 'ACK' by default to 'RESULT'
259
- 'stop': 'RESULT',
260
- },
261
- 'leverage_markets_suffix': '_LEVERAGE',
262
- 'collateralCurrencies': ['USD', 'EUR', 'USDT'],
263
- },
264
- 'features': {
265
- 'default': {
266
- 'sandbox': True,
267
- 'createOrder': {
268
- 'marginMode': True, # todo implementation
269
- 'triggerPrice': True,
270
- 'triggerPriceType': None,
271
- 'triggerDirection': False,
272
- 'stopLossPrice': False, # todo
273
- 'takeProfitPrice': False, # todo
274
- 'attachedStopLossTakeProfit': {
275
- 'triggerPriceType': None,
276
- 'price': False,
277
- },
278
- 'timeInForce': {
279
- 'IOC': True,
280
- 'FOK': True,
281
- 'PO': False,
282
- 'GTD': True,
283
- },
284
- 'hedged': False,
285
- 'selfTradePrevention': False,
286
- 'trailing': False,
287
- 'iceberg': False,
288
- 'leverage': True,
289
- 'marketBuyByCost': False,
290
- 'marketBuyRequiresPrice': False,
291
- },
292
- 'createOrders': None,
293
- 'fetchMyTrades': {
294
- 'marginMode': False,
295
- 'limit': 500,
296
- 'daysBack': 100000,
297
- 'untilDays': 100000, # todo implementation
298
- 'symbolRequired': False,
299
- },
300
- 'fetchOrder': {
301
- 'marginMode': False,
302
- 'trigger': False,
303
- 'trailing': False,
304
- 'symbolRequired': False,
305
- },
306
- 'fetchOpenOrders': {
307
- 'marginMode': True,
308
- 'limit': 100,
309
- 'trigger': False,
310
- 'trailing': False,
311
- 'symbolRequired': False,
312
- },
313
- 'fetchOrders': None,
314
- 'fetchClosedOrders': None,
315
- 'fetchOHLCV': {
316
- 'limit': 1000,
317
- },
318
- },
319
- 'spot': {
320
- 'extends': 'default',
321
- },
322
- 'swap': {
323
- 'linear': {
324
- 'extends': 'default',
325
- },
326
- 'inverse': {
327
- 'extends': 'default',
328
- },
329
- },
330
- 'future': {
331
- 'linear': {
332
- 'extends': 'default',
333
- },
334
- 'inverse': {
335
- 'extends': 'default',
336
- },
337
- },
338
- },
339
- 'exceptions': {
340
- 'broad': {
341
- 'FIELD_VALIDATION_ERROR Cancel is available only for LIMIT order': InvalidOrder,
342
- 'API key does not exist': AuthenticationError,
343
- 'Order would trigger immediately.': InvalidOrder,
344
- 'Account has insufficient balance for requested action.': InsufficientFunds,
345
- 'Rest API trading is not enabled.': ExchangeNotAvailable,
346
- 'Combination of parameters invalid': BadRequest,
347
- 'Invalid limit price': BadRequest,
348
- 'Only leverage symbol allowed here:': BadSymbol, # when you fetchLeverage for non-leverage symbols, like 'BTC/USDT' instead of 'BTC/USDT_LEVERAGE': {"code":"-1128","msg":"Only leverage symbol allowed here: BTC/USDT"}
349
- 'market data service is not available': ExchangeNotAvailable, # {"code":"-1021","msg":"market data service is not available"}
350
- 'your time is ahead of server': InvalidNonce, # {"code":"-1021","msg":"your time is ahead of server"}
351
- 'Can not find account': BadRequest, # -1128
352
- 'You mentioned an invalid value for the price parameter': BadRequest, # -1030
353
- },
354
- 'exact': {
355
- '-1000': ExchangeNotAvailable, # {"code":-1000,"msg":"An unknown error occured while processing the request."}
356
- '-1013': InvalidOrder, # createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL
357
- # '-1021': InvalidNonce, # {"code":"-1021","msg":"your time is ahead of server"} # see above in the broad section
358
- '-1022': AuthenticationError, # {"code":-1022,"msg":"Signature for self request is not valid."}
359
- '-1030': InvalidOrder, # {"code":"-1030","msg":"You mentioned an invalid value for the price parameter."}
360
- '-1100': InvalidOrder, # createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'
361
- '-1104': ExchangeError, # Not all sent parameters were read, read 8 parameters but was sent 9
362
- '-1025': AuthenticationError, # {"code":-1025,"msg":"Invalid API-key, IP, or permissions for action"}
363
- '-1128': BadRequest, # {"code":-1128,"msg":"Combination of optional parameters invalid."} | {"code":"-1128","msg":"Combination of parameters invalid"} | {"code":"-1128","msg":"Invalid limit price"} | {"code":"-1128","msg":"Can not find account: null"}
364
- '-2010': ExchangeError, # generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc...
365
- '-2011': OrderNotFound, # cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'
366
- '-2013': OrderNotFound, # fetchOrder(1, 'BTC/USDT') -> 'Order does not exist'
367
- '-2014': AuthenticationError, # {"code":-2014, "msg": "API-key format invalid."}
368
- '-2015': AuthenticationError, # "Invalid API-key, IP, or permissions for action."
369
- },
370
- },
371
- 'commonCurrencies': {
372
- 'ACN': 'Accenture',
373
- 'AMC': 'AMC Entertainment Holdings',
374
- 'BNS': 'Bank of Nova Scotia',
375
- 'CAR': 'Avis Budget Group Inc',
376
- 'CLR': 'Continental Resources',
377
- 'EDU': 'New Oriental Education & Technology Group Inc',
378
- 'ETN': 'Eaton',
379
- 'FOX': 'Fox Corporation',
380
- 'GM': 'General Motors Co',
381
- 'IQ': 'iQIYI',
382
- 'OSK': 'Oshkosh',
383
- 'PLAY': "Dave & Buster's Entertainment",
384
- },
385
- })
386
-
387
- def nonce(self):
388
- return self.milliseconds() - self.options['timeDifference']
389
-
390
- async def fetch_time(self, params={}) -> Int:
391
- """
392
- fetches the current integer timestamp in milliseconds from the exchange server
393
-
394
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/timeUsingGET
395
-
396
- :param dict [params]: extra parameters specific to the exchange API endpoint
397
- :returns int: the current integer timestamp in milliseconds from the exchange server
398
- """
399
- response = await self.publicGetV2Time(params)
400
- #
401
- # {
402
- # "serverTime": 1590998366609
403
- # }
404
- #
405
- return self.safe_integer(response, 'serverTime')
406
-
407
- async def fetch_currencies(self, params={}) -> Currencies:
408
- """
409
- fetches all available currencies on an exchange
410
-
411
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getCurrenciesUsingGET
412
-
413
- :param dict [params]: extra parameters specific to the exchange API endpoint
414
- :returns dict: an associative dictionary of currencies
415
- """
416
- # requires authentication
417
- if not self.check_required_credentials(False):
418
- return None
419
- response = await self.privateGetV2Currencies(params)
420
- #
421
- # [
422
- # {
423
- # "name": "Euro",
424
- # "displaySymbol": "EUR.cx",
425
- # "precision": "2",
426
- # "type": "FIAT",
427
- # "minWithdrawal": "90.0",
428
- # "maxWithdrawal": "1.0E+8",
429
- # "commissionMin": "0.02", # some instruments do not have self property
430
- # "commissionPercent": "1.5", # some instruments do not have self property
431
- # "minDeposit": "90.0",
432
- # },
433
- # {
434
- # "name": "Bitcoin",
435
- # "displaySymbol": "BTC",
436
- # "precision": "8",
437
- # "type": "CRYPTO", # only a few major currencies have self value, others like USDT have a value of "TOKEN"
438
- # "minWithdrawal": "0.00020",
439
- # "commissionFixed": "0.00010",
440
- # "minDeposit": "0.00010",
441
- # },
442
- # ]
443
- #
444
- result: dict = {}
445
- for i in range(0, len(response)):
446
- currency = response[i]
447
- id = self.safe_string(currency, 'displaySymbol')
448
- code = self.safe_currency_code(id)
449
- fee = self.safe_number(currency, 'commissionFixed')
450
- result[code] = {
451
- 'id': id,
452
- 'code': code,
453
- 'type': self.safe_string_lower(currency, 'type'),
454
- 'name': self.safe_string(currency, 'name'),
455
- 'active': None,
456
- 'deposit': None,
457
- 'withdraw': None,
458
- 'fee': fee,
459
- 'precision': self.parse_number(self.parse_precision(self.safe_string(currency, 'precision'))),
460
- 'limits': {
461
- 'amount': {
462
- 'min': None,
463
- 'max': None,
464
- },
465
- 'withdraw': {
466
- 'min': self.safe_number(currency, 'minWithdrawal'),
467
- 'max': self.safe_number(currency, 'maxWithdrawal'),
468
- },
469
- 'deposit': {
470
- 'min': self.safe_number(currency, 'minDeposit'),
471
- 'max': None,
472
- },
473
- },
474
- 'info': currency,
475
- }
476
- return result
477
-
478
- async def fetch_markets(self, params={}) -> List[Market]:
479
- """
480
- retrieves data on all markets for currencycom
481
-
482
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/exchangeInfoUsingGET
483
-
484
- :param dict [params]: extra parameters specific to the exchange API endpoint
485
- :returns dict[]: an array of objects representing market data
486
- """
487
- response = await self.publicGetV2ExchangeInfo(params)
488
- #
489
- # {
490
- # "timezone": "UTC",
491
- # "serverTime": "1645186287261",
492
- # "rateLimits": [
493
- # {rateLimitType: "REQUEST_WEIGHT", interval: "MINUTE", intervalNum: "1", limit: "1200"},
494
- # {rateLimitType: "ORDERS", interval: "SECOND", intervalNum: "1", limit: "10"},
495
- # {rateLimitType: "ORDERS", interval: "DAY", intervalNum: "1", limit: "864000"},
496
- # ],
497
- # "exchangeFilters": [],
498
- # "symbols": [
499
- # {
500
- # "symbol": "BTC/USDT", # BTC/USDT, BTC/USDT_LEVERAGE
501
- # "name": "Bitcoin / Tether",
502
- # "status": "TRADING", # TRADING, BREAK, HALT
503
- # "baseAsset": "BTC",
504
- # "baseAssetPrecision": "4",
505
- # "quoteAsset": "USDT",
506
- # "quoteAssetId": "USDT", # USDT, USDT_LEVERAGE
507
- # "quotePrecision": "4",
508
- # "orderTypes": ["LIMIT", "MARKET"], # LIMIT, MARKET, STOP
509
- # "filters": [
510
- # {filterType: "LOT_SIZE", minQty: "0.0001", maxQty: "100", stepSize: "0.0001",},
511
- # {filterType: "MIN_NOTIONAL", minNotional: "5",},
512
- # ],
513
- # "marketModes": ["REGULAR"], # CLOSE_ONLY, LONG_ONLY, REGULAR
514
- # "marketType": "SPOT", # SPOT, LEVERAGE
515
- # "longRate": -0.0684932, # LEVERAGE only
516
- # "shortRate": -0.0684932, # LEVERAGE only
517
- # "swapChargeInterval": 1440, # LEVERAGE only
518
- # "country": "",
519
- # "sector": "",
520
- # "industry": "",
521
- # "tradingHours": "UTC; Mon - 22:00, 22:05 -; Tue - 22:00, 22:05 -; Wed - 22:00, 22:05 -; Thu - 22:00, 22:05 -; Fri - 22:00, 23:01 -; Sat - 22:00, 22:05 -; Sun - 21:00, 22:05 -",
522
- # "tickSize": "0.01",
523
- # "tickValue": "403.4405", # not available in BTC/USDT_LEVERAGE, but available in BTC/USD_LEVERAGE
524
- # "exchangeFee": "0.2", # SPOT only
525
- # "tradingFee": 0.075, # LEVERAGE only
526
- # "makerFee": -0.025, # LEVERAGE only
527
- # "takerFee": 0.06, # LEVERAGE only
528
- # "maxSLGap": 50, # LEVERAGE only
529
- # "minSLGap": 1, # LEVERAGE only
530
- # "maxTPGap": 50, # LEVERAGE only
531
- # "minTPGap": 0.5, # LEVERAGE only
532
- # "assetType": "CRYPTOCURRENCY",
533
- # },
534
- # ]
535
- # }
536
- #
537
- if self.options['adjustForTimeDifference']:
538
- await self.load_time_difference()
539
- markets = self.safe_value(response, 'symbols', [])
540
- result = []
541
- for i in range(0, len(markets)):
542
- market = markets[i]
543
- id = self.safe_string(market, 'symbol')
544
- baseId = self.safe_string(market, 'baseAsset')
545
- quoteId = self.safe_string(market, 'quoteAsset')
546
- base = self.safe_currency_code(baseId)
547
- quote = self.safe_currency_code(quoteId)
548
- symbol = base + '/' + quote
549
- typeRaw = self.safe_string(market, 'marketType')
550
- spot = (typeRaw == 'SPOT')
551
- futures = False
552
- swap = (typeRaw == 'LEVERAGE')
553
- type = 'swap' if swap else 'spot'
554
- margin = None
555
- if swap:
556
- symbol = symbol.replace(self.options['leverage_markets_suffix'], '')
557
- symbol += ':' + quote
558
- active = self.safe_string(market, 'status') == 'TRADING'
559
- # to set taker & maker fees, we use one from the below data - pairs either have 'exchangeFee' or 'tradingFee', if none of them(rare cases), then they should have 'takerFee & makerFee'
560
- exchangeFee = self.safe_string_2(market, 'exchangeFee', 'tradingFee')
561
- makerFee = self.safe_string(market, 'makerFee', exchangeFee)
562
- takerFee = self.safe_string(market, 'takerFee', exchangeFee)
563
- makerFee = Precise.string_div(makerFee, '100')
564
- takerFee = Precise.string_div(takerFee, '100')
565
- filters = self.safe_value(market, 'filters', [])
566
- filtersByType = self.index_by(filters, 'filterType')
567
- limitPriceMin = None
568
- limitPriceMax = None
569
- precisionPrice = self.safe_number(market, 'tickSize')
570
- if 'PRICE_FILTER' in filtersByType:
571
- filter = self.safe_value(filtersByType, 'PRICE_FILTER', {})
572
- precisionPrice = self.safe_number(filter, 'tickSize')
573
- # PRICE_FILTER reports zero values for maxPrice
574
- # since they updated filter types in November 2018
575
- # https://github.com/ccxt/ccxt/issues/4286
576
- # therefore limits['price']['max'] doesn't have any meaningful value except None
577
- limitPriceMin = self.safe_number(filter, 'minPrice')
578
- maxPrice = self.safe_string(filter, 'maxPrice')
579
- if (maxPrice is not None) and (Precise.string_gt(maxPrice, '0')):
580
- limitPriceMax = maxPrice
581
- precisionAmount = self.parse_number(self.parse_precision(self.safe_string(market, 'baseAssetPrecision')))
582
- limitAmount: dict = {
583
- 'min': None,
584
- 'max': None,
585
- }
586
- if 'LOT_SIZE' in filtersByType:
587
- filter = self.safe_value(filtersByType, 'LOT_SIZE', {})
588
- precisionAmount = self.safe_number(filter, 'stepSize')
589
- limitAmount = {
590
- 'min': self.safe_number(filter, 'minQty'),
591
- 'max': self.safe_number(filter, 'maxQty'),
592
- }
593
- limitMarket: dict = {
594
- 'min': None,
595
- 'max': None,
596
- }
597
- if 'MARKET_LOT_SIZE' in filtersByType:
598
- filter = self.safe_value(filtersByType, 'MARKET_LOT_SIZE', {})
599
- limitMarket = {
600
- 'min': self.safe_number(filter, 'minQty'),
601
- 'max': self.safe_number(filter, 'maxQty'),
602
- }
603
- costMin = None
604
- if 'MIN_NOTIONAL' in filtersByType:
605
- filter = self.safe_value(filtersByType, 'MIN_NOTIONAL', {})
606
- costMin = self.safe_number(filter, 'minNotional')
607
- isContract = swap or futures
608
- result.append({
609
- 'id': id,
610
- 'symbol': symbol,
611
- 'base': base,
612
- 'quote': quote,
613
- 'settle': None,
614
- 'baseId': baseId,
615
- 'quoteId': quoteId,
616
- 'settleId': None,
617
- 'type': type,
618
- 'spot': spot,
619
- 'margin': margin,
620
- 'swap': swap,
621
- 'future': futures,
622
- 'option': False,
623
- 'active': active,
624
- 'contract': isContract,
625
- 'linear': True if isContract else None,
626
- 'inverse': None,
627
- 'taker': self.parse_number(takerFee),
628
- 'maker': self.parse_number(makerFee),
629
- 'contractSize': None,
630
- 'expiry': None,
631
- 'expiryDatetime': None,
632
- 'strike': None,
633
- 'optionType': None,
634
- 'precision': {
635
- 'amount': precisionAmount,
636
- 'price': precisionPrice,
637
- },
638
- 'limits': {
639
- 'leverage': {
640
- 'min': None,
641
- 'max': None,
642
- },
643
- 'amount': limitAmount,
644
- 'market': limitMarket,
645
- 'price': {
646
- 'min': limitPriceMin,
647
- 'max': self.parse_number(limitPriceMax),
648
- },
649
- 'cost': {
650
- 'min': costMin,
651
- 'max': None,
652
- },
653
- },
654
- 'created': None,
655
- 'info': market,
656
- })
657
- return result
658
-
659
- async def fetch_accounts(self, params={}) -> List[Account]:
660
- """
661
- fetch all the accounts associated with a profile
662
-
663
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/accountUsingGET
664
-
665
- :param dict [params]: extra parameters specific to the exchange API endpoint
666
- :returns dict: a dictionary of `account structures <https://docs.ccxt.com/#/?id=account-structure>` indexed by the account type
667
- """
668
- response = await self.privateGetV2Account(params)
669
- #
670
- # {
671
- # "makerCommission": "0.20",
672
- # "takerCommission": "0.20",
673
- # "buyerCommission": "0.20",
674
- # "sellerCommission": "0.20",
675
- # "canTrade": True,
676
- # "canWithdraw": True,
677
- # "canDeposit": True,
678
- # "updateTime": "1645266330",
679
- # "userId": "644722",
680
- # "balances": [
681
- # {
682
- # "accountId": "120702016179403605",
683
- # "collateralCurrency": False,
684
- # "asset": "CAKE",
685
- # "free": "3.1",
686
- # "locked": "0.0",
687
- # "default": False,
688
- # },
689
- # {
690
- # "accountId": "109698017713125316",
691
- # "collateralCurrency": True,
692
- # "asset": "USD",
693
- # "free": "17.58632",
694
- # "locked": "0.0",
695
- # "default": True,
696
- # }
697
- # ]
698
- # }
699
- #
700
- accounts = self.safe_value(response, 'balances', [])
701
- result = []
702
- for i in range(0, len(accounts)):
703
- account = accounts[i]
704
- accountId = self.safe_string(account, 'accountId') # must be string, because the numeric value is far too big for integer, and causes bugs
705
- currencyId = self.safe_string(account, 'asset')
706
- currencyCode = self.safe_currency_code(currencyId)
707
- result.append({
708
- 'id': accountId,
709
- 'type': None,
710
- 'currency': currencyCode,
711
- 'info': account,
712
- })
713
- return result
714
-
715
- async def fetch_trading_fees(self, params={}) -> TradingFees:
716
- """
717
- fetch the trading fees for multiple markets
718
-
719
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/accountUsingGET
720
-
721
- :param dict [params]: extra parameters specific to the exchange API endpoint
722
- :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
723
- """
724
- await self.load_markets()
725
- response = await self.privateGetV2Account(params)
726
- #
727
- # {
728
- # "makerCommission": "0.20",
729
- # "takerCommission": "0.20",
730
- # "buyerCommission": "0.20",
731
- # "sellerCommission": "0.20",
732
- # "canTrade": True,
733
- # "canWithdraw": True,
734
- # "canDeposit": True,
735
- # "updateTime": "1645738976",
736
- # "userId": "-1924114235",
737
- # "balances": []
738
- # }
739
- #
740
- makerFee = self.safe_number(response, 'makerCommission')
741
- takerFee = self.safe_number(response, 'takerCommission')
742
- result: dict = {}
743
- for i in range(0, len(self.symbols)):
744
- symbol = self.symbols[i]
745
- result[symbol] = {
746
- 'info': response,
747
- 'symbol': symbol,
748
- 'maker': makerFee,
749
- 'taker': takerFee,
750
- 'percentage': True,
751
- 'tierBased': False,
752
- }
753
- return result
754
-
755
- def parse_balance(self, response, type=None):
756
- #
757
- # {
758
- # "makerCommission":0.20,
759
- # "takerCommission":0.20,
760
- # "buyerCommission":0.20,
761
- # "sellerCommission":0.20,
762
- # "canTrade":true,
763
- # "canWithdraw":true,
764
- # "canDeposit":true,
765
- # "updateTime":1591056268,
766
- # "balances":[
767
- # {
768
- # "accountId":5470306579272368,
769
- # "collateralCurrency":true,
770
- # "asset":"ETH",
771
- # "free":0.0,
772
- # "locked":0.0,
773
- # "default":false,
774
- # },
775
- # ]
776
- # }
777
- #
778
- result: dict = {'info': response}
779
- balances = self.safe_value(response, 'balances', [])
780
- for i in range(0, len(balances)):
781
- balance = balances[i]
782
- currencyId = self.safe_string(balance, 'asset')
783
- code = self.safe_currency_code(currencyId)
784
- account = self.account()
785
- account['free'] = self.safe_string(balance, 'free')
786
- account['used'] = self.safe_string(balance, 'locked')
787
- result[code] = account
788
- return self.safe_balance(result)
789
-
790
- async def fetch_balance(self, params={}) -> Balances:
791
- """
792
- query for balance and get the amount of funds available for trading or funds locked in orders
793
-
794
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/accountUsingGET
795
-
796
- :param dict [params]: extra parameters specific to the exchange API endpoint
797
- :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
798
- """
799
- await self.load_markets()
800
- response = await self.privateGetV2Account(params)
801
- #
802
- # {
803
- # "makerCommission": "0.20",
804
- # "takerCommission": "0.20",
805
- # "buyerCommission": "0.20",
806
- # "sellerCommission": "0.20",
807
- # "canTrade": True,
808
- # "canWithdraw": True,
809
- # "canDeposit": True,
810
- # "updateTime": "1645266330",
811
- # "userId": "644722",
812
- # "balances": [
813
- # {
814
- # "accountId": "120702016179403605",
815
- # "collateralCurrency": False,
816
- # "asset": "CAKE",
817
- # "free": "1.784",
818
- # "locked": "0.0",
819
- # "default": False,
820
- # },
821
- # {
822
- # "accountId": "109698017413175316",
823
- # "collateralCurrency": True,
824
- # "asset": "USD",
825
- # "free": "7.58632",
826
- # "locked": "0.0",
827
- # "default": True,
828
- # }
829
- # ]
830
- # }
831
- #
832
- return self.parse_balance(response)
833
-
834
- async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
835
- """
836
- fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
837
-
838
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/depthUsingGET
839
-
840
- :param str symbol: unified symbol of the market to fetch the order book for
841
- :param int [limit]: the maximum amount of order book entries to return
842
- :param dict [params]: extra parameters specific to the exchange API endpoint
843
- :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
844
- """
845
- await self.load_markets()
846
- market = self.market(symbol)
847
- request: dict = {
848
- 'symbol': market['id'],
849
- }
850
- if limit is not None:
851
- request['limit'] = limit # default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000, 5000
852
- response = await self.publicGetV2Depth(self.extend(request, params))
853
- #
854
- # {
855
- # "lastUpdateId":1590999849037,
856
- # "asks":[
857
- # [0.02495,60.0345],
858
- # [0.02496,34.1],
859
- # ...
860
- # ],
861
- # "bids":[
862
- # [0.02487,72.4144854],
863
- # [0.02486,24.043],
864
- # ...
865
- # ]
866
- # }
867
- #
868
- orderbook = self.parse_order_book(response, symbol)
869
- orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
870
- return orderbook
871
-
872
- def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
873
- #
874
- # fetchTicker
875
- #
876
- # {
877
- # "symbol":"ETH/BTC",
878
- # "priceChange":"0.00030",
879
- # "priceChangePercent":"1.21",
880
- # "weightedAvgPrice":"0.02481",
881
- # "prevClosePrice":"0.02447",
882
- # "lastPrice":"0.02477",
883
- # "lastQty":"60.0",
884
- # "bidPrice":"0.02477",
885
- # "askPrice":"0.02484",
886
- # "openPrice":"0.02447",
887
- # "highPrice":"0.02524",
888
- # "lowPrice":"0.02438",
889
- # "volume":"11.97",
890
- # "quoteVolume":"0.298053",
891
- # "openTime":1590969600000,
892
- # "closeTime":1591000072693
893
- # }
894
- #
895
- # fetchTickers
896
- #
897
- # {
898
- # "symbol": "SHIB/USD_LEVERAGE",
899
- # "weightedAvgPrice": "0.000027595",
900
- # "lastPrice": "0.00002737",
901
- # "lastQty": "1.11111111E8",
902
- # "bidPrice": "0.00002737",
903
- # "askPrice": "0.00002782",
904
- # "highPrice": "0.00002896",
905
- # "lowPrice": "0.00002738",
906
- # "volume": "16472160000",
907
- # "quoteVolume": "454796.3376",
908
- # "openTime": "1645187472000",
909
- # "closeTime": "1645273872000",
910
- # }
911
- #
912
- # ws:marketData.subscribe
913
- #
914
- # {
915
- # "symbolName":"TXN",
916
- # "bid":139.85,
917
- # "bidQty":2500,
918
- # "ofr":139.92000000000002,
919
- # "ofrQty":2500,
920
- # "timestamp":1597850971558
921
- # }
922
- #
923
- timestamp = self.safe_integer_2(ticker, 'closeTime', 'timestamp')
924
- marketId = self.safe_string_2(ticker, 'symbol', 'symbolName')
925
- market = self.safe_market(marketId, market, '/')
926
- last = self.safe_string(ticker, 'lastPrice')
927
- return self.safe_ticker({
928
- 'symbol': market['symbol'],
929
- 'timestamp': timestamp,
930
- 'datetime': self.iso8601(timestamp),
931
- 'high': self.safe_string(ticker, 'highPrice'),
932
- 'low': self.safe_string(ticker, 'lowPrice'),
933
- 'bid': self.safe_string_2(ticker, 'bidPrice', 'bid'),
934
- 'bidVolume': self.safe_string(ticker, 'bidQty'),
935
- 'ask': self.safe_string_2(ticker, 'askPrice', 'ofr'),
936
- 'askVolume': self.safe_string(ticker, 'ofrQty'),
937
- 'vwap': self.safe_string(ticker, 'weightedAvgPrice'),
938
- 'open': self.safe_string(ticker, 'openPrice'),
939
- 'close': last,
940
- 'last': last,
941
- 'previousClose': self.safe_string(ticker, 'prevClosePrice'), # previous day close
942
- 'change': self.safe_string(ticker, 'priceChange'),
943
- 'percentage': self.safe_string(ticker, 'priceChangePercent'),
944
- 'average': None,
945
- 'baseVolume': self.safe_string(ticker, 'volume'),
946
- 'quoteVolume': self.safe_string(ticker, 'quoteVolume'),
947
- 'info': ticker,
948
- }, market)
949
-
950
- async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
951
- """
952
- fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
953
-
954
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/ticker_24hrUsingGET
955
-
956
- :param str symbol: unified symbol of the market to fetch the ticker for
957
- :param dict [params]: extra parameters specific to the exchange API endpoint
958
- :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
959
- """
960
- await self.load_markets()
961
- market = self.market(symbol)
962
- request: dict = {
963
- 'symbol': market['id'],
964
- }
965
- response = await self.publicGetV2Ticker24hr(self.extend(request, params))
966
- #
967
- # {
968
- # "symbol":"ETH/BTC",
969
- # "priceChange":"0.00030",
970
- # "priceChangePercent":"1.21",
971
- # "weightedAvgPrice":"0.02481",
972
- # "prevClosePrice":"0.02447",
973
- # "lastPrice":"0.02477",
974
- # "lastQty":"60.0",
975
- # "bidPrice":"0.02477",
976
- # "askPrice":"0.02484",
977
- # "openPrice":"0.02447",
978
- # "highPrice":"0.02524",
979
- # "lowPrice":"0.02438",
980
- # "volume":"11.97",
981
- # "quoteVolume":"0.298053",
982
- # "openTime":1590969600000,
983
- # "closeTime":1591000072693
984
- # }
985
- #
986
- return self.parse_ticker(response, market)
987
-
988
- async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
989
- """
990
- fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
991
-
992
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/ticker_24hrUsingGET
993
-
994
- :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
995
- :param dict [params]: extra parameters specific to the exchange API endpoint
996
- :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
997
- """
998
- await self.load_markets()
999
- response = await self.publicGetV2Ticker24hr(params)
1000
- #
1001
- # [
1002
- # {
1003
- # "symbol": "SHIB/USD_LEVERAGE",
1004
- # "weightedAvgPrice": "0.000027595",
1005
- # "lastPrice": "0.00002737",
1006
- # "lastQty": "1.11111111E8",
1007
- # "bidPrice": "0.00002737",
1008
- # "askPrice": "0.00002782",
1009
- # "highPrice": "0.00002896",
1010
- # "lowPrice": "0.00002738",
1011
- # "volume": "16472160000",
1012
- # "quoteVolume": "454796.3376",
1013
- # "openTime": "1645187472000",
1014
- # "closeTime": "1645273872000",
1015
- # }
1016
- # ]
1017
- #
1018
- return self.parse_tickers(response, symbols)
1019
-
1020
- def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
1021
- #
1022
- # [
1023
- # 1590971040000,
1024
- # "0.02454",
1025
- # "0.02456",
1026
- # "0.02452",
1027
- # "0.02456",
1028
- # 249
1029
- # ]
1030
- #
1031
- return [
1032
- self.safe_integer(ohlcv, 0),
1033
- self.safe_number(ohlcv, 1),
1034
- self.safe_number(ohlcv, 2),
1035
- self.safe_number(ohlcv, 3),
1036
- self.safe_number(ohlcv, 4),
1037
- self.safe_number(ohlcv, 5),
1038
- ]
1039
-
1040
- async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1041
- """
1042
- fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1043
-
1044
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/klinesUsingGET
1045
-
1046
- :param str symbol: unified symbol of the market to fetch OHLCV data for
1047
- :param str timeframe: the length of time each candle represents
1048
- :param int [since]: timestamp in ms of the earliest candle to fetch
1049
- :param int [limit]: the maximum amount of candles to fetch
1050
- :param dict [params]: extra parameters specific to the exchange API endpoint
1051
- :returns int[][]: A list of candles ordered, open, high, low, close, volume
1052
- """
1053
- await self.load_markets()
1054
- market = self.market(symbol)
1055
- request: dict = {
1056
- 'symbol': market['id'],
1057
- 'interval': self.safe_string(self.timeframes, timeframe, timeframe),
1058
- }
1059
- if since is not None:
1060
- request['startTime'] = since
1061
- if limit is not None:
1062
- request['limit'] = min(limit, 1000) # default 500, max 1000
1063
- response = await self.publicGetV2Klines(self.extend(request, params))
1064
- #
1065
- # [
1066
- # [1590971040000,"0.02454","0.02456","0.02452","0.02456",249],
1067
- # [1590971100000,"0.02455","0.02457","0.02452","0.02456",300],
1068
- # [1590971160000,"0.02455","0.02456","0.02453","0.02454",286],
1069
- # ]
1070
- #
1071
- return self.parse_ohlcvs(response, market, timeframe, since, limit)
1072
-
1073
- def parse_trade(self, trade: dict, market: Market = None) -> Trade:
1074
- #
1075
- # fetchTrades(public aggregate trades)
1076
- #
1077
- # {
1078
- # "a":"1658318071", # Aggregate tradeId
1079
- # "p":"0.02476", # Price
1080
- # "q":"0.0", # Official doc says: "Quantity(should be ignored)"
1081
- # "T":"1591001423382", # Epoch timestamp in MS
1082
- # "m":false # Was the buyer the maker
1083
- # }
1084
- #
1085
- # createOrder fills(private)
1086
- #
1087
- # {
1088
- # "price": "9807.05",
1089
- # "qty": "0.01",
1090
- # "commission": "0",
1091
- # "commissionAsset": "dUSD"
1092
- # }
1093
- #
1094
- # fetchMyTrades
1095
- #
1096
- # {
1097
- # "symbol": "DOGE/USD",
1098
- # "id": "116046000",
1099
- # "orderId": "00000000-0000-0000-0000-000006dbb8ad",
1100
- # "price": "0.14094",
1101
- # "qty": "40.0",
1102
- # "commission": "0.01",
1103
- # "commissionAsset": "USD",
1104
- # "time": "1645283022351",
1105
- # "buyer": False,
1106
- # "maker": False,
1107
- # "isBuyer": False,
1108
- # "isMaker": False
1109
- # }
1110
- #
1111
- timestamp = self.safe_integer_2(trade, 'T', 'time')
1112
- priceString = self.safe_string_2(trade, 'p', 'price')
1113
- amountString = self.safe_string_2(trade, 'q', 'qty')
1114
- id = self.safe_string_2(trade, 'a', 'id')
1115
- side = None
1116
- orderId = self.safe_string(trade, 'orderId')
1117
- takerOrMaker = None
1118
- if 'm' in trade:
1119
- side = 'sell' if trade['m'] else 'buy' # self is reversed intentionally [TODO: needs reason to be mentioned]
1120
- takerOrMaker = 'taker' # in public trades, it's always taker
1121
- elif 'isBuyer' in trade:
1122
- side = 'buy' if (trade['isBuyer']) else 'sell' # self is a True side
1123
- takerOrMaker = 'maker' if trade['isMaker'] else 'taker'
1124
- fee = None
1125
- if 'commission' in trade:
1126
- fee = {
1127
- 'cost': self.safe_string(trade, 'commission'),
1128
- 'currency': self.safe_currency_code(self.safe_string(trade, 'commissionAsset')),
1129
- }
1130
- marketId = self.safe_string(trade, 'symbol')
1131
- symbol = self.safe_symbol(marketId, market)
1132
- return self.safe_trade({
1133
- 'id': id,
1134
- 'order': orderId,
1135
- 'timestamp': timestamp,
1136
- 'datetime': self.iso8601(timestamp),
1137
- 'symbol': symbol,
1138
- 'type': None,
1139
- 'takerOrMaker': takerOrMaker,
1140
- 'side': side,
1141
- 'price': priceString,
1142
- 'amount': amountString,
1143
- 'cost': None,
1144
- 'fee': fee,
1145
- 'info': trade,
1146
- }, market)
1147
-
1148
- async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1149
- """
1150
- get the list of most recent trades for a particular symbol
1151
-
1152
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/aggTradesUsingGET
1153
-
1154
- :param str symbol: unified symbol of the market to fetch trades for
1155
- :param int [since]: timestamp in ms of the earliest trade to fetch
1156
- :param int [limit]: the maximum amount of trades to fetch
1157
- :param dict [params]: extra parameters specific to the exchange API endpoint
1158
- :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1159
- """
1160
- await self.load_markets()
1161
- market = self.market(symbol)
1162
- request: dict = {
1163
- 'symbol': market['id'],
1164
- # 'limit': 500, # default 500, max 1000
1165
- }
1166
- if limit is not None:
1167
- request['limit'] = min(limit, 1000) # default 500, max 1000
1168
- if since is not None:
1169
- request['startTime'] = since
1170
- response = await self.publicGetV2AggTrades(self.extend(request, params))
1171
- #
1172
- # [
1173
- # {
1174
- # "a":"1658318071", # Aggregate tradeId
1175
- # "p":"0.02476", # Price
1176
- # "q":"0.0", # Official doc says: "Quantity(should be ignored)"
1177
- # "T":"1591001423382", # Epoch timestamp in MS
1178
- # "m":false # Was the buyer the maker
1179
- # },
1180
- # ]
1181
- #
1182
- return self.parse_trades(response, market, since, limit)
1183
-
1184
- def parse_order(self, order: dict, market: Market = None) -> Order:
1185
- #
1186
- # createOrder
1187
- #
1188
- # limit
1189
- #
1190
- # {
1191
- # "symbol": "BTC/USD",
1192
- # "orderId": "00000000-0000-0000-0000-000006eacaa0",
1193
- # "transactTime": "1645281669295",
1194
- # "price": "30000.00000000",
1195
- # "origQty": "0.0002", # might not be present for "market" order
1196
- # "executedQty": "0.0", # positive for BUY, negative for SELL. This property might not be present in Leverage markets
1197
- # "margin": 0.1, # present in leverage markets
1198
- # "status": "NEW", # NEW, FILLED, ...
1199
- # "timeInForce": "GTC",
1200
- # "type": "LIMIT", # LIMIT, MARKET
1201
- # "side": "BUY",
1202
- # "fills": [ # self field might not be present if there were no fills
1203
- # {
1204
- # "price": "0.14094",
1205
- # "qty": "40.0",
1206
- # "commission": "0",
1207
- # "commissionAsset": "dUSD",
1208
- # },
1209
- # ],
1210
- # }
1211
- #
1212
- # fetchOrder(fetchOpenOrders is an array same structure, with some extra fields)
1213
- #
1214
- # {
1215
- # "symbol": "BTC/USD_LEVERAGE",
1216
- # "accountId": "123456789012345678",
1217
- # "orderId": "00a01234-0123-54c4-0000-123451234567",
1218
- # "price": "25779.35",
1219
- # "status": "MODIFIED",
1220
- # "type": "LIMIT",
1221
- # "timeInForceType": "GTC",
1222
- # "side": "BUY",
1223
- # "guaranteedStopLoss": False,
1224
- # "trailingStopLoss": False,
1225
- # "margin": "0.05",
1226
- # "takeProfit": "27020.00",
1227
- # "stopLoss": "24500.35",
1228
- # "fills": [], # might not be present
1229
- # "timestamp": "1685958369623", # "time" in "fetchOpenOrders"
1230
- # "expireTime": "1686167960000", # "expireTimestamp" in "fetchOpenOrders"
1231
- # "quantity": "0.00040", # "origQty" in "fetchOpenOrders"
1232
- # "executedQty": "0.0", # present in "fetchOpenOrders"
1233
- # "updateTime": "1685958369542", # present in "fetchOpenOrders"
1234
- # "leverage": True, # present in "fetchOpenOrders"
1235
- # "working": True # present in "fetchOpenOrders"
1236
- # }
1237
- #
1238
- # cancelOrder
1239
- #
1240
- # {
1241
- # "symbol": "DOGE/USD",
1242
- # "orderId": "00000000-0000-0003-0000-000006db714c",
1243
- # "price": "0.13",
1244
- # "origQty": "30.0",
1245
- # "executedQty": "0.0",
1246
- # "status": "CANCELED",
1247
- # "timeInForce": "GTC",
1248
- # "type": "LIMIT",
1249
- # "side": "BUY",
1250
- # }
1251
- #
1252
- marketId = self.safe_string(order, 'symbol')
1253
- symbol = self.safe_symbol(marketId, market, '/')
1254
- id = self.safe_string(order, 'orderId')
1255
- price = self.safe_string(order, 'price')
1256
- amount = self.safe_string_2(order, 'origQty', 'quantity')
1257
- filledRaw = self.safe_string(order, 'executedQty')
1258
- filled = Precise.string_abs(filledRaw)
1259
- status = self.parse_order_status(self.safe_string(order, 'status'))
1260
- timeInForce = self.parse_order_time_in_force(self.safe_string_2(order, 'timeInForce', 'timeInForceType'))
1261
- type = self.parse_order_type(self.safe_string(order, 'type'))
1262
- side = self.parse_order_side(self.safe_string(order, 'side'))
1263
- timestamp = self.safe_integer_n(order, ['time', 'transactTime', 'timestamp'])
1264
- fills = self.safe_value(order, 'fills')
1265
- return self.safe_order({
1266
- 'info': order,
1267
- 'id': id,
1268
- 'timestamp': timestamp,
1269
- 'datetime': self.iso8601(timestamp),
1270
- 'lastTradeTimestamp': None,
1271
- 'symbol': symbol,
1272
- 'type': type,
1273
- 'timeInForce': timeInForce,
1274
- 'side': side,
1275
- 'price': price,
1276
- 'triggerPrice': None,
1277
- 'amount': amount,
1278
- 'cost': None,
1279
- 'average': None,
1280
- 'filled': filled,
1281
- 'remaining': None,
1282
- 'status': status,
1283
- 'fee': None,
1284
- 'trades': fills,
1285
- }, market)
1286
-
1287
- def parse_order_status(self, status: Str):
1288
- statuses: dict = {
1289
- 'NEW': 'open',
1290
- 'CREATED': 'open',
1291
- 'MODIFIED': 'open',
1292
- 'PARTIALLY_FILLED': 'open',
1293
- 'FILLED': 'closed',
1294
- 'CANCELED': 'canceled',
1295
- 'PENDING_CANCEL': 'canceling',
1296
- 'REJECTED': 'rejected',
1297
- 'EXPIRED': 'expired',
1298
- }
1299
- return self.safe_string(statuses, status, status)
1300
-
1301
- def parse_order_type(self, status):
1302
- statuses: dict = {
1303
- 'MARKET': 'market',
1304
- 'LIMIT': 'limit',
1305
- 'STOP': 'stop',
1306
- # temporarily we remove custom mappings
1307
- # 'LIMIT_MAKER': '',
1308
- # 'STOP_LOSS': 'stop-loss',
1309
- # 'STOP_LOSS_LIMIT': 'stop-limit',
1310
- # 'TAKE_PROFIT': 'take-profit',
1311
- # 'TAKE_PROFIT_LIMIT': 'take-profit',
1312
- }
1313
- return self.safe_string(statuses, status, status)
1314
-
1315
- def parse_order_time_in_force(self, status):
1316
- statuses: dict = {
1317
- 'GTC': 'GTC',
1318
- 'FOK': 'FOK',
1319
- 'IOC': 'IOC',
1320
- }
1321
- return self.safe_string(statuses, status, status)
1322
-
1323
- def parse_order_side(self, status):
1324
- statuses: dict = {
1325
- 'BUY': 'buy',
1326
- 'SELL': 'sell',
1327
- }
1328
- return self.safe_string(statuses, status, status)
1329
-
1330
- async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1331
- """
1332
- create a trade order
1333
-
1334
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/orderUsingPOST
1335
-
1336
- :param str symbol: unified symbol of the market to create an order in
1337
- :param str type: 'market' or 'limit'
1338
- :param str side: 'buy' or 'sell'
1339
- :param float amount: how much of currency you want to trade in units of base currency
1340
- :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1341
- :param dict [params]: extra parameters specific to the exchange API endpoint
1342
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1343
- """
1344
- await self.load_markets()
1345
- market = self.market(symbol)
1346
- accountId = None
1347
- if market['margin']:
1348
- accountId = self.safe_string(self.options, 'accountId')
1349
- accountId = self.safe_string(params, 'accountId', accountId)
1350
- if accountId is None:
1351
- raise ArgumentsRequired(self.id + " createOrder() requires an accountId parameter or an exchange.options['accountId'] option for " + market['type'] + ' markets')
1352
- newOrderRespType = self.safe_value(self.options['newOrderRespType'], type, 'RESULT')
1353
- request: dict = {
1354
- 'symbol': market['id'],
1355
- 'quantity': self.amount_to_precision(symbol, amount),
1356
- 'type': type.upper(),
1357
- 'side': side.upper(),
1358
- 'newOrderRespType': newOrderRespType, # 'RESULT' for full order or 'FULL' for order with fills
1359
- # 'leverage': 1,
1360
- # 'accountId': 5470306579272968, # required for leverage markets
1361
- # 'takeProfit': '123.45',
1362
- # 'stopLoss': '54.321',
1363
- # 'guaranteedStopLoss': '54.321',
1364
- }
1365
- if type == 'limit':
1366
- request['price'] = self.price_to_precision(symbol, price)
1367
- request['timeInForce'] = self.options['defaultTimeInForce']
1368
- else:
1369
- if type == 'stop':
1370
- request['type'] = 'STOP'
1371
- request['price'] = self.price_to_precision(symbol, price)
1372
- elif type == 'market':
1373
- triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
1374
- params = self.omit(params, ['triggerPrice', 'stopPrice'])
1375
- if triggerPrice is not None:
1376
- request['type'] = 'STOP'
1377
- request['price'] = self.price_to_precision(symbol, triggerPrice)
1378
- response = await self.privatePostV2Order(self.extend(request, params))
1379
- #
1380
- # limit
1381
- #
1382
- # {
1383
- # "symbol": "BTC/USD",
1384
- # "orderId": "00000000-0000-0000-0000-000006eaaaa0",
1385
- # "transactTime": "1645281669295",
1386
- # "price": "30000.00000000",
1387
- # "origQty": "0.0002",
1388
- # "executedQty": "0.0", # positive for BUY, negative for SELL
1389
- # "status": "NEW",
1390
- # "timeInForce": "GTC",
1391
- # "type": "LIMIT",
1392
- # "side": "BUY",
1393
- # }
1394
- #
1395
- # market
1396
- #
1397
- # {
1398
- # "symbol": "DOGE/USD",
1399
- # "orderId": "00000000-0000-0000-0000-000006eab8ad",
1400
- # "transactTime": "1645283022252",
1401
- # "price": "0.14066000",
1402
- # "origQty": "40",
1403
- # "executedQty": "40.0", # positive for BUY, negative for SELL
1404
- # "status": "FILLED",
1405
- # "timeInForce": "FOK",
1406
- # "type": "MARKET",
1407
- # "side": "BUY",
1408
- # "fills": [
1409
- # {
1410
- # "price": "0.14094",
1411
- # "qty": "40.0",
1412
- # "commission": "0",
1413
- # "commissionAsset": "dUSD"
1414
- # }
1415
- # ]
1416
- # }
1417
- #
1418
- return self.parse_order(response, market)
1419
-
1420
- async def fetch_order(self, id: str, symbol: Str = None, params={}):
1421
- """
1422
- fetches information on an order made by the user
1423
-
1424
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getOrderUsingGET
1425
-
1426
- :param str id: order id
1427
- :param str symbol: unified symbol of the market the order was made in
1428
- :param dict [params]: extra parameters specific to the exchange API endpoint
1429
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1430
- """
1431
- if symbol is None:
1432
- raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
1433
- await self.load_markets()
1434
- market = self.market(symbol)
1435
- request: dict = {
1436
- 'orderId': id,
1437
- 'symbol': market['id'],
1438
- }
1439
- response = await self.privateGetV2FetchOrder(self.extend(request, params))
1440
- #
1441
- # {
1442
- # "accountId": "109698017413125316",
1443
- # "orderId": "2810f1c5-0079-54c4-0000-000080421601",
1444
- # "quantity": "20.0",
1445
- # "price": "0.06",
1446
- # "timestamp": "1661157503788",
1447
- # "status": "CREATED",
1448
- # "type": "LIMIT",
1449
- # "timeInForceType": "GTC",
1450
- # "side": "BUY",
1451
- # "margin": "0.1",
1452
- # "fills": [ # might not be present
1453
- # {
1454
- # "price": "0.14094",
1455
- # "qty": "40.0",
1456
- # "commission": "0",
1457
- # "commissionAsset": "dUSD"
1458
- # }
1459
- # ]
1460
- # }
1461
- #
1462
- return self.parse_order(response)
1463
-
1464
- async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1465
- """
1466
- fetch all unfilled currently open orders
1467
-
1468
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/openOrdersUsingGET
1469
-
1470
- :param str symbol: unified market symbol
1471
- :param int [since]: the earliest time in ms to fetch open orders for
1472
- :param int [limit]: the maximum number of open orders structures to retrieve
1473
- :param dict [params]: extra parameters specific to the exchange API endpoint
1474
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1475
- """
1476
- await self.load_markets()
1477
- market = None
1478
- request: dict = {}
1479
- if symbol is not None:
1480
- market = self.market(symbol)
1481
- request['symbol'] = market['id']
1482
- elif self.options['warnOnFetchOpenOrdersWithoutSymbol']:
1483
- symbols = self.symbols
1484
- numSymbols = len(symbols)
1485
- fetchOpenOrdersRateLimit = self.parse_to_int(numSymbols / 2)
1486
- raise ExchangeError(self.id + ' fetchOpenOrders() WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + str(fetchOpenOrdersRateLimit) + ' seconds. Do not call self method frequently to avoid ban. Set ' + self.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = False to suppress self warning message.')
1487
- response = await self.privateGetV2OpenOrders(self.extend(request, params))
1488
- #
1489
- # [
1490
- # {
1491
- # "symbol": "DOGE/USD",
1492
- # "orderId": "00000000-0000-0003-0000-000004bac57a",
1493
- # "price": "0.13",
1494
- # "origQty": "39.0",
1495
- # "executedQty": "0.0", # positive for BUY, negative for SELL
1496
- # "status": "NEW",
1497
- # "timeInForce": "GTC",
1498
- # "type": "LIMIT",
1499
- # "side": "BUY",
1500
- # "time": "1645284216240",
1501
- # "updateTime": "1645284216240",
1502
- # "leverage": False,
1503
- # "working": True
1504
- # },
1505
- # ]
1506
- #
1507
- return self.parse_orders(response, market, since, limit, params)
1508
-
1509
- async def cancel_order(self, id: str, symbol: Str = None, params={}):
1510
- """
1511
- cancels an open order
1512
-
1513
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/cancelOrderUsingDELETE
1514
-
1515
- :param str id: order id
1516
- :param str symbol: unified symbol of the market the order was made in
1517
- :param dict [params]: extra parameters specific to the exchange API endpoint
1518
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1519
- """
1520
- if symbol is None:
1521
- raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
1522
- await self.load_markets()
1523
- market = self.market(symbol)
1524
- origClientOrderId = self.safe_value(params, 'origClientOrderId')
1525
- request: dict = {
1526
- 'symbol': market['id'],
1527
- # 'orderId': int(id),
1528
- # 'origClientOrderId': id,
1529
- }
1530
- if origClientOrderId is None:
1531
- request['orderId'] = id
1532
- else:
1533
- request['origClientOrderId'] = origClientOrderId
1534
- response = await self.privateDeleteV2Order(self.extend(request, params))
1535
- #
1536
- # {
1537
- # "symbol": "DOGE/USD",
1538
- # "orderId": "00000000-0000-0003-0000-000006db764c",
1539
- # "price": "0.13",
1540
- # "origQty": "30.0",
1541
- # "executedQty": "0.0", # positive for BUY, negative for SELL
1542
- # "status": "CANCELED",
1543
- # "timeInForce": "GTC",
1544
- # "type": "LIMIT",
1545
- # "side": "BUY",
1546
- # }
1547
- #
1548
- return self.parse_order(response, market)
1549
-
1550
- async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1551
- """
1552
- fetch all trades made by the user
1553
-
1554
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/myTradesUsingGET
1555
-
1556
- :param str symbol: unified market symbol
1557
- :param int [since]: the earliest time in ms to fetch trades for
1558
- :param int [limit]: the maximum number of trades structures to retrieve
1559
- :param dict [params]: extra parameters specific to the exchange API endpoint
1560
- :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1561
- """
1562
- if symbol is None:
1563
- raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
1564
- await self.load_markets()
1565
- market = self.market(symbol)
1566
- request: dict = {
1567
- 'symbol': market['id'],
1568
- }
1569
- if limit is not None:
1570
- request['limit'] = limit
1571
- response = await self.privateGetV2MyTrades(self.extend(request, params))
1572
- #
1573
- # [
1574
- # {
1575
- # "symbol": "DOGE/USD",
1576
- # "id": "116046000",
1577
- # "orderId": "00000000-0000-0000-0000-000006dbb8ad",
1578
- # "price": "0.14094",
1579
- # "qty": "40.0",
1580
- # "commission": "0.01",
1581
- # "commissionAsset": "USD",
1582
- # "time": "1645283022351",
1583
- # "buyer": False,
1584
- # "maker": False,
1585
- # "isBuyer": False,
1586
- # "isMaker": False
1587
- # },
1588
- # ]
1589
- #
1590
- return self.parse_trades(response, market, since, limit)
1591
-
1592
- async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1593
- """
1594
- fetch all deposits made to an account
1595
-
1596
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getDepositsUsingGET
1597
-
1598
- :param str code: unified currency code
1599
- :param int [since]: the earliest time in ms to fetch deposits for
1600
- :param int [limit]: the maximum number of deposits structures to retrieve
1601
- :param dict [params]: extra parameters specific to the exchange API endpoint
1602
- :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
1603
- """
1604
- return await self.fetch_transactions_by_method('privateGetV2Deposits', code, since, limit, params)
1605
-
1606
- async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1607
- """
1608
- fetch all withdrawals made from an account
1609
-
1610
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getWithdrawalsUsingGET
1611
-
1612
- :param str code: unified currency code
1613
- :param int [since]: the earliest time in ms to fetch withdrawals for
1614
- :param int [limit]: the maximum number of withdrawals structures to retrieve
1615
- :param dict [params]: extra parameters specific to the exchange API endpoint
1616
- :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
1617
- """
1618
- return await self.fetch_transactions_by_method('privateGetV2Withdrawals', code, since, limit, params)
1619
-
1620
- async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1621
- """
1622
- fetch history of deposits and withdrawals
1623
-
1624
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getTransactionsUsingGET
1625
-
1626
- :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
1627
- :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
1628
- :param int [limit]: max number of deposit/withdrawals to return, default is None
1629
- :param dict [params]: extra parameters specific to the exchange API endpoint
1630
- :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1631
- """
1632
- return await self.fetch_transactions_by_method('privateGetV2Transactions', code, since, limit, params)
1633
-
1634
- async def fetch_transactions_by_method(self, method, code: Str = None, since: Int = None, limit: Int = None, params={}):
1635
- await self.load_markets()
1636
- request: dict = {}
1637
- currency = None
1638
- if code is not None:
1639
- currency = self.currency(code)
1640
- if since is not None:
1641
- request['startTime'] = since
1642
- if limit is not None:
1643
- request['limit'] = limit
1644
- response = None
1645
- if method == 'privateGetV2Deposits':
1646
- response = await self.privateGetV2Deposits(self.extend(request, params))
1647
- elif method == 'privateGetV2Withdrawals':
1648
- response = await self.privateGetV2Withdrawals(self.extend(request, params))
1649
- elif method == 'privateGetV2Transactions':
1650
- response = await self.privateGetV2Transactions(self.extend(request, params))
1651
- else:
1652
- raise NotSupported(self.id + ' fetchTransactionsByMethod() not support self method')
1653
- #
1654
- # [
1655
- # {
1656
- # "id": "616769213",
1657
- # "balance": "2.088",
1658
- # "amount": "1.304", # negative for 'withdrawal'
1659
- # "currency": "CAKE",
1660
- # "type": "deposit",
1661
- # "timestamp": "1645282121023",
1662
- # "paymentMethod": "BLOCKCHAIN",
1663
- # "blockchainTransactionHash": "0x57c68c1f2ae74d5eda5a2a00516361d241a5c9e1ee95bf32573523857c38c112",
1664
- # "status": "PROCESSED",
1665
- # "commission": "0.14", # self property only exists in withdrawal
1666
- # },
1667
- # ]
1668
- #
1669
- return self.parse_transactions(response, currency, since, limit, params)
1670
-
1671
- def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
1672
- #
1673
- # {
1674
- # "id": "616769213",
1675
- # "balance": "2.088",
1676
- # "amount": "1.304", # negative for 'withdrawal'
1677
- # "currency": "CAKE",
1678
- # "type": "deposit",
1679
- # "timestamp": "1645282121023",
1680
- # "paymentMethod": "BLOCKCHAIN",
1681
- # "blockchainTransactionHash": "0x57c68c1f2ae74d5eda5a2a00516361d241a5c9e1ee95bf32573523857c38c112",
1682
- # "status": "PROCESSED",
1683
- # "commission": "0.14", # self property only exists in withdrawal
1684
- # }
1685
- #
1686
- timestamp = self.safe_integer(transaction, 'timestamp')
1687
- currencyId = self.safe_string(transaction, 'currency')
1688
- code = self.safe_currency_code(currencyId, currency)
1689
- feeCost = self.safe_string(transaction, 'commission')
1690
- fee = {
1691
- 'currency': None,
1692
- 'cost': None,
1693
- 'rate': None,
1694
- }
1695
- if feeCost is not None:
1696
- fee['currency'] = code
1697
- fee['cost'] = feeCost
1698
- return {
1699
- 'info': transaction,
1700
- 'id': self.safe_string(transaction, 'id'),
1701
- 'txid': self.safe_string(transaction, 'blockchainTransactionHash'),
1702
- 'type': self.parse_transaction_type(self.safe_string(transaction, 'type')),
1703
- 'currency': code,
1704
- 'network': None,
1705
- 'amount': self.safe_number(transaction, 'amount'),
1706
- 'status': self.parse_transaction_status(self.safe_string(transaction, 'state')),
1707
- 'timestamp': timestamp,
1708
- 'datetime': self.iso8601(timestamp),
1709
- 'address': None,
1710
- 'addressFrom': None,
1711
- 'addressTo': None,
1712
- 'tag': None,
1713
- 'tagFrom': None,
1714
- 'tagTo': None,
1715
- 'updated': None,
1716
- 'internal': None,
1717
- 'comment': None,
1718
- 'fee': fee,
1719
- }
1720
-
1721
- def parse_transaction_status(self, status: Str):
1722
- statuses: dict = {
1723
- 'APPROVAL': 'pending',
1724
- 'PROCESSED': 'ok',
1725
- }
1726
- return self.safe_string(statuses, status, status)
1727
-
1728
- def parse_transaction_type(self, type):
1729
- types: dict = {
1730
- 'deposit': 'deposit',
1731
- 'withdrawal': 'withdrawal',
1732
- }
1733
- return self.safe_string(types, type, type)
1734
-
1735
- async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
1736
- """
1737
- fetch the history of changes, actions done by the user or operations that altered the balance of the user
1738
-
1739
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getLedgerUsingGET
1740
-
1741
- :param str [code]: unified currency code, default is None
1742
- :param int [since]: timestamp in ms of the earliest ledger entry, default is None
1743
- :param int [limit]: max number of ledger entries to return, default is None
1744
- :param dict [params]: extra parameters specific to the exchange API endpoint
1745
- :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
1746
- """
1747
- await self.load_markets()
1748
- request: dict = {}
1749
- currency = None
1750
- if code is not None:
1751
- currency = self.currency(code)
1752
- if since is not None:
1753
- request['startTime'] = since
1754
- if limit is not None:
1755
- request['limit'] = limit
1756
- response = await self.privateGetV2Ledger(self.extend(request, params))
1757
- # in the below example, first item expresses withdrawal/deposit type, second example expresses trade
1758
- #
1759
- # [
1760
- # {
1761
- # "id": "619031398",
1762
- # "balance": "0.0",
1763
- # "amount": "-1.088",
1764
- # "currency": "CAKE",
1765
- # "type": "withdrawal",
1766
- # "timestamp": "1645460496425",
1767
- # "commission": "0.13",
1768
- # "paymentMethod": "BLOCKCHAIN", # present in withdrawal/deposit
1769
- # "blockchainTransactionHash": "0x400ac905557c3d34638b1c60eba110b3ee0f97f4eb0f7318015ab76e7f16b7d6", # present in withdrawal/deposit
1770
- # "status": "PROCESSED"
1771
- # },
1772
- # {
1773
- # "id": "619031034",
1774
- # "balance": "8.17223588",
1775
- # "amount": "-0.01326294",
1776
- # "currency": "USD",
1777
- # "type": "exchange_commission",
1778
- # "timestamp": "1645460461235",
1779
- # "commission": "0.01326294",
1780
- # "status": "PROCESSED"
1781
- # },
1782
- # ]
1783
- #
1784
- return self.parse_ledger(response, currency, since, limit)
1785
-
1786
- def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
1787
- id = self.safe_string(item, 'id')
1788
- amountString = self.safe_string(item, 'amount')
1789
- amount = Precise.string_abs(amountString)
1790
- timestamp = self.safe_integer(item, 'timestamp')
1791
- currencyId = self.safe_string(item, 'currency')
1792
- code = self.safe_currency_code(currencyId, currency)
1793
- currency = self.safe_currency(currencyId, currency)
1794
- feeCost = self.safe_string(item, 'commission')
1795
- fee = None
1796
- if feeCost is not None:
1797
- fee = {'currency': code, 'cost': feeCost}
1798
- direction = 'out' if Precise.string_lt(amountString, '0') else 'in'
1799
- return self.safe_ledger_entry({
1800
- 'id': id,
1801
- 'timestamp': timestamp,
1802
- 'datetime': self.iso8601(timestamp),
1803
- 'direction': direction,
1804
- 'account': None,
1805
- 'referenceId': self.safe_string(item, 'blockchainTransactionHash'),
1806
- 'referenceAccount': None,
1807
- 'type': self.parse_ledger_entry_type(self.safe_string(item, 'type')),
1808
- 'currency': code,
1809
- 'amount': amount,
1810
- 'before': None,
1811
- 'after': self.safe_string(item, 'balance'),
1812
- 'status': self.parse_ledger_entry_status(self.safe_string(item, 'status')),
1813
- 'fee': fee,
1814
- 'info': item,
1815
- }, currency)
1816
-
1817
- def parse_ledger_entry_status(self, status):
1818
- statuses: dict = {
1819
- 'APPROVAL': 'pending',
1820
- 'PROCESSED': 'ok',
1821
- 'CANCELLED': 'canceled',
1822
- }
1823
- return self.safe_string(statuses, status, status)
1824
-
1825
- def parse_ledger_entry_type(self, type):
1826
- types: dict = {
1827
- 'deposit': 'transaction',
1828
- 'withdrawal': 'transaction',
1829
- 'exchange_commission': 'fee',
1830
- }
1831
- return self.safe_string(types, type, type)
1832
-
1833
- async def fetch_leverage(self, symbol: str, params={}) -> Leverage:
1834
- """
1835
- fetch the set leverage for a market
1836
-
1837
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/leverageSettingsUsingGET
1838
-
1839
- :param str symbol: unified market symbol
1840
- :param dict [params]: extra parameters specific to the exchange API endpoint
1841
- :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
1842
- """
1843
- await self.load_markets()
1844
- market = self.market(symbol)
1845
- request: dict = {
1846
- 'symbol': market['id'],
1847
- }
1848
- response = await self.privateGetV2LeverageSettings(self.extend(request, params))
1849
- #
1850
- # {
1851
- # "values": [1, 2, 5, 10,],
1852
- # "value": "10",
1853
- # }
1854
- #
1855
- return self.parse_leverage(response, market)
1856
-
1857
- def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
1858
- leverageValue = self.safe_integer(leverage, 'value')
1859
- return {
1860
- 'info': leverage,
1861
- 'symbol': market['symbol'],
1862
- 'marginMode': None,
1863
- 'longLeverage': leverageValue,
1864
- 'shortLeverage': leverageValue,
1865
- }
1866
-
1867
- async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
1868
- """
1869
- fetch the deposit address for a currency associated with self account
1870
-
1871
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/getDepositAddressUsingGET
1872
-
1873
- :param str code: unified currency code
1874
- :param dict [params]: extra parameters specific to the exchange API endpoint
1875
- :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1876
- """
1877
- await self.load_markets()
1878
- currency = self.currency(code)
1879
- request: dict = {
1880
- 'coin': currency['id'],
1881
- }
1882
- response = await self.privateGetV2DepositAddress(self.extend(request, params))
1883
- #
1884
- # {"address":"0x97d64eb014ac779194991e7264f01c74c90327f0"}
1885
- #
1886
- return self.parse_deposit_address(response, currency)
1887
-
1888
- def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
1889
- address = self.safe_string(depositAddress, 'address')
1890
- self.check_address(address)
1891
- currency = self.safe_currency(None, currency)
1892
- return {
1893
- 'info': depositAddress,
1894
- 'currency': currency['code'],
1895
- 'network': None,
1896
- 'address': address,
1897
- 'tag': None,
1898
- }
1899
-
1900
- def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
1901
- url = self.urls['api'][api] + '/' + path
1902
- if path == 'historicalTrades':
1903
- headers = {
1904
- 'X-MBX-APIKEY': self.apiKey,
1905
- }
1906
- if api == 'private':
1907
- self.check_required_credentials()
1908
- query = self.urlencode(self.extend({
1909
- 'timestamp': self.nonce(),
1910
- 'recvWindow': self.options['recvWindow'],
1911
- }, params))
1912
- signature = self.hmac(self.encode(query), self.encode(self.secret), hashlib.sha256)
1913
- query += '&' + 'signature=' + signature
1914
- headers = {
1915
- 'X-MBX-APIKEY': self.apiKey,
1916
- }
1917
- if (method == 'GET') or (method == 'DELETE'):
1918
- url += '?' + query
1919
- else:
1920
- body = query
1921
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
1922
- else:
1923
- if params:
1924
- url += '?' + self.urlencode(params)
1925
- url = self.implode_hostname(url)
1926
- return {'url': url, 'method': method, 'body': body, 'headers': headers}
1927
-
1928
- async def fetch_positions(self, symbols: Strings = None, params={}):
1929
- """
1930
- fetch all open positions
1931
-
1932
- https://apitradedoc.currency.com/swagger-ui.html#/rest-api/tradingPositionsUsingGET
1933
-
1934
- :param str[]|None symbols: list of unified market symbols
1935
- :param dict [params]: extra parameters specific to the exchange API endpoint
1936
- :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
1937
- """
1938
- await self.load_markets()
1939
- response = await self.privateGetV2TradingPositions(params)
1940
- #
1941
- # {
1942
- # "positions": [
1943
- # {
1944
- # "accountId": "109698017416453793",
1945
- # "id": "00a18490-0079-54c4-0000-0000803e73d3",
1946
- # "instrumentId": "45463225268524228",
1947
- # "orderId": "00a18490-0079-54c4-0000-0000803e73d2",
1948
- # "openQuantity": "13.6",
1949
- # "openPrice": "0.75724",
1950
- # "closeQuantity": "0.0",
1951
- # "closePrice": "0",
1952
- # "rpl": "-0.007723848",
1953
- # "rplConverted": "0",
1954
- # "upl": "-0.006664",
1955
- # "uplConverted": "-0.006664",
1956
- # "swap": "0",
1957
- # "swapConverted": "0",
1958
- # "fee": "-0.007723848",
1959
- # "dividend": "0",
1960
- # "margin": "0.2",
1961
- # "state": "ACTIVE",
1962
- # "currency": "USD",
1963
- # "createdTimestamp": "1645473877236",
1964
- # "openTimestamp": "1645473877193",
1965
- # "type": "NET",
1966
- # "cost": "2.0583600",
1967
- # "symbol": "XRP/USD_LEVERAGE"
1968
- # }
1969
- # ]
1970
- # }
1971
- #
1972
- data = self.safe_list(response, 'positions', [])
1973
- return self.parse_positions(data, symbols)
1974
-
1975
- def parse_position(self, position: dict, market: Market = None):
1976
- #
1977
- # {
1978
- # "accountId": "109698017416453793",
1979
- # "id": "00a18490-0079-54c4-0000-0000803e73d3",
1980
- # "instrumentId": "45463225268524228",
1981
- # "orderId": "00a18490-0079-54c4-0000-0000803e73d2",
1982
- # "openQuantity": "13.6",
1983
- # "openPrice": "0.75724",
1984
- # "closeQuantity": "0.0",
1985
- # "closePrice": "0",
1986
- # "rpl": "-0.007723848",
1987
- # "rplConverted": "0",
1988
- # "upl": "-0.006664",
1989
- # "uplConverted": "-0.006664",
1990
- # "swap": "0",
1991
- # "swapConverted": "0",
1992
- # "fee": "-0.007723848",
1993
- # "dividend": "0",
1994
- # "margin": "0.2",
1995
- # "state": "ACTIVE",
1996
- # "currency": "USD",
1997
- # "createdTimestamp": "1645473877236",
1998
- # "openTimestamp": "1645473877193",
1999
- # "type": "NET",
2000
- # "cost": "2.0583600",
2001
- # "symbol": "XRP/USD_LEVERAGE"
2002
- # }
2003
- #
2004
- market = self.safe_market(self.safe_string(position, 'symbol'), market)
2005
- symbol = market['symbol']
2006
- timestamp = self.safe_integer(position, 'createdTimestamp')
2007
- quantityRaw = self.safe_string(position, 'openQuantity')
2008
- side = 'long' if Precise.string_gt(quantityRaw, '0') else 'short'
2009
- quantity = Precise.string_abs(quantityRaw)
2010
- entryPrice = self.safe_number(position, 'openPrice')
2011
- unrealizedProfit = self.safe_number(position, 'upl')
2012
- marginCoeff = self.safe_string(position, 'margin')
2013
- leverage = Precise.string_div('1', marginCoeff)
2014
- return self.safe_position({
2015
- 'info': position,
2016
- 'symbol': symbol,
2017
- 'timestamp': timestamp,
2018
- 'datetime': self.iso8601(timestamp),
2019
- 'lastUpdateTimestamp': None,
2020
- 'contracts': self.parse_number(quantity),
2021
- 'contractSize': None,
2022
- 'entryPrice': entryPrice,
2023
- 'collateral': None,
2024
- 'side': side,
2025
- # 'realizedProfit': self.safe_number(position, 'rpl'),
2026
- 'unrealizedPnl': unrealizedProfit,
2027
- 'leverage': leverage,
2028
- 'percentage': None,
2029
- 'marginMode': None,
2030
- 'notional': None,
2031
- 'markPrice': None,
2032
- 'lastPrice': None,
2033
- 'liquidationPrice': None,
2034
- 'initialMargin': None,
2035
- 'initialMarginPercentage': None,
2036
- 'maintenanceMargin': self.parse_number(marginCoeff),
2037
- 'maintenanceMarginPercentage': None,
2038
- 'marginRatio': None,
2039
- 'id': None,
2040
- 'hedged': None,
2041
- 'stopLossPrice': None,
2042
- 'takeProfitPrice': None,
2043
- })
2044
-
2045
- def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
2046
- if (httpCode == 418) or (httpCode == 429):
2047
- raise DDoSProtection(self.id + ' ' + str(httpCode) + ' ' + reason + ' ' + body)
2048
- # error response in a form: {"code": -1013, "msg": "Invalid quantity."}
2049
- # following block cointains legacy checks against message patterns in "msg" property
2050
- # will switch "code" checks eventually, when we know all of them
2051
- if httpCode >= 400:
2052
- if body.find('Price * QTY is zero or less') >= 0:
2053
- raise InvalidOrder(self.id + ' order cost = amount * price is zero or less ' + body)
2054
- if body.find('LOT_SIZE') >= 0:
2055
- raise InvalidOrder(self.id + ' order amount should be evenly divisible by lot size ' + body)
2056
- if body.find('PRICE_FILTER') >= 0:
2057
- raise InvalidOrder(self.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use self.price_to_precision(symbol, amount) ' + body)
2058
- if response is None:
2059
- return None # fallback to default error handler
2060
- #
2061
- # {"code":-1128,"msg":"Combination of optional parameters invalid."}
2062
- #
2063
- errorCode = self.safe_string(response, 'code')
2064
- if (errorCode is not None) and (errorCode != '0'):
2065
- feedback = self.id + ' ' + self.json(response)
2066
- self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
2067
- message = self.safe_string(response, 'msg')
2068
- self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
2069
- raise ExchangeError(feedback)
2070
- return None