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