ccxt 4.3.85__py2.py3-none-any.whl → 4.3.87__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 (49) hide show
  1. ccxt/__init__.py +4 -1
  2. ccxt/abstract/hashkey.py +67 -0
  3. ccxt/async_support/__init__.py +4 -1
  4. ccxt/async_support/base/exchange.py +2 -2
  5. ccxt/async_support/binance.py +4 -2
  6. ccxt/async_support/bingx.py +5 -1
  7. ccxt/async_support/bitfinex.py +2 -2
  8. ccxt/async_support/hashkey.py +4061 -0
  9. ccxt/async_support/hyperliquid.py +80 -62
  10. ccxt/async_support/indodax.py +29 -8
  11. ccxt/async_support/kraken.py +32 -5
  12. ccxt/async_support/krakenfutures.py +10 -9
  13. ccxt/async_support/upbit.py +1 -1
  14. ccxt/base/errors.py +7 -1
  15. ccxt/base/exchange.py +2 -2
  16. ccxt/binance.py +4 -2
  17. ccxt/bingx.py +5 -1
  18. ccxt/bitfinex.py +2 -2
  19. ccxt/hashkey.py +4061 -0
  20. ccxt/hyperliquid.py +80 -62
  21. ccxt/indodax.py +29 -8
  22. ccxt/kraken.py +32 -5
  23. ccxt/krakenfutures.py +10 -9
  24. ccxt/pro/__init__.py +3 -1
  25. ccxt/pro/ascendex.py +41 -5
  26. ccxt/pro/bingx.py +13 -12
  27. ccxt/pro/bitget.py +143 -16
  28. ccxt/pro/hashkey.py +783 -0
  29. ccxt/pro/hyperliquid.py +118 -1
  30. ccxt/pro/mexc.py +13 -7
  31. ccxt/pro/p2b.py +30 -7
  32. ccxt/pro/poloniex.py +32 -3
  33. ccxt/pro/poloniexfutures.py +1 -0
  34. ccxt/pro/probit.py +2 -0
  35. ccxt/pro/upbit.py +44 -3
  36. ccxt/pro/vertex.py +1 -0
  37. ccxt/pro/wazirx.py +3 -0
  38. ccxt/pro/whitebit.py +9 -0
  39. ccxt/pro/woo.py +1 -0
  40. ccxt/pro/woofipro.py +1 -0
  41. ccxt/pro/xt.py +1 -0
  42. ccxt/test/tests_async.py +31 -31
  43. ccxt/test/tests_sync.py +31 -31
  44. ccxt/upbit.py +1 -1
  45. {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/METADATA +9 -6
  46. {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/RECORD +49 -45
  47. {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/LICENSE.txt +0 -0
  48. {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/WHEEL +0 -0
  49. {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,4061 @@
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.hashkey import ImplicitAPI
8
+ import hashlib
9
+ from ccxt.base.types import Account, Balances, Bool, Currencies, Currency, Int, LastPrice, LastPrices, Leverage, LeverageTier, LeverageTiers, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction, TransferEntry
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 AccountNotEnabled
15
+ from ccxt.base.errors import AccountSuspended
16
+ from ccxt.base.errors import ArgumentsRequired
17
+ from ccxt.base.errors import BadRequest
18
+ from ccxt.base.errors import BadSymbol
19
+ from ccxt.base.errors import OperationRejected
20
+ from ccxt.base.errors import InsufficientFunds
21
+ from ccxt.base.errors import InvalidAddress
22
+ from ccxt.base.errors import InvalidOrder
23
+ from ccxt.base.errors import OrderNotFound
24
+ from ccxt.base.errors import OrderImmediatelyFillable
25
+ from ccxt.base.errors import OrderNotFillable
26
+ from ccxt.base.errors import DuplicateOrderId
27
+ from ccxt.base.errors import ContractUnavailable
28
+ from ccxt.base.errors import NotSupported
29
+ from ccxt.base.errors import OperationFailed
30
+ from ccxt.base.errors import DDoSProtection
31
+ from ccxt.base.errors import RateLimitExceeded
32
+ from ccxt.base.errors import ExchangeNotAvailable
33
+ from ccxt.base.errors import InvalidNonce
34
+ from ccxt.base.errors import RequestTimeout
35
+ from ccxt.base.decimal_to_precision import TICK_SIZE
36
+ from ccxt.base.precise import Precise
37
+
38
+
39
+ class hashkey(Exchange, ImplicitAPI):
40
+
41
+ def describe(self):
42
+ return self.deep_extend(super(hashkey, self).describe(), {
43
+ 'id': 'hashkey',
44
+ 'name': 'HashKey Global',
45
+ 'countries': ['BM'], # Bermuda
46
+ 'rateLimit': 100,
47
+ 'version': 'v1',
48
+ 'certified': True,
49
+ 'pro': True,
50
+ 'has': {
51
+ 'CORS': None,
52
+ 'spot': True,
53
+ 'margin': False,
54
+ 'swap': False,
55
+ 'future': False,
56
+ 'option': False,
57
+ 'addMargin': False,
58
+ 'cancelAllOrders': True,
59
+ 'cancelAllOrdersAfter': False,
60
+ 'cancelOrder': True,
61
+ 'cancelOrders': True,
62
+ 'cancelWithdraw': False,
63
+ 'closePosition': False,
64
+ 'createConvertTrade': False,
65
+ 'createDepositAddress': False,
66
+ 'createMarketBuyOrderWithCost': True,
67
+ 'createMarketOrder': True,
68
+ 'createMarketOrderWithCost': False,
69
+ 'createMarketSellOrderWithCost': False,
70
+ 'createOrder': True,
71
+ 'createOrderWithTakeProfitAndStopLoss': False,
72
+ 'createReduceOnlyOrder': True,
73
+ 'createStopLimitOrder': True,
74
+ 'createStopLossOrder': False,
75
+ 'createStopMarketOrder': True,
76
+ 'createStopOrder': True,
77
+ 'createTakeProfitOrder': False,
78
+ 'createTrailingAmountOrder': False,
79
+ 'createTrailingPercentOrder': False,
80
+ 'createTriggerOrder': True,
81
+ 'fetchAccounts': True,
82
+ 'fetchBalance': True,
83
+ 'fetchCanceledAndClosedOrders': True,
84
+ 'fetchCanceledOrders': True,
85
+ 'fetchClosedOrder': True,
86
+ 'fetchClosedOrders': False,
87
+ 'fetchConvertCurrencies': False,
88
+ 'fetchConvertQuote': False,
89
+ 'fetchConvertTrade': False,
90
+ 'fetchConvertTradeHistory': False,
91
+ 'fetchCurrencies': True,
92
+ 'fetchDepositAddress': True,
93
+ 'fetchDeposits': True,
94
+ 'fetchDepositsWithdrawals': False,
95
+ 'fetchFundingHistory': False,
96
+ 'fetchFundingRate': True,
97
+ 'fetchFundingRateHistory': True,
98
+ 'fetchFundingRates': True,
99
+ 'fetchIndexOHLCV': False,
100
+ 'fetchLedger': True,
101
+ 'fetchLeverage': True,
102
+ 'fetchLeverageTiers': True,
103
+ 'fetchMarginAdjustmentHistory': False,
104
+ 'fetchMarginMode': False,
105
+ 'fetchMarkets': True,
106
+ 'fetchMarkOHLCV': False,
107
+ 'fetchMyTrades': True,
108
+ 'fetchOHLCV': True,
109
+ 'fetchOpenInterestHistory': False,
110
+ 'fetchOpenOrder': False,
111
+ 'fetchOpenOrders': True,
112
+ 'fetchOrder': True,
113
+ 'fetchOrderBook': True,
114
+ 'fetchOrders': False,
115
+ 'fetchOrderTrades': False,
116
+ 'fetchPosition': False,
117
+ 'fetchPositionHistory': False,
118
+ 'fetchPositionMode': False,
119
+ 'fetchPositions': True,
120
+ 'fetchPositionsForSymbol': True,
121
+ 'fetchPositionsHistory': False,
122
+ 'fetchPremiumIndexOHLCV': False,
123
+ 'fetchStatus': True,
124
+ 'fetchTicker': True,
125
+ 'fetchTickers': True,
126
+ 'fetchTime': True,
127
+ 'fetchTrades': True,
128
+ 'fetchTradingFee': True, # emulated for spot markets
129
+ 'fetchTradingFees': True, # for spot markets only
130
+ 'fetchTransactions': False,
131
+ 'fetchTransfers': False,
132
+ 'fetchWithdrawals': True,
133
+ 'reduceMargin': False,
134
+ 'sandbox': False,
135
+ 'setLeverage': True,
136
+ 'setMargin': False,
137
+ 'setPositionMode': False,
138
+ 'transfer': True,
139
+ 'withdraw': True,
140
+ },
141
+ 'timeframes': {
142
+ '1m': '1m',
143
+ '3m': '3m',
144
+ '5m': '5m',
145
+ '15m': '15m',
146
+ '30m': '30m',
147
+ '1h': '1h',
148
+ '2h': '2h',
149
+ '4h': '4h',
150
+ '6h': '6h',
151
+ '8h': '8h',
152
+ '12h': '12h',
153
+ '1d': '1d',
154
+ '1w': '1w',
155
+ '1M': '1M',
156
+ },
157
+ 'urls': {
158
+ 'logo': 'https://github.com/user-attachments/assets/6dd6127b-cc19-4a13-9b29-a98d81f80e98',
159
+ 'api': {
160
+ 'public': 'https://api-glb.hashkey.com',
161
+ 'private': 'https://api-glb.hashkey.com',
162
+ },
163
+ 'test': {
164
+ 'public': 'https://api-glb.sim.hashkeydev.com',
165
+ 'private': 'https://api-glb.sim.hashkeydev.com',
166
+ },
167
+ 'www': 'https://global.hashkey.com/',
168
+ 'doc': 'https://hashkeyglobal-apidoc.readme.io/',
169
+ 'fees': 'https://support.global.hashkey.com/hc/en-us/articles/13199900083612-HashKey-Global-Fee-Structure',
170
+ 'referral': 'https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN',
171
+ },
172
+ 'api': {
173
+ 'public': {
174
+ 'get': {
175
+ 'api/v1/exchangeInfo': 5,
176
+ 'quote/v1/depth': 1,
177
+ 'quote/v1/trades': 1,
178
+ 'quote/v1/klines': 1,
179
+ 'quote/v1/ticker/24hr': 1,
180
+ 'quote/v1/ticker/price': 1,
181
+ 'quote/v1/ticker/bookTicker': 1, # not unified
182
+ 'quote/v1/depth/merged': 1,
183
+ 'quote/v1/markPrice': 1,
184
+ 'quote/v1/index': 1,
185
+ 'api/v1/futures/fundingRate': 1,
186
+ 'api/v1/futures/historyFundingRate': 1,
187
+ 'api/v1/ping': 1,
188
+ 'api/v1/time': 1,
189
+ },
190
+ },
191
+ 'private': {
192
+ 'get': {
193
+ 'api/v1/spot/order': 1,
194
+ 'api/v1/spot/openOrders': 1,
195
+ 'api/v1/spot/tradeOrders': 5,
196
+ 'api/v1/futures/leverage': 1,
197
+ 'api/v1/futures/order': 1,
198
+ 'api/v1/futures/openOrders': 1,
199
+ 'api/v1/futures/userTrades': 1,
200
+ 'api/v1/futures/positions': 1,
201
+ 'api/v1/futures/historyOrders': 1,
202
+ 'api/v1/futures/balance': 1,
203
+ 'api/v1/futures/liquidationAssignStatus': 1,
204
+ 'api/v1/futures/riskLimit': 1,
205
+ 'api/v1/futures/commissionRate': 1,
206
+ 'api/v1/futures/getBestOrder': 1,
207
+ 'api/v1/account/vipInfo': 1,
208
+ 'api/v1/account': 1,
209
+ 'api/v1/account/trades': 5,
210
+ 'api/v1/account/type': 5,
211
+ 'api/v1/account/checkApiKey': 1,
212
+ 'api/v1/account/balanceFlow': 5,
213
+ 'api/v1/spot/subAccount/openOrders': 1,
214
+ 'api/v1/spot/subAccount/tradeOrders': 1,
215
+ 'api/v1/subAccount/trades': 1,
216
+ 'api/v1/futures/subAccount/openOrders': 1,
217
+ 'api/v1/futures/subAccount/historyOrders': 1,
218
+ 'api/v1/futures/subAccount/userTrades': 1,
219
+ 'api/v1/account/deposit/address': 1,
220
+ 'api/v1/account/depositOrders': 1,
221
+ 'api/v1/account/withdrawOrders': 1,
222
+ },
223
+ 'post': {
224
+ 'api/v1/userDataStream': 1,
225
+ 'api/v1/spot/orderTest': 1,
226
+ 'api/v1/spot/order': 1,
227
+ 'api/v1.1/spot/order': 1,
228
+ 'api/v1/spot/batchOrders': 5,
229
+ 'api/v1/futures/leverage': 1,
230
+ 'api/v1/futures/order': 1,
231
+ 'api/v1/futures/position/trading-stop': 3,
232
+ 'api/v1/futures/batchOrders': 5,
233
+ 'api/v1/account/assetTransfer': 1,
234
+ 'api/v1/account/authAddress': 1,
235
+ 'api/v1/account/withdraw': 1,
236
+ },
237
+ 'put': {
238
+ 'api/v1/userDataStream': 1,
239
+ },
240
+ 'delete': {
241
+ 'api/v1/spot/order': 1,
242
+ 'api/v1/spot/openOrders': 5,
243
+ 'api/v1/spot/cancelOrderByIds': 5,
244
+ 'api/v1/futures/order': 1,
245
+ 'api/v1/futures/batchOrders': 1,
246
+ 'api/v1/futures/cancelOrderByIds': 1,
247
+ 'api/v1/userDataStream': 1,
248
+ },
249
+ },
250
+ },
251
+ 'fees': {
252
+ 'trading': {
253
+ 'spot': {
254
+ 'tierBased': True,
255
+ 'percentage': True,
256
+ 'feeSide': 'get',
257
+ 'maker': self.parse_number('0.0012'),
258
+ 'taker': self.parse_number('0.0012'),
259
+ 'tiers': {
260
+ 'maker': [
261
+ [self.parse_number('0'), self.parse_number('0.0012')],
262
+ [self.parse_number('1000000'), self.parse_number('0.00080')],
263
+ [self.parse_number('5000000'), self.parse_number('0.00070')],
264
+ [self.parse_number('10000000'), self.parse_number('0.00060')],
265
+ [self.parse_number('50000000'), self.parse_number('0.00040')],
266
+ [self.parse_number('200000000'), self.parse_number('0.00030')],
267
+ [self.parse_number('400000000'), self.parse_number('0.00010')],
268
+ [self.parse_number('800000000'), self.parse_number('0.00')],
269
+ ],
270
+ 'taker': [
271
+ [self.parse_number('0'), self.parse_number('0.0012')],
272
+ [self.parse_number('1000000'), self.parse_number('0.00090')],
273
+ [self.parse_number('5000000'), self.parse_number('0.00085')],
274
+ [self.parse_number('10000000'), self.parse_number('0.00075')],
275
+ [self.parse_number('50000000'), self.parse_number('0.00065')],
276
+ [self.parse_number('200000000'), self.parse_number('0.00045')],
277
+ [self.parse_number('400000000'), self.parse_number('0.00040')],
278
+ [self.parse_number('800000000'), self.parse_number('0.00035')],
279
+ ],
280
+ },
281
+ },
282
+ 'swap': {
283
+ 'tierBased': True,
284
+ 'percentage': True,
285
+ 'feeSide': 'get',
286
+ 'maker': self.parse_number('0.00025'),
287
+ 'taker': self.parse_number('0.00060'),
288
+ 'tiers': {
289
+ 'maker': [
290
+ [self.parse_number('0'), self.parse_number('0.00025')],
291
+ [self.parse_number('1000000'), self.parse_number('0.00016')],
292
+ [self.parse_number('5000000'), self.parse_number('0.00014')],
293
+ [self.parse_number('10000000'), self.parse_number('0.00012')],
294
+ [self.parse_number('50000000'), self.parse_number('0.000080')],
295
+ [self.parse_number('200000000'), self.parse_number('0.000060')],
296
+ [self.parse_number('400000000'), self.parse_number('0.000020')],
297
+ [self.parse_number('800000000'), self.parse_number('0.00')],
298
+ ],
299
+ 'taker': [
300
+ [self.parse_number('0'), self.parse_number('0.00060')],
301
+ [self.parse_number('1000000'), self.parse_number('0.00050')],
302
+ [self.parse_number('5000000'), self.parse_number('0.00045')],
303
+ [self.parse_number('10000000'), self.parse_number('0.00040')],
304
+ [self.parse_number('50000000'), self.parse_number('0.00035')],
305
+ [self.parse_number('200000000'), self.parse_number('0.00030')],
306
+ [self.parse_number('400000000'), self.parse_number('0.00025')],
307
+ [self.parse_number('800000000'), self.parse_number('0.00020')],
308
+ ],
309
+ },
310
+ },
311
+ },
312
+ },
313
+ 'options': {
314
+ 'broker': '10000700011',
315
+ 'recvWindow': None,
316
+ 'sandboxMode': False,
317
+ 'networks': {
318
+ 'BTC': 'BTC',
319
+ 'ERC20': 'ETH',
320
+ 'AVAX': 'AvalancheC',
321
+ 'SOL': 'Solana',
322
+ 'MATIC': 'Polygon',
323
+ 'ATOM': 'Cosmos',
324
+ 'DOT': 'Polkadot',
325
+ 'LTC': 'LTC',
326
+ 'OPTIMISM': 'Optimism',
327
+ 'ARB': 'Arbitrum',
328
+ 'DOGE': 'Dogecoin',
329
+ 'TRC20': 'Tron',
330
+ 'ZKSYNC': 'zkSync',
331
+ 'TON': 'TON',
332
+ 'KLAYTN': 'Klaytn',
333
+ 'MERLINCHAIN': 'Merlin Chain',
334
+ },
335
+ 'networksById': {
336
+ 'BTC': 'BTC',
337
+ 'Bitcoin': 'BTC',
338
+ 'ETH': 'ERC20',
339
+ 'ERC20': 'ERC20',
340
+ 'AvalancheC': 'AVAX',
341
+ 'AVAX C-Chain': 'AVAX',
342
+ 'Solana': 'SOL',
343
+ 'Cosmos': 'ATOM',
344
+ 'Arbitrum': 'ARB',
345
+ 'Polygon': 'MATIC',
346
+ 'Optimism': 'OPTIMISM',
347
+ 'Polkadot': 'DOT',
348
+ 'LTC': 'LTC',
349
+ 'Litecoin': 'LTC',
350
+ 'Dogecoin': 'DOGE',
351
+ 'Merlin Chain': 'MERLINCHAIN',
352
+ 'zkSync': 'ZKSYNC',
353
+ 'TRC20': 'TRC20',
354
+ 'Tron': 'TRC20',
355
+ 'TON': 'TON',
356
+ 'BSC(BEP20)': 'BSC',
357
+ 'Klaytn': 'KLAYTN',
358
+ },
359
+ 'defaultNetwork': 'ERC20',
360
+ },
361
+ 'commonCurrencies': {},
362
+ 'exceptions': {
363
+ 'exact': {
364
+ '0001': BadRequest, # Required field '%s' missing or invalid.
365
+ '0002': AuthenticationError, # Incorrect signature
366
+ '0003': RateLimitExceeded, # Rate limit exceeded
367
+ '0102': AuthenticationError, # Invalid APIKey
368
+ '0103': AuthenticationError, # APIKey expired
369
+ '0104': PermissionDenied, # The accountId defined is not permissible
370
+ '0201': ExchangeError, # Instrument not found
371
+ '0202': PermissionDenied, # Invalid IP
372
+ '0206': BadRequest, # Unsupported order type
373
+ '0207': BadRequest, # Invalid price
374
+ '0209': BadRequest, # Invalid price precision
375
+ '0210': BadRequest, # Price outside of allowed range
376
+ '0211': OrderNotFound, # Order not found
377
+ '0401': InsufficientFunds, # Insufficient asset
378
+ '0402': BadRequest, # Invalid asset
379
+ '-1000': ExchangeError, # An unknown error occurred while processing the request
380
+ '-1001': ExchangeError, # Internal error
381
+ '-100010': BadSymbol, # Invalid Symbols!
382
+ '-100012': BadSymbol, # Parameter symbol [str] missing!
383
+ '-1002': AuthenticationError, # Unauthorized operation
384
+ '-1004': BadRequest, # Bad request
385
+ '-1005': PermissionDenied, # No permission
386
+ '-1006': ExchangeError, # Execution status unknown
387
+ '-1007': RequestTimeout, # Timeout waiting for response from server
388
+ '-1014': InvalidOrder, # Unsupported order combination
389
+ '-1015': InvalidOrder, # Too many new orders
390
+ '-1020': OperationRejected, # Unsupported operation
391
+ '-1021': InvalidNonce, # Timestamp for self request is outside of the recvWindow
392
+ '-1024': BadRequest, # Duplicate request
393
+ '-1101': ExchangeNotAvailable, # Feature has been offline
394
+ '-1115': InvalidOrder, # Invalid timeInForce
395
+ '-1117': InvalidOrder, # Invalid order side
396
+ '-1123': InvalidOrder, # Invalid client order id
397
+ '-1124': InvalidOrder, # Invalid price
398
+ '-1126': InvalidOrder, # Invalid quantity
399
+ '-1129': BadRequest, # Invalid parameters, quantity and amount are not allowed to be sent at the same time.
400
+ '-1130': BadRequest, # Illegal parameter '%s'
401
+ '-1132': BadRequest, # Order price greater than the maximum
402
+ '-1133': BadRequest, # Order price lower than the minimum
403
+ '-1135': BadRequest, # Order quantity greater than the maximum
404
+ '-1136': BadRequest, # Order quantity lower than the minimum
405
+ '-1138': InvalidOrder, # Order has been partially cancelled
406
+ '-1137': InvalidOrder, # Order quantity precision too large
407
+ '-1139': OrderImmediatelyFillable, # Order has been filled
408
+ '-1140': InvalidOrder, # Order amount lower than the minimum
409
+ '-1141': DuplicateOrderId, # Duplicate order
410
+ '-1142': OrderNotFillable, # Order has been cancelled
411
+ '-1143': OrderNotFound, # Order not found on order book
412
+ '-1144': OperationRejected, # Order has been locked
413
+ '-1145': NotSupported, # Cancellation on self order type not supported
414
+ '-1146': RequestTimeout, # Order creation timeout
415
+ '-1147': RequestTimeout, # Order cancellation timeout
416
+ '-1148': InvalidOrder, # Order amount precision too large
417
+ '-1149': OperationRejected, # Order creation failed
418
+ '-1150': OperationFailed, # Order cancellation failed
419
+ '-1151': OperationRejected, # The trading pair is not open yet
420
+ '-1152': AccountNotEnabled, # User does not exist
421
+ '-1153': InvalidOrder, # Invalid price type
422
+ '-1154': InvalidOrder, # Invalid position side
423
+ '-1155': OperationRejected, # The trading pair is not available for api trading
424
+ '-1156': OperationFailed, # Limit maker order creation failed
425
+ '-1157': OperationFailed, # Modify futures margin failed
426
+ '-1158': OperationFailed, # Reduce margin is forbidden
427
+ '-1159': AccountNotEnabled, # Finance account already exists
428
+ '-1160': AccountNotEnabled, # Account does not exist
429
+ '-1161': OperationFailed, # Balance transfer failed
430
+ '-1162': ContractUnavailable, # Unsupport contract address
431
+ '-1163': InvalidAddress, # Illegal withdrawal address
432
+ '-1164': OperationFailed, # Withdraw failed
433
+ '-1165': ArgumentsRequired, # Withdrawal amount cannot be null
434
+ '-1166': OperationRejected, # Withdrawal amount exceeds the daily limit
435
+ '-1167': BadRequest, # Withdrawal amount less than the minimum
436
+ '-1168': BadRequest, # Illegal withdrawal amount
437
+ '-1169': PermissionDenied, # Withdraw not allowed
438
+ '-1170': PermissionDenied, # Deposit not allowed
439
+ '-1171': PermissionDenied, # Withdrawal address not in whitelist
440
+ '-1172': BadRequest, # Invalid from account id
441
+ '-1173': BadRequest, # Invalid to account i
442
+ '-1174': PermissionDenied, # Transfer not allowed between the same account
443
+ '-1175': BadRequest, # Invalid fiat deposit status
444
+ '-1176': BadRequest, # Invalid fiat withdrawal status
445
+ '-1177': InvalidOrder, # Invalid fiat order type
446
+ '-1178': AccountNotEnabled, # Brokerage account does not exist
447
+ '-1179': AccountSuspended, # Address owner is not True
448
+ '-1181': ExchangeError, # System error
449
+ '-1193': OperationRejected, # Order creation count exceeds the limit
450
+ '-1194': OperationRejected, # Market order creation forbidden
451
+ '-1195': BadRequest, # Market order long position cannot exceed %s above the market price
452
+ '-1196': BadRequest, # Market order short position cannot be below %s of the market price
453
+ '-1200': BadRequest, # Order buy quantity too small
454
+ '-1201': BadRequest, # Order buy quantity too large
455
+ '-1202': BadRequest, # Order sell quantity too small
456
+ '-1203': BadRequest, # Order sell quantity too large
457
+ '-1204': BadRequest, # From account must be a main account
458
+ '-1205': AccountNotEnabled, # Account not authorized
459
+ '-1206': BadRequest, # Order amount greater than the maximum
460
+ '-1207': BadRequest, # The status of deposit is invalid
461
+ '-1208': BadRequest, # The orderType of fiat is invalid
462
+ '-1209': BadRequest, # The status of withdraw is invalid
463
+ '-2001': ExchangeNotAvailable, # Platform is yet to open trading
464
+ '-2002': OperationFailed, # The number of open orders exceeds the limit 300
465
+ '-2003': OperationFailed, # Position size cannot meet target leverage
466
+ '-2004': OperationFailed, # Adjust leverage fail
467
+ '-2005': RequestTimeout, # Adjust leverage timeout
468
+ '-2010': OperationRejected, # New order rejected
469
+ '-2011': OperationRejected, # Order cancellation rejected
470
+ '-2016': OperationRejected, # API key creation exceeds the limit
471
+ '-2017': OperationRejected, # Open orders exceeds the limit of the trading pair
472
+ '-2018': OperationRejected, # Trade user creation exceeds the limit
473
+ '-2019': PermissionDenied, # Trader and omnibus user not allowed to login app
474
+ '-2020': PermissionDenied, # Not allowed to trade self trading pair
475
+ '-2021': PermissionDenied, # Not allowed to trade self trading pair
476
+ '-2022': OperationRejected, # Order batch size exceeds the limit
477
+ '-2023': AuthenticationError, # Need to pass KYC verification
478
+ '-2024': AccountNotEnabled, # Fiat account does not exist
479
+ '-2025': AccountNotEnabled, # Custody account not exist
480
+ '-2026': BadRequest, # Invalid type
481
+ '-2027': OperationRejected, # Exceed maximum time range of 30 days
482
+ '-2028': OperationRejected, # The search is limited to data within the last one month
483
+ '-2029': OperationRejected, # The search is limited to data within the last three months
484
+ '-2030': InsufficientFunds, # Insufficient margin
485
+ '-2031': NotSupported, # Leverage reduction is not supported in Isolated Margin Mode with open positions
486
+ '-2032': OperationRejected, # After the transaction, your %s position will account for %s of the total position, which poses concentration risk. Do you want to continue with the transaction?
487
+ '-2033': OperationFailed, # Order creation failed. Please verify if the order parameters comply with the trading rules
488
+ '-2034': InsufficientFunds, # Trade account holding limit is zero
489
+ '-2035': OperationRejected, # The sub account has been frozen and cannot transfer
490
+ '-2036': NotSupported, # We do not support queries for records exceeding 30 days
491
+ '-2037': ExchangeError, # Position and order data error
492
+ '-2038': InsufficientFunds, # Insufficient margin
493
+ '-2039': NotSupported, # Leverage reduction is not supported in Isolated Margin Mode with open positions
494
+ '-2040': ExchangeNotAvailable, # There is a request being processed. Please try again later
495
+ '-2041': BadRequest, # Token does not exist
496
+ '-2042': OperationRejected, # You have passed the trade limit, please pay attention to the risks
497
+ '-2043': OperationRejected, # Maximum allowed leverage reached, please lower your leverage
498
+ '-2044': BadRequest, # This order price is unreasonable to exceed(or be lower than) the liquidation price
499
+ '-2045': BadRequest, # Price too low, please order again!
500
+ '-2046': BadRequest, # Price too high, please order again!
501
+ '-2048': BadRequest, # Exceed the maximum number of conditional orders of %s
502
+ '-2049': BadRequest, # Create stop order buy price too big
503
+ '-2050': BadRequest, # Create stop order sell price too small
504
+ '-2051': OperationRejected, # Create order rejected
505
+ '-2052': OperationRejected, # Create stop profit-loss plan order reject
506
+ '-2053': OperationRejected, # Position not enough
507
+ '-2054': BadRequest, # Invalid long stop profit price
508
+ '-2055': BadRequest, # Invalid long stop loss price
509
+ '-2056': BadRequest, # Invalid short stop profit price
510
+ '-2057': BadRequest, # Invalid short stop loss price
511
+ '-3117': PermissionDenied, # Invalid permission
512
+ '-3143': PermissionDenied, # According to KYC and risk assessment, your trading account has exceeded the limit.
513
+ '-3144': PermissionDenied, # Currently, your trading account has exceeded its limit and is temporarily unable to perform transfers
514
+ '-3145': DDoSProtection, # Please DO NOT submit request too frequently
515
+ '-4001': BadRequest, # Invalid asset
516
+ '-4002': BadRequest, # Withdrawal amount less than Minimum Withdrawal Amount
517
+ '-4003': InsufficientFunds, # Insufficient Balance
518
+ '-4004': BadRequest, # Invalid bank account number
519
+ '-4005': BadRequest, # Assets are not listed
520
+ '-4006': AccountNotEnabled, # KYC is not certified
521
+ '-4007': NotSupported, # Withdrawal channels are not supported
522
+ '-4008': AccountNotEnabled, # This currency does not support self customer type
523
+ '-4009': PermissionDenied, # No withdrawal permission
524
+ '-4010': PermissionDenied, # Withdrawals on the same day exceed the maximum limit for a single day
525
+ '-4011': ExchangeError, # System error
526
+ '-4012': ExchangeError, # Parameter error
527
+ '-4013': OperationFailed, # Withdraw repeatly
528
+ },
529
+ 'broad': {},
530
+ },
531
+ 'precisionMode': TICK_SIZE,
532
+ })
533
+
534
+ async def fetch_time(self, params={}) -> Int:
535
+ """
536
+ fetches the current integer timestamp in milliseconds from the exchange server
537
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/check-server-time
538
+ :param dict [params]: extra parameters specific to the exchange API endpoint
539
+ :returns int: the current integer timestamp in milliseconds from the exchange server
540
+ """
541
+ response = await self.publicGetApiV1Time(params)
542
+ #
543
+ # {
544
+ # "serverTime": 1721661553214
545
+ # }
546
+ #
547
+ return self.safe_integer(response, 'serverTime')
548
+
549
+ async def fetch_status(self, params={}):
550
+ """
551
+ the latest known information on the availability of the exchange API
552
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/test-connectivity
553
+ :param dict [params]: extra parameters specific to the exchange API endpoint
554
+ :returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
555
+ """
556
+ response = await self.publicGetApiV1Ping(params)
557
+ #
558
+ # {}
559
+ #
560
+ return {
561
+ 'status': 'ok',
562
+ 'updated': None,
563
+ 'eta': None,
564
+ 'url': None,
565
+ 'info': response,
566
+ }
567
+
568
+ async def fetch_markets(self, params={}) -> List[Market]:
569
+ """
570
+ retrieves data on all markets for the exchange
571
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/exchangeinfo
572
+ :param dict [params]: extra parameters specific to the exchange API endpoint
573
+ :param str [params.symbol]: the id of the market to fetch
574
+ :returns dict[]: an array of objects representing market data
575
+ """
576
+ symbol: Str = None
577
+ request: dict = {}
578
+ symbol, params = self.handle_option_and_params(params, 'fetchMarkets', 'symbol')
579
+ if symbol is not None:
580
+ request['symbol'] = symbol
581
+ response = await self.publicGetApiV1ExchangeInfo(self.extend(request, params))
582
+ #
583
+ # {
584
+ # "timezone": "UTC",
585
+ # "serverTime": "1721661653952",
586
+ # "brokerFilters": [],
587
+ # "symbols": [
588
+ # {
589
+ # "symbol": "BTCUSDT",
590
+ # "symbolName": "BTCUSDT",
591
+ # "status": "TRADING",
592
+ # "baseAsset": "BTC",
593
+ # "baseAssetName": "BTC",
594
+ # "baseAssetPrecision": "0.00001",
595
+ # "quoteAsset": "USDT",
596
+ # "quoteAssetName": "USDT",
597
+ # "quotePrecision": "0.0000001",
598
+ # "retailAllowed": True,
599
+ # "piAllowed": True,
600
+ # "corporateAllowed": True,
601
+ # "omnibusAllowed": True,
602
+ # "icebergAllowed": False,
603
+ # "isAggregate": False,
604
+ # "allowMargin": False,
605
+ # "filters": [
606
+ # {
607
+ # "minPrice": "0.01",
608
+ # "maxPrice": "100000.00000000",
609
+ # "tickSize": "0.01",
610
+ # "filterType": "PRICE_FILTER"
611
+ # },
612
+ # {
613
+ # "minQty": "0.00001",
614
+ # "maxQty": "8",
615
+ # "stepSize": "0.00001",
616
+ # "marketOrderMinQty": "0.00001",
617
+ # "marketOrderMaxQty": "4",
618
+ # "filterType": "LOT_SIZE"
619
+ # },
620
+ # {
621
+ # "minNotional": "1",
622
+ # "filterType": "MIN_NOTIONAL"
623
+ # },
624
+ # {
625
+ # "minAmount": "1",
626
+ # "maxAmount": "400000",
627
+ # "minBuyPrice": "0",
628
+ # "marketOrderMinAmount": "1",
629
+ # "marketOrderMaxAmount": "200000",
630
+ # "filterType": "TRADE_AMOUNT"
631
+ # },
632
+ # {
633
+ # "maxSellPrice": "0",
634
+ # "buyPriceUpRate": "0.1",
635
+ # "sellPriceDownRate": "0.1",
636
+ # "filterType": "LIMIT_TRADING"
637
+ # },
638
+ # {
639
+ # "buyPriceUpRate": "0.1",
640
+ # "sellPriceDownRate": "0.1",
641
+ # "filterType": "MARKET_TRADING"
642
+ # },
643
+ # {
644
+ # "noAllowMarketStartTime": "1710485700000",
645
+ # "noAllowMarketEndTime": "1710486000000",
646
+ # "limitOrderStartTime": "0",
647
+ # "limitOrderEndTime": "0",
648
+ # "limitMinPrice": "0",
649
+ # "limitMaxPrice": "0",
650
+ # "filterType": "OPEN_QUOTE"
651
+ # }
652
+ # ]
653
+ # }
654
+ # ],
655
+ # "options": [],
656
+ # "contracts": [
657
+ # {
658
+ # "filters": [
659
+ # {
660
+ # "minPrice": "0.1",
661
+ # "maxPrice": "100000.00000000",
662
+ # "tickSize": "0.1",
663
+ # "filterType": "PRICE_FILTER"
664
+ # },
665
+ # {
666
+ # "minQty": "0.001",
667
+ # "maxQty": "10",
668
+ # "stepSize": "0.001",
669
+ # "marketOrderMinQty": "0",
670
+ # "marketOrderMaxQty": "0",
671
+ # "filterType": "LOT_SIZE"
672
+ # },
673
+ # {
674
+ # "minNotional": "0",
675
+ # "filterType": "MIN_NOTIONAL"
676
+ # },
677
+ # {
678
+ # "maxSellPrice": "999999",
679
+ # "buyPriceUpRate": "0.05",
680
+ # "sellPriceDownRate": "0.05",
681
+ # "maxEntrustNum": 200,
682
+ # "maxConditionNum": 200,
683
+ # "filterType": "LIMIT_TRADING"
684
+ # },
685
+ # {
686
+ # "buyPriceUpRate": "0.05",
687
+ # "sellPriceDownRate": "0.05",
688
+ # "filterType": "MARKET_TRADING"
689
+ # },
690
+ # {
691
+ # "noAllowMarketStartTime": "0",
692
+ # "noAllowMarketEndTime": "0",
693
+ # "limitOrderStartTime": "0",
694
+ # "limitOrderEndTime": "0",
695
+ # "limitMinPrice": "0",
696
+ # "limitMaxPrice": "0",
697
+ # "filterType": "OPEN_QUOTE"
698
+ # }
699
+ # ],
700
+ # "exchangeId": "301",
701
+ # "symbol": "BTCUSDT-PERPETUAL",
702
+ # "symbolName": "BTCUSDT-PERPETUAL",
703
+ # "status": "TRADING",
704
+ # "baseAsset": "BTCUSDT-PERPETUAL",
705
+ # "baseAssetPrecision": "0.001",
706
+ # "quoteAsset": "USDT",
707
+ # "quoteAssetPrecision": "0.1",
708
+ # "icebergAllowed": False,
709
+ # "inverse": False,
710
+ # "index": "USDT",
711
+ # "marginToken": "USDT",
712
+ # "marginPrecision": "0.0001",
713
+ # "contractMultiplier": "0.001",
714
+ # "underlying": "BTC",
715
+ # "riskLimits": [
716
+ # {
717
+ # "riskLimitId": "200000722",
718
+ # "quantity": "1000.00",
719
+ # "initialMargin": "0.10",
720
+ # "maintMargin": "0.005",
721
+ # "isWhite": False
722
+ # },
723
+ # {
724
+ # "riskLimitId": "200000723",
725
+ # "quantity": "2000.00",
726
+ # "initialMargin": "0.10",
727
+ # "maintMargin": "0.01",
728
+ # "isWhite": False
729
+ # }
730
+ # ]
731
+ # }
732
+ # ],
733
+ # "coins": [
734
+ # {
735
+ # "orgId": "9001",
736
+ # "coinId": "BTC",
737
+ # "coinName": "BTC",
738
+ # "coinFullName": "Bitcoin",
739
+ # "allowWithdraw": True,
740
+ # "allowDeposit": True,
741
+ # "tokenType": "CHAIN_TOKEN",
742
+ # "chainTypes": [
743
+ # {
744
+ # "chainType": "Bitcoin",
745
+ # "withdrawFee": "0",
746
+ # "minWithdrawQuantity": "0.002",
747
+ # "maxWithdrawQuantity": "0",
748
+ # "minDepositQuantity": "0.0005",
749
+ # "allowDeposit": True,
750
+ # "allowWithdraw": True
751
+ # }
752
+ # ]
753
+ # }
754
+ # ]
755
+ # }
756
+ #
757
+ spotMarkets = self.safe_list(response, 'symbols', [])
758
+ swapMarkets = self.safe_list(response, 'contracts', [])
759
+ markets = self.array_concat(spotMarkets, swapMarkets)
760
+ if self.is_empty(markets):
761
+ markets = [response] # if user provides params.symbol the exchange returns a single object insted of list of objects
762
+ return self.parse_markets(markets)
763
+
764
+ def parse_market(self, market: dict) -> Market:
765
+ # spot
766
+ # {
767
+ # "symbol": "BTCUSDT",
768
+ # "symbolName": "BTCUSDT",
769
+ # "status": "TRADING",
770
+ # "baseAsset": "BTC",
771
+ # "baseAssetName": "BTC",
772
+ # "baseAssetPrecision": "0.00001",
773
+ # "quoteAsset": "USDT",
774
+ # "quoteAssetName": "USDT",
775
+ # "quotePrecision": "0.0000001",
776
+ # "retailAllowed": True,
777
+ # "piAllowed": True,
778
+ # "corporateAllowed": True,
779
+ # "omnibusAllowed": True,
780
+ # "icebergAllowed": False,
781
+ # "isAggregate": False,
782
+ # "allowMargin": False,
783
+ # "filters": [
784
+ # {
785
+ # "minPrice": "0.01",
786
+ # "maxPrice": "100000.00000000",
787
+ # "tickSize": "0.01",
788
+ # "filterType": "PRICE_FILTER"
789
+ # },
790
+ # {
791
+ # "minQty": "0.00001",
792
+ # "maxQty": "8",
793
+ # "stepSize": "0.00001",
794
+ # "marketOrderMinQty": "0.00001",
795
+ # "marketOrderMaxQty": "4",
796
+ # "filterType": "LOT_SIZE"
797
+ # },
798
+ # {
799
+ # "minNotional": "1",
800
+ # "filterType": "MIN_NOTIONAL"
801
+ # },
802
+ # {
803
+ # "minAmount": "1",
804
+ # "maxAmount": "400000",
805
+ # "minBuyPrice": "0",
806
+ # "marketOrderMinAmount": "1",
807
+ # "marketOrderMaxAmount": "200000",
808
+ # "filterType": "TRADE_AMOUNT"
809
+ # },
810
+ # {
811
+ # "maxSellPrice": "0",
812
+ # "buyPriceUpRate": "0.1",
813
+ # "sellPriceDownRate": "0.1",
814
+ # "filterType": "LIMIT_TRADING"
815
+ # },
816
+ # {
817
+ # "buyPriceUpRate": "0.1",
818
+ # "sellPriceDownRate": "0.1",
819
+ # "filterType": "MARKET_TRADING"
820
+ # },
821
+ # {
822
+ # "noAllowMarketStartTime": "1710485700000",
823
+ # "noAllowMarketEndTime": "1710486000000",
824
+ # "limitOrderStartTime": "0",
825
+ # "limitOrderEndTime": "0",
826
+ # "limitMinPrice": "0",
827
+ # "limitMaxPrice": "0",
828
+ # "filterType": "OPEN_QUOTE"
829
+ # }
830
+ # ]
831
+ # }
832
+ #
833
+ # swap
834
+ # {
835
+ # "filters": [
836
+ # {
837
+ # "minPrice": "0.1",
838
+ # "maxPrice": "100000.00000000",
839
+ # "tickSize": "0.1",
840
+ # "filterType": "PRICE_FILTER"
841
+ # },
842
+ # {
843
+ # "minQty": "0.001",
844
+ # "maxQty": "10",
845
+ # "stepSize": "0.001",
846
+ # "marketOrderMinQty": "0",
847
+ # "marketOrderMaxQty": "0",
848
+ # "filterType": "LOT_SIZE"
849
+ # },
850
+ # {
851
+ # "minNotional": "0",
852
+ # "filterType": "MIN_NOTIONAL"
853
+ # },
854
+ # {
855
+ # "maxSellPrice": "999999",
856
+ # "buyPriceUpRate": "0.05",
857
+ # "sellPriceDownRate": "0.05",
858
+ # "maxEntrustNum": 200,
859
+ # "maxConditionNum": 200,
860
+ # "filterType": "LIMIT_TRADING"
861
+ # },
862
+ # {
863
+ # "buyPriceUpRate": "0.05",
864
+ # "sellPriceDownRate": "0.05",
865
+ # "filterType": "MARKET_TRADING"
866
+ # },
867
+ # {
868
+ # "noAllowMarketStartTime": "0",
869
+ # "noAllowMarketEndTime": "0",
870
+ # "limitOrderStartTime": "0",
871
+ # "limitOrderEndTime": "0",
872
+ # "limitMinPrice": "0",
873
+ # "limitMaxPrice": "0",
874
+ # "filterType": "OPEN_QUOTE"
875
+ # }
876
+ # ],
877
+ # "exchangeId": "301",
878
+ # "symbol": "BTCUSDT-PERPETUAL",
879
+ # "symbolName": "BTCUSDT-PERPETUAL",
880
+ # "status": "TRADING",
881
+ # "baseAsset": "BTCUSDT-PERPETUAL",
882
+ # "baseAssetPrecision": "0.001",
883
+ # "quoteAsset": "USDT",
884
+ # "quoteAssetPrecision": "0.1",
885
+ # "icebergAllowed": False,
886
+ # "inverse": False,
887
+ # "index": "USDT",
888
+ # "marginToken": "USDT",
889
+ # "marginPrecision": "0.0001",
890
+ # "contractMultiplier": "0.001",
891
+ # "underlying": "BTC",
892
+ # "riskLimits": [
893
+ # {
894
+ # "riskLimitId": "200000722",
895
+ # "quantity": "1000.00",
896
+ # "initialMargin": "0.10",
897
+ # "maintMargin": "0.005",
898
+ # "isWhite": False
899
+ # },
900
+ # {
901
+ # "riskLimitId": "200000723",
902
+ # "quantity": "2000.00",
903
+ # "initialMargin": "0.10",
904
+ # "maintMargin": "0.01",
905
+ # "isWhite": False
906
+ # }
907
+ # ]
908
+ # }
909
+ #
910
+ marketId = self.safe_string(market, 'symbol')
911
+ quoteId = self.safe_string(market, 'quoteAsset')
912
+ quote = self.safe_currency_code(quoteId)
913
+ settleId = self.safe_string(market, 'marginToken')
914
+ settle = self.safe_currency_code(settleId)
915
+ baseId = self.safe_string(market, 'baseAsset')
916
+ marketType = 'spot'
917
+ isSpot = True
918
+ isSwap = False
919
+ suffix = ''
920
+ parts = marketId.split('-')
921
+ secondPart = self.safe_string(parts, 1)
922
+ if secondPart == 'PERPETUAL':
923
+ marketType = 'swap'
924
+ isSpot = False
925
+ isSwap = True
926
+ baseId = self.safe_string(market, 'underlying')
927
+ suffix += ':' + settleId
928
+ base = self.safe_currency_code(baseId)
929
+ symbol = base + '/' + quote + suffix
930
+ status = self.safe_string(market, 'status')
931
+ active = status == 'TRADING'
932
+ isLinear: Bool = None
933
+ subType = None
934
+ isInverse = self.safe_bool(market, 'inverse')
935
+ if isInverse is not None:
936
+ if isInverse:
937
+ isLinear = False
938
+ subType = 'inverse'
939
+ else:
940
+ isLinear = True
941
+ subType = 'linear'
942
+ filtersList = self.safe_list(market, 'filters', [])
943
+ filters = self.index_by(filtersList, 'filterType')
944
+ priceFilter = self.safe_dict(filters, 'PRICE_FILTER', {})
945
+ amountFilter = self.safe_dict(filters, 'LOT_SIZE', {})
946
+ costFilter = self.safe_dict(filters, 'MIN_NOTIONAL', {})
947
+ minCostString = self.omit_zero(self.safe_string(costFilter, 'min_notional'))
948
+ contractSizeString = self.safe_string(market, 'contractMultiplier')
949
+ amountPrecisionString = self.safe_string(amountFilter, 'stepSize')
950
+ amountMinLimitString = self.safe_string(amountFilter, 'minQty')
951
+ amountMaxLimitString = self.safe_string(amountFilter, 'maxQty')
952
+ minLeverage: Int = None
953
+ maxLeverage: Int = None
954
+ if isSwap:
955
+ amountPrecisionString = Precise.string_div(amountPrecisionString, contractSizeString)
956
+ amountMinLimitString = Precise.string_div(amountMinLimitString, contractSizeString)
957
+ amountMaxLimitString = Precise.string_div(amountMaxLimitString, contractSizeString)
958
+ riskLimits = self.safe_list(market, 'riskLimits')
959
+ if riskLimits is not None:
960
+ first = self.safe_dict(riskLimits, 0)
961
+ arrayLength = len(riskLimits)
962
+ last = self.safe_dict(riskLimits, arrayLength - 1)
963
+ minInitialMargin = self.safe_string(first, 'initialMargin')
964
+ maxInitialMargin = self.safe_string(last, 'initialMargin')
965
+ if Precise.string_gt(minInitialMargin, maxInitialMargin):
966
+ minInitialMargin, maxInitialMargin = [maxInitialMargin, minInitialMargin]
967
+ minLeverage = self.parse_to_int(Precise.string_div('1', maxInitialMargin))
968
+ maxLeverage = self.parse_to_int(Precise.string_div('1', minInitialMargin))
969
+ tradingFees = self.safe_dict(self.fees, 'trading')
970
+ fees = self.safe_dict(tradingFees, 'spot') if isSpot else self.safe_dict(tradingFees, 'swap')
971
+ return self.safe_market_structure({
972
+ 'id': marketId,
973
+ 'symbol': symbol,
974
+ 'base': base,
975
+ 'quote': quote,
976
+ 'baseId': baseId,
977
+ 'quoteId': quoteId,
978
+ 'active': active,
979
+ 'type': marketType,
980
+ 'subType': subType,
981
+ 'spot': isSpot,
982
+ 'margin': self.safe_bool(market, 'allowMargin'),
983
+ 'swap': isSwap,
984
+ 'future': False,
985
+ 'option': False,
986
+ 'contract': isSwap,
987
+ 'settle': settle,
988
+ 'settleId': settleId,
989
+ 'contractSize': self.parse_number(contractSizeString),
990
+ 'linear': isLinear,
991
+ 'inverse': isInverse,
992
+ 'taker': self.safe_number(fees, 'taker'),
993
+ 'maker': self.safe_number(fees, 'maker'),
994
+ 'percentage': self.safe_bool(fees, 'percentage'),
995
+ 'tierBased': self.safe_bool(fees, 'tierBased'),
996
+ 'feeSide': self.safe_string(fees, 'feeSide'),
997
+ 'expiry': None,
998
+ 'expiryDatetime': None,
999
+ 'strike': None,
1000
+ 'optionType': None,
1001
+ 'precision': {
1002
+ 'amount': self.parse_number(amountPrecisionString),
1003
+ 'price': self.safe_number(priceFilter, 'tickSize'),
1004
+ },
1005
+ 'limits': {
1006
+ 'amount': {
1007
+ 'min': self.parse_number(amountMinLimitString),
1008
+ 'max': self.parse_number(amountMaxLimitString),
1009
+ },
1010
+ 'price': {
1011
+ 'min': self.safe_number(priceFilter, 'minPrice'),
1012
+ 'max': self.safe_number(priceFilter, 'maxPrice'),
1013
+ },
1014
+ 'leverage': {
1015
+ 'min': minLeverage,
1016
+ 'max': maxLeverage,
1017
+ },
1018
+ 'cost': {
1019
+ 'min': self.parse_number(minCostString),
1020
+ 'max': None,
1021
+ },
1022
+ },
1023
+ 'created': None,
1024
+ 'info': market,
1025
+ })
1026
+
1027
+ async def fetch_currencies(self, params={}) -> Currencies:
1028
+ """
1029
+ fetches all available currencies on an exchange
1030
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/exchangeinfo
1031
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1032
+ :returns dict: an associative dictionary of currencies
1033
+ """
1034
+ response = await self.publicGetApiV1ExchangeInfo(params)
1035
+ coins = self.safe_list(response, 'coins')
1036
+ #
1037
+ # {
1038
+ # ...
1039
+ # "coins": [
1040
+ # {
1041
+ # "orgId": "9001",
1042
+ # "coinId": "BTC",
1043
+ # "coinName": "BTC",
1044
+ # "coinFullName": "Bitcoin",
1045
+ # "allowWithdraw": True,
1046
+ # "allowDeposit": True,
1047
+ # "tokenType": "CHAIN_TOKEN",
1048
+ # "chainTypes": [
1049
+ # {
1050
+ # "chainType": "Bitcoin",
1051
+ # "withdrawFee": "0",
1052
+ # "minWithdrawQuantity": "0.002",
1053
+ # "maxWithdrawQuantity": "0",
1054
+ # "minDepositQuantity": "0.0005",
1055
+ # "allowDeposit": True,
1056
+ # "allowWithdraw": True
1057
+ # }
1058
+ # ]
1059
+ # }
1060
+ # ]
1061
+ # }
1062
+ #
1063
+ result: dict = {}
1064
+ for i in range(0, len(coins)):
1065
+ currecy = coins[i]
1066
+ currencyId = self.safe_string(currecy, 'coinId')
1067
+ code = self.safe_currency_code(currencyId)
1068
+ allowWithdraw = self.safe_bool(currecy, 'allowWithdraw')
1069
+ allowDeposit = self.safe_bool(currecy, 'allowDeposit')
1070
+ networks = self.safe_list(currecy, 'chainTypes')
1071
+ networksById = self.safe_dict(self.options, 'networksById')
1072
+ parsedNetworks: dict = {}
1073
+ for j in range(0, len(networks)):
1074
+ network = networks[j]
1075
+ networkId = self.safe_string(network, 'chainType')
1076
+ networkName = self.safe_string(networksById, networkId, networkId)
1077
+ maxWithdrawQuantity = self.omit_zero(self.safe_string(network, 'maxWithdrawQuantity'))
1078
+ networkDeposit = self.safe_bool(network, 'allowDeposit')
1079
+ networkWithdraw = self.safe_bool(network, 'allowWithdraw')
1080
+ parsedNetworks[networkName] = {
1081
+ 'id': networkId,
1082
+ 'network': networkName,
1083
+ 'limits': {
1084
+ 'withdraw': {
1085
+ 'min': self.safe_number(network, 'minWithdrawQuantity'),
1086
+ 'max': self.parse_number(maxWithdrawQuantity),
1087
+ },
1088
+ 'deposit': {
1089
+ 'min': self.safe_number(network, 'minDepositQuantity'),
1090
+ 'max': None,
1091
+ },
1092
+ },
1093
+ 'active': networkDeposit and networkWithdraw,
1094
+ 'deposit': networkDeposit,
1095
+ 'withdraw': networkWithdraw,
1096
+ 'fee': self.safe_number(network, 'withdrawFee'),
1097
+ 'precision': None,
1098
+ 'info': network,
1099
+ }
1100
+ result[code] = {
1101
+ 'id': currencyId,
1102
+ 'code': code,
1103
+ 'precision': None,
1104
+ 'type': self.parse_currency_type(self.safe_string(currecy, 'tokenType')),
1105
+ 'name': self.safe_string(currecy, 'coinFullName'),
1106
+ 'active': allowWithdraw and allowDeposit,
1107
+ 'deposit': allowDeposit,
1108
+ 'withdraw': allowWithdraw,
1109
+ 'fee': None,
1110
+ 'limits': {
1111
+ 'deposit': {
1112
+ 'min': None,
1113
+ 'max': None,
1114
+ },
1115
+ 'withdraw': {
1116
+ 'min': None,
1117
+ 'max': None,
1118
+ },
1119
+ },
1120
+ 'networks': parsedNetworks,
1121
+ 'info': currecy,
1122
+ }
1123
+ return result
1124
+
1125
+ def parse_currency_type(self, type):
1126
+ types = {
1127
+ 'CHAIN_TOKEN': 'crypto',
1128
+ 'ERC20_TOKEN': 'crypto',
1129
+ 'BSC_TOKEN': 'crypto',
1130
+ 'REAL_MONEY': 'fiat',
1131
+ }
1132
+ return self.safe_string(types, type)
1133
+
1134
+ async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
1135
+ """
1136
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1137
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-order-book
1138
+ :param str symbol: unified symbol of the market to fetch the order book for
1139
+ :param int [limit]: the maximum amount of order book entries to return(maximum value is 200)
1140
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1141
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
1142
+ """
1143
+ await self.load_markets()
1144
+ market = self.market(symbol)
1145
+ request: dict = {
1146
+ 'symbol': market['id'],
1147
+ }
1148
+ if limit is not None:
1149
+ request['limit'] = limit
1150
+ response = await self.publicGetQuoteV1Depth(self.extend(request, params))
1151
+ #
1152
+ # {
1153
+ # "t": 1721681436393,
1154
+ # "b": [
1155
+ # ["67902.49", "0.00112"],
1156
+ # ["67901.08", "0.01014"]
1157
+ # ...
1158
+ # ],
1159
+ # "a": [
1160
+ # ["67905.99", "0.87134"],
1161
+ # ["67906", "0.57361"]
1162
+ # ...
1163
+ # ]
1164
+ # }
1165
+ #
1166
+ timestamp = self.safe_integer(response, 't')
1167
+ return self.parse_order_book(response, symbol, timestamp, 'b', 'a')
1168
+
1169
+ async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1170
+ """
1171
+ get the list of most recent trades for a particular symbol
1172
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-recent-trade-list
1173
+ :param str symbol: unified symbol of the market to fetch trades for
1174
+ :param int [since]: timestamp in ms of the earliest trade to fetch
1175
+ :param int [limit]: the maximum amount of trades to fetch(maximum value is 100)
1176
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1177
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1178
+ """
1179
+ await self.load_markets()
1180
+ market = self.market(symbol)
1181
+ request: dict = {
1182
+ 'symbol': market['id'],
1183
+ }
1184
+ if limit is not None:
1185
+ request['limit'] = limit
1186
+ response = await self.publicGetQuoteV1Trades(self.extend(request, params))
1187
+ #
1188
+ # [
1189
+ # {
1190
+ # "t": 1721682745779,
1191
+ # "p": "67835.99",
1192
+ # "q": "0.00017",
1193
+ # "ibm": True
1194
+ # },
1195
+ # ...
1196
+ # ]
1197
+ #
1198
+ return self.parse_trades(response, market, since, limit)
1199
+
1200
+ async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1201
+ """
1202
+ fetch all trades made by the user
1203
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-account-trade-list
1204
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-futures-trades
1205
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-user
1206
+ :param str symbol: *is mandatory for swap markets* unified market symbol
1207
+ :param int [since]: the earliest time in ms to fetch trades for
1208
+ :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
1209
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1210
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch trades for(default 'spot')
1211
+ :param int [params.until]: the latest time in ms to fetch trades for, only supports the last 30 days timeframe
1212
+ :param str [params.fromId]: srarting trade id
1213
+ :param str [params.toId]: ending trade id
1214
+ :param str [params.clientOrderId]: *spot markets only* filter trades by orderId
1215
+ :param str [params.accountId]: account id to fetch the orders from
1216
+ :returns Trade[]: a list of `trade structures <https://github.com/ccxt/ccxt/wiki/Manual#trade-structure>`
1217
+ """
1218
+ methodName = 'fetchMyTrades'
1219
+ await self.load_markets()
1220
+ request: dict = {}
1221
+ market: Market = None
1222
+ if symbol is not None:
1223
+ market = self.market(symbol)
1224
+ marketType = 'spot'
1225
+ marketType, params = self.handle_market_type_and_params(methodName, market, params)
1226
+ if since is not None:
1227
+ request['startTime'] = since
1228
+ if limit is not None:
1229
+ request['limit'] = limit
1230
+ until: Int = None
1231
+ until, params = self.handle_option_and_params(params, methodName, 'until')
1232
+ if until is not None:
1233
+ request['endTime'] = until
1234
+ accountId: Str = None
1235
+ accountId, params = self.handle_option_and_params(params, methodName, 'accountId')
1236
+ response = None
1237
+ if marketType == 'spot':
1238
+ if market is not None:
1239
+ request['symbol'] = market['id']
1240
+ clientOrderId: Str = None
1241
+ clientOrderId, params = self.handle_option_and_params(params, methodName, 'clientOrderId')
1242
+ if clientOrderId is not None:
1243
+ request['clientOrderId'] = clientOrderId
1244
+ if accountId is not None:
1245
+ request['accountId'] = accountId
1246
+ response = await self.privateGetApiV1AccountTrades(self.extend(request, params))
1247
+ #
1248
+ # [
1249
+ # {
1250
+ # "id": "1739352552862964736",
1251
+ # "clientOrderId": "1722082982086472",
1252
+ # "ticketId": "1739352552795029504",
1253
+ # "symbol": "ETHUSDT",
1254
+ # "symbolName": "ETHUSDT",
1255
+ # "orderId": "1739352552762301440",
1256
+ # "matchOrderId": "0",
1257
+ # "price": "3289.96",
1258
+ # "qty": "0.001",
1259
+ # "commission": "0.0000012",
1260
+ # "commissionAsset": "ETH",
1261
+ # "time": "1722082982097",
1262
+ # "isBuyer": True,
1263
+ # "isMaker": False,
1264
+ # "fee": {
1265
+ # "feeCoinId": "ETH",
1266
+ # "feeCoinName": "ETH",
1267
+ # "fee": "0.0000012"
1268
+ # },
1269
+ # "feeCoinId": "ETH",
1270
+ # "feeAmount": "0.0000012",
1271
+ # "makerRebate": "0"
1272
+ # },
1273
+ # ...
1274
+ # ]
1275
+ #
1276
+ elif marketType == 'swap':
1277
+ if symbol is None:
1278
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument for swap markets')
1279
+ request['symbol'] = market['id']
1280
+ if accountId is not None:
1281
+ request['subAccountId'] = accountId
1282
+ response = await self.privateGetApiV1FuturesSubAccountUserTrades(self.extend(request, params))
1283
+ else:
1284
+ response = await self.privateGetApiV1FuturesUserTrades(self.extend(request, params))
1285
+ #
1286
+ # [
1287
+ # {
1288
+ # "time": "1722429951648",
1289
+ # "tradeId": "1742263144691139328",
1290
+ # "orderId": "1742263144028363776",
1291
+ # "symbol": "ETHUSDT-PERPETUAL",
1292
+ # "price": "3327.54",
1293
+ # "quantity": "4",
1294
+ # "commissionAsset": "USDT",
1295
+ # "commission": "0.00798609",
1296
+ # "makerRebate": "0",
1297
+ # "type": "LIMIT",
1298
+ # "side": "BUY_OPEN",
1299
+ # "realizedPnl": "0",
1300
+ # "isMarker": False
1301
+ # }
1302
+ # ]
1303
+ #
1304
+ else:
1305
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
1306
+ return self.parse_trades(response, market, since, limit)
1307
+
1308
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
1309
+ #
1310
+ # fetchTrades
1311
+ #
1312
+ # {
1313
+ # "t": 1721682745779,
1314
+ # "p": "67835.99",
1315
+ # "q": "0.00017",
1316
+ # "ibm": True
1317
+ # }
1318
+ #
1319
+ # fetchMyTrades spot
1320
+ #
1321
+ # {
1322
+ # "id": "1739352552862964736",
1323
+ # "clientOrderId": "1722082982086472",
1324
+ # "ticketId": "1739352552795029504",
1325
+ # "symbol": "ETHUSDT",
1326
+ # "symbolName": "ETHUSDT",
1327
+ # "orderId": "1739352552762301440",
1328
+ # "matchOrderId": "0",
1329
+ # "price": "3289.96",
1330
+ # "qty": "0.001",
1331
+ # "commission": "0.0000012",
1332
+ # "commissionAsset": "ETH",
1333
+ # "time": "1722082982097",
1334
+ # "isBuyer": True,
1335
+ # "isMaker": False,
1336
+ # "fee": {
1337
+ # "feeCoinId": "ETH",
1338
+ # "feeCoinName": "ETH",
1339
+ # "fee": "0.0000012"
1340
+ # },
1341
+ # "feeCoinId": "ETH",
1342
+ # "feeAmount": "0.0000012",
1343
+ # "makerRebate": "0"
1344
+ # }
1345
+ #
1346
+ # fetchMyTrades swap
1347
+ # {
1348
+ # "time": "1722429951648",
1349
+ # "tradeId": "1742263144691139328",
1350
+ # "orderId": "1742263144028363776",
1351
+ # "symbol": "ETHUSDT-PERPETUAL",
1352
+ # "price": "3327.54",
1353
+ # "quantity": "4",
1354
+ # "commissionAsset": "USDT",
1355
+ # "commission": "0.00798609",
1356
+ # "makerRebate": "0",
1357
+ # "type": "LIMIT",
1358
+ # "side": "BUY_OPEN",
1359
+ # "realizedPnl": "0",
1360
+ # "isMarker": False
1361
+ # }
1362
+ timestamp = self.safe_integer_2(trade, 't', 'time')
1363
+ marketId = self.safe_string(trade, 'symbol')
1364
+ market = self.safe_market(marketId, market)
1365
+ side = self.safe_string_lower(trade, 'side') # swap trades have side param
1366
+ if side is not None:
1367
+ side = self.safe_string(side.split('_'), 0)
1368
+ isBuyer = self.safe_bool(trade, 'isBuyer')
1369
+ if isBuyer is not None:
1370
+ side = 'buy' if isBuyer else 'sell'
1371
+ takerOrMaker = None
1372
+ isMaker = self.safe_bool_n(trade, ['isMaker', 'isMarker', 'ibm'])
1373
+ if isMaker is not None:
1374
+ takerOrMaker = 'maker' if isMaker else 'taker'
1375
+ feeCost = self.safe_string(trade, 'commission')
1376
+ feeCurrncyId = self.safe_string(trade, 'commissionAsset')
1377
+ feeInfo = self.safe_dict(trade, 'fee')
1378
+ fee = None
1379
+ if feeInfo is not None:
1380
+ feeCost = self.safe_string(feeInfo, 'fee')
1381
+ feeCurrncyId = self.safe_string(feeInfo, 'feeCoinId')
1382
+ if feeCost is not None:
1383
+ fee = {
1384
+ 'cost': self.parse_number(feeCost),
1385
+ 'currency': self.safe_currency_code(feeCurrncyId),
1386
+ }
1387
+ return self.safe_trade({
1388
+ 'id': self.safe_string_2(trade, 'id', 'tradeId'),
1389
+ 'timestamp': timestamp,
1390
+ 'datetime': self.iso8601(timestamp),
1391
+ 'symbol': market['symbol'],
1392
+ 'side': side,
1393
+ 'price': self.safe_string_2(trade, 'p', 'price'),
1394
+ 'amount': self.safe_string_n(trade, ['q', 'qty', 'quantity']),
1395
+ 'cost': None,
1396
+ 'takerOrMaker': takerOrMaker,
1397
+ 'type': None,
1398
+ 'order': self.safe_string(trade, 'orderId'),
1399
+ 'fee': fee,
1400
+ 'info': trade,
1401
+ }, market)
1402
+
1403
+ async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1404
+ """
1405
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-kline
1406
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1407
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
1408
+ :param str timeframe: the length of time each candle represents
1409
+ :param int [since]: timestamp in ms of the earliest candle to fetch
1410
+ :param int [limit]: the maximum amount of candles to fetch
1411
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1412
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
1413
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1414
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1415
+ """
1416
+ methodName = 'fetchOHLCV'
1417
+ await self.load_markets()
1418
+ paginate = False
1419
+ paginate, params = self.handle_option_and_params(params, methodName, 'paginate')
1420
+ if paginate:
1421
+ return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1000)
1422
+ market = self.market(symbol)
1423
+ timeframe = self.safe_string(self.timeframes, timeframe, timeframe)
1424
+ request: dict = {
1425
+ 'symbol': market['id'],
1426
+ 'interval': timeframe,
1427
+ }
1428
+ if since is not None:
1429
+ request['startTime'] = since
1430
+ if limit is not None:
1431
+ request['limit'] = limit
1432
+ until: Int = None
1433
+ until, params = self.handle_option_and_params(params, methodName, 'until')
1434
+ if until is not None:
1435
+ request['endTime'] = until
1436
+ response = await self.publicGetQuoteV1Klines(self.extend(request, params))
1437
+ #
1438
+ # [
1439
+ # [
1440
+ # 1721684280000,
1441
+ # "67832.49",
1442
+ # "67862.5",
1443
+ # "67832.49",
1444
+ # "67861.44",
1445
+ # "0.01122",0,
1446
+ # "761.2763533",68,
1447
+ # "0.00561",
1448
+ # "380.640643"
1449
+ # ],
1450
+ # ...
1451
+ # ]
1452
+ #
1453
+ return self.parse_ohlcvs(response, market, timeframe, since, limit)
1454
+
1455
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
1456
+ #
1457
+ # [
1458
+ # 1721684280000,
1459
+ # "67832.49",
1460
+ # "67862.5",
1461
+ # "67832.49",
1462
+ # "67861.44",
1463
+ # "0.01122",0,
1464
+ # "761.2763533",68,
1465
+ # "0.00561",
1466
+ # "380.640643"
1467
+ # ]
1468
+ #
1469
+ return [
1470
+ self.safe_integer(ohlcv, 0),
1471
+ self.safe_number(ohlcv, 1),
1472
+ self.safe_number(ohlcv, 2),
1473
+ self.safe_number(ohlcv, 3),
1474
+ self.safe_number(ohlcv, 4),
1475
+ self.safe_number(ohlcv, 5),
1476
+ ]
1477
+
1478
+ async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
1479
+ """
1480
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1481
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-24hr-ticker-price-change
1482
+ :param str symbol: unified symbol of the market to fetch the ticker for
1483
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1484
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1485
+ """
1486
+ await self.load_markets()
1487
+ market = self.market(symbol)
1488
+ request: dict = {
1489
+ 'symbol': market['id'],
1490
+ }
1491
+ response = await self.publicGetQuoteV1Ticker24hr(self.extend(request, params))
1492
+ #
1493
+ # [
1494
+ # {
1495
+ # "t": 1721685896846,
1496
+ # "s": "BTCUSDT-PERPETUAL",
1497
+ # "c": "67756.7",
1498
+ # "h": "68479.9",
1499
+ # "l": "66594.3",
1500
+ # "o": "68279.7",
1501
+ # "b": "67756.6",
1502
+ # "a": "67756.7",
1503
+ # "v": "1604722",
1504
+ # "qv": "108827258.7761"
1505
+ # }
1506
+ # ]
1507
+ #
1508
+ ticker = self.safe_dict(response, 0, {})
1509
+ return self.parse_ticker(ticker, market)
1510
+
1511
+ async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1512
+ """
1513
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
1514
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-24hr-ticker-price-change
1515
+ :param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
1516
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1517
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
1518
+ """
1519
+ await self.load_markets()
1520
+ symbols = self.market_symbols(symbols)
1521
+ response = await self.publicGetQuoteV1Ticker24hr(params)
1522
+ return self.parse_tickers(response, symbols)
1523
+
1524
+ def parse_ticker(self, ticker, market: Market = None) -> Ticker:
1525
+ #
1526
+ # {
1527
+ # "t": 1721685896846,
1528
+ # "s": "BTCUSDT-PERPETUAL",
1529
+ # "c": "67756.7",
1530
+ # "h": "68479.9",
1531
+ # "l": "66594.3",
1532
+ # "o": "68279.7",
1533
+ # "b": "67756.6",
1534
+ # "a": "67756.7",
1535
+ # "v": "1604722",
1536
+ # "qv": "108827258.7761"
1537
+ # }
1538
+ #
1539
+ timestamp = self.safe_integer(ticker, 't')
1540
+ marketId = self.safe_string(ticker, 's')
1541
+ market = self.safe_market(marketId, market)
1542
+ symbol = market['symbol']
1543
+ last = self.safe_string(ticker, 'c')
1544
+ return self.safe_ticker({
1545
+ 'symbol': symbol,
1546
+ 'timestamp': timestamp,
1547
+ 'datetime': self.iso8601(timestamp),
1548
+ 'high': self.safe_string(ticker, 'h'),
1549
+ 'low': self.safe_string(ticker, 'l'),
1550
+ 'bid': self.safe_string(ticker, 'b'),
1551
+ 'bidVolume': None,
1552
+ 'ask': self.safe_string(ticker, 'a'),
1553
+ 'askVolume': None,
1554
+ 'vwap': None,
1555
+ 'open': self.safe_string(ticker, 'o'),
1556
+ 'close': last,
1557
+ 'last': last,
1558
+ 'previousClose': None,
1559
+ 'change': None,
1560
+ 'percentage': None,
1561
+ 'average': None,
1562
+ 'baseVolume': self.safe_string(ticker, 'v'),
1563
+ 'quoteVolume': self.safe_string(ticker, 'qv'),
1564
+ 'info': ticker,
1565
+ }, market)
1566
+
1567
+ async def fetch_last_prices(self, symbols: Strings = None, params={}) -> LastPrices:
1568
+ """
1569
+ fetches the last price for multiple markets
1570
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-symbol-price-ticker
1571
+ :param str[] [symbols]: unified symbols of the markets to fetch the last prices
1572
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1573
+ :param str [params.symbol]: the id of the market to fetch last price for
1574
+ :returns dict: a dictionary of lastprices structures
1575
+ """
1576
+ await self.load_markets()
1577
+ symbols = self.market_symbols(symbols)
1578
+ request: dict = {}
1579
+ symbol: Str = None
1580
+ symbol, params = self.handle_option_and_params(params, 'fetchLastPrices', 'symbol')
1581
+ if symbol is not None:
1582
+ request['symbol'] = symbol
1583
+ response = await self.publicGetQuoteV1TickerPrice(self.extend(request, params))
1584
+ #
1585
+ # [
1586
+ # {
1587
+ # "s": "BTCUSDT-PERPETUAL",
1588
+ # "p": "64871"
1589
+ # },
1590
+ # ...
1591
+ # ]
1592
+ #
1593
+ return self.parse_last_prices(response, symbols)
1594
+
1595
+ def parse_last_price(self, entry, market: Market = None) -> LastPrice:
1596
+ marketId = self.safe_string(entry, 's')
1597
+ market = self.safe_market(marketId, market)
1598
+ return {
1599
+ 'symbol': market['symbol'],
1600
+ 'timestamp': None,
1601
+ 'datetime': None,
1602
+ 'price': self.safe_number(entry, 'p'),
1603
+ 'side': None,
1604
+ 'info': entry,
1605
+ }
1606
+
1607
+ async def fetch_balance(self, params={}) -> Balances:
1608
+ """
1609
+ query for balance and get the amount of funds available for trading or funds locked in orders
1610
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-account-information
1611
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1612
+ :param str [params.accountId]: account ID, for Master Key only
1613
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch balance for(default 'spot')
1614
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
1615
+ """
1616
+ await self.load_markets()
1617
+ request: dict = {}
1618
+ methodName = 'fetchBalance'
1619
+ marketType = 'spot'
1620
+ marketType, params = self.handle_market_type_and_params(methodName, None, params, marketType)
1621
+ if marketType == 'swap':
1622
+ response = await self.privateGetApiV1FuturesBalance(params)
1623
+ #
1624
+ # [
1625
+ # {
1626
+ # "balance": "30.63364672",
1627
+ # "availableBalance": "28.85635534",
1628
+ # "positionMargin": "4.3421",
1629
+ # "orderMargin": "0",
1630
+ # "asset": "USDT",
1631
+ # "crossUnRealizedPnl": "2.5649"
1632
+ # }
1633
+ # ]
1634
+ #
1635
+ balance = self.safe_dict(response, 0, {})
1636
+ return self.parse_swap_balance(balance)
1637
+ elif marketType == 'spot':
1638
+ accountId: Str = None
1639
+ accountId, params = self.handle_option_and_params(params, methodName, 'accountId')
1640
+ if accountId is not None:
1641
+ request['accountId'] = accountId
1642
+ response = await self.privateGetApiV1Account(self.extend(request, params))
1643
+ #
1644
+ # {
1645
+ # "balances": [
1646
+ # {
1647
+ # "asset":"USDT",
1648
+ # "assetId":"USDT",
1649
+ # "assetName":"USDT",
1650
+ # "total":"40",
1651
+ # "free":"40",
1652
+ # "locked":"0"
1653
+ # },
1654
+ # ...
1655
+ # ],
1656
+ # "userId": "1732885739572845312"
1657
+ # }
1658
+ #
1659
+ return self.parse_balance(response)
1660
+ else:
1661
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
1662
+
1663
+ def parse_balance(self, balance) -> Balances:
1664
+ #
1665
+ # {
1666
+ # "balances": [
1667
+ # {
1668
+ # "asset":"USDT",
1669
+ # "assetId":"USDT",
1670
+ # "assetName":"USDT",
1671
+ # "total":"40",
1672
+ # "free":"40",
1673
+ # "locked":"0"
1674
+ # },
1675
+ # ...
1676
+ # ],
1677
+ # "userId": "1732885739572845312"
1678
+ # }
1679
+ #
1680
+ result: dict = {
1681
+ 'info': balance,
1682
+ }
1683
+ balances = self.safe_list(balance, 'balances', [])
1684
+ for i in range(0, len(balances)):
1685
+ balanceEntry = balances[i]
1686
+ currencyId = self.safe_string(balanceEntry, 'asset')
1687
+ code = self.safe_currency_code(currencyId)
1688
+ account = self.account()
1689
+ account['total'] = self.safe_string(balanceEntry, 'total')
1690
+ account['free'] = self.safe_string(balanceEntry, 'free')
1691
+ account['used'] = self.safe_string(balanceEntry, 'locked')
1692
+ result[code] = account
1693
+ return self.safe_balance(result)
1694
+
1695
+ def parse_swap_balance(self, balance) -> Balances:
1696
+ #
1697
+ # {
1698
+ # "balance": "30.63364672",
1699
+ # "availableBalance": "28.85635534",
1700
+ # "positionMargin": "4.3421",
1701
+ # "orderMargin": "0",
1702
+ # "asset": "USDT",
1703
+ # "crossUnRealizedPnl": "2.5649"
1704
+ # }
1705
+ #
1706
+ currencyId = self.safe_string(balance, 'asset')
1707
+ code = self.safe_currency_code(currencyId)
1708
+ account = self.account()
1709
+ account['total'] = self.safe_string(balance, 'balance')
1710
+ positionMargin = self.safe_string(balance, 'positionMargin')
1711
+ orderMargin = self.safe_string(balance, 'orderMargin')
1712
+ account['used'] = Precise.string_add(positionMargin, orderMargin)
1713
+ result: dict = {
1714
+ 'info': balance,
1715
+ }
1716
+ result[code] = account
1717
+ return self.safe_balance(result)
1718
+
1719
+ async def fetch_deposit_address(self, code: str, params={}):
1720
+ """
1721
+ fetch the deposit address for a currency associated with self account
1722
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-deposit-address
1723
+ :param str code: unified currency code(default is 'USDT')
1724
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1725
+ :param str [params.network]: network for fetch deposit address(default is 'ETH')
1726
+ :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1727
+ """
1728
+ await self.load_markets()
1729
+ currency = self.currency(code)
1730
+ request: dict = {
1731
+ 'coin': currency['id'],
1732
+ }
1733
+ networkCode: Str = None
1734
+ networkCode, params = self.handle_network_code_and_params(params)
1735
+ if networkCode is None:
1736
+ networkCode = self.default_network_code(code)
1737
+ request['chainType'] = self.network_code_to_id(networkCode, code)
1738
+ response = await self.privateGetApiV1AccountDepositAddress(self.extend(request, params))
1739
+ #
1740
+ # {
1741
+ # "canDeposit": True,
1742
+ # "address": "0x61AAd7F763e2C7fF1CC996918740F67f9dC8BF4e",
1743
+ # "addressExt": "",
1744
+ # "minQuantity": "1",
1745
+ # "needAddressTag": False,
1746
+ # "requiredConfirmTimes": 64,
1747
+ # "canWithdrawConfirmTimes": 64,
1748
+ # "coinType": "ERC20_TOKEN"
1749
+ # }
1750
+ #
1751
+ depositAddress = self.parse_deposit_address(response, currency)
1752
+ depositAddress['network'] = networkCode
1753
+ return depositAddress
1754
+
1755
+ def parse_deposit_address(self, depositAddress, currency: Currency = None):
1756
+ #
1757
+ # {
1758
+ # "canDeposit": True,
1759
+ # "address": "0x61AAd7F763e2C7fF1CC996918740F67f9dC8BF4e",
1760
+ # "addressExt": "",
1761
+ # "minQuantity": "1",
1762
+ # "needAddressTag": False,
1763
+ # "requiredConfirmTimes": 64,
1764
+ # "canWithdrawConfirmTimes": 64,
1765
+ # "coinType": "ERC20_TOKEN"
1766
+ # }
1767
+ #
1768
+ address = self.safe_string(depositAddress, 'address')
1769
+ self.check_address(address)
1770
+ tag = self.safe_string(depositAddress, 'addressExt')
1771
+ if tag == '':
1772
+ tag = None
1773
+ return {
1774
+ 'currency': currency['code'],
1775
+ 'address': address,
1776
+ 'tag': tag,
1777
+ 'network': None,
1778
+ 'info': depositAddress,
1779
+ }
1780
+
1781
+ async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1782
+ """
1783
+ fetch all deposits made to an account
1784
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-deposit-history
1785
+ :param str code: unified currency code of the currency transferred
1786
+ :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
1787
+ :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
1788
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1789
+ :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
1790
+ :param int [params.fromId]: starting ID(To be released)
1791
+ :returns dict[]: a list of `transfer structures <https://docs.ccxt.com/#/?id=transfer-structure>`
1792
+ """
1793
+ methodName = 'fetchDeposits'
1794
+ await self.load_markets()
1795
+ request: dict = {}
1796
+ currency: Currency = None
1797
+ if code is not None:
1798
+ currency = self.currency(code)
1799
+ request['coin'] = currency['id']
1800
+ if since is not None:
1801
+ request['startTime'] = since
1802
+ if limit is not None:
1803
+ request['limit'] = limit
1804
+ until: Int = None
1805
+ until, params = self.handle_option_and_params(params, methodName, 'until')
1806
+ if until is not None:
1807
+ request['endTime'] = until
1808
+ response = await self.privateGetApiV1AccountDepositOrders(self.extend(request, params))
1809
+ #
1810
+ # [
1811
+ # {
1812
+ # "time": "1721641082163",
1813
+ # "coin": "TRXUSDT",
1814
+ # "coinName": "TRXUSDT",
1815
+ # "address": "TBA6CypYJizwA9XdC7Ubgc5F1bxrQ7SqPt",
1816
+ # "quantity": "86.00000000000000000000",
1817
+ # "status": 4,
1818
+ # "statusCode": "4",
1819
+ # "txId": "0970c14da4d7412295fa7b21c03a08da319e746a0d59ef14462a74183d118da4"
1820
+ # }
1821
+ # ]
1822
+ #
1823
+ return self.parse_transactions(response, currency, since, limit, {'type': 'deposit'})
1824
+
1825
+ async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1826
+ """
1827
+ fetch all withdrawals made from an account
1828
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/withdrawal-records
1829
+ :param str code: unified currency code of the currency transferred
1830
+ :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
1831
+ :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
1832
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1833
+ :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
1834
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
1835
+ """
1836
+ methodName = 'fetchWithdrawals'
1837
+ await self.load_markets()
1838
+ request: dict = {}
1839
+ currency: Currency = None
1840
+ if code is not None:
1841
+ currency = self.currency(code)
1842
+ request['coin'] = currency['id']
1843
+ if since is not None:
1844
+ request['startTime'] = since
1845
+ if limit is not None:
1846
+ request['limit'] = limit
1847
+ until: Int = None
1848
+ until, params = self.handle_option_and_params(params, methodName, 'until')
1849
+ if until is not None:
1850
+ request['endTime'] = until
1851
+ response = await self.privateGetApiV1AccountWithdrawOrders(self.extend(request, params))
1852
+ #
1853
+ # [
1854
+ # {
1855
+ # "time": "1723545505366",
1856
+ # "id": "W611267400947572736",
1857
+ # "coin": "USDT",
1858
+ # "coinId": "USDT",
1859
+ # "coinName": "USDT",
1860
+ # "address": "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
1861
+ # "quantity": "2.00000000",
1862
+ # "arriveQuantity": "2.00000000",
1863
+ # "txId": "f83f94e7d2e81fbec98c66c25d6615872cc2d426145629b6cf22e5e0a0753715",
1864
+ # "addressUrl": "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
1865
+ # "feeCoinId": "USDT",
1866
+ # "feeCoinName": "USDT",
1867
+ # "fee": "1.00000000",
1868
+ # "remark": "",
1869
+ # "platform": ""
1870
+ # }
1871
+ # ]
1872
+ #
1873
+ return self.parse_transactions(response, currency, since, limit, {'type': 'withdrawal'})
1874
+
1875
+ async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
1876
+ """
1877
+ make a withdrawal
1878
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/withdraw
1879
+ :param str code: unified currency code
1880
+ :param float amount: the amount to withdraw
1881
+ :param str address: the address to withdraw to
1882
+ :param str tag:
1883
+ :param str [params.network]: network for withdraw
1884
+ :param str [params.clientOrderId]: client order id
1885
+ :param str [params.platform]: the platform to withdraw to(hashkey, HashKey HK)
1886
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1887
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1888
+ """
1889
+ tag, params = self.handle_withdraw_tag_and_params(tag, params)
1890
+ await self.load_markets()
1891
+ currency = self.currency(code)
1892
+ request: dict = {
1893
+ 'coin': currency['id'],
1894
+ 'address': address,
1895
+ 'quantity': amount,
1896
+ }
1897
+ if tag is not None:
1898
+ request['addressExt'] = tag
1899
+ clientOrderId: Str = None
1900
+ clientOrderId, params = self.handle_option_and_params(params, 'withdraw', 'clientOrderId')
1901
+ if clientOrderId is not None:
1902
+ request['clientOrderId'] = clientOrderId
1903
+ networkCode: Str = None
1904
+ networkCode, params = self.handle_network_code_and_params(params)
1905
+ if networkCode is not None:
1906
+ request['chainType'] = self.network_code_to_id(networkCode)
1907
+ platform: Str = None
1908
+ platform, params = self.handle_option_and_params(params, 'withdraw', 'platform')
1909
+ if platform is not None:
1910
+ request['platform'] = platform
1911
+ response = await self.privatePostApiV1AccountWithdraw(self.extend(request, params))
1912
+ #
1913
+ # {
1914
+ # "success": True,
1915
+ # "id": "0",
1916
+ # "orderId": "W611267400947572736",
1917
+ # "accountId": "1732885739589466115"
1918
+ # }
1919
+ #
1920
+ return self.parse_transaction(response, currency)
1921
+
1922
+ def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
1923
+ #
1924
+ # fetchDeposits
1925
+ # {
1926
+ # "time": "1721641082163",
1927
+ # "coin": "TRXUSDT", # todo how to parse it?
1928
+ # "coinName": "TRXUSDT",
1929
+ # "address": "TBA6CypYJizwA9XdC7Ubgc5F1bxrQ7SqPt",
1930
+ # "quantity": "86.00000000000000000000",
1931
+ # "status": 4,
1932
+ # "statusCode": "4",
1933
+ # "txId": "0970c14da4d7412295fa7b21c03a08da319e746a0d59ef14462a74183d118da4"
1934
+ # }
1935
+ #
1936
+ # fetchWithdrawals
1937
+ # {
1938
+ # "time": "1723545505366",
1939
+ # "id": "W611267400947572736",
1940
+ # "coin": "USDT",
1941
+ # "coinId": "USDT",
1942
+ # "coinName": "USDT",
1943
+ # "address": "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
1944
+ # "quantity": "2.00000000",
1945
+ # "arriveQuantity": "2.00000000",
1946
+ # "txId": "f83f94e7d2e81fbec98c66c25d6615872cc2d426145629b6cf22e5e0a0753715",
1947
+ # "addressUrl": "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
1948
+ # "feeCoinId": "USDT",
1949
+ # "feeCoinName": "USDT",
1950
+ # "fee": "1.00000000",
1951
+ # "remark": "",
1952
+ # "platform": ""
1953
+ # }
1954
+ #
1955
+ # withdraw
1956
+ # {
1957
+ # "success": True,
1958
+ # "id": "0",
1959
+ # "orderId": "W611267400947572736",
1960
+ # "accountId": "1732885739589466115"
1961
+ # }
1962
+ #
1963
+ id = self.safe_string_2(transaction, 'id', 'orderId')
1964
+ address = self.safe_string(transaction, 'address')
1965
+ status = self.safe_string(transaction, 'status') # for fetchDeposits
1966
+ if status is None:
1967
+ success = self.safe_bool(transaction, 'success', False) # for withdraw
1968
+ if success:
1969
+ status = 'ok'
1970
+ else:
1971
+ addressUrl = self.safe_string(transaction, 'addressUrl') # for fetchWithdrawals
1972
+ if addressUrl is not None:
1973
+ status = 'ok'
1974
+ txid = self.safe_string(transaction, 'txId')
1975
+ coin = self.safe_string(transaction, 'coin')
1976
+ code = self.safe_currency_code(coin, currency)
1977
+ timestamp = self.safe_integer(transaction, 'time')
1978
+ amount = self.safe_number(transaction, 'quantity')
1979
+ feeCost = self.safe_number(transaction, 'fee')
1980
+ fee = None
1981
+ if feeCost is not None:
1982
+ fee = {
1983
+ 'cost': feeCost,
1984
+ 'currency': code,
1985
+ }
1986
+ return {
1987
+ 'info': transaction,
1988
+ 'id': id,
1989
+ 'txid': txid,
1990
+ 'timestamp': timestamp,
1991
+ 'datetime': self.iso8601(timestamp),
1992
+ 'network': None,
1993
+ 'address': address,
1994
+ 'addressTo': None,
1995
+ 'addressFrom': None,
1996
+ 'tag': None,
1997
+ 'tagTo': None,
1998
+ 'tagFrom': None,
1999
+ 'type': None,
2000
+ 'amount': amount,
2001
+ 'currency': code,
2002
+ 'status': self.parse_transaction_status(status),
2003
+ 'updated': None,
2004
+ 'internal': None,
2005
+ 'comment': None,
2006
+ 'fee': fee,
2007
+ }
2008
+
2009
+ def parse_transaction_status(self, status):
2010
+ statuses: dict = {
2011
+ '1': 'pending',
2012
+ '2': 'pending',
2013
+ '3': 'failed',
2014
+ '4': 'ok',
2015
+ '5': 'pending',
2016
+ '6': 'ok',
2017
+ '7': 'failed',
2018
+ '8': 'cancelled',
2019
+ '9': 'failed',
2020
+ '10': 'failed',
2021
+ 'successful': 'ok',
2022
+ 'success': 'ok',
2023
+ }
2024
+ return self.safe_string(statuses, status, status)
2025
+
2026
+ async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
2027
+ """
2028
+ transfer currency internally between wallets on the same account
2029
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/new-account-transfer
2030
+ :param str code: unified currency code
2031
+ :param float amount: amount to transfer
2032
+ :param str fromAccount: account id to transfer from
2033
+ :param str toAccount: account id to transfer to
2034
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2035
+ :param str [params.clientOrderId]: a unique id for the transfer
2036
+ :param str [params.remark]: a note for the transfer
2037
+ :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
2038
+ """
2039
+ await self.load_markets()
2040
+ currency = self.currency(code)
2041
+ request: dict = {
2042
+ 'coin': currency['id'],
2043
+ 'quantity': self.currency_to_precision(code, amount),
2044
+ 'fromAccountId': fromAccount,
2045
+ 'toAccountId': toAccount,
2046
+ }
2047
+ clientOrderId: Str = None
2048
+ clientOrderId, params = self.handle_option_and_params(params, 'transfer', 'clientOrderId')
2049
+ if clientOrderId is not None:
2050
+ request['clientOrderId'] = clientOrderId
2051
+ remark: Str = None
2052
+ remark, params = self.handle_option_and_params(params, 'transfer', 'remark')
2053
+ if remark is not None:
2054
+ request['remark'] = remark
2055
+ response = await self.privatePostApiV1AccountAssetTransfer(self.extend(request, params))
2056
+ #
2057
+ # {
2058
+ # "success": True,
2059
+ # "timestamp": 1722260230773,
2060
+ # "clientOrderId": "",
2061
+ # "orderId": "1740839420695806720"
2062
+ # }
2063
+ #
2064
+ return self.parse_transfer(response, currency)
2065
+
2066
+ def parse_transfer(self, transfer, currency: Currency = None):
2067
+ timestamp = self.safe_integer(transfer, 'timestamp')
2068
+ currencyId = self.safe_string(currency, 'id')
2069
+ status: Str = None
2070
+ success = self.safe_bool(transfer, 'success', False)
2071
+ if success:
2072
+ status = 'ok'
2073
+ return {
2074
+ 'id': self.safe_string(transfer, 'orderId'),
2075
+ 'timestamp': timestamp,
2076
+ 'datetime': self.iso8601(timestamp),
2077
+ 'currency': self.safe_currency_code(currencyId, currency),
2078
+ 'amount': None,
2079
+ 'fromAccount': None,
2080
+ 'toAccount': None,
2081
+ 'status': status,
2082
+ 'info': transfer,
2083
+ }
2084
+
2085
+ async def fetch_accounts(self, params={}) -> List[Account]:
2086
+ """
2087
+ fetch all the accounts associated with a profile
2088
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-sub-account
2089
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2090
+ :returns dict: a dictionary of `account structures <https://docs.ccxt.com/#/?id=account-structure>` indexed by the account type
2091
+ """
2092
+ await self.load_markets()
2093
+ response = await self.privateGetApiV1AccountType(params)
2094
+ #
2095
+ # [
2096
+ # {
2097
+ # "accountId": "1732885739589466112",
2098
+ # "accountLabel": "Main Trading Account",
2099
+ # "accountType": 1,
2100
+ # "accountIndex": 0
2101
+ # },
2102
+ # ...
2103
+ # ]
2104
+ #
2105
+ return self.parse_accounts(response, params)
2106
+
2107
+ def parse_account(self, account):
2108
+ accountLabel = self.safe_string(account, 'accountLabel')
2109
+ label = ''
2110
+ if accountLabel == 'Main Trading Account' or accountLabel == 'Main Future Account':
2111
+ label = 'main'
2112
+ elif accountLabel == 'Sub Main Trading Account' or accountLabel == 'Sub Main Future Account':
2113
+ label = 'sub'
2114
+ accountType = self.parse_account_type(self.safe_string(account, 'accountType'))
2115
+ type = label + ' ' + accountType
2116
+ return {
2117
+ 'id': self.safe_string(account, 'accountId'),
2118
+ 'type': type,
2119
+ 'code': None,
2120
+ 'info': account,
2121
+ }
2122
+
2123
+ def parse_account_type(self, type):
2124
+ types: dict = {
2125
+ '1': 'spot account',
2126
+ '3': 'swap account',
2127
+ '5': 'custody account',
2128
+ '6': 'fiat account',
2129
+ }
2130
+ return self.safe_string(types, type, type)
2131
+
2132
+ def encode_account_type(self, type):
2133
+ types = {
2134
+ 'spot': '1',
2135
+ 'swap': '3',
2136
+ 'custody': '5',
2137
+ }
2138
+ return self.safe_integer(types, type, type)
2139
+
2140
+ def encode_flow_type(self, type):
2141
+ types = {
2142
+ 'trade': '1',
2143
+ 'fee': '3',
2144
+ 'transfer': '51',
2145
+ 'deposit': '900',
2146
+ 'withdraw': '904',
2147
+ }
2148
+ return self.safe_integer(types, type, type)
2149
+
2150
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
2151
+ """
2152
+ fetch the history of changes, actions done by the user or operations that altered balance of the user
2153
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-account-transaction-list
2154
+ :param str code: unified currency code, default is None(not used)
2155
+ :param int [since]: timestamp in ms of the earliest ledger entry, default is None
2156
+ :param int [limit]: max number of ledger entrys to return, default is None
2157
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2158
+ :param int [params.until]: the latest time in ms to fetch entries for
2159
+ :param int [params.flowType]: trade, fee, transfer, deposit, withdrawal
2160
+ :param int [params.accountType]: spot, swap, custody
2161
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger-structure>`
2162
+ """
2163
+ methodName = 'fetchLedger'
2164
+ if since is None:
2165
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a since argument')
2166
+ until: Int = None
2167
+ until, params = self.handle_option_and_params(params, methodName, 'until')
2168
+ if until is None:
2169
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires an until argument')
2170
+ await self.load_markets()
2171
+ currency = self.currency(code)
2172
+ request = {}
2173
+ request['startTime'] = since
2174
+ if limit is not None:
2175
+ request['limit'] = limit
2176
+ request['endTime'] = until
2177
+ flowType = None
2178
+ flowType, params = self.handle_option_and_params(params, methodName, 'flowType')
2179
+ if flowType is not None:
2180
+ request['flowType'] = self.encode_flow_type(flowType)
2181
+ accountType = None
2182
+ accountType, params = self.handle_option_and_params(params, methodName, 'accountType')
2183
+ if accountType is not None:
2184
+ request['accountType'] = self.encode_account_type(accountType)
2185
+ response = await self.privateGetApiV1AccountBalanceFlow(self.extend(request, params))
2186
+ #
2187
+ # [
2188
+ # {
2189
+ # "id": "1740844413612065537",
2190
+ # "accountId": "1732885739589466112",
2191
+ # "coin": "USDT",
2192
+ # "coinId": "USDT",
2193
+ # "coinName": "USDT",
2194
+ # "flowTypeValue": 51,
2195
+ # "flowType": "USER_ACCOUNT_TRANSFER",
2196
+ # "flowName": "",
2197
+ # "change": "-1",
2198
+ # "total": "8.015680088",
2199
+ # "created": "1722260825765"
2200
+ # },
2201
+ # ...
2202
+ # ]
2203
+ #
2204
+ return self.parse_ledger(response, currency, since, limit)
2205
+
2206
+ def parse_ledger_entry_type(self, type):
2207
+ types: dict = {
2208
+ '1': 'trade', # transfer
2209
+ '2': 'fee', # trade
2210
+ '51': 'transfer',
2211
+ '900': 'deposit',
2212
+ '904': 'withdraw',
2213
+ }
2214
+ return self.safe_string(types, type, type)
2215
+
2216
+ def parse_ledger_entry(self, item: dict, currency: Currency = None):
2217
+ #
2218
+ # {
2219
+ # "id": "1740844413612065537",
2220
+ # "accountId": "1732885739589466112",
2221
+ # "coin": "USDT",
2222
+ # "coinId": "USDT",
2223
+ # "coinName": "USDT",
2224
+ # "flowTypeValue": 51,
2225
+ # "flowType": "USER_ACCOUNT_TRANSFER",
2226
+ # "flowName": "",
2227
+ # "change": "-1",
2228
+ # "total": "8.015680088",
2229
+ # "created": "1722260825765"
2230
+ # }
2231
+ #
2232
+ id = self.safe_string(item, 'id')
2233
+ account = self.safe_string(item, 'accountId')
2234
+ timestamp = self.safe_integer(item, 'created')
2235
+ type = self.parse_ledger_entry_type(self.safe_string(item, 'flowTypeValue'))
2236
+ code = self.safe_currency_code(self.safe_string(item, 'coin'), currency)
2237
+ amountString = self.safe_string(item, 'change')
2238
+ amount = self.parse_number(amountString)
2239
+ direction = 'in'
2240
+ if amountString.find('-') >= 0:
2241
+ direction = 'out'
2242
+ afterString = self.safe_string(item, 'total')
2243
+ after = self.parse_number(afterString)
2244
+ status = 'ok'
2245
+ return {
2246
+ 'id': id,
2247
+ 'info': item,
2248
+ 'timestamp': timestamp,
2249
+ 'datetime': self.iso8601(timestamp),
2250
+ 'account': account,
2251
+ 'direction': direction,
2252
+ 'referenceId': None,
2253
+ 'referenceAccount': None,
2254
+ 'type': type,
2255
+ 'currency': code,
2256
+ 'symbol': None,
2257
+ 'amount': amount,
2258
+ 'before': None,
2259
+ 'after': after,
2260
+ 'status': status,
2261
+ 'fee': None,
2262
+ }
2263
+
2264
+ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
2265
+ """
2266
+ create a trade order
2267
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/test-new-order
2268
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/create-order
2269
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/create-new-futures-order
2270
+ :param str symbol: unified symbol of the market to create an order in
2271
+ :param str type: 'market' or 'limit' or 'LIMIT_MAKER' for spot, 'market' or 'limit' or 'STOP' for swap
2272
+ :param str side: 'buy' or 'sell'
2273
+ :param float amount: how much of you want to trade in units of the base currency
2274
+ :param float [price]: the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2275
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2276
+ :param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
2277
+ :param boolean [params.test]: *spot markets only* whether to use the test endpoint or not, default is False
2278
+ :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately
2279
+ :param str [params.timeInForce]: "GTC" or "IOC" or "PO" for spot, 'GTC' or 'FOK' or 'IOC' or 'LIMIT_MAKER' or 'PO' for swap
2280
+ :param str [params.clientOrderId]: a unique id for the order - is mandatory for swap
2281
+ :param float [params.triggerPrice]: *swap markets only* The price at which a trigger order is triggered at
2282
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2283
+ """
2284
+ await self.load_markets()
2285
+ market = self.market(symbol)
2286
+ if market['spot']:
2287
+ return await self.create_spot_order(symbol, type, side, amount, price, params)
2288
+ elif market['swap']:
2289
+ return await self.create_swap_order(symbol, type, side, amount, price, params)
2290
+ else:
2291
+ raise NotSupported(self.id + ' createOrder() is not supported for ' + market['type'] + ' type of markets')
2292
+
2293
+ async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}) -> Order:
2294
+ """
2295
+ create a market buy order by providing the symbol and cost
2296
+ :param str symbol: unified symbol of the market to create an order in
2297
+ :param float cost: how much you want to trade in units of the quote currency
2298
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2299
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2300
+ """
2301
+ await self.load_markets()
2302
+ market = self.market(symbol)
2303
+ if not market['spot']:
2304
+ raise NotSupported(self.id + ' createMarketBuyOrderWithCost() is supported for spot markets only')
2305
+ params['cost'] = cost
2306
+ return await self.create_order(symbol, 'market', 'buy', cost, None, params)
2307
+
2308
+ async def create_spot_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
2309
+ """
2310
+ create a trade order on spot market
2311
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/test-new-order
2312
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/create-order
2313
+ :param str symbol: unified symbol of the market to create an order in
2314
+ :param str type: 'market' or 'limit' or 'LIMIT_MAKER'
2315
+ :param str side: 'buy' or 'sell'
2316
+ :param float amount: how much of you want to trade in units of the base currency
2317
+ :param float [price]: the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2318
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2319
+ :param float [params.cost]: *market buy only* the quote quantity that can be used alternative for the amount
2320
+ :param bool [params.test]: whether to use the test endpoint or not, default is False
2321
+ :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately
2322
+ :param str [params.timeInForce]: 'GTC', 'IOC', or 'PO'
2323
+ :param str [params.clientOrderId]: a unique id for the order
2324
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2325
+ """
2326
+ triggerPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice')
2327
+ if triggerPrice is not None:
2328
+ raise NotSupported(self.id + ' trigger orders are not supported for spot markets')
2329
+ await self.load_markets()
2330
+ market = self.market(symbol)
2331
+ isMarketBuy = (type == 'market') and (side == 'buy')
2332
+ cost = self.safe_string(params, 'cost')
2333
+ if (not isMarketBuy) and (cost is not None):
2334
+ raise NotSupported(self.id + ' createOrder() supports cost parameter for spot market buy orders only')
2335
+ request: dict = self.create_spot_order_request(symbol, type, side, amount, price, params)
2336
+ response: dict = {}
2337
+ test = self.safe_bool(params, 'test')
2338
+ if test:
2339
+ params = self.omit(params, 'test')
2340
+ response = await self.privatePostApiV1SpotOrderTest(request)
2341
+ elif isMarketBuy and (cost is None):
2342
+ response = await self.privatePostApiV11SpotOrder(request) # the endpoint for market buy orders by amount
2343
+ #
2344
+ # {
2345
+ # "accountId": "1732885739589466112",
2346
+ # "symbol": "ETHUSDT",
2347
+ # "symbolName": "ETHUSDT",
2348
+ # "clientOrderId": "1722005792096557",
2349
+ # "orderId": "1738705036219839744",
2350
+ # "transactTime": "1722005792106",
2351
+ # "price": "0",
2352
+ # "origQty": "0.006",
2353
+ # "executedQty": "0.0059",
2354
+ # "status": "FILLED",
2355
+ # "timeInForce": "IOC",
2356
+ # "type": "MARKET",
2357
+ # "side": "BUY",
2358
+ # "reqAmount": "0",
2359
+ # "concentration": ""
2360
+ # }
2361
+ #
2362
+ else:
2363
+ response = await self.privatePostApiV1SpotOrder(request) # the endpoint for market buy orders by cost and other orders
2364
+ #
2365
+ # market buy
2366
+ # {
2367
+ # "accountId": "1732885739589466112",
2368
+ # "symbol": "ETHUSDT",
2369
+ # "symbolName": "ETHUSDT",
2370
+ # "clientOrderId": "1722004623170558",
2371
+ # "orderId": "1738695230608169984",
2372
+ # "transactTime": "1722004623186",
2373
+ # "price": "0",
2374
+ # "origQty": "0",
2375
+ # "executedQty": "0.0061",
2376
+ # "status": "FILLED",
2377
+ # "timeInForce": "IOC",
2378
+ # "type": "MARKET",
2379
+ # "side": "BUY",
2380
+ # "reqAmount": "20",
2381
+ # "concentration": ""
2382
+ # }
2383
+ #
2384
+ # market sell
2385
+ # {
2386
+ # "accountId": "1732885739589466112",
2387
+ # "symbol": "ETHUSDT",
2388
+ # "symbolName": "ETHUSDT",
2389
+ # "clientOrderId": "1722005654516362",
2390
+ # "orderId": "1738703882140316928",
2391
+ # "transactTime": "1722005654529",
2392
+ # "price": "0",
2393
+ # "origQty": "0.006",
2394
+ # "executedQty": "0.006",
2395
+ # "status": "FILLED",
2396
+ # "timeInForce": "IOC",
2397
+ # "type": "MARKET",
2398
+ # "side": "SELL",
2399
+ # "reqAmount": "0",
2400
+ # "concentration": ""
2401
+ # }
2402
+ #
2403
+ # limit
2404
+ # {
2405
+ # "accountId": "1732885739589466112",
2406
+ # "symbol": "ETHUSDT",
2407
+ # "symbolName": "ETHUSDT",
2408
+ # "clientOrderId": "1722006209978370",
2409
+ # "orderId": "1738708541676585728",
2410
+ # "transactTime": "1722006209989",
2411
+ # "price": "5000",
2412
+ # "origQty": "0.005",
2413
+ # "executedQty": "0",
2414
+ # "status": "NEW",
2415
+ # "timeInForce": "GTC",
2416
+ # "type": "LIMIT_MAKER",
2417
+ # "side": "SELL",
2418
+ # "reqAmount": "0",
2419
+ # "concentration": ""
2420
+ # }
2421
+ #
2422
+ return self.parse_order(response, market)
2423
+
2424
+ def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> dict:
2425
+ market = self.market(symbol)
2426
+ if market['spot']:
2427
+ return self.create_spot_order_request(symbol, type, side, amount, price, params)
2428
+ elif market['swap']:
2429
+ return self.create_swap_order_request(symbol, type, side, amount, price, params)
2430
+ else:
2431
+ raise NotSupported(self.id + ' ' + 'createOrderRequest() is not supported for ' + market['type'] + ' type of markets')
2432
+
2433
+ def create_spot_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> dict:
2434
+ """
2435
+ * @ignore
2436
+ helper function to build request
2437
+ :param str symbol: unified symbol of the market to create an order in
2438
+ :param str type: 'market' or 'limit' or 'LIMIT_MAKER'
2439
+ :param str side: 'buy' or 'sell'
2440
+ :param float amount: how much of you want to trade in units of the base currency
2441
+ :param float [price]: the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2442
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2443
+ :param float [params.cost]: *market buy only* the quote quantity that can be used alternative for the amount
2444
+ :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately
2445
+ :param str [params.timeInForce]: "GTC", "IOC", or "PO"
2446
+ :param str [params.clientOrderId]: a unique id for the order
2447
+ :returns dict: request to be sent to the exchange
2448
+ """
2449
+ market = self.market(symbol)
2450
+ type = type.upper()
2451
+ request: dict = {
2452
+ 'symbol': market['id'],
2453
+ 'side': side.upper(),
2454
+ 'type': type,
2455
+ }
2456
+ if amount is not None:
2457
+ request['quantity'] = self.amount_to_precision(symbol, amount)
2458
+ cost: Str = None
2459
+ cost, params = self.handle_param_string(params, 'cost')
2460
+ if cost is not None:
2461
+ request['quantity'] = self.cost_to_precision(symbol, cost)
2462
+ if price is not None:
2463
+ request['price'] = self.price_to_precision(symbol, price)
2464
+ isMarketOrder = type == 'MARKET'
2465
+ postOnly = False
2466
+ postOnly, params = self.handle_post_only(isMarketOrder, type == 'LIMIT_MAKER', params)
2467
+ if postOnly and (type == 'LIMIT'):
2468
+ request['type'] = 'LIMIT_MAKER'
2469
+ clientOrderId: Str = None
2470
+ clientOrderId, params = self.handle_param_string(params, 'clientOrderId')
2471
+ if clientOrderId is not None:
2472
+ params['newClientOrderId'] = clientOrderId
2473
+ return self.extend(request, params)
2474
+
2475
+ def create_swap_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> dict:
2476
+ """
2477
+ * @ignore
2478
+ helper function to build request
2479
+ :param str symbol: unified symbol of the market to create an order in
2480
+ :param str type: 'market' or 'limit' or 'STOP'
2481
+ :param str side: 'buy' or 'sell'
2482
+ :param float amount: how much of you want to trade in units of the base currency
2483
+ :param float [price]: the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2484
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2485
+ :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately
2486
+ :param bool [params.reduceOnly]: True or False whether the order is reduce only
2487
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
2488
+ :param str [params.timeInForce]: 'GTC', 'FOK', 'IOC', 'LIMIT_MAKER' or 'PO'
2489
+ :param str [params.clientOrderId]: a unique id for the order
2490
+ :returns dict: request to be sent to the exchange
2491
+ """
2492
+ market = self.market(symbol)
2493
+ request: dict = {
2494
+ 'symbol': market['id'],
2495
+ 'type': 'LIMIT',
2496
+ 'quantity': self.amount_to_precision(symbol, amount),
2497
+ }
2498
+ isMarketOrder = type == 'market'
2499
+ if isMarketOrder:
2500
+ request['priceType'] = 'MARKET'
2501
+ if price is not None:
2502
+ request['price'] = self.price_to_precision(symbol, price)
2503
+ request['priceType'] = 'INPUT'
2504
+ reduceOnly = False
2505
+ reduceOnly, params = self.handle_param_bool(params, 'reduceOnly', reduceOnly)
2506
+ suffix = '_OPEN'
2507
+ if reduceOnly:
2508
+ suffix = '_CLOSE'
2509
+ request['side'] = side.upper() + suffix
2510
+ timeInForce: Str = None
2511
+ timeInForce, params = self.handle_param_string(params, 'timeInForce')
2512
+ postOnly = False
2513
+ postOnly, params = self.handle_post_only(isMarketOrder, timeInForce == 'LIMIT_MAKER', params)
2514
+ if postOnly:
2515
+ timeInForce = 'LIMIT_MAKER'
2516
+ if timeInForce is not None:
2517
+ request['timeInForce'] = timeInForce
2518
+ clientOrderId = self.safe_string(params, 'clientOrderId')
2519
+ if clientOrderId is None:
2520
+ request['clientOrderId'] = self.uuid()
2521
+ triggerPrice = self.safe_string(params, 'triggerPrice')
2522
+ if triggerPrice is not None:
2523
+ request['stopPrice'] = self.price_to_precision(symbol, triggerPrice)
2524
+ request['type'] = 'STOP'
2525
+ params = self.omit(params, 'triggerPrice')
2526
+ return self.extend(request, params)
2527
+
2528
+ async def create_swap_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
2529
+ """
2530
+ create a trade order on swap market
2531
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/create-new-futures-order
2532
+ :param str symbol: unified symbol of the market to create an order in
2533
+ :param str type: 'market' or 'limit' or 'STOP'
2534
+ :param str side: 'buy' or 'sell'
2535
+ :param float amount: how much of you want to trade in units of the base currency
2536
+ :param float [price]: the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2537
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2538
+ :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately
2539
+ :param bool [params.reduceOnly]: True or False whether the order is reduce only
2540
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
2541
+ :param str [params.timeInForce]: 'GTC', 'FOK', 'IOC', 'LIMIT_MAKER' or 'PO'
2542
+ :param str [params.clientOrderId]: a unique id for the order
2543
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2544
+ """
2545
+ await self.load_markets()
2546
+ market = self.market(symbol)
2547
+ request = self.create_swap_order_request(symbol, type, side, amount, price, params)
2548
+ response = await self.privatePostApiV1FuturesOrder(self.extend(request, params))
2549
+ #
2550
+ # {
2551
+ # "time": "1722429951611",
2552
+ # "updateTime": "1722429951648",
2553
+ # "orderId": "1742263144028363776",
2554
+ # "clientOrderId": "1722429950315",
2555
+ # "symbol": "ETHUSDT-PERPETUAL",
2556
+ # "price": "3460.62",
2557
+ # "leverage": "5",
2558
+ # "origQty": "10",
2559
+ # "executedQty": "10",
2560
+ # "avgPrice": "0",
2561
+ # "marginLocked": "6.9212",
2562
+ # "type": "LIMIT",
2563
+ # "side": "BUY_OPEN",
2564
+ # "timeInForce": "IOC",
2565
+ # "status": "FILLED",
2566
+ # "priceType": "MARKET",
2567
+ # "contractMultiplier": "0.00100000"
2568
+ # }
2569
+ #
2570
+ return self.parse_order(response, market)
2571
+
2572
+ async def create_orders(self, orders: List[OrderRequest], params={}):
2573
+ """
2574
+ create a list of trade orders(all orders should be of the same symbol)
2575
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/create-multiple-orders
2576
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/batch-create-new-futures-order
2577
+ :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
2578
+ :param dict [params]: extra parameters specific to the api endpoint
2579
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2580
+ """
2581
+ await self.load_markets()
2582
+ ordersRequests = []
2583
+ for i in range(0, len(orders)):
2584
+ rawOrder = orders[i]
2585
+ symbol = self.safe_string(rawOrder, 'symbol')
2586
+ type = self.safe_string(rawOrder, 'type')
2587
+ side = self.safe_string(rawOrder, 'side')
2588
+ amount = self.safe_number(rawOrder, 'amount')
2589
+ price = self.safe_number(rawOrder, 'price')
2590
+ orderParams = self.safe_dict(rawOrder, 'params', {})
2591
+ orderRequest = self.create_order_request(symbol, type, side, amount, price, orderParams)
2592
+ clientOrderId = self.safe_string(orderRequest, 'clientOrderId')
2593
+ if clientOrderId is None:
2594
+ orderRequest['clientOrderId'] = self.uuid() # both spot and swap endpoints require clientOrderId
2595
+ ordersRequests.append(orderRequest)
2596
+ firstOrder = ordersRequests[0]
2597
+ firstSymbol = self.safe_string(firstOrder, 'symbol')
2598
+ market = self.market(firstSymbol)
2599
+ request: dict = {
2600
+ 'orders': ordersRequests,
2601
+ }
2602
+ response = None
2603
+ if market['spot']:
2604
+ response = await self.privatePostApiV1SpotBatchOrders(self.extend(request, params))
2605
+ #
2606
+ # {
2607
+ # "code": 0,
2608
+ # "result": [
2609
+ # {
2610
+ # "code": "0000",
2611
+ # "order": {
2612
+ # "accountId": "1732885739589466112",
2613
+ # "symbol": "ETHUSDT",
2614
+ # "symbolName": "ETHUSDT",
2615
+ # "clientOrderId": "1722701490163000",
2616
+ # "orderId": "1744540984757258752",
2617
+ # "transactTime": "1722701491385",
2618
+ # "price": "1500",
2619
+ # "origQty": "0.001",
2620
+ # "executedQty": "0",
2621
+ # "status": "NEW",
2622
+ # "timeInForce": "GTC",
2623
+ # "type": "LIMIT",
2624
+ # "side": "BUY",
2625
+ # "reqAmount": "0"
2626
+ # }
2627
+ # }
2628
+ # ],
2629
+ # "concentration": ""
2630
+ # }
2631
+ #
2632
+ elif market['swap']:
2633
+ response = await self.privatePostApiV1FuturesBatchOrders(self.extend(request, params))
2634
+ #
2635
+ # {
2636
+ # "code": "0000",
2637
+ # "result": [
2638
+ # {
2639
+ # "code": "0000",
2640
+ # "order": {
2641
+ # "time": "1722704251911",
2642
+ # "updateTime": "1722704251918",
2643
+ # "orderId": "1744564141727808768",
2644
+ # "clientOrderId": "1722704250648000",
2645
+ # "symbol": "ETHUSDT-PERPETUAL",
2646
+ # "price": "1500",
2647
+ # "leverage": "4",
2648
+ # "origQty": "1",
2649
+ # "executedQty": "0",
2650
+ # "avgPrice": "0",
2651
+ # "marginLocked": "0.375",
2652
+ # "type": "LIMIT",
2653
+ # "side": "BUY_OPEN",
2654
+ # "timeInForce": "GTC",
2655
+ # "status": "NEW",
2656
+ # "priceType": "INPUT",
2657
+ # "isLiquidationOrder": False,
2658
+ # "indexPrice": "0",
2659
+ # "liquidationType": ""
2660
+ # }
2661
+ # },
2662
+ # {
2663
+ # "code": "0207",
2664
+ # "msg": "Create limit order sell price too low"
2665
+ # }
2666
+ # ]
2667
+ # }
2668
+ #
2669
+ else:
2670
+ raise NotSupported(self.id + ' ' + 'createOrderRequest() is not supported for ' + market['type'] + ' type of markets')
2671
+ result = self.safe_list(response, 'result', [])
2672
+ responseOrders = []
2673
+ for i in range(0, len(result)):
2674
+ responseEntry = self.safe_dict(result, i, {})
2675
+ responseOrder = self.safe_dict(responseEntry, 'order', {})
2676
+ responseOrders.append(responseOrder)
2677
+ return self.parse_orders(responseOrders)
2678
+
2679
+ async def cancel_order(self, id: str, symbol: Str = None, params={}):
2680
+ """
2681
+ cancels an open order
2682
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/cancel-order
2683
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/cancel-futures-order
2684
+ :param str id: order id
2685
+ :param str symbol: unified symbol of the market the order was made in
2686
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2687
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch entry for(default 'spot')
2688
+ :param str [params.clientOrderId]: a unique id for the order that can be used alternative for the id
2689
+ :param bool [params.trigger]: *swap markets only* True for canceling a trigger order(default False)
2690
+ :param bool [params.stop]: *swap markets only* an alternative for trigger param
2691
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2692
+ """
2693
+ methodName = 'cancelOrder'
2694
+ self.check_type_param(methodName, params)
2695
+ await self.load_markets()
2696
+ request: dict = {}
2697
+ clientOrderId = self.safe_string(params, 'clientOrderId')
2698
+ if clientOrderId is None:
2699
+ request['orderId'] = id
2700
+ market: Market = None
2701
+ if symbol is not None:
2702
+ market = self.market(symbol)
2703
+ marketType = 'spot'
2704
+ marketType, params = self.handle_market_type_and_params(methodName, market, params, marketType)
2705
+ response = None
2706
+ if marketType == 'spot':
2707
+ response = await self.privateDeleteApiV1SpotOrder(self.extend(request, params))
2708
+ #
2709
+ # {
2710
+ # "accountId": "1732885739589466112",
2711
+ # "symbol": "ETHUSDT",
2712
+ # "clientOrderId": "1722006209978370",
2713
+ # "orderId": "1738708541676585728",
2714
+ # "transactTime": "1722006209989",
2715
+ # "price": "5000",
2716
+ # "origQty": "0.005",
2717
+ # "executedQty": "0",
2718
+ # "status": "NEW",
2719
+ # "timeInForce": "GTC",
2720
+ # "type": "LIMIT_MAKER",
2721
+ # "side": "SELL"
2722
+ # }
2723
+ #
2724
+ elif marketType == 'swap':
2725
+ isTrigger = False
2726
+ isTrigger, params = self.handle_trigger_option_and_params(params, methodName, isTrigger)
2727
+ if isTrigger:
2728
+ request['type'] = 'STOP'
2729
+ else:
2730
+ request['type'] = 'LIMIT'
2731
+ if market is not None:
2732
+ request['symbol'] = market['id']
2733
+ response = await self.privateDeleteApiV1FuturesOrder(self.extend(request, params))
2734
+ #
2735
+ # {
2736
+ # "time": "1722432302919",
2737
+ # "updateTime": "1722432302925",
2738
+ # "orderId": "1742282868229463040",
2739
+ # "clientOrderId": "1722432301670",
2740
+ # "symbol": "ETHUSDT-PERPETUAL",
2741
+ # "price": "4000",
2742
+ # "leverage": "5",
2743
+ # "origQty": "10",
2744
+ # "executedQty": "0",
2745
+ # "avgPrice": "0",
2746
+ # "marginLocked": "0",
2747
+ # "type": "LIMIT_MAKER",
2748
+ # "side": "SELL_CLOSE",
2749
+ # "timeInForce": "GTC",
2750
+ # "status": "NEW",
2751
+ # "priceType": "INPUT",
2752
+ # "isLiquidationOrder": False,
2753
+ # "indexPrice": "0",
2754
+ # "liquidationType": ""
2755
+ # }
2756
+ #
2757
+ else:
2758
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
2759
+ return self.parse_order(response)
2760
+
2761
+ async def cancel_all_orders(self, symbol: Str = None, params={}):
2762
+ """
2763
+ cancel all open orders
2764
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/cancel-all-open-orders
2765
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/batch-cancel-futures-order
2766
+ :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
2767
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2768
+ :param str [params.side]: 'buy' or 'sell'
2769
+ :returns dict: response from exchange
2770
+ """
2771
+ # Does not cancel trigger orders. For canceling trigger order use cancelOrder() or cancelOrders()
2772
+ methodName = 'cancelAllOrders'
2773
+ if symbol is None:
2774
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument')
2775
+ await self.load_markets()
2776
+ market = self.market(symbol)
2777
+ request: dict = {
2778
+ 'symbol': market['id'],
2779
+ }
2780
+ side = self.safe_string(params, 'side')
2781
+ if side is not None:
2782
+ request['side'] = side
2783
+ response = None
2784
+ if market['spot']:
2785
+ response = await self.privateDeleteApiV1SpotOpenOrders(self.extend(request, params))
2786
+ #
2787
+ # {"success": True}
2788
+ #
2789
+ elif market['swap']:
2790
+ response = await self.privateDeleteApiV1FuturesBatchOrders(self.extend(request, params))
2791
+ #
2792
+ # {"message": "success", "timestamp": "1723127222198", "code": "0000"}
2793
+ #
2794
+ else:
2795
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + market['type'] + ' type of markets')
2796
+ order = self.safe_order(response)
2797
+ order['info'] = response
2798
+ return [order]
2799
+
2800
+ async def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
2801
+ """
2802
+ cancel multiple orders
2803
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/cancel-multiple-orders
2804
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/batch-cancel-futures-order-by-order-id
2805
+ :param str[] ids: order ids
2806
+ :param str [symbol]: unified market symbol(not used by hashkey)
2807
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch entry for(default 'spot')
2808
+ :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2809
+ """
2810
+ methodName = 'cancelOrders'
2811
+ await self.load_markets()
2812
+ request = {}
2813
+ orderIds = ','.join(ids)
2814
+ request['ids'] = orderIds
2815
+ market: Market = None
2816
+ if symbol is not None:
2817
+ market = self.market(symbol)
2818
+ marketType = 'spot'
2819
+ marketType, params = self.handle_market_type_and_params(methodName, market, params, marketType)
2820
+ response = None
2821
+ if marketType == 'spot':
2822
+ response = await self.privateDeleteApiV1SpotCancelOrderByIds(self.extend(request))
2823
+ #
2824
+ # {
2825
+ # "code": "0000",
2826
+ # "result": []
2827
+ # }
2828
+ #
2829
+ elif marketType == 'swap':
2830
+ response = self.privateDeleteApiV1FuturesCancelOrderByIds(self.extend(request))
2831
+ else:
2832
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
2833
+ order = self.safe_order(response)
2834
+ order['info'] = response
2835
+ return [order]
2836
+
2837
+ async def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
2838
+ """
2839
+ fetches information on an order made by the user
2840
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-order
2841
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-order
2842
+ :param str id: the order id
2843
+ :param str symbol: unified symbol of the market the order was made in
2844
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2845
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch entry for(default 'spot')
2846
+ :param str [params.clientOrderId]: a unique id for the order that can be used alternative for the id
2847
+ :param str [params.accountId]: *spot markets only* account id to fetch the order from
2848
+ :param bool [params.trigger]: *swap markets only* True for fetching a trigger order(default False)
2849
+ :param bool [params.stop]: *swap markets only* an alternative for trigger param
2850
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2851
+ """
2852
+ methodName = 'fetchOrder'
2853
+ self.check_type_param(methodName, params)
2854
+ await self.load_markets()
2855
+ request: dict = {}
2856
+ clientOrderId: Str = None
2857
+ clientOrderId, params = self.handle_param_string(params, 'clientOrderId')
2858
+ if clientOrderId is None:
2859
+ request['orderId'] = id
2860
+ market: Market = None
2861
+ if symbol is not None:
2862
+ market = self.market(symbol)
2863
+ marketType = 'spot'
2864
+ marketType, params = self.handle_market_type_and_params(methodName, market, params, marketType)
2865
+ response = None
2866
+ if marketType == 'spot':
2867
+ if clientOrderId is not None:
2868
+ request['origClientOrderId'] = clientOrderId
2869
+ accountId: Str = None
2870
+ accountId, params = self.handle_option_and_params(params, methodName, 'accountId')
2871
+ if accountId is not None:
2872
+ request['accountId'] = accountId
2873
+ response = await self.privateGetApiV1SpotOrder(self.extend(request, params))
2874
+ #
2875
+ # {
2876
+ # "accountId": "1732885739589466112",
2877
+ # "exchangeId": "301",
2878
+ # "symbol": "ETHUSDT",
2879
+ # "symbolName": "ETHUSDT",
2880
+ # "clientOrderId": "1722004623170558",
2881
+ # "orderId": "1738695230608169984",
2882
+ # "price": "0",
2883
+ # "origQty": "0",
2884
+ # "executedQty": "0.0061",
2885
+ # "cummulativeQuoteQty": "19.736489",
2886
+ # "cumulativeQuoteQty": "19.736489",
2887
+ # "avgPrice": "3235.49",
2888
+ # "status": "FILLED",
2889
+ # "timeInForce": "IOC",
2890
+ # "type": "MARKET",
2891
+ # "side": "BUY",
2892
+ # "stopPrice": "0.0",
2893
+ # "icebergQty": "0.0",
2894
+ # "time": "1722004623186",
2895
+ # "updateTime": "1722004623406",
2896
+ # "isWorking": True,
2897
+ # "reqAmount": "20",
2898
+ # "feeCoin": "",
2899
+ # "feeAmount": "0",
2900
+ # "sumFeeAmount": "0"
2901
+ # }
2902
+ #
2903
+ elif marketType == 'swap':
2904
+ isTrigger = False
2905
+ isTrigger, params = self.handle_trigger_option_and_params(params, methodName, isTrigger)
2906
+ if isTrigger:
2907
+ request['type'] = 'STOP'
2908
+ response = await self.privateGetApiV1FuturesOrder(self.extend(request, params))
2909
+ #
2910
+ # {
2911
+ # "time": "1722429951611",
2912
+ # "updateTime": "1722429951700",
2913
+ # "orderId": "1742263144028363776",
2914
+ # "clientOrderId": "1722429950315",
2915
+ # "symbol": "ETHUSDT-PERPETUAL",
2916
+ # "price": "3460.62",
2917
+ # "leverage": "5",
2918
+ # "origQty": "10",
2919
+ # "executedQty": "10",
2920
+ # "avgPrice": "3327.52",
2921
+ # "marginLocked": "0",
2922
+ # "type": "LIMIT",
2923
+ # "side": "BUY_OPEN",
2924
+ # "timeInForce": "IOC",
2925
+ # "status": "FILLED",
2926
+ # "priceType": "MARKET",
2927
+ # "isLiquidationOrder": False,
2928
+ # "indexPrice": "0",
2929
+ # "liquidationType": ""
2930
+ # }
2931
+ #
2932
+ else:
2933
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
2934
+ return self.parse_order(response)
2935
+
2936
+ async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2937
+ """
2938
+ fetch all unfilled currently open orders
2939
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-current-open-orders
2940
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-open-orders
2941
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/sub
2942
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-open-futures-orders
2943
+ :param str [symbol]: unified market symbol of the market orders were made in - is mandatory for swap markets
2944
+ :param int [since]: the earliest time in ms to fetch orders for
2945
+ :param int [limit]: the maximum number of order structures to retrieve - default 500, maximum 1000
2946
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2947
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch entries for(default 'spot')
2948
+ :param str [params.orderId]: *spot markets only* the id of the order to fetch
2949
+ :param str [params.side]: *spot markets only* 'buy' or 'sell' - the side of the orders to fetch
2950
+ :param str [params.fromOrderId]: *swap markets only* the id of the order to start from
2951
+ :param bool [params.trigger]: *swap markets only* True for fetching trigger orders(default False)
2952
+ :param bool [params.stop]: *swap markets only* an alternative for trigger param
2953
+ :param str [params.accountId]: account id to fetch the orders from
2954
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2955
+ """
2956
+ methodName = 'fetchOpenOrders'
2957
+ self.check_type_param(methodName, params)
2958
+ await self.load_markets()
2959
+ market: Market = None
2960
+ if symbol is not None:
2961
+ market = self.market(symbol)
2962
+ marketType = 'spot'
2963
+ marketType, params = self.handle_market_type_and_params(methodName, market, params, marketType)
2964
+ params = self.extend({'methodName': methodName}, params)
2965
+ if marketType == 'spot':
2966
+ return await self.fetch_open_spot_orders(symbol, since, limit, params)
2967
+ elif marketType == 'swap':
2968
+ return await self.fetch_open_swap_orders(symbol, since, limit, params)
2969
+ else:
2970
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
2971
+
2972
+ async def fetch_open_spot_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2973
+ """
2974
+ * @ignore
2975
+ fetch all unfilled currently open orders for spot markets
2976
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-current-open-orders
2977
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/sub
2978
+ :param str [symbol]: unified market symbol of the market orders were made in
2979
+ :param int [since]: the earliest time in ms to fetch orders for
2980
+ :param int [limit]: the maximum number of order structures to retrieve - default 500, maximum 1000
2981
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2982
+ :param str [params.orderId]: the id of the order to fetch
2983
+ :param str [params.side]: 'buy' or 'sell' - the side of the orders to fetch
2984
+ :param str [params.accountId]: account id to fetch the orders from
2985
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2986
+ """
2987
+ await self.load_markets()
2988
+ methodName = 'fetchOpenSpotOrders'
2989
+ methodName, params = self.handle_param_string(params, 'methodName', methodName)
2990
+ market: Market = None
2991
+ request: dict = {}
2992
+ response = None
2993
+ accountId: Str = None
2994
+ accountId, params = self.handle_option_and_params(params, methodName, 'accountId')
2995
+ if accountId is not None:
2996
+ request['subAccountId'] = accountId
2997
+ response = await self.privateGetApiV1SpotSubAccountOpenOrders(self.extend(request, params))
2998
+ else:
2999
+ if symbol is not None:
3000
+ market = self.market(symbol)
3001
+ request['symbol'] = market['id']
3002
+ if limit is not None:
3003
+ request['limit'] = limit
3004
+ orderId: Str = None
3005
+ orderId, params = self.handle_option_and_params(params, methodName, 'orderId')
3006
+ if orderId is not None:
3007
+ request['orderId'] = orderId
3008
+ side: Str = None
3009
+ side, params = self.handle_option_and_params(params, methodName, 'side')
3010
+ if side is not None:
3011
+ request['side'] = side.upper()
3012
+ response = await self.privateGetApiV1SpotOpenOrders(self.extend(request, params))
3013
+ #
3014
+ # [
3015
+ # {
3016
+ # "accountId": "1732885739589466112",
3017
+ # "exchangeId": "301",
3018
+ # "symbol": "ETHUSDT",
3019
+ # "symbolName": "ETHUSDT",
3020
+ # "clientOrderId": "1",
3021
+ # "orderId": "1739491435386897152",
3022
+ # "price": "2000",
3023
+ # "origQty": "0.001",
3024
+ # "executedQty": "0",
3025
+ # "cummulativeQuoteQty": "0",
3026
+ # "cumulativeQuoteQty": "0",
3027
+ # "avgPrice": "0",
3028
+ # "status": "NEW",
3029
+ # "timeInForce": "GTC",
3030
+ # "type": "LIMIT",
3031
+ # "side": "BUY",
3032
+ # "stopPrice": "0.0",
3033
+ # "icebergQty": "0.0",
3034
+ # "time": "1722099538193",
3035
+ # "updateTime": "1722099538197",
3036
+ # "isWorking": True,
3037
+ # "reqAmount": "0"
3038
+ # }
3039
+ # ]
3040
+ #
3041
+ return self.parse_orders(response, market, since, limit)
3042
+
3043
+ async def fetch_open_swap_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
3044
+ """
3045
+ * @ignore
3046
+ fetch all unfilled currently open orders for swap markets
3047
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-open-futures-orders
3048
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-open-orders
3049
+ :param str symbol: *is mandatory* unified market symbol of the market orders were made in
3050
+ :param int [since]: the earliest time in ms to fetch orders for
3051
+ :param int [limit]: the maximum number of order structures to retrieve - maximum 500
3052
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3053
+ :param str [params.fromOrderId]: the id of the order to start from
3054
+ :param bool [params.trigger]: True for fetching trigger orders(default False)
3055
+ :param bool [params.stop]: an alternative for trigger param
3056
+ :param str [params.accountId]: account id to fetch the orders from
3057
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
3058
+ """
3059
+ methodName = 'fetchOpenSwapOrders'
3060
+ methodName, params = self.handle_param_string(params, 'methodName', methodName)
3061
+ if symbol is None:
3062
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument for swap market orders')
3063
+ market = self.market(symbol)
3064
+ request: dict = {
3065
+ 'symbol': market['id'],
3066
+ }
3067
+ isTrigger = False
3068
+ isTrigger, params = self.handle_trigger_option_and_params(params, methodName, isTrigger)
3069
+ if isTrigger:
3070
+ request['type'] = 'STOP'
3071
+ else:
3072
+ request['type'] = 'LIMIT'
3073
+ if limit is not None:
3074
+ request['limit'] = limit
3075
+ fromOrderId: Str = None
3076
+ fromOrderId, params = self.handle_option_and_params(params, methodName, 'fromOrderId')
3077
+ if fromOrderId is not None:
3078
+ request['fromOrderId'] = fromOrderId
3079
+ response = None
3080
+ accountId: Str = None
3081
+ accountId, params = self.handle_option_and_params(params, methodName, 'accountId')
3082
+ if accountId is not None:
3083
+ request['subAccountId'] = accountId
3084
+ response = await self.privateGetApiV1FuturesSubAccountOpenOrders(self.extend(request, params))
3085
+ else:
3086
+ response = await self.privateGetApiV1FuturesOpenOrders(self.extend(request, params))
3087
+ # 'LIMIT'
3088
+ # [
3089
+ # {
3090
+ # "time": "1722432302919",
3091
+ # "updateTime": "1722432302925",
3092
+ # "orderId": "1742282868229463040",
3093
+ # "clientOrderId": "1722432301670",
3094
+ # "symbol": "ETHUSDT-PERPETUAL",
3095
+ # "price": "4000",
3096
+ # "leverage": "5",
3097
+ # "origQty": "10",
3098
+ # "executedQty": "0",
3099
+ # "avgPrice": "0",
3100
+ # "marginLocked": "0",
3101
+ # "type": "LIMIT_MAKER",
3102
+ # "side": "SELL_CLOSE",
3103
+ # "timeInForce": "GTC",
3104
+ # "status": "NEW",
3105
+ # "priceType": "INPUT",
3106
+ # "isLiquidationOrder": False,
3107
+ # "indexPrice": "0",
3108
+ # "liquidationType": ""
3109
+ # }
3110
+ # ]
3111
+ #
3112
+ # 'STOP'
3113
+ # [
3114
+ # {
3115
+ # "time": "1722433095688",
3116
+ # "updateTime": "1722433095688",
3117
+ # "orderId": "1742289518466225664",
3118
+ # "accountId": "1735619524953226496",
3119
+ # "clientOrderId": "1722433094438",
3120
+ # "symbol": "ETHUSDT-PERPETUAL",
3121
+ # "price": "3700",
3122
+ # "leverage": "0",
3123
+ # "origQty": "10",
3124
+ # "type": "STOP",
3125
+ # "side": "SELL_CLOSE",
3126
+ # "status": "ORDER_NEW",
3127
+ # "stopPrice": "3600"
3128
+ # }
3129
+ # ]
3130
+ return self.parse_orders(response, market, since, limit)
3131
+
3132
+ async def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
3133
+ """
3134
+ fetches information on multiple canceled and closed orders made by the user
3135
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-all-orders
3136
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-futures-history-orders
3137
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-history-orders
3138
+ :param str symbol: *is mandatory for swap markets* unified market symbol of the market orders were made in
3139
+ :param int [since]: the earliest time in ms to fetch orders for
3140
+ :param int [limit]: the maximum number of order structures to retrieve - default 500, maximum 1000
3141
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3142
+ :param int [params.until]: the latest time in ms to fetch entries for - only supports the last 90 days timeframe
3143
+ :param str [params.type]: 'spot' or 'swap' - the type of the market to fetch entries for(default 'spot')
3144
+ :param str [params.orderId]: *spot markets only* the id of the order to fetch
3145
+ :param str [params.side]: *spot markets only* 'buy' or 'sell' - the side of the orders to fetch
3146
+ :param str [params.fromOrderId]: *swap markets only* the id of the order to start from
3147
+ :param bool [params.trigger]: *swap markets only* the id of the order to start from True for fetching trigger orders(default False)
3148
+ :param bool [params.stop]: *swap markets only* the id of the order to start from an alternative for trigger param
3149
+ :param str [params.accountId]: account id to fetch the orders from
3150
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
3151
+ """
3152
+ methodName = 'fetchCanceledAndClosedOrders'
3153
+ self.check_type_param(methodName, params)
3154
+ await self.load_markets()
3155
+ request: dict = {}
3156
+ if limit is not None:
3157
+ request['limit'] = limit
3158
+ if since is not None:
3159
+ request['startTime'] = since
3160
+ until: Int = None
3161
+ until, params = self.handle_option_and_params(params, methodName, 'until')
3162
+ if until is not None:
3163
+ request['endTime'] = until
3164
+ accountId: Str = None
3165
+ accountId, params = self.handle_option_and_params(params, methodName, 'accountId')
3166
+ market: Market = None
3167
+ if symbol is not None:
3168
+ market = self.market(symbol)
3169
+ marketType = 'spot'
3170
+ marketType, params = self.handle_market_type_and_params(methodName, market, params, marketType)
3171
+ response = None
3172
+ if marketType == 'spot':
3173
+ if market is not None:
3174
+ request['symbol'] = market['id']
3175
+ orderId: Str = None
3176
+ orderId, params = self.handle_option_and_params(params, methodName, 'orderId')
3177
+ if orderId is not None:
3178
+ request['orderId'] = orderId
3179
+ side: Str = None
3180
+ side, params = self.handle_option_and_params(params, methodName, 'side')
3181
+ if side is not None:
3182
+ request['side'] = side.upper()
3183
+ if accountId is not None:
3184
+ request['accountId'] = accountId
3185
+ response = await self.privateGetApiV1SpotTradeOrders(self.extend(request, params))
3186
+ #
3187
+ # [
3188
+ # {
3189
+ # "accountId": "1732885739589466112",
3190
+ # "exchangeId": "301",
3191
+ # "symbol": "ETHUSDT",
3192
+ # "symbolName": "ETHUSDT",
3193
+ # "clientOrderId": "1722082982086472",
3194
+ # "orderId": "1739352552762301440",
3195
+ # "price": "0",
3196
+ # "origQty": "0.001",
3197
+ # "executedQty": "0.001",
3198
+ # "cummulativeQuoteQty": "3.28996",
3199
+ # "cumulativeQuoteQty": "3.28996",
3200
+ # "avgPrice": "3289.96",
3201
+ # "status": "FILLED",
3202
+ # "timeInForce": "IOC",
3203
+ # "type": "MARKET",
3204
+ # "side": "BUY",
3205
+ # "stopPrice": "0.0",
3206
+ # "icebergQty": "0.0",
3207
+ # "time": "1722082982093",
3208
+ # "updateTime": "1722082982097",
3209
+ # "isWorking": True,
3210
+ # "reqAmount": "0"
3211
+ # },
3212
+ # ...
3213
+ # ]
3214
+ #
3215
+ elif marketType == 'swap':
3216
+ if symbol is None:
3217
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument for swap markets')
3218
+ request['symbol'] = market['id']
3219
+ isTrigger = False
3220
+ isTrigger, params = self.handle_trigger_option_and_params(params, methodName, isTrigger)
3221
+ if isTrigger:
3222
+ request['type'] = 'STOP'
3223
+ else:
3224
+ request['type'] = 'LIMIT'
3225
+ fromOrderId: Str = None
3226
+ fromOrderId, params = self.handle_option_and_params(params, methodName, 'fromOrderId')
3227
+ if fromOrderId is not None:
3228
+ request['fromOrderId'] = fromOrderId
3229
+ if accountId is not None:
3230
+ request['subAccountId'] = accountId
3231
+ response = await self.privateGetApiV1FuturesSubAccountHistoryOrders(self.extend(request, params))
3232
+ else:
3233
+ response = await self.privateGetApiV1FuturesHistoryOrders(self.extend(request, params))
3234
+ #
3235
+ # [
3236
+ # {
3237
+ # "time": "1722429951611",
3238
+ # "updateTime": "1722429951700",
3239
+ # "orderId": "1742263144028363776",
3240
+ # "clientOrderId": "1722429950315",
3241
+ # "symbol": "ETHUSDT-PERPETUAL",
3242
+ # "price": "3460.62",
3243
+ # "leverage": "5",
3244
+ # "origQty": "10",
3245
+ # "executedQty": "10",
3246
+ # "avgPrice": "3327.52",
3247
+ # "marginLocked": "0",
3248
+ # "type": "LIMIT",
3249
+ # "side": "BUY_OPEN",
3250
+ # "timeInForce": "IOC",
3251
+ # "status": "FILLED",
3252
+ # "priceType": "MARKET",
3253
+ # "isLiquidationOrder": False,
3254
+ # "indexPrice": "0",
3255
+ # "liquidationType": ""
3256
+ # }
3257
+ # ]
3258
+ #
3259
+ else:
3260
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + marketType + ' type of markets')
3261
+ return self.parse_orders(response, market, since, limit)
3262
+
3263
+ def check_type_param(self, methodName, params):
3264
+ # some hashkey endpoints have a type param for swap markets that defines the type of an order
3265
+ # type param is reserved in ccxt for defining the type of the market
3266
+ # current method warns user if he provides the exchange specific value in type parameter
3267
+ paramsType = self.safe_string(params, 'type')
3268
+ if (paramsType is not None) and (paramsType != 'spot') and (paramsType != 'swap'):
3269
+ raise BadRequest(self.id + ' ' + methodName + '() type parameter can not be "' + paramsType + '". It should define the type of the market("spot" or "swap"). To define the type of an order use the trigger parameter(True for trigger orders)')
3270
+
3271
+ def handle_trigger_option_and_params(self, params: object, methodName: str, defaultValue=None):
3272
+ isStop = defaultValue
3273
+ isStop, params = self.handle_option_and_params(params, methodName, 'stop', isStop)
3274
+ isTrigger = isStop
3275
+ isTrigger, params = self.handle_option_and_params(params, methodName, 'trigger', isTrigger)
3276
+ return [isTrigger, params]
3277
+
3278
+ def parse_order(self, order: dict, market: Market = None) -> Order:
3279
+ #
3280
+ # createOrder spot
3281
+ # {
3282
+ # "accountId": "1732885739589466112",
3283
+ # "symbol": "ETHUSDT",
3284
+ # "symbolName": "ETHUSDT",
3285
+ # "clientOrderId": "1722004623170558",
3286
+ # "orderId": "1738695230608169984",
3287
+ # "transactTime": "1722004623186",
3288
+ # "price": "0",
3289
+ # "origQty": "0",
3290
+ # "executedQty": "0.0061",
3291
+ # "status": "FILLED",
3292
+ # "timeInForce": "IOC",
3293
+ # "type": "MARKET",
3294
+ # "side": "BUY",
3295
+ # "reqAmount": "20",
3296
+ # "concentration": ""
3297
+ # }
3298
+ #
3299
+ # fetchOrder spot
3300
+ # {
3301
+ # "accountId": "1732885739589466112",
3302
+ # "exchangeId": "301",
3303
+ # "symbol": "ETHUSDT",
3304
+ # "symbolName": "ETHUSDT",
3305
+ # "clientOrderId": "1722004623170558",
3306
+ # "orderId": "1738695230608169984",
3307
+ # "price": "0",
3308
+ # "origQty": "0",
3309
+ # "executedQty": "0.0061",
3310
+ # "cummulativeQuoteQty": "19.736489",
3311
+ # "cumulativeQuoteQty": "19.736489",
3312
+ # "avgPrice": "3235.49",
3313
+ # "status": "FILLED",
3314
+ # "timeInForce": "IOC",
3315
+ # "type": "MARKET",
3316
+ # "side": "BUY",
3317
+ # "stopPrice": "0.0",
3318
+ # "icebergQty": "0.0",
3319
+ # "time": "1722004623186",
3320
+ # "updateTime": "1722004623406",
3321
+ # "isWorking": True,
3322
+ # "reqAmount": "20",
3323
+ # "feeCoin": "",
3324
+ # "feeAmount": "0",
3325
+ # "sumFeeAmount": "0"
3326
+ # }
3327
+ #
3328
+ # cancelOrder
3329
+ # {
3330
+ # "accountId": "1732885739589466112",
3331
+ # "symbol": "ETHUSDT",
3332
+ # "clientOrderId": "1722006209978370",
3333
+ # "orderId": "1738708541676585728",
3334
+ # "transactTime": "1722006209989",
3335
+ # "price": "5000",
3336
+ # "origQty": "0.005",
3337
+ # "executedQty": "0",
3338
+ # "status": "NEW",
3339
+ # "timeInForce": "GTC",
3340
+ # "type": "LIMIT_MAKER",
3341
+ # "side": "SELL"
3342
+ # }
3343
+ #
3344
+ # createOrder swap
3345
+ # {
3346
+ # "time": "1722429951611",
3347
+ # "updateTime": "1722429951648",
3348
+ # "orderId": "1742263144028363776",
3349
+ # "clientOrderId": "1722429950315",
3350
+ # "symbol": "ETHUSDT-PERPETUAL",
3351
+ # "price": "3460.62",
3352
+ # "leverage": "5",
3353
+ # "origQty": "10",
3354
+ # "executedQty": "10",
3355
+ # "avgPrice": "0",
3356
+ # "marginLocked": "6.9212",
3357
+ # "type": "LIMIT",
3358
+ # "side": "BUY_OPEN",
3359
+ # "timeInForce": "IOC",
3360
+ # "status": "FILLED",
3361
+ # "priceType": "MARKET",
3362
+ # "contractMultiplier": "0.00100000"
3363
+ # }
3364
+ #
3365
+ # fetchOrder swap
3366
+ # {
3367
+ # "time": "1722429951611",
3368
+ # "updateTime": "1722429951700",
3369
+ # "orderId": "1742263144028363776",
3370
+ # "clientOrderId": "1722429950315",
3371
+ # "symbol": "ETHUSDT-PERPETUAL",
3372
+ # "price": "3460.62",
3373
+ # "leverage": "5",
3374
+ # "origQty": "10",
3375
+ # "executedQty": "10",
3376
+ # "avgPrice": "3327.52",
3377
+ # "marginLocked": "0",
3378
+ # "type": "LIMIT",
3379
+ # "side": "BUY_OPEN",
3380
+ # "timeInForce": "IOC",
3381
+ # "status": "FILLED",
3382
+ # "priceType": "MARKET",
3383
+ # "isLiquidationOrder": False,
3384
+ # "indexPrice": "0",
3385
+ # "liquidationType": ""
3386
+ # }
3387
+ #
3388
+ marketId = self.safe_string(order, 'symbol')
3389
+ market = self.safe_market(marketId, market)
3390
+ timestamp = self.safe_integer_2(order, 'transactTime', 'time')
3391
+ status = self.safe_string(order, 'status')
3392
+ type = self.safe_string(order, 'type')
3393
+ priceType = self.safe_string(order, 'priceType')
3394
+ if priceType == 'MARKET':
3395
+ type = 'market'
3396
+ price = self.omit_zero(self.safe_string(order, 'price'))
3397
+ if type == 'STOP':
3398
+ if price is None:
3399
+ type = 'market'
3400
+ else:
3401
+ type = 'limit'
3402
+ timeInForce = self.safe_string(order, 'timeInForce')
3403
+ postOnly: Bool = None
3404
+ type, timeInForce, postOnly = self.parse_order_type_time_in_force_and_post_only(type, timeInForce)
3405
+ average = self.omit_zero(self.safe_string(order, 'avgPrice'))
3406
+ if price is None:
3407
+ price = average
3408
+ side = self.safe_string_lower(order, 'side')
3409
+ reduceOnly: Bool = None
3410
+ side, reduceOnly = self.parse_order_side_and_reduce_only(side)
3411
+ feeCurrncyId = self.safe_string(order, 'feeCoin')
3412
+ if feeCurrncyId == '':
3413
+ feeCurrncyId = None
3414
+ triggerPrice = self.omit_zero(self.safe_string(order, 'stopPrice'))
3415
+ return self.safe_order({
3416
+ 'id': self.safe_string(order, 'orderId'),
3417
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
3418
+ 'datetime': self.iso8601(timestamp),
3419
+ 'timestamp': timestamp,
3420
+ 'lastTradeTimestamp': None,
3421
+ 'lastUpdateTimestamp': self.safe_integer(order, 'updateTime'),
3422
+ 'status': self.parse_order_status(status),
3423
+ 'symbol': market['symbol'],
3424
+ 'type': type,
3425
+ 'timeInForce': timeInForce,
3426
+ 'side': side,
3427
+ 'price': price,
3428
+ 'average': average,
3429
+ 'amount': self.omit_zero(self.safe_string(order, 'origQty')),
3430
+ 'filled': self.safe_string(order, 'executedQty'),
3431
+ 'remaining': None,
3432
+ 'stopPrice': triggerPrice,
3433
+ 'triggerPrice': triggerPrice,
3434
+ 'takeProfitPrice': None,
3435
+ 'stopLossPrice': None,
3436
+ 'cost': self.omit_zero(self.safe_string_2(order, 'cumulativeQuoteQty', 'cummulativeQuoteQty')),
3437
+ 'trades': None,
3438
+ 'fee': {
3439
+ 'currency': self.safe_currency_code(feeCurrncyId),
3440
+ 'amount': self.omit_zero(self.safe_string(order, 'feeAmount')),
3441
+ },
3442
+ 'reduceOnly': reduceOnly,
3443
+ 'postOnly': postOnly,
3444
+ 'info': order,
3445
+ }, market)
3446
+
3447
+ def parse_order_side_and_reduce_only(self, unparsed):
3448
+ parts = unparsed.split('_')
3449
+ side = parts[0]
3450
+ reduceOnly: Bool = None
3451
+ secondPart = self.safe_string(parts, 1)
3452
+ if secondPart is not None:
3453
+ if secondPart == 'open':
3454
+ reduceOnly = False
3455
+ elif (secondPart == 'close'):
3456
+ reduceOnly = True
3457
+ return [side, reduceOnly]
3458
+
3459
+ def parse_order_status(self, status):
3460
+ statuses = {
3461
+ 'NEW': 'open',
3462
+ 'PARTIALLY_FILLED': 'open',
3463
+ 'PARTIALLY_CANCELED': 'canceled',
3464
+ 'FILLED': 'closed',
3465
+ 'CANCELED': 'canceled',
3466
+ 'ORDER_CANCELED': 'canceled',
3467
+ 'PENDING_CANCEL': 'canceled',
3468
+ 'REJECTED': 'rejected',
3469
+ 'ORDER_NEW': 'open',
3470
+ }
3471
+ return self.safe_string(statuses, status, status)
3472
+
3473
+ def parse_order_type_time_in_force_and_post_only(self, type, timeInForce):
3474
+ postOnly: Bool = None
3475
+ if type == 'LIMIT_MAKER':
3476
+ postOnly = True
3477
+ elif (timeInForce == 'LIMIT_MAKER') or (timeInForce == 'MAKER'):
3478
+ postOnly = True
3479
+ timeInForce = 'PO'
3480
+ type = self.parse_order_type(type)
3481
+ return [type, timeInForce, postOnly]
3482
+
3483
+ def parse_order_type(self, type):
3484
+ types = {
3485
+ 'MARKET': 'market',
3486
+ 'LIMIT': 'limit',
3487
+ 'LIMIT_MAKER': 'limit',
3488
+ 'MARKET_OF_BASE': 'market',
3489
+ }
3490
+ return self.safe_string(types, type, type)
3491
+
3492
+ async def fetch_funding_rate(self, symbol: str, params={}):
3493
+ """
3494
+ fetch the current funding rate
3495
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-funding-rate
3496
+ :param str symbol: unified market symbol
3497
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3498
+ :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
3499
+ """
3500
+ await self.load_markets()
3501
+ market = self.market(symbol)
3502
+ request: dict = {
3503
+ 'symbol': market['id'],
3504
+ 'timestamp': self.milliseconds(),
3505
+ }
3506
+ response = await self.publicGetApiV1FuturesFundingRate(self.extend(request, params))
3507
+ #
3508
+ # [
3509
+ # {"symbol": "ETHUSDT-PERPETUAL", "rate": "0.0001", "nextSettleTime": "1722297600000"}
3510
+ # ]
3511
+ #
3512
+ rate = self.safe_dict(response, 0, {})
3513
+ return self.parse_funding_rate(rate, market)
3514
+
3515
+ async def fetch_funding_rates(self, symbols: Strings = None, params={}):
3516
+ """
3517
+ fetch the funding rate for multiple markets
3518
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-funding-rate
3519
+ :param str[]|None symbols: list of unified market symbols
3520
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3521
+ :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexe by market symbols
3522
+ """
3523
+ await self.load_markets()
3524
+ symbols = self.market_symbols(symbols)
3525
+ request: dict = {
3526
+ 'timestamp': self.milliseconds(),
3527
+ }
3528
+ response = await self.publicGetApiV1FuturesFundingRate(self.extend(request, params))
3529
+ #
3530
+ # [
3531
+ # {"symbol": "BTCUSDT-PERPETUAL", "rate": "0.0001", "nextSettleTime": "1722297600000"},
3532
+ # {"symbol": "ETHUSDT-PERPETUAL", "rate": "0.0001", "nextSettleTime": "1722297600000"}
3533
+ # ]
3534
+ #
3535
+ fundingRates = self.parse_funding_rates(response)
3536
+ return self.filter_by_array(fundingRates, 'symbol', symbols)
3537
+
3538
+ def parse_funding_rate(self, contract, market: Market = None):
3539
+ #
3540
+ # fetchFundingRates
3541
+ # {
3542
+ # "symbol": "ETHUSDT-PERPETUAL",
3543
+ # "rate": "0.0001",
3544
+ # "nextSettleTime": "1722297600000"
3545
+ # }
3546
+ #
3547
+ marketId = self.safe_string(contract, 'symbol')
3548
+ market = self.safe_market(marketId, market, None, 'swap')
3549
+ fundingRate = self.safe_number(contract, 'rate')
3550
+ fundingTimestamp = self.safe_integer(contract, 'nextSettleTime')
3551
+ return {
3552
+ 'info': contract,
3553
+ 'symbol': market['symbol'],
3554
+ 'markPrice': None,
3555
+ 'indexPrice': None,
3556
+ 'interestRate': None,
3557
+ 'estimatedSettlePrice': None,
3558
+ 'timestamp': None,
3559
+ 'datetime': None,
3560
+ 'fundingRate': fundingRate,
3561
+ 'fundingTimestamp': None,
3562
+ 'fundingDatetime': None,
3563
+ 'nextFundingRate': None,
3564
+ 'nextFundingTimestamp': fundingTimestamp,
3565
+ 'nextFundingDatetime': self.iso8601(fundingTimestamp),
3566
+ 'previousFundingRate': None,
3567
+ 'previousFundingTimestamp': None,
3568
+ 'previousFundingDatetime': None,
3569
+ }
3570
+
3571
+ async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
3572
+ """
3573
+ fetches historical funding rate prices
3574
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-history-funding-rate
3575
+ :param str symbol: unified symbol of the market to fetch the funding rate history for
3576
+ :param int [since]: timestamp in ms of the earliest funding rate to fetch
3577
+ :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
3578
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3579
+ :param int [params.fromId]: the id of the entry to start from
3580
+ :param int [params.endId]: the id of the entry to end with
3581
+ :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
3582
+ """
3583
+ await self.load_markets()
3584
+ if symbol is None:
3585
+ raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
3586
+ market = self.market(symbol)
3587
+ request: dict = {
3588
+ 'symbol': market['id'],
3589
+ }
3590
+ if limit is not None:
3591
+ request['limit'] = limit
3592
+ response = await self.publicGetApiV1FuturesHistoryFundingRate(self.extend(request, params))
3593
+ #
3594
+ # [
3595
+ # {
3596
+ # "id": "10698",
3597
+ # "symbol": "ETHUSDT-PERPETUAL",
3598
+ # "settleTime": "1722268800000",
3599
+ # "settleRate": "0.0001"
3600
+ # },
3601
+ # ...
3602
+ # ]
3603
+ #
3604
+ rates = []
3605
+ for i in range(0, len(response)):
3606
+ entry = response[i]
3607
+ timestamp = self.safe_integer(entry, 'settleTime')
3608
+ rates.append({
3609
+ 'info': entry,
3610
+ 'symbol': self.safe_symbol(self.safe_string(entry, 'symbol'), market, None, 'swap'),
3611
+ 'fundingRate': self.safe_number(entry, 'settleRate'),
3612
+ 'timestamp': timestamp,
3613
+ 'datetime': self.iso8601(timestamp),
3614
+ })
3615
+ sorted = self.sort_by(rates, 'timestamp')
3616
+ return self.filter_by_since_limit(sorted, since, limit)
3617
+
3618
+ async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
3619
+ """
3620
+ fetch open positions for a market
3621
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-positions
3622
+ fetch all open positions
3623
+ :param str[]|None symbols: list of unified market symbols
3624
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3625
+ :param str [params.side]: 'LONG' or 'SHORT' - the direction of the position(if not provided, positions for both sides will be returned)
3626
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
3627
+ """
3628
+ methodName = 'fetchPositions'
3629
+ if (symbols is None):
3630
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument with one single market symbol')
3631
+ else:
3632
+ symbolsLength = len(symbols)
3633
+ if symbolsLength != 1:
3634
+ raise NotSupported(self.id + ' ' + methodName + '() is supported for a symbol argument with one single market symbol only')
3635
+ await self.load_markets()
3636
+ return await self.fetch_positions_for_symbol(symbols[0], self.extend({'methodName': 'fetchPositions'}, params))
3637
+
3638
+ async def fetch_positions_for_symbol(self, symbol: str, params={}) -> List[Position]:
3639
+ """
3640
+ fetch open positions for a single market
3641
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-positions
3642
+ fetch all open positions for specific symbol
3643
+ :param str symbol: unified market symbol
3644
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3645
+ :param str [params.side]: 'LONG' or 'SHORT' - the direction of the position(if not provided, positions for both sides will be returned)
3646
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
3647
+ """
3648
+ await self.load_markets()
3649
+ market = self.market(symbol)
3650
+ methodName = 'fetchPosition'
3651
+ methodName, params = self.handle_param_string(params, 'methodName', methodName)
3652
+ if not market['swap']:
3653
+ raise NotSupported(self.id + ' ' + methodName + '() supports swap markets only')
3654
+ request: dict = {
3655
+ 'symbol': market['id'],
3656
+ }
3657
+ side: Str = None
3658
+ side, params = self.handle_option_and_params(params, methodName, 'side')
3659
+ if side is not None:
3660
+ request['side'] = side.upper()
3661
+ response = await self.privateGetApiV1FuturesPositions(self.extend(request, params))
3662
+ #
3663
+ # [
3664
+ # {
3665
+ # "symbol": "ETHUSDT-PERPETUAL",
3666
+ # "side": "LONG",
3667
+ # "avgPrice": "3327.52",
3668
+ # "position": "10",
3669
+ # "available": "0",
3670
+ # "leverage": "5",
3671
+ # "lastPrice": "3324.44",
3672
+ # "positionValue": "33.2752",
3673
+ # "liquidationPrice": "-953.83",
3674
+ # "margin": "6.9012",
3675
+ # "marginRate": "",
3676
+ # "unrealizedPnL": "-0.0288",
3677
+ # "profitRate": "-0.0041",
3678
+ # "realizedPnL": "-0.0199",
3679
+ # "minMargin": "0.2173"
3680
+ # }
3681
+ # ]
3682
+ #
3683
+ return self.parse_positions(response, [symbol])
3684
+
3685
+ def parse_position(self, position: dict, market: Market = None):
3686
+ marketId = self.safe_string(position, 'symbol')
3687
+ market = self.safe_market(marketId, market)
3688
+ symbol = market['symbol']
3689
+ return self.safe_position({
3690
+ 'symbol': symbol,
3691
+ 'id': None,
3692
+ 'timestamp': None,
3693
+ 'datetime': None,
3694
+ 'contracts': self.safe_number(position, 'position'),
3695
+ 'contractSize': None,
3696
+ 'side': self.safe_string_lower(position, 'side'),
3697
+ 'notional': self.safe_number(position, 'positionValue'),
3698
+ 'leverage': self.safe_integer(position, 'leverage'),
3699
+ 'unrealizedPnl': self.safe_number(position, 'unrealizedPnL'),
3700
+ 'realizedPnl': self.safe_number(position, 'realizedPnL'),
3701
+ 'collateral': None,
3702
+ 'entryPrice': self.safe_number(position, 'avgPrice'),
3703
+ 'markPrice': None,
3704
+ 'liquidationPrice': self.safe_number(position, 'liquidationPrice'),
3705
+ 'marginMode': 'cross',
3706
+ 'hedged': True,
3707
+ 'maintenanceMargin': self.safe_number(position, 'minMargin'),
3708
+ 'maintenanceMarginPercentage': None,
3709
+ 'initialMargin': self.safe_number(position, 'margin'),
3710
+ 'initialMarginPercentage': None,
3711
+ 'marginRatio': None,
3712
+ 'lastUpdateTimestamp': None,
3713
+ 'lastPrice': self.safe_number(position, 'lastPrice'),
3714
+ 'stopLossPrice': None,
3715
+ 'takeProfitPrice': None,
3716
+ 'percentage': None,
3717
+ 'info': position,
3718
+ })
3719
+
3720
+ async def fetch_leverage(self, symbol: str, params={}) -> Leverage:
3721
+ """
3722
+ fetch the set leverage for a market
3723
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/query-futures-leverage-trade
3724
+ :param str symbol: unified market symbol
3725
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3726
+ :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
3727
+ """
3728
+ await self.load_markets()
3729
+ market = self.market(symbol)
3730
+ request: dict = {
3731
+ 'symbol': market['id'],
3732
+ }
3733
+ response = await self.privateGetApiV1FuturesLeverage(self.extend(request, params))
3734
+ #
3735
+ # [
3736
+ # {
3737
+ # "symbolId": "ETHUSDT-PERPETUAL",
3738
+ # "leverage": "5",
3739
+ # "marginType": "CROSS"
3740
+ # }
3741
+ # ]
3742
+ #
3743
+ leverage = self.safe_dict(response, 0, {})
3744
+ return self.parse_leverage(leverage, market)
3745
+
3746
+ def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
3747
+ marginMode = self.safe_string_lower(leverage, 'marginType')
3748
+ leverageValue = self.safe_number(leverage, 'leverage')
3749
+ return {
3750
+ 'info': leverage,
3751
+ 'symbol': market['symbol'],
3752
+ 'marginMode': marginMode,
3753
+ 'longLeverage': leverageValue,
3754
+ 'shortLeverage': leverageValue,
3755
+ }
3756
+
3757
+ async def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
3758
+ """
3759
+ set the level of leverage for a market
3760
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/change-futures-leverage-trade
3761
+ :param float leverage: the rate of leverage
3762
+ :param str symbol: unified market symbol
3763
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3764
+ :returns dict: response from the exchange
3765
+ """
3766
+ if symbol is None:
3767
+ raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
3768
+ await self.load_markets()
3769
+ request: dict = {
3770
+ 'leverage': leverage,
3771
+ }
3772
+ market = self.market(symbol)
3773
+ request['symbol'] = market['id']
3774
+ response = await self.privatePostApiV1FuturesLeverage(self.extend(request, params))
3775
+ #
3776
+ # {
3777
+ # "code": "0000",
3778
+ # "symbolId": "ETHUSDT-PERPETUAL",
3779
+ # "leverage": "3"
3780
+ # }
3781
+ #
3782
+ return self.parse_leverage(response, market)
3783
+
3784
+ async def fetch_leverage_tiers(self, symbols: Strings = None, params={}) -> LeverageTiers:
3785
+ """
3786
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/exchangeinfo
3787
+ retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
3788
+ :param str[]|None symbols: list of unified market symbols
3789
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3790
+ :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
3791
+ """
3792
+ await self.load_markets()
3793
+ response = await self.publicGetApiV1ExchangeInfo(params)
3794
+ # response is the same fetchMarkets()
3795
+ data = self.safe_list(response, 'contracts', [])
3796
+ symbols = self.market_symbols(symbols)
3797
+ return self.parse_leverage_tiers(data, symbols, 'symbol')
3798
+
3799
+ def parse_market_leverage_tiers(self, info, market: Market = None) -> List[LeverageTier]:
3800
+ #
3801
+ # {
3802
+ # "filters": [
3803
+ # {
3804
+ # "minPrice": "0.1",
3805
+ # "maxPrice": "100000.00000000",
3806
+ # "tickSize": "0.1",
3807
+ # "filterType": "PRICE_FILTER"
3808
+ # },
3809
+ # {
3810
+ # "minQty": "0.001",
3811
+ # "maxQty": "10",
3812
+ # "stepSize": "0.001",
3813
+ # "marketOrderMinQty": "0",
3814
+ # "marketOrderMaxQty": "0",
3815
+ # "filterType": "LOT_SIZE"
3816
+ # },
3817
+ # {
3818
+ # "minNotional": "0",
3819
+ # "filterType": "MIN_NOTIONAL"
3820
+ # },
3821
+ # {
3822
+ # "maxSellPrice": "999999",
3823
+ # "buyPriceUpRate": "0.05",
3824
+ # "sellPriceDownRate": "0.05",
3825
+ # "maxEntrustNum": 200,
3826
+ # "maxConditionNum": 200,
3827
+ # "filterType": "LIMIT_TRADING"
3828
+ # },
3829
+ # {
3830
+ # "buyPriceUpRate": "0.05",
3831
+ # "sellPriceDownRate": "0.05",
3832
+ # "filterType": "MARKET_TRADING"
3833
+ # },
3834
+ # {
3835
+ # "noAllowMarketStartTime": "0",
3836
+ # "noAllowMarketEndTime": "0",
3837
+ # "limitOrderStartTime": "0",
3838
+ # "limitOrderEndTime": "0",
3839
+ # "limitMinPrice": "0",
3840
+ # "limitMaxPrice": "0",
3841
+ # "filterType": "OPEN_QUOTE"
3842
+ # }
3843
+ # ],
3844
+ # "exchangeId": "301",
3845
+ # "symbol": "BTCUSDT-PERPETUAL",
3846
+ # "symbolName": "BTCUSDT-PERPETUAL",
3847
+ # "status": "TRADING",
3848
+ # "baseAsset": "BTCUSDT-PERPETUAL",
3849
+ # "baseAssetPrecision": "0.001",
3850
+ # "quoteAsset": "USDT",
3851
+ # "quoteAssetPrecision": "0.1",
3852
+ # "icebergAllowed": False,
3853
+ # "inverse": False,
3854
+ # "index": "USDT",
3855
+ # "marginToken": "USDT",
3856
+ # "marginPrecision": "0.0001",
3857
+ # "contractMultiplier": "0.001",
3858
+ # "underlying": "BTC",
3859
+ # "riskLimits": [
3860
+ # {
3861
+ # "riskLimitId": "200000722",
3862
+ # "quantity": "1000.00",
3863
+ # "initialMargin": "0.10",
3864
+ # "maintMargin": "0.005",
3865
+ # "isWhite": False
3866
+ # },
3867
+ # {
3868
+ # "riskLimitId": "200000723",
3869
+ # "quantity": "2000.00",
3870
+ # "initialMargin": "0.10",
3871
+ # "maintMargin": "0.01",
3872
+ # "isWhite": False
3873
+ # }
3874
+ # ]
3875
+ # }
3876
+ #
3877
+ riskLimits = self.safe_list(info, 'riskLimits', [])
3878
+ id = self.safe_string(info, 'symbol')
3879
+ market = self.safe_market(id, market)
3880
+ tiers = []
3881
+ for i in range(0, len(riskLimits)):
3882
+ tier = riskLimits[i]
3883
+ initialMarginRate = self.safe_string(tier, 'initialMargin')
3884
+ tiers.append({
3885
+ 'tier': self.sum(i, 1),
3886
+ 'currency': market['settle'],
3887
+ 'minNotional': None,
3888
+ 'maxNotional': self.safe_number(tier, 'quantity'),
3889
+ 'maintenanceMarginRate': self.safe_number(tier, 'maintMargin'),
3890
+ 'maxLeverage': self.parse_number(Precise.string_div('1', initialMarginRate)),
3891
+ 'info': tier,
3892
+ })
3893
+ return tiers
3894
+
3895
+ async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
3896
+ """
3897
+ fetch the trading fees for a market
3898
+ :see: https://developers.binance.com/docs/wallet/asset/trade-fee # spot
3899
+ :see: https://hashkeyglobal-apidoc.readme.io/reference/get-futures-commission-rate-request-weight # swap
3900
+ :param str symbol: unified market symbol
3901
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3902
+ :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
3903
+ """
3904
+ await self.load_markets()
3905
+ market = self.market(symbol)
3906
+ methodName = 'fetchTradingFee'
3907
+ response = None
3908
+ if market['spot']:
3909
+ response = await self.fetch_trading_fees(params)
3910
+ return self.safe_dict(response, symbol)
3911
+ elif market['swap']:
3912
+ response = await self.privateGetApiV1FuturesCommissionRate(self.extend({'symbol': market['id']}, params))
3913
+ return self.parse_trading_fee(response, market)
3914
+ #
3915
+ # {
3916
+ # "openMakerFee": "0.00025",
3917
+ # "openTakerFee": "0.0006",
3918
+ # "closeMakerFee": "0.00025",
3919
+ # "closeTakerFee": "0.0006"
3920
+ # }
3921
+ #
3922
+ else:
3923
+ raise NotSupported(self.id + ' ' + methodName + '() is not supported for ' + market['type'] + ' type of markets')
3924
+
3925
+ async def fetch_trading_fees(self, params={}) -> TradingFees:
3926
+ """
3927
+ *for spot markets only* fetch the trading fees for multiple markets
3928
+ :see: https://developers.binance.com/docs/wallet/asset/trade-fee
3929
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3930
+ :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
3931
+ """
3932
+ await self.load_markets()
3933
+ response = await self.privateGetApiV1AccountVipInfo(params)
3934
+ #
3935
+ # {
3936
+ # "code": 0,
3937
+ # "vipLevel": "0",
3938
+ # "tradeVol30Day": "67",
3939
+ # "totalAssetBal": "0",
3940
+ # "data": [
3941
+ # {
3942
+ # "symbol": "UXLINKUSDT",
3943
+ # "productType": "Token-Token",
3944
+ # "buyMakerFeeCurrency": "UXLINK",
3945
+ # "buyTakerFeeCurrency": "UXLINK",
3946
+ # "sellMakerFeeCurrency": "USDT",
3947
+ # "sellTakerFeeCurrency": "USDT",
3948
+ # "actualMakerRate": "0.0012",
3949
+ # "actualTakerRate": "0.0012"
3950
+ # },
3951
+ # ...
3952
+ # ],
3953
+ # "updateTimestamp": "1722320137809"
3954
+ # }
3955
+ #
3956
+ data = self.safe_list(response, 'data', [])
3957
+ result: dict = {}
3958
+ for i in range(0, len(data)):
3959
+ fee = self.safe_dict(data, i, {})
3960
+ parsedFee = self.parse_trading_fee(fee)
3961
+ result[parsedFee['symbol']] = parsedFee
3962
+ return result
3963
+
3964
+ def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
3965
+ #
3966
+ # spot
3967
+ # {
3968
+ # "symbol": "UXLINKUSDT",
3969
+ # "productType": "Token-Token",
3970
+ # "buyMakerFeeCurrency": "UXLINK",
3971
+ # "buyTakerFeeCurrency": "UXLINK",
3972
+ # "sellMakerFeeCurrency": "USDT",
3973
+ # "sellTakerFeeCurrency": "USDT",
3974
+ # "actualMakerRate": "0.0012",
3975
+ # "actualTakerRate": "0.0012"
3976
+ # }
3977
+ #
3978
+ # swap
3979
+ # {
3980
+ # "openMakerFee": "0.00025",
3981
+ # "openTakerFee": "0.0006",
3982
+ # "closeMakerFee": "0.00025",
3983
+ # "closeTakerFee": "0.0006"
3984
+ # }
3985
+ #
3986
+ marketId = self.safe_string(fee, 'symbol')
3987
+ market = self.safe_market(marketId, market)
3988
+ return {
3989
+ 'info': fee,
3990
+ 'symbol': market['symbol'],
3991
+ 'maker': self.safe_number_2(fee, 'openMakerFee', 'actualMakerRate'),
3992
+ 'taker': self.safe_number_2(fee, 'openTakerFee', 'actualTakerRate'),
3993
+ 'percentage': True,
3994
+ 'tierBased': True,
3995
+ }
3996
+
3997
+ def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
3998
+ url = self.urls['api'][api] + '/' + path
3999
+ query: Str = None
4000
+ if api == 'private':
4001
+ self.check_required_credentials()
4002
+ timestamp = self.milliseconds()
4003
+ additionalParams = {
4004
+ 'timestamp': timestamp,
4005
+ }
4006
+ recvWindow = self.safe_integer(self.options, 'recvWindow')
4007
+ if recvWindow is not None:
4008
+ additionalParams['recvWindow'] = recvWindow
4009
+ headers = {
4010
+ 'X-HK-APIKEY': self.apiKey,
4011
+ 'Content-Type': 'application/x-www-form-urlencoded',
4012
+ }
4013
+ signature: Str = None
4014
+ if (method == 'POST') and ((path == 'api/v1/spot/batchOrders') or (path == 'api/v1/futures/batchOrders')):
4015
+ headers['Content-Type'] = 'application/json'
4016
+ body = self.json(self.safe_list(params, 'orders'))
4017
+ signature = self.hmac(self.encode(self.custom_urlencode(additionalParams)), self.encode(self.secret), hashlib.sha256)
4018
+ query = self.custom_urlencode(self.extend(additionalParams, {'signature': signature}))
4019
+ url += '?' + query
4020
+ else:
4021
+ totalParams = self.extend(additionalParams, params)
4022
+ signature = self.hmac(self.encode(self.custom_urlencode(totalParams)), self.encode(self.secret), hashlib.sha256)
4023
+ totalParams['signature'] = signature
4024
+ query = self.custom_urlencode(totalParams)
4025
+ if method == 'GET':
4026
+ url += '?' + query
4027
+ else:
4028
+ body = query
4029
+ headers['INPUT-SOURCE'] = self.safe_string(self.options, 'broker', '10000700011')
4030
+ headers['broker_sign'] = signature
4031
+ else:
4032
+ query = self.urlencode(params)
4033
+ if len(query) != 0:
4034
+ url += '?' + query
4035
+ return {'url': url, 'method': method, 'body': body, 'headers': headers}
4036
+
4037
+ def custom_urlencode(self, params: dict = {}) -> Str:
4038
+ result = self.urlencode(params)
4039
+ result = result.replace('%2C', ',')
4040
+ return result
4041
+
4042
+ def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
4043
+ if response is None:
4044
+ return None
4045
+ errorInArray = False
4046
+ responseCodeString = self.safe_string(response, 'code', None)
4047
+ responseCodeInteger = self.safe_integer(response, 'code', None) # some codes in response are returned as '0000' others
4048
+ if responseCodeInteger == 0:
4049
+ result = self.safe_list(response, 'result', []) # for batch methods
4050
+ for i in range(0, len(result)):
4051
+ entry = self.safe_dict(result, i)
4052
+ entryCodeInteger = self.safe_integer(entry, 'code')
4053
+ if entryCodeInteger != 0:
4054
+ errorInArray = True
4055
+ responseCodeString = self.safe_string(entry, 'code')
4056
+ if (code != 200) or errorInArray:
4057
+ feedback = self.id + ' ' + body
4058
+ self.throw_broadly_matched_exception(self.exceptions['broad'], responseCodeString, feedback)
4059
+ self.throw_exactly_matched_exception(self.exceptions['exact'], responseCodeString, feedback)
4060
+ raise ExchangeError(feedback)
4061
+ return None