ccxt 4.2.37 → 4.2.39

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 (113) hide show
  1. package/README.md +6 -5
  2. package/dist/ccxt.browser.js +3015 -509
  3. package/dist/ccxt.browser.min.js +7 -7
  4. package/dist/cjs/ccxt.js +4 -1
  5. package/dist/cjs/src/abstract/blofin.js +9 -0
  6. package/dist/cjs/src/base/Exchange.js +14 -2
  7. package/dist/cjs/src/binance.js +156 -35
  8. package/dist/cjs/src/bitget.js +1 -1
  9. package/dist/cjs/src/bitso.js +18 -2
  10. package/dist/cjs/src/bitstamp.js +24 -2
  11. package/dist/cjs/src/bl3p.js +6 -0
  12. package/dist/cjs/src/blockchaincom.js +21 -0
  13. package/dist/cjs/src/blofin.js +2103 -0
  14. package/dist/cjs/src/btcalpha.js +9 -0
  15. package/dist/cjs/src/btcbox.js +9 -0
  16. package/dist/cjs/src/btcmarkets.js +19 -0
  17. package/dist/cjs/src/coinbase.js +13 -2
  18. package/dist/cjs/src/krakenfutures.js +7 -14
  19. package/dist/cjs/src/luno.js +1 -1
  20. package/dist/cjs/src/okx.js +2 -2
  21. package/dist/cjs/src/poloniexfutures.js +11 -5
  22. package/dist/cjs/src/pro/bitmart.js +110 -35
  23. package/dist/cjs/src/pro/kucoin.js +93 -40
  24. package/dist/cjs/src/pro/mexc.js +1 -1
  25. package/dist/cjs/src/wavesexchange.js +1 -1
  26. package/dist/cjs/src/woo.js +1 -1
  27. package/js/ccxt.d.ts +5 -2
  28. package/js/ccxt.js +4 -2
  29. package/js/src/abstract/blofin.d.ts +36 -0
  30. package/js/src/abstract/blofin.js +11 -0
  31. package/js/src/abstract/coinbase.d.ts +1 -0
  32. package/js/src/abstract/okx.d.ts +1 -0
  33. package/js/src/ascendex.d.ts +2 -2
  34. package/js/src/base/Exchange.d.ts +4 -0
  35. package/js/src/base/Exchange.js +14 -2
  36. package/js/src/base/types.d.ts +2 -0
  37. package/js/src/bigone.d.ts +2 -2
  38. package/js/src/binance.d.ts +8 -8
  39. package/js/src/binance.js +156 -35
  40. package/js/src/bingx.d.ts +5 -5
  41. package/js/src/bitfinex.d.ts +3 -3
  42. package/js/src/bitfinex2.d.ts +2 -2
  43. package/js/src/bitget.d.ts +5 -5
  44. package/js/src/bitget.js +1 -1
  45. package/js/src/bitmart.d.ts +2 -2
  46. package/js/src/bitmex.d.ts +2 -2
  47. package/js/src/bitrue.d.ts +2 -2
  48. package/js/src/bitso.d.ts +1 -1
  49. package/js/src/bitso.js +18 -2
  50. package/js/src/bitstamp.d.ts +1 -1
  51. package/js/src/bitstamp.js +24 -2
  52. package/js/src/bitvavo.d.ts +1 -1
  53. package/js/src/bl3p.js +6 -0
  54. package/js/src/blockchaincom.js +21 -0
  55. package/js/src/blofin.d.ts +124 -0
  56. package/js/src/blofin.js +2104 -0
  57. package/js/src/btcalpha.js +9 -0
  58. package/js/src/btcbox.js +9 -0
  59. package/js/src/btcmarkets.js +19 -0
  60. package/js/src/bybit.d.ts +7 -7
  61. package/js/src/cex.d.ts +1 -1
  62. package/js/src/coinbase.d.ts +2 -2
  63. package/js/src/coinbase.js +13 -2
  64. package/js/src/coinex.d.ts +4 -4
  65. package/js/src/coinlist.d.ts +2 -2
  66. package/js/src/coinone.d.ts +1 -1
  67. package/js/src/delta.d.ts +2 -2
  68. package/js/src/deribit.d.ts +3 -3
  69. package/js/src/digifinex.d.ts +3 -3
  70. package/js/src/exmo.d.ts +2 -2
  71. package/js/src/gate.d.ts +6 -6
  72. package/js/src/hitbtc.d.ts +2 -2
  73. package/js/src/hollaex.d.ts +1 -1
  74. package/js/src/htx.d.ts +3 -3
  75. package/js/src/huobijp.d.ts +1 -1
  76. package/js/src/kraken.d.ts +2 -2
  77. package/js/src/krakenfutures.d.ts +2 -2
  78. package/js/src/krakenfutures.js +7 -14
  79. package/js/src/kucoin.d.ts +5 -5
  80. package/js/src/kucoinfutures.d.ts +2 -2
  81. package/js/src/latoken.d.ts +1 -1
  82. package/js/src/lbank.d.ts +2 -2
  83. package/js/src/luno.d.ts +1 -1
  84. package/js/src/luno.js +1 -1
  85. package/js/src/mexc.d.ts +4 -4
  86. package/js/src/ndax.d.ts +1 -1
  87. package/js/src/novadax.d.ts +1 -1
  88. package/js/src/okcoin.d.ts +2 -2
  89. package/js/src/okx.d.ts +7 -7
  90. package/js/src/okx.js +2 -2
  91. package/js/src/paymium.d.ts +2 -2
  92. package/js/src/phemex.d.ts +4 -4
  93. package/js/src/poloniex.d.ts +2 -2
  94. package/js/src/poloniexfutures.d.ts +2 -2
  95. package/js/src/poloniexfutures.js +11 -5
  96. package/js/src/pro/bitmart.d.ts +2 -0
  97. package/js/src/pro/bitmart.js +110 -35
  98. package/js/src/pro/bitvavo.d.ts +1 -1
  99. package/js/src/pro/cex.d.ts +2 -2
  100. package/js/src/pro/coinbase.d.ts +2 -2
  101. package/js/src/pro/coinex.d.ts +1 -1
  102. package/js/src/pro/kucoin.js +93 -40
  103. package/js/src/pro/lbank.d.ts +1 -1
  104. package/js/src/pro/mexc.js +1 -1
  105. package/js/src/probit.d.ts +1 -1
  106. package/js/src/timex.d.ts +1 -1
  107. package/js/src/upbit.d.ts +1 -1
  108. package/js/src/wavesexchange.js +1 -1
  109. package/js/src/whitebit.d.ts +2 -2
  110. package/js/src/woo.d.ts +3 -3
  111. package/js/src/woo.js +1 -1
  112. package/js/src/zonda.d.ts +3 -3
  113. package/package.json +2 -2
@@ -0,0 +1,2104 @@
1
+ // ----------------------------------------------------------------------------
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
+ // EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
+
7
+ // ---------------------------------------------------------------------------
8
+ import Exchange from './abstract/blofin.js';
9
+ import { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, InvalidOrder, AuthenticationError, RateLimitExceeded, InsufficientFunds } from './base/errors.js';
10
+ import { Precise } from './base/Precise.js';
11
+ import { TICK_SIZE } from './base/functions/number.js';
12
+ import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * @class blofin
16
+ * @augments Exchange
17
+ */
18
+ export default class blofin extends Exchange {
19
+ describe() {
20
+ return this.deepExtend(super.describe(), {
21
+ 'id': 'blofin',
22
+ 'name': 'BloFin',
23
+ 'countries': ['US'],
24
+ 'version': 'v1',
25
+ 'rateLimit': 100,
26
+ 'has': {
27
+ 'CORS': undefined,
28
+ 'spot': false,
29
+ 'margin': false,
30
+ 'swap': true,
31
+ 'future': false,
32
+ 'option': false,
33
+ 'addMargin': false,
34
+ 'borrowMargin': false,
35
+ 'cancelAllOrders': false,
36
+ 'cancelOrder': true,
37
+ 'cancelOrders': true,
38
+ 'closeAllPositions': false,
39
+ 'closePosition': true,
40
+ 'createDepositAddress': false,
41
+ 'createMarketBuyOrderWithCost': false,
42
+ 'createMarketSellOrderWithCost': false,
43
+ 'createOrder': true,
44
+ 'createOrders': true,
45
+ 'createOrderWithTakeProfitAndStopLoss': true,
46
+ 'createPostOnlyOrder': false,
47
+ 'createReduceOnlyOrder': false,
48
+ 'createStopLimitOrder': false,
49
+ 'createStopLossOrder': true,
50
+ 'createStopMarketOrder': false,
51
+ 'createStopOrder': false,
52
+ 'createTakeProfitOrder': true,
53
+ 'editOrder': false,
54
+ 'fetchAccounts': false,
55
+ 'fetchBalance': true,
56
+ 'fetchBidsAsks': undefined,
57
+ 'fetchBorrowInterest': false,
58
+ 'fetchBorrowRateHistories': false,
59
+ 'fetchBorrowRateHistory': false,
60
+ 'fetchCanceledOrders': false,
61
+ 'fetchClosedOrder': false,
62
+ 'fetchClosedOrders': false,
63
+ 'fetchCrossBorrowRate': false,
64
+ 'fetchCrossBorrowRates': false,
65
+ 'fetchCurrencies': false,
66
+ 'fetchDeposit': false,
67
+ 'fetchDepositAddress': false,
68
+ 'fetchDepositAddresses': false,
69
+ 'fetchDepositAddressesByNetwork': false,
70
+ 'fetchDeposits': true,
71
+ 'fetchDepositsWithdrawals': false,
72
+ 'fetchDepositWithdrawFee': 'emulated',
73
+ 'fetchDepositWithdrawFees': false,
74
+ 'fetchFundingHistory': true,
75
+ 'fetchFundingRate': true,
76
+ 'fetchFundingRateHistory': true,
77
+ 'fetchFundingRates': false,
78
+ 'fetchGreeks': false,
79
+ 'fetchIndexOHLCV': false,
80
+ 'fetchIsolatedBorrowRate': false,
81
+ 'fetchIsolatedBorrowRates': false,
82
+ 'fetchL3OrderBook': false,
83
+ 'fetchLedger': true,
84
+ 'fetchLedgerEntry': undefined,
85
+ 'fetchLeverage': true,
86
+ 'fetchLeverageTiers': false,
87
+ 'fetchMarketLeverageTiers': false,
88
+ 'fetchMarkets': true,
89
+ 'fetchMarkOHLCV': false,
90
+ 'fetchMySettlementHistory': false,
91
+ 'fetchMyTrades': true,
92
+ 'fetchOHLCV': true,
93
+ 'fetchOpenInterest': false,
94
+ 'fetchOpenInterestHistory': false,
95
+ 'fetchOpenOrder': undefined,
96
+ 'fetchOpenOrders': true,
97
+ 'fetchOrder': true,
98
+ 'fetchOrderBook': true,
99
+ 'fetchOrderBooks': false,
100
+ 'fetchOrders': false,
101
+ 'fetchOrderTrades': true,
102
+ 'fetchPermissions': undefined,
103
+ 'fetchPosition': true,
104
+ 'fetchPositions': true,
105
+ 'fetchPositionsForSymbol': false,
106
+ 'fetchPositionsRisk': false,
107
+ 'fetchPremiumIndexOHLCV': false,
108
+ 'fetchSettlementHistory': false,
109
+ 'fetchStatus': false,
110
+ 'fetchTicker': true,
111
+ 'fetchTickers': true,
112
+ 'fetchTime': false,
113
+ 'fetchTrades': true,
114
+ 'fetchTradingFee': false,
115
+ 'fetchTradingFees': false,
116
+ 'fetchTradingLimits': false,
117
+ 'fetchTransactionFee': false,
118
+ 'fetchTransactionFees': false,
119
+ 'fetchTransactions': false,
120
+ 'fetchTransfer': false,
121
+ 'fetchTransfers': false,
122
+ 'fetchUnderlyingAssets': false,
123
+ 'fetchVolatilityHistory': false,
124
+ 'fetchWithdrawal': false,
125
+ 'fetchWithdrawals': true,
126
+ 'fetchWithdrawalWhitelist': false,
127
+ 'reduceMargin': false,
128
+ 'repayCrossMargin': false,
129
+ 'setLeverage': true,
130
+ 'setMargin': false,
131
+ 'setMarginMode': false,
132
+ 'setPositionMode': false,
133
+ 'signIn': false,
134
+ 'transfer': true,
135
+ 'withdraw': false,
136
+ },
137
+ 'timeframes': {
138
+ '1m': '1m',
139
+ '3m': '3m',
140
+ '5m': '5m',
141
+ '15m': '15m',
142
+ '30m': '30m',
143
+ '1h': '1H',
144
+ '2h': '2H',
145
+ '4h': '4H',
146
+ '6h': '6H',
147
+ '12h': '12H',
148
+ '1d': '1D',
149
+ '1w': '1W',
150
+ '1M': '1M',
151
+ '3M': '3M',
152
+ },
153
+ 'hostname': 'www.blofin.com',
154
+ 'urls': {
155
+ 'logo': 'https://github.com/ccxt/ccxt/assets/43336371/255a7b29-341f-4d20-8342-fbfae4932807',
156
+ 'api': {
157
+ 'rest': 'https://openapi.blofin.com',
158
+ },
159
+ 'referral': {
160
+ 'url': 'https://blofin.com/register?referral_code=jBd8U1',
161
+ 'discount': 0.05,
162
+ },
163
+ 'www': 'https://www.blofin.com',
164
+ 'doc': 'https://blofin.com/docs',
165
+ },
166
+ 'api': {
167
+ 'public': {
168
+ 'get': {
169
+ 'market/instruments': 1,
170
+ 'market/tickers': 1,
171
+ 'market/books': 1,
172
+ 'market/trades': 1,
173
+ 'market/candles': 1,
174
+ 'market/mark-price': 1,
175
+ 'market/funding-rate': 1,
176
+ 'market/funding-rate-history': 1,
177
+ },
178
+ },
179
+ 'private': {
180
+ 'get': {
181
+ 'asset/balances': 1,
182
+ 'trade/orders-pending': 1,
183
+ 'trade/fills-history': 1,
184
+ 'asset/deposit-history': 1,
185
+ 'asset/withdrawal-history': 1,
186
+ 'asset/bills': 1,
187
+ 'account/balance': 1,
188
+ 'account/positions': 1,
189
+ 'account/leverage-info': 1,
190
+ 'trade/orders-tpsl-pending': 1,
191
+ 'trade/orders-history': 1,
192
+ 'trade/orders-tpsl-history': 1,
193
+ },
194
+ 'post': {
195
+ 'trade/order': 1,
196
+ 'trade/cancel-order': 1,
197
+ 'account/set-leverage': 1,
198
+ 'trade/batch-orders': 1,
199
+ 'trade/order-tpsl': 1,
200
+ 'trade/cancel-batch-orders': 1,
201
+ 'trade/cancel-tpsl': 1,
202
+ 'trade/close-position': 1,
203
+ 'asset/transfer': 1,
204
+ },
205
+ },
206
+ },
207
+ 'fees': {
208
+ 'swap': {
209
+ 'taker': this.parseNumber('0.00060'),
210
+ 'maker': this.parseNumber('0.00020'),
211
+ },
212
+ },
213
+ 'requiredCredentials': {
214
+ 'apiKey': true,
215
+ 'secret': true,
216
+ 'password': true,
217
+ },
218
+ 'exceptions': {
219
+ 'exact': {
220
+ '400': BadRequest,
221
+ '401': AuthenticationError,
222
+ '500': ExchangeError,
223
+ '404': BadRequest,
224
+ '405': BadRequest,
225
+ '406': BadRequest,
226
+ '429': RateLimitExceeded,
227
+ '152001': BadRequest,
228
+ '152002': BadRequest,
229
+ '152003': BadRequest,
230
+ '152004': BadRequest,
231
+ '152005': BadRequest,
232
+ '152006': InvalidOrder,
233
+ '152007': InvalidOrder,
234
+ '152008': InvalidOrder,
235
+ '152009': InvalidOrder,
236
+ '150003': InvalidOrder,
237
+ '150004': InvalidOrder,
238
+ '542': InvalidOrder,
239
+ '102002': InvalidOrder,
240
+ '102005': InvalidOrder,
241
+ '102014': InvalidOrder,
242
+ '102015': InvalidOrder,
243
+ '102022': InvalidOrder,
244
+ '102037': InvalidOrder,
245
+ '102038': InvalidOrder,
246
+ '102039': InvalidOrder,
247
+ '102040': InvalidOrder,
248
+ '102047': InvalidOrder,
249
+ '102048': InvalidOrder,
250
+ '102049': InvalidOrder,
251
+ '102050': InvalidOrder,
252
+ '102051': InvalidOrder,
253
+ '102052': InvalidOrder,
254
+ '102053': InvalidOrder,
255
+ '102054': InvalidOrder,
256
+ '102055': InvalidOrder,
257
+ '102064': BadRequest,
258
+ '102065': BadRequest,
259
+ '102068': BadRequest,
260
+ '103013': ExchangeError,
261
+ 'Order failed. Insufficient USDT margin in account': InsufficientFunds, // Insufficient USDT margin in account
262
+ },
263
+ 'broad': {
264
+ 'Internal Server Error': ExchangeNotAvailable,
265
+ 'server error': ExchangeNotAvailable, // {"code":500,"data":{},"detailMsg":"","error_code":"500","error_message":"server error 1236805249","msg":"server error 1236805249"}
266
+ },
267
+ },
268
+ 'httpExceptions': {
269
+ '429': ExchangeNotAvailable, // https://github.com/ccxt/ccxt/issues/9612
270
+ },
271
+ 'precisionMode': TICK_SIZE,
272
+ 'options': {
273
+ 'brokerId': 'ec6dd3a7dd982d0b',
274
+ 'accountsByType': {
275
+ 'swap': 'futures',
276
+ 'future': 'futures',
277
+ },
278
+ 'accountsById': {
279
+ 'futures': 'swap',
280
+ },
281
+ 'sandboxMode': false,
282
+ 'defaultNetwork': 'ERC20',
283
+ 'defaultNetworks': {
284
+ 'ETH': 'ERC20',
285
+ 'BTC': 'BTC',
286
+ 'USDT': 'TRC20',
287
+ },
288
+ 'networks': {
289
+ 'BTC': 'Bitcoin',
290
+ 'BEP20': 'BSC',
291
+ 'ERC20': 'ERC20',
292
+ 'TRC20': 'TRC20',
293
+ },
294
+ 'fetchOpenInterestHistory': {
295
+ 'timeframes': {
296
+ '5m': '5m',
297
+ '1h': '1H',
298
+ '8h': '8H',
299
+ '1d': '1D',
300
+ '5M': '5m',
301
+ '1H': '1H',
302
+ '8H': '8H',
303
+ '1D': '1D',
304
+ },
305
+ },
306
+ 'fetchOHLCV': {
307
+ // 'type': 'Candles', // Candles or HistoryCandles, IndexCandles, MarkPriceCandles
308
+ 'timezone': 'UTC', // UTC, HK
309
+ },
310
+ 'fetchPositions': {
311
+ 'method': 'privateGetAccountPositions', // privateGetAccountPositions or privateGetAccountPositionsHistory
312
+ },
313
+ 'createOrder': 'privatePostTradeOrder',
314
+ 'createMarketBuyOrderRequiresPrice': false,
315
+ 'fetchMarkets': ['swap'],
316
+ 'defaultType': 'swap',
317
+ 'fetchLedger': {
318
+ 'method': 'privateGetAssetBills',
319
+ },
320
+ 'fetchOpenOrders': {
321
+ 'method': 'privateGetTradeOrdersPending',
322
+ },
323
+ 'cancelOrders': {
324
+ 'method': 'privatePostTradeCancelBatchOrders',
325
+ },
326
+ 'fetchCanceledOrders': {
327
+ 'method': 'privateGetTradeOrdersHistory', // privateGetTradeOrdersTpslHistory
328
+ },
329
+ 'fetchClosedOrders': {
330
+ 'method': 'privateGetTradeOrdersHistory', // privateGetTradeOrdersTpslHistory
331
+ },
332
+ 'withdraw': {
333
+ // a funding password credential is required by the exchange for the
334
+ // withdraw call (not to be confused with the api password credential)
335
+ 'password': undefined,
336
+ 'pwd': undefined, // password or pwd both work
337
+ },
338
+ 'exchangeType': {
339
+ 'spot': 'SPOT',
340
+ 'swap': 'SWAP',
341
+ 'SPOT': 'SPOT',
342
+ 'SWAP': 'SWAP',
343
+ },
344
+ },
345
+ });
346
+ }
347
+ async fetchMarkets(params = {}) {
348
+ /**
349
+ * @method
350
+ * @name blofin#fetchMarkets
351
+ * @description retrieves data on all markets for blofin
352
+ * @see https://blofin.com/docs#get-instruments
353
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
354
+ * @returns {object[]} an array of objects representing market data
355
+ */
356
+ const response = await this.publicGetMarketInstruments(params);
357
+ const data = this.safeList(response, 'data', []);
358
+ return this.parseMarkets(data);
359
+ }
360
+ parseMarket(market) {
361
+ const id = this.safeString(market, 'instId');
362
+ const type = this.safeStringLower(market, 'instType');
363
+ const spot = (type === 'spot');
364
+ const future = (type === 'future');
365
+ const swap = (type === 'swap');
366
+ const option = (type === 'option');
367
+ const contract = swap || future;
368
+ const baseId = this.safeString(market, 'baseCurrency');
369
+ const quoteId = this.safeString(market, 'quoteCurrency');
370
+ const settleId = this.safeString(market, 'quoteCurrency');
371
+ const settle = this.safeCurrencyCode(settleId);
372
+ const base = this.safeCurrencyCode(baseId);
373
+ const quote = this.safeCurrencyCode(quoteId);
374
+ let symbol = base + '/' + quote;
375
+ if (swap) {
376
+ symbol = symbol + ':' + settle;
377
+ }
378
+ const expiry = undefined;
379
+ const strikePrice = undefined;
380
+ const optionType = undefined;
381
+ const tickSize = this.safeString(market, 'tickSize');
382
+ const fees = this.safeValue2(this.fees, type, 'trading', {});
383
+ const taker = this.safeNumber(fees, 'taker');
384
+ const maker = this.safeNumber(fees, 'maker');
385
+ let maxLeverage = this.safeString(market, 'maxLeverage', '100');
386
+ maxLeverage = Precise.stringMax(maxLeverage, '1');
387
+ const isActive = (this.safeString(market, 'state') === 'live');
388
+ return this.safeMarketStructure({
389
+ 'id': id,
390
+ 'symbol': symbol,
391
+ 'base': base,
392
+ 'quote': quote,
393
+ 'baseId': baseId,
394
+ 'quoteId': quoteId,
395
+ 'settle': settle,
396
+ 'settleId': settleId,
397
+ 'type': type,
398
+ 'spot': spot,
399
+ 'option': option,
400
+ 'margin': spot && (Precise.stringGt(maxLeverage, '1')),
401
+ 'swap': swap,
402
+ 'future': future,
403
+ 'active': isActive,
404
+ 'taker': taker,
405
+ 'maker': maker,
406
+ 'contract': contract,
407
+ 'linear': contract ? (quoteId === settleId) : undefined,
408
+ 'inverse': contract ? (baseId === settleId) : undefined,
409
+ 'contractSize': contract ? this.safeNumber(market, 'contractValue') : undefined,
410
+ 'expiry': expiry,
411
+ 'expiryDatetime': expiry,
412
+ 'strike': strikePrice,
413
+ 'optionType': optionType,
414
+ 'created': this.safeInteger(market, 'listTime'),
415
+ 'precision': {
416
+ 'amount': this.safeNumber(market, 'lotSize'),
417
+ 'price': this.parseNumber(tickSize),
418
+ },
419
+ 'limits': {
420
+ 'leverage': {
421
+ 'min': this.parseNumber('1'),
422
+ 'max': this.parseNumber(maxLeverage),
423
+ },
424
+ 'amount': {
425
+ 'min': this.safeNumber(market, 'minSize'),
426
+ 'max': undefined,
427
+ },
428
+ 'price': {
429
+ 'min': undefined,
430
+ 'max': undefined,
431
+ },
432
+ 'cost': {
433
+ 'min': undefined,
434
+ 'max': undefined,
435
+ },
436
+ },
437
+ 'info': market,
438
+ });
439
+ }
440
+ async fetchOrderBook(symbol, limit = undefined, params = {}) {
441
+ /**
442
+ * @method
443
+ * @name blofin#fetchOrderBook
444
+ * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
445
+ * @see https://blofin.com/docs#get-order-book
446
+ * @param {string} symbol unified symbol of the market to fetch the order book for
447
+ * @param {int} [limit] the maximum amount of order book entries to return
448
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
449
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
450
+ */
451
+ await this.loadMarkets();
452
+ const market = this.market(symbol);
453
+ const request = {
454
+ 'instId': market['id'],
455
+ };
456
+ limit = (limit === undefined) ? 50 : limit;
457
+ if (limit !== undefined) {
458
+ request['size'] = limit; // max 100
459
+ }
460
+ const response = await this.publicGetMarketBooks(this.extend(request, params));
461
+ //
462
+ // {
463
+ // "code": "0",
464
+ // "msg": "",
465
+ // "data": [
466
+ // {
467
+ // "asks": [
468
+ // ["0.07228","4.211619","0","2"], // price, amount, liquidated orders, total open orders
469
+ // ["0.0723","299.880364","0","2"],
470
+ // ["0.07231","3.72832","0","1"],
471
+ // ],
472
+ // "bids": [
473
+ // ["0.07221","18.5","0","1"],
474
+ // ["0.0722","18.5","0","1"],
475
+ // ["0.07219","0.505407","0","1"],
476
+ // ],
477
+ // "ts": "1621438475342"
478
+ // }
479
+ // ]
480
+ // }
481
+ //
482
+ const data = this.safeList(response, 'data', []);
483
+ const first = this.safeValue(data, 0, {});
484
+ const timestamp = this.safeInteger(first, 'ts');
485
+ return this.parseOrderBook(first, symbol, timestamp);
486
+ }
487
+ parseTicker(ticker, market = undefined) {
488
+ const timestamp = this.safeInteger(ticker, 'ts');
489
+ const marketId = this.safeString(ticker, 'instId');
490
+ market = this.safeMarket(marketId, market, '-');
491
+ const symbol = market['symbol'];
492
+ const last = this.safeString(ticker, 'last');
493
+ const open = this.safeString(ticker, 'open24h');
494
+ const spot = this.safeValue(market, 'spot', false);
495
+ const quoteVolume = spot ? this.safeString(ticker, 'volCurrency24h') : undefined;
496
+ const baseVolume = this.safeString(ticker, 'vol24h');
497
+ const high = this.safeString(ticker, 'high24h');
498
+ const low = this.safeString(ticker, 'low24h');
499
+ return this.safeTicker({
500
+ 'symbol': symbol,
501
+ 'timestamp': timestamp,
502
+ 'datetime': this.iso8601(timestamp),
503
+ 'high': high,
504
+ 'low': low,
505
+ 'bid': this.safeString(ticker, 'bidPrice'),
506
+ 'bidVolume': this.safeString(ticker, 'bidSize'),
507
+ 'ask': this.safeString(ticker, 'askPrice'),
508
+ 'askVolume': this.safeString(ticker, 'askSize'),
509
+ 'vwap': undefined,
510
+ 'open': open,
511
+ 'close': last,
512
+ 'last': last,
513
+ 'previousClose': undefined,
514
+ 'change': undefined,
515
+ 'percentage': undefined,
516
+ 'average': undefined,
517
+ 'baseVolume': baseVolume,
518
+ 'quoteVolume': quoteVolume,
519
+ 'info': ticker,
520
+ }, market);
521
+ }
522
+ async fetchTicker(symbol, params = {}) {
523
+ /**
524
+ * @method
525
+ * @name blofin#fetchTicker
526
+ * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
527
+ * @see https://blofin.com/docs#get-tickers
528
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
529
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
530
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
531
+ */
532
+ await this.loadMarkets();
533
+ const market = this.market(symbol);
534
+ const request = {
535
+ 'instId': market['id'],
536
+ };
537
+ const response = await this.publicGetMarketTickers(this.extend(request, params));
538
+ const data = this.safeList(response, 'data', []);
539
+ const first = this.safeValue(data, 0, {});
540
+ return this.parseTicker(first, market);
541
+ }
542
+ async fetchTickers(symbols = undefined, params = {}) {
543
+ /**
544
+ * @method
545
+ * @name blofin#fetchTickers
546
+ * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
547
+ * @see https://blofin.com/docs#get-tickers
548
+ * @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
549
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
550
+ * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
551
+ */
552
+ await this.loadMarkets();
553
+ symbols = this.marketSymbols(symbols);
554
+ const response = await this.publicGetMarketTickers(params);
555
+ const tickers = this.safeList(response, 'data', []);
556
+ return this.parseTickers(tickers, symbols);
557
+ }
558
+ parseTrade(trade, market = undefined) {
559
+ //
560
+ // fetch trades
561
+ // {
562
+ // "tradeId": "3263934920",
563
+ // "instId": "LTC-USDT",
564
+ // "price": "67.87",
565
+ // "size": "1",
566
+ // "side": "buy",
567
+ // "ts": "1707232020854"
568
+ // }
569
+ // my trades
570
+ // {
571
+ // "instId": "LTC-USDT",
572
+ // "tradeId": "1440847",
573
+ // "orderId": "2075705202",
574
+ // "fillPrice": "67.850000000000000000",
575
+ // "fillSize": "1.000000000000000000",
576
+ // "fillPnl": "0.000000000000000000",
577
+ // "side": "buy",
578
+ // "positionSide": "net",
579
+ // "fee": "0.040710000000000000",
580
+ // "ts": "1707224678878",
581
+ // "brokerId": ""
582
+ // }
583
+ //
584
+ const id = this.safeString(trade, 'tradeId');
585
+ const marketId = this.safeString(trade, 'instId');
586
+ market = this.safeMarket(marketId, market, '-');
587
+ const symbol = market['symbol'];
588
+ const timestamp = this.safeInteger(trade, 'ts');
589
+ const price = this.safeString2(trade, 'price', 'fillPrice');
590
+ const amount = this.safeString2(trade, 'size', 'fillSize');
591
+ const side = this.safeString(trade, 'side');
592
+ const orderId = this.safeString(trade, 'orderId');
593
+ const feeCost = this.safeString(trade, 'fee');
594
+ let fee = undefined;
595
+ if (feeCost !== undefined) {
596
+ fee = {
597
+ 'cost': feeCost,
598
+ 'currency': market['settle'],
599
+ };
600
+ }
601
+ return this.safeTrade({
602
+ 'info': trade,
603
+ 'timestamp': timestamp,
604
+ 'datetime': this.iso8601(timestamp),
605
+ 'symbol': symbol,
606
+ 'id': id,
607
+ 'order': orderId,
608
+ 'type': undefined,
609
+ 'takerOrMaker': undefined,
610
+ 'side': side,
611
+ 'price': price,
612
+ 'amount': amount,
613
+ 'cost': undefined,
614
+ 'fee': fee,
615
+ }, market);
616
+ }
617
+ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
618
+ /**
619
+ * @method
620
+ * @name blofin#fetchTrades
621
+ * @description get the list of most recent trades for a particular symbol
622
+ * @see https://blofin.com/docs#get-trades
623
+ * @param {string} symbol unified symbol of the market to fetch trades for
624
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
625
+ * @param {int} [limit] the maximum amount of trades to fetch
626
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
627
+ * @param {boolean} [params.paginate] *only applies to publicGetMarketHistoryTrades* default false, when true will automatically paginate by calling this endpoint multiple times
628
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
629
+ */
630
+ await this.loadMarkets();
631
+ let paginate = false;
632
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchTrades', 'paginate');
633
+ if (paginate) {
634
+ return await this.fetchPaginatedCallCursor('fetchTrades', symbol, since, limit, params, 'tradeId', 'after', undefined, 100);
635
+ }
636
+ const market = this.market(symbol);
637
+ const request = {
638
+ 'instId': market['id'],
639
+ };
640
+ let response = undefined;
641
+ if (limit !== undefined) {
642
+ request['limit'] = limit; // default 100
643
+ }
644
+ let method = undefined;
645
+ [method, params] = this.handleOptionAndParams(params, 'fetchTrades', 'method', 'publicGetMarketTrades');
646
+ if (method === 'publicGetMarketTrades') {
647
+ response = await this.publicGetMarketTrades(this.extend(request, params));
648
+ }
649
+ const data = this.safeList(response, 'data', []);
650
+ return this.parseTrades(data, market, since, limit);
651
+ }
652
+ parseOHLCV(ohlcv, market = undefined) {
653
+ //
654
+ // [
655
+ // "1678928760000", // timestamp
656
+ // "24341.4", // open
657
+ // "24344", // high
658
+ // "24313.2", // low
659
+ // "24323", // close
660
+ // "628", // contract volume
661
+ // "2.5819", // base volume
662
+ // "62800", // quote volume
663
+ // "0" // candlestick state
664
+ // ]
665
+ //
666
+ return [
667
+ this.safeInteger(ohlcv, 0),
668
+ this.safeNumber(ohlcv, 1),
669
+ this.safeNumber(ohlcv, 2),
670
+ this.safeNumber(ohlcv, 3),
671
+ this.safeNumber(ohlcv, 4),
672
+ this.safeNumber(ohlcv, 6),
673
+ ];
674
+ }
675
+ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
676
+ /**
677
+ * @method
678
+ * @name blofin#fetchOHLCV
679
+ * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
680
+ * @see https://blofin.com/docs#get-candlesticks
681
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
682
+ * @param {string} timeframe the length of time each candle represents
683
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
684
+ * @param {int} [limit] the maximum amount of candles to fetch
685
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
686
+ * @param {int} [params.until] timestamp in ms of the latest candle to fetch
687
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
688
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
689
+ */
690
+ await this.loadMarkets();
691
+ const market = this.market(symbol);
692
+ let paginate = false;
693
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate');
694
+ if (paginate) {
695
+ return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 100);
696
+ }
697
+ if (limit === undefined) {
698
+ limit = 100; // default 100, max 100
699
+ }
700
+ const request = {
701
+ 'instId': market['id'],
702
+ 'bar': this.safeString(this.timeframes, timeframe, timeframe),
703
+ 'limit': limit,
704
+ };
705
+ const until = this.safeInteger(params, 'until');
706
+ if (until !== undefined) {
707
+ request['after'] = until;
708
+ params = this.omit(params, 'until');
709
+ }
710
+ let response = undefined;
711
+ response = await this.publicGetMarketCandles(this.extend(request, params));
712
+ const data = this.safeList(response, 'data', []);
713
+ return this.parseOHLCVs(data, market, timeframe, since, limit);
714
+ }
715
+ async fetchFundingRateHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
716
+ /**
717
+ * @method
718
+ * @name blofin#fetchFundingRateHistory
719
+ * @description fetches historical funding rate prices
720
+ * @see https://blofin.com/docs#get-funding-rate-history
721
+ * @param {string} symbol unified symbol of the market to fetch the funding rate history for
722
+ * @param {int} [since] timestamp in ms of the earliest funding rate to fetch
723
+ * @param {int} [limit] the maximum amount of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure} to fetch
724
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
725
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
726
+ * @returns {object[]} a list of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure}
727
+ */
728
+ if (symbol === undefined) {
729
+ throw new ArgumentsRequired(this.id + ' fetchFundingRateHistory() requires a symbol argument');
730
+ }
731
+ await this.loadMarkets();
732
+ let paginate = false;
733
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchFundingRateHistory', 'paginate');
734
+ if (paginate) {
735
+ return await this.fetchPaginatedCallDeterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params);
736
+ }
737
+ const market = this.market(symbol);
738
+ const request = {
739
+ 'instId': market['id'],
740
+ };
741
+ if (since !== undefined) {
742
+ request['before'] = Math.max(since - 1, 0);
743
+ }
744
+ if (limit !== undefined) {
745
+ request['limit'] = limit;
746
+ }
747
+ const response = await this.publicGetMarketFundingRateHistory(this.extend(request, params));
748
+ const rates = [];
749
+ const data = this.safeList(response, 'data', []);
750
+ for (let i = 0; i < data.length; i++) {
751
+ const rate = data[i];
752
+ const timestamp = this.safeInteger(rate, 'fundingTime');
753
+ rates.push({
754
+ 'info': rate,
755
+ 'symbol': market['symbol'],
756
+ 'fundingRate': this.safeNumber(rate, 'fundingRate'),
757
+ 'timestamp': timestamp,
758
+ 'datetime': this.iso8601(timestamp),
759
+ });
760
+ }
761
+ const sorted = this.sortBy(rates, 'timestamp');
762
+ return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
763
+ }
764
+ parseFundingRate(contract, market = undefined) {
765
+ //
766
+ // {
767
+ // "fundingRate": "0.00027815",
768
+ // "fundingTime": "1634256000000",
769
+ // "instId": "BTC-USD-SWAP",
770
+ // "instType": "SWAP",
771
+ // "nextFundingRate": "0.00017",
772
+ // "nextFundingTime": "1634284800000"
773
+ // }
774
+ //
775
+ // in the response above nextFundingRate is actually two funding rates from now
776
+ //
777
+ const nextFundingRateTimestamp = this.safeInteger(contract, 'nextFundingTime');
778
+ const marketId = this.safeString(contract, 'instId');
779
+ const symbol = this.safeSymbol(marketId, market);
780
+ const nextFundingRate = this.safeNumber(contract, 'nextFundingRate');
781
+ const fundingTime = this.safeInteger(contract, 'fundingTime');
782
+ // > The current interest is 0.
783
+ return {
784
+ 'info': contract,
785
+ 'symbol': symbol,
786
+ 'markPrice': undefined,
787
+ 'indexPrice': undefined,
788
+ 'interestRate': this.parseNumber('0'),
789
+ 'estimatedSettlePrice': undefined,
790
+ 'timestamp': undefined,
791
+ 'datetime': undefined,
792
+ 'fundingRate': this.safeNumber(contract, 'fundingRate'),
793
+ 'fundingTimestamp': fundingTime,
794
+ 'fundingDatetime': this.iso8601(fundingTime),
795
+ 'nextFundingRate': nextFundingRate,
796
+ 'nextFundingTimestamp': nextFundingRateTimestamp,
797
+ 'nextFundingDatetime': this.iso8601(nextFundingRateTimestamp),
798
+ 'previousFundingRate': undefined,
799
+ 'previousFundingTimestamp': undefined,
800
+ 'previousFundingDatetime': undefined,
801
+ };
802
+ }
803
+ async fetchFundingRate(symbol, params = {}) {
804
+ /**
805
+ * @method
806
+ * @name blofin#fetchFundingRate
807
+ * @description fetch the current funding rate
808
+ * @see https://blofin.com/docs#get-funding-rate
809
+ * @param {string} symbol unified market symbol
810
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
811
+ * @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/#/?id=funding-rate-structure}
812
+ */
813
+ await this.loadMarkets();
814
+ const market = this.market(symbol);
815
+ if (!market['swap']) {
816
+ throw new ExchangeError(this.id + ' fetchFundingRate() is only valid for swap markets');
817
+ }
818
+ const request = {
819
+ 'instId': market['id'],
820
+ };
821
+ const response = await this.publicGetMarketFundingRate(this.extend(request, params));
822
+ //
823
+ // {
824
+ // "code": "0",
825
+ // "data": [
826
+ // {
827
+ // "fundingRate": "0.00027815",
828
+ // "fundingTime": "1634256000000",
829
+ // "instId": "BTC-USD-SWAP",
830
+ // "instType": "SWAP",
831
+ // "nextFundingRate": "0.00017",
832
+ // "nextFundingTime": "1634284800000"
833
+ // }
834
+ // ],
835
+ // "msg": ""
836
+ // }
837
+ //
838
+ const data = this.safeList(response, 'data', []);
839
+ const entry = this.safeDict(data, 0, {});
840
+ return this.parseFundingRate(entry, market);
841
+ }
842
+ parseBalanceByType(type, response) {
843
+ if (type) {
844
+ return this.parseFundingBalance(response);
845
+ }
846
+ else {
847
+ return this.parseTradingBalance(response);
848
+ }
849
+ }
850
+ parseTradingBalance(response) {
851
+ //
852
+ // {
853
+ // "code": "0",
854
+ // "msg": "success",
855
+ // "data": {
856
+ // "ts": "1697021343571",
857
+ // "totalEquity": "10011254.077985990315787910",
858
+ // "isolatedEquity": "861.763132108800000000",
859
+ // "details": [
860
+ // {
861
+ // "currency": "USDT",
862
+ // "equity": "10014042.988958415234430699548",
863
+ // "balance": "10013119.885958415234430699",
864
+ // "ts": "1697021343571",
865
+ // "isolatedEquity": "862.003200000000000000048",
866
+ // "available": "9996399.4708691159703362725",
867
+ // "availableEquity": "9996399.4708691159703362725",
868
+ // "frozen": "15805.149672632597427761",
869
+ // "orderFrozen": "14920.994472632597427761",
870
+ // "equityUsd": "10011254.077985990315787910",
871
+ // "isolatedUnrealizedPnl": "-22.151999999999999999952",
872
+ // "bonus": "0"
873
+ // }
874
+ // ]
875
+ // }
876
+ // }
877
+ //
878
+ const result = { 'info': response };
879
+ const data = this.safeDict(response, 'data', {});
880
+ const timestamp = this.safeInteger(data, 'ts');
881
+ const details = this.safeList(data, 'details', []);
882
+ for (let i = 0; i < details.length; i++) {
883
+ const balance = details[i];
884
+ const currencyId = this.safeString(balance, 'currency');
885
+ const code = this.safeCurrencyCode(currencyId);
886
+ const account = this.account();
887
+ // it may be incorrect to use total, free and used for swap accounts
888
+ const eq = this.safeString(balance, 'equity');
889
+ const availEq = this.safeString(balance, 'available');
890
+ if ((eq === undefined) || (availEq === undefined)) {
891
+ account['free'] = this.safeString(balance, 'availableEquity');
892
+ account['used'] = this.safeString(balance, 'frozen');
893
+ }
894
+ else {
895
+ account['total'] = eq;
896
+ account['free'] = availEq;
897
+ }
898
+ result[code] = account;
899
+ }
900
+ result['timestamp'] = timestamp;
901
+ result['datetime'] = this.iso8601(timestamp);
902
+ return this.safeBalance(result);
903
+ }
904
+ parseFundingBalance(response) {
905
+ //
906
+ // {
907
+ // "code": "0",
908
+ // "msg": "success",
909
+ // "data": [
910
+ // {
911
+ // "currency": "USDT",
912
+ // "balance": "10012514.919418081548717298",
913
+ // "available": "9872132.414278782284622898",
914
+ // "frozen": "138556.471805965930761067",
915
+ // "bonus": "0"
916
+ // }
917
+ // ]
918
+ // }
919
+ //
920
+ const result = { 'info': response };
921
+ const data = this.safeList(response, 'data', []);
922
+ for (let i = 0; i < data.length; i++) {
923
+ const balance = data[i];
924
+ const currencyId = this.safeString(balance, 'currency');
925
+ const code = this.safeCurrencyCode(currencyId);
926
+ const account = this.account();
927
+ // it may be incorrect to use total, free and used for swap accounts
928
+ account['total'] = this.safeString(balance, 'balance');
929
+ account['free'] = this.safeString(balance, 'available');
930
+ account['used'] = this.safeString(balance, 'frozen');
931
+ result[code] = account;
932
+ }
933
+ return this.safeBalance(result);
934
+ }
935
+ parseTradingFee(fee, market = undefined) {
936
+ return {
937
+ 'info': fee,
938
+ 'symbol': this.safeSymbol(undefined, market),
939
+ // blofin returns the fees as negative values opposed to other exchanges, so the sign needs to be flipped
940
+ 'maker': this.parseNumber(Precise.stringNeg(this.safeString2(fee, 'maker', 'makerU'))),
941
+ 'taker': this.parseNumber(Precise.stringNeg(this.safeString2(fee, 'taker', 'takerU'))),
942
+ };
943
+ }
944
+ async fetchBalance(params = {}) {
945
+ /**
946
+ * @method
947
+ * @name blofin#fetchBalance
948
+ * @description query for balance and get the amount of funds available for trading or funds locked in orders
949
+ * @see https://blofin.com/docs#get-balance
950
+ * @see https://blofin.com/docs#get-futures-account-balance
951
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
952
+ * @param {string} [params.accountType] the type of account to fetch the balance for, either 'funding' or 'futures' or 'copy_trading' or 'earn'
953
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
954
+ */
955
+ await this.loadMarkets();
956
+ const accountType = this.safeString2(params, 'accountType', 'type');
957
+ params = this.omit(params, ['accountType', 'type']);
958
+ const request = {};
959
+ let response = undefined;
960
+ if (accountType !== undefined) {
961
+ const parsedAccountType = this.safeString(this.options, 'accountsByType', accountType);
962
+ request['accountType'] = parsedAccountType;
963
+ response = await this.privateGetAssetBalances(this.extend(request, params));
964
+ }
965
+ else {
966
+ response = await this.privateGetAccountBalance(this.extend(request, params));
967
+ }
968
+ return this.parseBalanceByType(accountType, response);
969
+ }
970
+ createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
971
+ const market = this.market(symbol);
972
+ const request = {
973
+ 'instId': market['id'],
974
+ 'side': side,
975
+ 'orderType': type,
976
+ 'size': this.amountToPrecision(symbol, amount),
977
+ 'brokerId': this.safeString(this.options, 'brokerId', 'ec6dd3a7dd982d0b'),
978
+ };
979
+ let marginMode = undefined;
980
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params, 'cross');
981
+ request['marginMode'] = marginMode;
982
+ const timeInForce = this.safeString(params, 'timeInForce', 'GTC');
983
+ const isMarketOrder = type === 'market';
984
+ params = this.omit(params, ['timeInForce']);
985
+ const ioc = (timeInForce === 'IOC') || (type === 'ioc');
986
+ const marketIOC = (isMarketOrder && ioc);
987
+ if (isMarketOrder || marketIOC) {
988
+ request['orderType'] = 'market';
989
+ }
990
+ else {
991
+ request['price'] = this.priceToPrecision(symbol, price);
992
+ }
993
+ let postOnly = false;
994
+ [postOnly, params] = this.handlePostOnly(isMarketOrder, type === 'post_only', params);
995
+ if (postOnly) {
996
+ request['type'] = 'post_only';
997
+ }
998
+ const stopLoss = this.safeValue(params, 'stopLoss');
999
+ const takeProfit = this.safeValue(params, 'takeProfit');
1000
+ params = this.omit(params, ['stopLoss', 'takeProfit']);
1001
+ const isStopLoss = stopLoss !== undefined;
1002
+ const isTakeProfit = takeProfit !== undefined;
1003
+ if (isStopLoss || isTakeProfit) {
1004
+ if (isStopLoss) {
1005
+ const slTriggerPrice = this.safeString2(stopLoss, 'triggerPrice', 'stopPrice');
1006
+ request['slTriggerPrice'] = this.priceToPrecision(symbol, slTriggerPrice);
1007
+ const slOrderPrice = this.safeString(stopLoss, 'price', '-1');
1008
+ request['slOrderPrice'] = this.priceToPrecision(symbol, slOrderPrice);
1009
+ }
1010
+ if (isTakeProfit) {
1011
+ const tpTriggerPrice = this.safeString2(takeProfit, 'triggerPrice', 'stopPrice');
1012
+ request['tpTriggerPrice'] = this.priceToPrecision(symbol, tpTriggerPrice);
1013
+ const tpPrice = this.safeString(takeProfit, 'price', '-1');
1014
+ request['tpOrderPrice'] = this.priceToPrecision(symbol, tpPrice);
1015
+ }
1016
+ }
1017
+ return this.extend(request, params);
1018
+ }
1019
+ parseOrderStatus(status) {
1020
+ const statuses = {
1021
+ 'canceled': 'canceled',
1022
+ 'order_failed': 'canceled',
1023
+ 'live': 'open',
1024
+ 'partially_filled': 'open',
1025
+ 'filled': 'closed',
1026
+ 'effective': 'closed',
1027
+ };
1028
+ return this.safeString(statuses, status, status);
1029
+ }
1030
+ parseOrder(order, market = undefined) {
1031
+ //
1032
+ // {
1033
+ // "orderId": "2075628533",
1034
+ // "clientOrderId": "",
1035
+ // "instId": "LTC-USDT",
1036
+ // "marginMode": "cross",
1037
+ // "positionSide": "net",
1038
+ // "side": "buy",
1039
+ // "orderType": "market",
1040
+ // "price": "0.000000000000000000",
1041
+ // "size": "1.000000000000000000",
1042
+ // "reduceOnly": "true",
1043
+ // "leverage": "3",
1044
+ // "state": "filled",
1045
+ // "filledSize": "1.000000000000000000",
1046
+ // "pnl": "-0.050000000000000000",
1047
+ // "averagePrice": "68.110000000000000000",
1048
+ // "fee": "0.040866000000000000",
1049
+ // "createTime": "1706891359010",
1050
+ // "updateTime": "1706891359098",
1051
+ // "orderCategory": "normal",
1052
+ // "tpTriggerPrice": null,
1053
+ // "tpOrderPrice": null,
1054
+ // "slTriggerPrice": null,
1055
+ // "slOrderPrice": null,
1056
+ // "cancelSource": "not_canceled",
1057
+ // "cancelSourceReason": null,
1058
+ // "brokerId": "ec6dd3a7dd982d0b"
1059
+ // }
1060
+ //
1061
+ const id = this.safeString2(order, 'tpslId', 'orderId');
1062
+ const timestamp = this.safeInteger(order, 'createTime');
1063
+ const lastUpdateTimestamp = this.safeInteger(order, 'updateTime');
1064
+ const lastTradeTimestamp = this.safeInteger(order, 'fillTime');
1065
+ const side = this.safeString(order, 'side');
1066
+ let type = this.safeString(order, 'orderType');
1067
+ let postOnly = undefined;
1068
+ let timeInForce = undefined;
1069
+ if (type === 'post_only') {
1070
+ postOnly = true;
1071
+ type = 'limit';
1072
+ }
1073
+ else if (type === 'fok') {
1074
+ timeInForce = 'FOK';
1075
+ type = 'limit';
1076
+ }
1077
+ else if (type === 'ioc') {
1078
+ timeInForce = 'IOC';
1079
+ type = 'limit';
1080
+ }
1081
+ const marketId = this.safeString(order, 'instId');
1082
+ market = this.safeMarket(marketId, market);
1083
+ const symbol = this.safeSymbol(marketId, market, '-');
1084
+ const filled = this.safeString(order, 'filledSize');
1085
+ const price = this.safeString2(order, 'px', 'price');
1086
+ const average = this.safeString(order, 'averagePrice');
1087
+ const status = this.parseOrderStatus(this.safeString(order, 'state'));
1088
+ const feeCostString = this.safeString(order, 'fee');
1089
+ const amount = this.safeString(order, 'size');
1090
+ const leverage = this.safeString(order, 'leverage', '1');
1091
+ const contractSize = this.safeString(market, 'contractSize');
1092
+ const baseAmount = Precise.stringMul(contractSize, filled);
1093
+ let cost = undefined;
1094
+ if (average !== undefined) {
1095
+ cost = Precise.stringMul(average, baseAmount);
1096
+ cost = Precise.stringDiv(cost, leverage);
1097
+ }
1098
+ // spot market buy: "sz" can refer either to base currency units or to quote currency units
1099
+ let fee = undefined;
1100
+ if (feeCostString !== undefined) {
1101
+ const feeCostSigned = Precise.stringAbs(feeCostString);
1102
+ const feeCurrencyId = this.safeString(order, 'feeCcy', 'USDT');
1103
+ const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
1104
+ fee = {
1105
+ 'cost': this.parseNumber(feeCostSigned),
1106
+ 'currency': feeCurrencyCode,
1107
+ };
1108
+ }
1109
+ let clientOrderId = this.safeString(order, 'clientOrderId');
1110
+ if ((clientOrderId !== undefined) && (clientOrderId.length < 1)) {
1111
+ clientOrderId = undefined; // fix empty clientOrderId string
1112
+ }
1113
+ const stopLossTriggerPrice = this.safeNumber(order, 'slTriggerPrice');
1114
+ const stopLossPrice = this.safeNumber(order, 'slOrderPrice');
1115
+ const takeProfitTriggerPrice = this.safeNumber(order, 'tpTriggerPrice');
1116
+ const takeProfitPrice = this.safeNumber(order, 'tpOrderPrice');
1117
+ const reduceOnlyRaw = this.safeString(order, 'reduceOnly');
1118
+ const reduceOnly = (reduceOnlyRaw === 'true');
1119
+ return this.safeOrder({
1120
+ 'info': order,
1121
+ 'id': id,
1122
+ 'clientOrderId': clientOrderId,
1123
+ 'timestamp': timestamp,
1124
+ 'datetime': this.iso8601(timestamp),
1125
+ 'lastTradeTimestamp': lastTradeTimestamp,
1126
+ 'lastUpdateTimestamp': lastUpdateTimestamp,
1127
+ 'symbol': symbol,
1128
+ 'type': type,
1129
+ 'timeInForce': timeInForce,
1130
+ 'postOnly': postOnly,
1131
+ 'side': side,
1132
+ 'price': price,
1133
+ 'stopLossTriggerPrice': stopLossTriggerPrice,
1134
+ 'takeProfitTriggerPrice': takeProfitTriggerPrice,
1135
+ 'stopLossPrice': stopLossPrice,
1136
+ 'takeProfitPrice': takeProfitPrice,
1137
+ 'average': average,
1138
+ 'cost': cost,
1139
+ 'amount': amount,
1140
+ 'filled': filled,
1141
+ 'remaining': undefined,
1142
+ 'status': status,
1143
+ 'fee': fee,
1144
+ 'trades': undefined,
1145
+ 'reduceOnly': reduceOnly,
1146
+ }, market);
1147
+ }
1148
+ async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
1149
+ /**
1150
+ * @method
1151
+ * @name blofin#createOrder
1152
+ * @description create a trade order
1153
+ * @see https://blofin.com/docs#place-order
1154
+ * @see https://blofin.com/docs#place-tpsl-order
1155
+ * @param {string} symbol unified symbol of the market to create an order in
1156
+ * @param {string} type 'market' or 'limit' or 'post_only' or 'ioc' or 'fok'
1157
+ * @param {string} side 'buy' or 'sell'
1158
+ * @param {float} amount how much of currency you want to trade in units of base currency
1159
+ * @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1160
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1161
+ * @param {bool} [params.reduceOnly] a mark to reduce the position size for margin, swap and future orders
1162
+ * @param {bool} [params.postOnly] true to place a post only order
1163
+ * @param {string} [params.marginMode] 'cross' or 'isolated', default is 'cross'
1164
+ * @param {float} [params.stopLossPrice] stop loss trigger price (will use privatePostTradeOrderTpsl)
1165
+ * @param {float} [params.takeProfitPrice] take profit trigger price (will use privatePostTradeOrderTpsl)
1166
+ * @param {string} [param.positionSide] *stopLossPrice/takeProfitPrice orders only* 'long' or 'short' or 'net' default is 'net'
1167
+ * @param {string} [params.clientOrderId] a unique id for the order
1168
+ * @param {object} [params.takeProfit] *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered
1169
+ * @param {float} [params.takeProfit.triggerPrice] take profit trigger price
1170
+ * @param {float} [params.takeProfit.price] take profit order price (if not provided the order will be a market order)
1171
+ * @param {object} [params.stopLoss] *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered
1172
+ * @param {float} [params.stopLoss.triggerPrice] stop loss trigger price
1173
+ * @param {float} [params.stopLoss.price] stop loss order price (if not provided the order will be a market order)
1174
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1175
+ */
1176
+ await this.loadMarkets();
1177
+ const market = this.market(symbol);
1178
+ const tpsl = this.safeBool(params, 'tpsl', false);
1179
+ params = this.omit(params, 'tpsl');
1180
+ let method = undefined;
1181
+ [method, params] = this.handleOptionAndParams(params, 'createOrder', 'method', 'privatePostTradeOrder');
1182
+ const isStopLossPriceDefined = this.safeString(params, 'stopLossPrice') !== undefined;
1183
+ const isTakeProfitPriceDefined = this.safeString(params, 'takeProfitPrice') !== undefined;
1184
+ const isType2Order = (isStopLossPriceDefined || isTakeProfitPriceDefined);
1185
+ let response = undefined;
1186
+ if (tpsl || (method === 'privatePostTradeOrderTpsl') || isType2Order) {
1187
+ const tpslRequest = this.createTpslOrderRequest(symbol, type, side, amount, price, params);
1188
+ response = await this.privatePostTradeOrderTpsl(tpslRequest);
1189
+ }
1190
+ else {
1191
+ const request = this.createOrderRequest(symbol, type, side, amount, price, params);
1192
+ response = await this.privatePostTradeOrder(request);
1193
+ }
1194
+ const data = this.safeList(response, 'data', []);
1195
+ const first = this.safeDict(data, 0);
1196
+ const order = this.parseOrder(first, market);
1197
+ order['type'] = type;
1198
+ order['side'] = side;
1199
+ return order;
1200
+ }
1201
+ createTpslOrderRequest(symbol, type, side, amount = undefined, price = undefined, params = {}) {
1202
+ const market = this.market(symbol);
1203
+ const positionSide = this.safeString(params, 'positionSide', 'net');
1204
+ const request = {
1205
+ 'instId': market['id'],
1206
+ 'side': side,
1207
+ 'positionSide': positionSide,
1208
+ 'brokerId': this.safeString(this.options, 'brokerId', 'ec6dd3a7dd982d0b'),
1209
+ };
1210
+ if (amount !== undefined) {
1211
+ request['size'] = this.amountToPrecision(symbol, amount);
1212
+ }
1213
+ const marginMode = this.safeString(params, 'marginMode', 'cross'); // cross or isolated
1214
+ if (marginMode !== 'cross' && marginMode !== 'isolated') {
1215
+ throw new BadRequest(this.id + ' createTpslOrder() requires a marginMode parameter that must be either cross or isolated');
1216
+ }
1217
+ const stopLossPrice = this.safeString(params, 'stopLossPrice');
1218
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
1219
+ if (stopLossPrice !== undefined) {
1220
+ request['slTriggerPrice'] = this.priceToPrecision(symbol, stopLossPrice);
1221
+ if (type === 'market') {
1222
+ request['slOrderPrice'] = '-1';
1223
+ }
1224
+ else {
1225
+ request['slOrderPrice'] = this.priceToPrecision(symbol, price);
1226
+ }
1227
+ }
1228
+ else if (takeProfitPrice !== undefined) {
1229
+ request['tpTriggerPrice'] = this.priceToPrecision(symbol, takeProfitPrice);
1230
+ if (type === 'market') {
1231
+ request['tpOrderPrice'] = '-1';
1232
+ }
1233
+ else {
1234
+ request['tpOrderPrice'] = this.priceToPrecision(symbol, price);
1235
+ }
1236
+ }
1237
+ request['marginMode'] = marginMode;
1238
+ params = this.omit(params, ['stopLossPrice', 'takeProfitPrice']);
1239
+ return this.extend(request, params);
1240
+ }
1241
+ async cancelOrder(id, symbol = undefined, params = {}) {
1242
+ /**
1243
+ * @method
1244
+ * @name blofin#cancelOrder
1245
+ * @description cancels an open order
1246
+ * @see https://blofin.com/docs#cancel-order
1247
+ * @see https://blofin.com/docs#cancel-tpsl-order
1248
+ * @param {string} id order id
1249
+ * @param {string} symbol unified symbol of the market the order was made in
1250
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1251
+ * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1252
+ */
1253
+ if (symbol === undefined) {
1254
+ throw new ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
1255
+ }
1256
+ await this.loadMarkets();
1257
+ const market = this.market(symbol);
1258
+ const request = {
1259
+ 'instId': market['id'],
1260
+ };
1261
+ const clientOrderId = this.safeString(params, 'clientOrderId');
1262
+ if (clientOrderId !== undefined) {
1263
+ request['clientOrderId'] = clientOrderId;
1264
+ }
1265
+ else {
1266
+ request['orderId'] = id;
1267
+ }
1268
+ const query = this.omit(params, ['orderId', 'clientOrderId']);
1269
+ const response = await this.privatePostTradeCancelOrder(this.extend(request, query));
1270
+ const data = this.safeList(response, 'data', []);
1271
+ const order = this.safeDict(data, 0);
1272
+ return this.parseOrder(order, market);
1273
+ }
1274
+ async createOrders(orders, params = {}) {
1275
+ /**
1276
+ * @method
1277
+ * @name blofin#createOrders
1278
+ * @description create a list of trade orders
1279
+ * @see https://blofin.com/docs#place-multiple-orders
1280
+ * @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
1281
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1282
+ */
1283
+ await this.loadMarkets();
1284
+ const ordersRequests = [];
1285
+ for (let i = 0; i < orders.length; i++) {
1286
+ const rawOrder = orders[i];
1287
+ const marketId = this.safeString(rawOrder, 'symbol');
1288
+ const type = this.safeString(rawOrder, 'type');
1289
+ const side = this.safeString(rawOrder, 'side');
1290
+ const amount = this.safeValue(rawOrder, 'amount');
1291
+ const price = this.safeValue(rawOrder, 'price');
1292
+ const orderParams = this.safeValue(rawOrder, 'params', {});
1293
+ const extendedParams = this.extend(orderParams, params); // the request does not accept extra params since it's a list, so we're extending each order with the common params
1294
+ const orderRequest = this.createOrderRequest(marketId, type, side, amount, price, extendedParams);
1295
+ ordersRequests.push(orderRequest);
1296
+ }
1297
+ const response = await this.privatePostTradeBatchOrders(ordersRequests);
1298
+ const data = this.safeList(response, 'data', []);
1299
+ return this.parseOrders(data);
1300
+ }
1301
+ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1302
+ /**
1303
+ * @method
1304
+ * @name blofin#fetchOpenOrders
1305
+ * @description Fetch orders that are still open
1306
+ * @see https://blofin.com/docs#get-active-orders
1307
+ * @see https://blofin.com/docs#get-active-tpsl-orders
1308
+ * @param {string} symbol unified market symbol
1309
+ * @param {int} [since] the earliest time in ms to fetch open orders for
1310
+ * @param {int} [limit] the maximum number of open orders structures to retrieve
1311
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1312
+ * @param {bool} [params.stop] True if fetching trigger or conditional orders
1313
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1314
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1315
+ */
1316
+ await this.loadMarkets();
1317
+ let paginate = false;
1318
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'paginate');
1319
+ if (paginate) {
1320
+ return await this.fetchPaginatedCallDynamic('fetchOpenOrders', symbol, since, limit, params);
1321
+ }
1322
+ const request = {};
1323
+ let market = undefined;
1324
+ if (symbol !== undefined) {
1325
+ market = this.market(symbol);
1326
+ request['instId'] = market['id'];
1327
+ }
1328
+ if (limit !== undefined) {
1329
+ request['limit'] = limit; // default 100, max 100
1330
+ }
1331
+ const isStop = this.safeValueN(params, ['stop', 'trigger', 'tpsl', 'TPSL'], false);
1332
+ let method = undefined;
1333
+ [method, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'method', 'privateGetTradeOrdersPending');
1334
+ const query = this.omit(params, ['method', 'stop', 'trigger', 'tpsl', 'TPSL']);
1335
+ let response = undefined;
1336
+ if (isStop || (method === 'privateGetTradeOrdersTpslPending')) {
1337
+ response = await this.privateGetTradeOrdersTpslPending(this.extend(request, query));
1338
+ }
1339
+ else {
1340
+ response = await this.privateGetTradeOrdersPending(this.extend(request, query));
1341
+ }
1342
+ const data = this.safeList(response, 'data', []);
1343
+ return this.parseOrders(data, market, since, limit);
1344
+ }
1345
+ async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1346
+ /**
1347
+ * @method
1348
+ * @name blofin#fetchMyTrades
1349
+ * @description fetch all trades made by the user
1350
+ * @see https://blofin.com/docs#get-trade-history
1351
+ * @param {string} symbol unified market symbol
1352
+ * @param {int} [since] the earliest time in ms to fetch trades for
1353
+ * @param {int} [limit] the maximum number of trades structures to retrieve
1354
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1355
+ * @param {int} [params.until] Timestamp in ms of the latest time to retrieve trades for
1356
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1357
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
1358
+ */
1359
+ await this.loadMarkets();
1360
+ let paginate = false;
1361
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'paginate');
1362
+ if (paginate) {
1363
+ return await this.fetchPaginatedCallDynamic('fetchMyTrades', symbol, since, limit, params);
1364
+ }
1365
+ let request = {};
1366
+ let market = undefined;
1367
+ if (symbol !== undefined) {
1368
+ market = this.market(symbol);
1369
+ request['instId'] = market['id'];
1370
+ }
1371
+ [request, params] = this.handleUntilOption('end', request, params);
1372
+ if (limit !== undefined) {
1373
+ request['limit'] = limit; // default 100, max 100
1374
+ }
1375
+ const response = await this.privateGetTradeFillsHistory(this.extend(request, params));
1376
+ const data = this.safeList(response, 'data', []);
1377
+ return this.parseTrades(data, market, since, limit);
1378
+ }
1379
+ async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
1380
+ /**
1381
+ * @method
1382
+ * @name blofin#fetchDeposits
1383
+ * @description fetch all deposits made to an account
1384
+ * @see https://blofin.com/docs#get-deposite-history
1385
+ * @param {string} code unified currency code
1386
+ * @param {int} [since] the earliest time in ms to fetch deposits for
1387
+ * @param {int} [limit] the maximum number of deposits structures to retrieve
1388
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1389
+ * @param {int} [params.until] the latest time in ms to fetch entries for
1390
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1391
+ * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
1392
+ */
1393
+ await this.loadMarkets();
1394
+ let paginate = false;
1395
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchDeposits', 'paginate');
1396
+ if (paginate) {
1397
+ return await this.fetchPaginatedCallDynamic('fetchDeposits', code, since, limit, params);
1398
+ }
1399
+ let request = {};
1400
+ let currency = undefined;
1401
+ if (code !== undefined) {
1402
+ currency = this.currency(code);
1403
+ request['currency'] = currency['id'];
1404
+ }
1405
+ if (since !== undefined) {
1406
+ request['before'] = Math.max(since - 1, 0);
1407
+ }
1408
+ if (limit !== undefined) {
1409
+ request['limit'] = limit; // default 100, max 100
1410
+ }
1411
+ [request, params] = this.handleUntilOption('after', request, params);
1412
+ const response = await this.privateGetAssetDepositHistory(this.extend(request, params));
1413
+ const data = this.safeList(response, 'data', []);
1414
+ return this.parseTransactions(data, currency, since, limit, params);
1415
+ }
1416
+ async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
1417
+ /**
1418
+ * @method
1419
+ * @name blofin#fetchWithdrawals
1420
+ * @description fetch all withdrawals made from an account
1421
+ * @see https://blofin.com/docs#get-withdraw-history
1422
+ * @param {string} code unified currency code
1423
+ * @param {int} [since] the earliest time in ms to fetch withdrawals for
1424
+ * @param {int} [limit] the maximum number of withdrawals structures to retrieve
1425
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1426
+ * @param {int} [params.until] the latest time in ms to fetch entries for
1427
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1428
+ * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
1429
+ */
1430
+ await this.loadMarkets();
1431
+ let paginate = false;
1432
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchWithdrawals', 'paginate');
1433
+ if (paginate) {
1434
+ return await this.fetchPaginatedCallDynamic('fetchWithdrawals', code, since, limit, params);
1435
+ }
1436
+ let request = {};
1437
+ let currency = undefined;
1438
+ if (code !== undefined) {
1439
+ currency = this.currency(code);
1440
+ request['currency'] = currency['id'];
1441
+ }
1442
+ if (since !== undefined) {
1443
+ request['before'] = Math.max(since - 1, 0);
1444
+ }
1445
+ if (limit !== undefined) {
1446
+ request['limit'] = limit; // default 100, max 100
1447
+ }
1448
+ [request, params] = this.handleUntilOption('after', request, params);
1449
+ const response = await this.privateGetAssetWithdrawalHistory(this.extend(request, params));
1450
+ const data = this.safeList(response, 'data', []);
1451
+ return this.parseTransactions(data, currency, since, limit, params);
1452
+ }
1453
+ async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
1454
+ /**
1455
+ * @method
1456
+ * @name blofin#fetchLedger
1457
+ * @description fetch the history of changes, actions done by the user or operations that altered balance of the user
1458
+ * @see https://blofin.com/docs#get-funds-transfer-history
1459
+ * @param {string} code unified currency code, default is undefined
1460
+ * @param {int} [since] timestamp in ms of the earliest ledger entry, default is undefined
1461
+ * @param {int} [limit] max number of ledger entrys to return, default is undefined
1462
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1463
+ * @param {string} [params.marginMode] 'cross' or 'isolated'
1464
+ * @param {int} [params.until] the latest time in ms to fetch entries for
1465
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1466
+ * @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger-structure}
1467
+ */
1468
+ await this.loadMarkets();
1469
+ let paginate = false;
1470
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchLedger', 'paginate');
1471
+ if (paginate) {
1472
+ return await this.fetchPaginatedCallDynamic('fetchLedger', code, since, limit, params);
1473
+ }
1474
+ let request = {};
1475
+ if (limit !== undefined) {
1476
+ request['limit'] = limit;
1477
+ }
1478
+ let currency = undefined;
1479
+ if (code !== undefined) {
1480
+ currency = this.currency(code);
1481
+ request['currency'] = currency['id'];
1482
+ }
1483
+ [request, params] = this.handleUntilOption('end', request, params);
1484
+ let response = undefined;
1485
+ response = await this.privateGetAssetBills(this.extend(request, params));
1486
+ const data = this.safeList(response, 'data', []);
1487
+ return this.parseLedger(data, currency, since, limit);
1488
+ }
1489
+ parseTransaction(transaction, currency = undefined) {
1490
+ //
1491
+ //
1492
+ // fetchDeposits
1493
+ //
1494
+ // {
1495
+ // "currency": "USDT",
1496
+ // "chain": "TRC20",
1497
+ // "address": "TGfJLtnsh3B9EqekFEBZ1nR14QanBUf5Bi",
1498
+ // "txId": "892f4e0c32268b29b2e541ef30d32a30bbf10f902adcc4b1428319ed7c3758fd",
1499
+ // "type": "0",
1500
+ // "amount": "86.975843",
1501
+ // "state": "1",
1502
+ // "ts": "1703163304153",
1503
+ // "tag": null,
1504
+ // "confirm": "16",
1505
+ // "depositId": "36c8e2a7ea184a219de72215a696acaf"
1506
+ // }
1507
+ // fetchWithdrawals
1508
+ // {
1509
+ // "currency": "USDT",
1510
+ // "chain": "TRC20",
1511
+ // "address": "TYgB3sVXHPEDQUu288EG1uMFh9Pk2swLgW",
1512
+ // "txId": "1fd5ac52df414d7ea66194cadd9a5b4d2422c2b9720037f66d98207f9858fd96",
1513
+ // "type": "0",
1514
+ // "amount": "9",
1515
+ // "fee": "1",
1516
+ // "feeCurrency": "USDT",
1517
+ // "state": "3",
1518
+ // "clientId": null,
1519
+ // "ts": "1707217439351",
1520
+ // "tag": null,
1521
+ // "memo": null,
1522
+ // "withdrawId": "e0768698cfdf4aee8e54654c3775914b"
1523
+ // }
1524
+ //
1525
+ let type = undefined;
1526
+ let id = undefined;
1527
+ const withdrawalId = this.safeString(transaction, 'withdrawId');
1528
+ const depositId = this.safeString(transaction, 'depositId');
1529
+ const addressTo = this.safeString(transaction, 'address');
1530
+ const address = addressTo;
1531
+ const tagTo = this.safeString(transaction, 'tag');
1532
+ if (withdrawalId !== undefined) {
1533
+ type = 'withdrawal';
1534
+ id = withdrawalId;
1535
+ }
1536
+ else {
1537
+ id = depositId;
1538
+ type = 'deposit';
1539
+ }
1540
+ const currencyId = this.safeString(transaction, 'currency');
1541
+ const code = this.safeCurrencyCode(currencyId);
1542
+ const amount = this.safeNumber(transaction, 'amount');
1543
+ const status = this.parseTransactionStatus(this.safeString(transaction, 'state'));
1544
+ const txid = this.safeString(transaction, 'txId');
1545
+ const timestamp = this.safeInteger(transaction, 'ts');
1546
+ const feeCurrencyId = this.safeString(transaction, 'feeCurrency');
1547
+ const feeCode = this.safeCurrencyCode(feeCurrencyId);
1548
+ const feeCost = this.safeNumber(transaction, 'fee');
1549
+ return {
1550
+ 'info': transaction,
1551
+ 'id': id,
1552
+ 'currency': code,
1553
+ 'amount': amount,
1554
+ 'network': undefined,
1555
+ 'addressFrom': undefined,
1556
+ 'addressTo': addressTo,
1557
+ 'address': address,
1558
+ 'tagFrom': undefined,
1559
+ 'tagTo': tagTo,
1560
+ 'tag': tagTo,
1561
+ 'status': status,
1562
+ 'type': type,
1563
+ 'updated': undefined,
1564
+ 'txid': txid,
1565
+ 'timestamp': timestamp,
1566
+ 'datetime': this.iso8601(timestamp),
1567
+ 'internal': undefined,
1568
+ 'comment': undefined,
1569
+ 'fee': {
1570
+ 'currency': feeCode,
1571
+ 'cost': feeCost,
1572
+ },
1573
+ };
1574
+ }
1575
+ parseTransactionStatus(status) {
1576
+ const statuses = {
1577
+ '0': 'pending',
1578
+ '1': 'ok',
1579
+ '2': 'failed',
1580
+ '3': 'pending',
1581
+ };
1582
+ return this.safeString(statuses, status, status);
1583
+ }
1584
+ parseLedgerEntryType(type) {
1585
+ const types = {
1586
+ '1': 'transfer',
1587
+ '2': 'trade',
1588
+ '3': 'trade',
1589
+ '4': 'rebate',
1590
+ '5': 'trade',
1591
+ '6': 'transfer',
1592
+ '7': 'trade',
1593
+ '8': 'fee',
1594
+ '9': 'trade',
1595
+ '10': 'trade',
1596
+ '11': 'trade', // system token conversion
1597
+ };
1598
+ return this.safeString(types, type, type);
1599
+ }
1600
+ parseLedgerEntry(item, currency = undefined) {
1601
+ const id = this.safeString(item, 'transferId');
1602
+ const referenceId = this.safeString(item, 'clientId');
1603
+ const fromAccount = this.safeString(item, 'fromAccount');
1604
+ const toAccount = this.safeString(item, 'toAccount');
1605
+ const type = this.parseLedgerEntryType(this.safeString(item, 'type'));
1606
+ const code = this.safeCurrencyCode(this.safeString(item, 'currency'), currency);
1607
+ const amountString = this.safeString(item, 'amount');
1608
+ const amount = this.parseNumber(amountString);
1609
+ const timestamp = this.safeInteger(item, 'ts');
1610
+ const status = 'ok';
1611
+ return {
1612
+ 'id': id,
1613
+ 'info': item,
1614
+ 'timestamp': timestamp,
1615
+ 'datetime': this.iso8601(timestamp),
1616
+ 'fromAccount': fromAccount,
1617
+ 'toAccount': toAccount,
1618
+ 'type': type,
1619
+ 'currency': code,
1620
+ 'amount': amount,
1621
+ 'clientId': referenceId,
1622
+ 'status': status,
1623
+ };
1624
+ }
1625
+ parseIds(ids) {
1626
+ /**
1627
+ * @ignore
1628
+ * @method
1629
+ * @name blofin#parseIds
1630
+ * @param {string[]|string} ids order ids
1631
+ * @returns {string[]} list of order ids
1632
+ */
1633
+ if (typeof ids === 'string') {
1634
+ return ids.split(',');
1635
+ }
1636
+ else {
1637
+ return ids;
1638
+ }
1639
+ }
1640
+ async cancelOrders(ids, symbol = undefined, params = {}) {
1641
+ /**
1642
+ * @method
1643
+ * @name blofin#cancelOrders
1644
+ * @description cancel multiple orders
1645
+ * @see https://blofin.com/docs#cancel-multiple-orders
1646
+ * @param {string[]} ids order ids
1647
+ * @param {string} symbol unified market symbol
1648
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1649
+ * @param {boolean} [params.trigger] whether the order is a stop/trigger order
1650
+ * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1651
+ */
1652
+ // TODO : the original endpoint signature differs, according to that you can skip individual symbol and assign ids in batch. At this moment, `params` is not being used too.
1653
+ if (symbol === undefined) {
1654
+ throw new ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument');
1655
+ }
1656
+ await this.loadMarkets();
1657
+ const market = this.market(symbol);
1658
+ const request = [];
1659
+ const options = this.safeValue(this.options, 'cancelOrders', {});
1660
+ const defaultMethod = this.safeString(options, 'method', 'privatePostTradeCancelBatchOrders');
1661
+ let method = this.safeString(params, 'method', defaultMethod);
1662
+ const clientOrderIds = this.parseIds(this.safeValue(params, 'clientOrderId'));
1663
+ const tpslIds = this.parseIds(this.safeValue(params, 'tpslId'));
1664
+ const stop = this.safeBoolN(params, ['stop', 'trigger', 'tpsl']);
1665
+ if (stop) {
1666
+ method = 'privatePostTradeCancelTpsl';
1667
+ }
1668
+ if (clientOrderIds === undefined) {
1669
+ ids = this.parseIds(ids);
1670
+ if (tpslIds !== undefined) {
1671
+ for (let i = 0; i < tpslIds.length; i++) {
1672
+ request.push({
1673
+ 'tpslId': tpslIds[i],
1674
+ 'instId': market['id'],
1675
+ });
1676
+ }
1677
+ }
1678
+ for (let i = 0; i < ids.length; i++) {
1679
+ if (stop) {
1680
+ request.push({
1681
+ 'tpslId': ids[i],
1682
+ 'instId': market['id'],
1683
+ });
1684
+ }
1685
+ else {
1686
+ request.push({
1687
+ 'orderId': ids[i],
1688
+ 'instId': market['id'],
1689
+ });
1690
+ }
1691
+ }
1692
+ }
1693
+ else {
1694
+ for (let i = 0; i < clientOrderIds.length; i++) {
1695
+ request.push({
1696
+ 'instId': market['id'],
1697
+ 'clientOrderId': clientOrderIds[i],
1698
+ });
1699
+ }
1700
+ }
1701
+ let response = undefined;
1702
+ if (method === 'privatePostTradeCancelTpsl') {
1703
+ response = await this.privatePostTradeCancelTpsl(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
1704
+ }
1705
+ else {
1706
+ response = await this.privatePostTradeCancelBatchOrders(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
1707
+ }
1708
+ const ordersData = this.safeList(response, 'data', []);
1709
+ return this.parseOrders(ordersData, market, undefined, undefined, params);
1710
+ }
1711
+ async transfer(code, amount, fromAccount, toAccount, params = {}) {
1712
+ /**
1713
+ * @method
1714
+ * @name blofin#transfer
1715
+ * @description transfer currency internally between wallets on the same account
1716
+ * @see https://blofin.com/docs#funds-transfer
1717
+ * @param {string} code unified currency code
1718
+ * @param {float} amount amount to transfer
1719
+ * @param {string} fromAccount account to transfer from (funding, swap, copy_trading, earn)
1720
+ * @param {string} toAccount account to transfer to (funding, swap, copy_trading, earn)
1721
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1722
+ * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
1723
+ */
1724
+ await this.loadMarkets();
1725
+ const currency = this.currency(code);
1726
+ const accountsByType = this.safeValue(this.options, 'accountsByType', {});
1727
+ const fromId = this.safeString(accountsByType, fromAccount, fromAccount);
1728
+ const toId = this.safeString(accountsByType, toAccount, toAccount);
1729
+ const request = {
1730
+ 'currency': currency['id'],
1731
+ 'amount': this.currencyToPrecision(code, amount),
1732
+ 'fromAccount': fromId,
1733
+ 'toAccount': toId,
1734
+ };
1735
+ const response = await this.privatePostAssetTransfer(this.extend(request, params));
1736
+ const data = this.safeDict(response, 'data', {});
1737
+ return this.parseTransfer(data, currency);
1738
+ }
1739
+ parseTransfer(transfer, currency = undefined) {
1740
+ const id = this.safeString(transfer, 'transferId');
1741
+ return {
1742
+ 'info': transfer,
1743
+ 'id': id,
1744
+ 'timestamp': undefined,
1745
+ 'datetime': undefined,
1746
+ 'currency': undefined,
1747
+ 'amount': undefined,
1748
+ 'fromAccount': undefined,
1749
+ 'toAccount': undefined,
1750
+ 'status': undefined,
1751
+ };
1752
+ }
1753
+ async fetchPosition(symbol, params = {}) {
1754
+ /**
1755
+ * @method
1756
+ * @name blofin#fetchPosition
1757
+ * @description fetch data on a single open contract trade position
1758
+ * @see https://blofin.com/docs#get-positions
1759
+ * @param {string} symbol unified market symbol of the market the position is held in, default is undefined
1760
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1761
+ * @param {string} [params.instType] MARGIN, SWAP, FUTURES, OPTION
1762
+ * @returns {object} a [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
1763
+ */
1764
+ await this.loadMarkets();
1765
+ const market = this.market(symbol);
1766
+ const request = {
1767
+ 'instId': market['id'],
1768
+ };
1769
+ const response = await this.privateGetAccountPositions(this.extend(request, params));
1770
+ const data = this.safeList(response, 'data', []);
1771
+ const position = this.safeDict(data, 0);
1772
+ if (position === undefined) {
1773
+ return undefined;
1774
+ }
1775
+ return this.parsePosition(position, market);
1776
+ }
1777
+ async fetchPositions(symbols = undefined, params = {}) {
1778
+ /**
1779
+ * @method
1780
+ * @name blofin#fetchPosition
1781
+ * @description fetch data on a single open contract trade position
1782
+ * @see https://blofin.com/docs#get-positions
1783
+ * @param {string[]} [symbols] list of unified market symbols
1784
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1785
+ * @param {string} [params.instType] MARGIN, SWAP, FUTURES, OPTION
1786
+ * @returns {object} a [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
1787
+ */
1788
+ await this.loadMarkets();
1789
+ symbols = this.marketSymbols(symbols);
1790
+ const response = await this.privateGetAccountPositions(params);
1791
+ const data = this.safeList(response, 'data', []);
1792
+ const result = this.parsePositions(data);
1793
+ return this.filterByArrayPositions(result, 'symbol', symbols, false);
1794
+ }
1795
+ parsePosition(position, market = undefined) {
1796
+ const marketId = this.safeString(position, 'instId');
1797
+ market = this.safeMarket(marketId, market);
1798
+ const symbol = market['symbol'];
1799
+ const pos = this.safeString(position, 'positions');
1800
+ const contractsAbs = Precise.stringAbs(pos);
1801
+ let side = this.safeString(position, 'positionSide');
1802
+ const hedged = side !== 'net';
1803
+ const contracts = this.parseNumber(contractsAbs);
1804
+ if (pos !== undefined) {
1805
+ if (side === 'net') {
1806
+ if (Precise.stringGt(pos, '0')) {
1807
+ side = 'long';
1808
+ }
1809
+ else if (Precise.stringLt(pos, '0')) {
1810
+ side = 'short';
1811
+ }
1812
+ else {
1813
+ side = undefined;
1814
+ }
1815
+ }
1816
+ }
1817
+ const contractSize = this.safeNumber(market, 'contractSize');
1818
+ const contractSizeString = this.numberToString(contractSize);
1819
+ const markPriceString = this.safeString(position, 'markPrice');
1820
+ let notionalString = this.safeString(position, 'notionalUsd');
1821
+ if (market['inverse']) {
1822
+ notionalString = Precise.stringDiv(Precise.stringMul(contractsAbs, contractSizeString), markPriceString);
1823
+ }
1824
+ const notional = this.parseNumber(notionalString);
1825
+ const marginMode = this.safeString(position, 'marginMode');
1826
+ let initialMarginString = undefined;
1827
+ const entryPriceString = this.safeString(position, 'averagePrice');
1828
+ const unrealizedPnlString = this.safeString(position, 'unrealizedPnl');
1829
+ const leverageString = this.safeString(position, 'leverage');
1830
+ let initialMarginPercentage = undefined;
1831
+ let collateralString = undefined;
1832
+ if (marginMode === 'cross') {
1833
+ initialMarginString = this.safeString(position, 'initialMargin');
1834
+ collateralString = Precise.stringAdd(initialMarginString, unrealizedPnlString);
1835
+ }
1836
+ else if (marginMode === 'isolated') {
1837
+ initialMarginPercentage = Precise.stringDiv('1', leverageString);
1838
+ collateralString = this.safeString(position, 'margin');
1839
+ }
1840
+ const maintenanceMarginString = this.safeString(position, 'maintenanceMargin');
1841
+ const maintenanceMargin = this.parseNumber(maintenanceMarginString);
1842
+ const maintenanceMarginPercentageString = Precise.stringDiv(maintenanceMarginString, notionalString);
1843
+ if (initialMarginPercentage === undefined) {
1844
+ initialMarginPercentage = this.parseNumber(Precise.stringDiv(initialMarginString, notionalString, 4));
1845
+ }
1846
+ else if (initialMarginString === undefined) {
1847
+ initialMarginString = Precise.stringMul(initialMarginPercentage, notionalString);
1848
+ }
1849
+ const rounder = '0.00005'; // round to closest 0.01%
1850
+ const maintenanceMarginPercentage = this.parseNumber(Precise.stringDiv(Precise.stringAdd(maintenanceMarginPercentageString, rounder), '1', 4));
1851
+ const liquidationPrice = this.safeNumber(position, 'liquidationPrice');
1852
+ const percentageString = this.safeString(position, 'unrealizedPnlRatio');
1853
+ const percentage = this.parseNumber(Precise.stringMul(percentageString, '100'));
1854
+ const timestamp = this.safeInteger(position, 'updateTime');
1855
+ const marginRatio = this.parseNumber(Precise.stringDiv(maintenanceMarginString, collateralString, 4));
1856
+ return this.safePosition({
1857
+ 'info': position,
1858
+ 'id': undefined,
1859
+ 'symbol': symbol,
1860
+ 'notional': notional,
1861
+ 'marginMode': marginMode,
1862
+ 'liquidationPrice': liquidationPrice,
1863
+ 'entryPrice': this.parseNumber(entryPriceString),
1864
+ 'unrealizedPnl': this.parseNumber(unrealizedPnlString),
1865
+ 'percentage': percentage,
1866
+ 'contracts': contracts,
1867
+ 'contractSize': contractSize,
1868
+ 'markPrice': this.parseNumber(markPriceString),
1869
+ 'lastPrice': undefined,
1870
+ 'side': side,
1871
+ 'hedged': hedged,
1872
+ 'timestamp': timestamp,
1873
+ 'datetime': this.iso8601(timestamp),
1874
+ 'lastUpdateTimestamp': undefined,
1875
+ 'maintenanceMargin': maintenanceMargin,
1876
+ 'maintenanceMarginPercentage': maintenanceMarginPercentage,
1877
+ 'collateral': this.parseNumber(collateralString),
1878
+ 'initialMargin': this.parseNumber(initialMarginString),
1879
+ 'initialMarginPercentage': this.parseNumber(initialMarginPercentage),
1880
+ 'leverage': this.parseNumber(leverageString),
1881
+ 'marginRatio': marginRatio,
1882
+ 'stopLossPrice': undefined,
1883
+ 'takeProfitPrice': undefined,
1884
+ });
1885
+ }
1886
+ async fetchLeverage(symbol, params = {}) {
1887
+ /**
1888
+ * @method
1889
+ * @name blofin#fetchLeverage
1890
+ * @description fetch the set leverage for a market
1891
+ * @see https://blofin.com/docs#set-leverage
1892
+ * @param {string} symbol unified market symbol
1893
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1894
+ * @param {string} [params.marginMode] 'cross' or 'isolated'
1895
+ * @returns {object} a [leverage structure]{@link https://docs.ccxt.com/#/?id=leverage-structure}
1896
+ */
1897
+ await this.loadMarkets();
1898
+ let marginMode = undefined;
1899
+ [marginMode, params] = this.handleMarginModeAndParams('fetchLeverage', params);
1900
+ if (marginMode === undefined) {
1901
+ marginMode = this.safeString(params, 'marginMode', 'cross'); // cross as default marginMode
1902
+ }
1903
+ if ((marginMode !== 'cross') && (marginMode !== 'isolated')) {
1904
+ throw new BadRequest(this.id + ' fetchLeverage() requires a marginMode parameter that must be either cross or isolated');
1905
+ }
1906
+ const market = this.market(symbol);
1907
+ const request = {
1908
+ 'instId': market['id'],
1909
+ 'marginMode': marginMode,
1910
+ };
1911
+ const response = await this.privateGetAccountLeverageInfo(this.extend(request, params));
1912
+ return response;
1913
+ }
1914
+ async setLeverage(leverage, symbol = undefined, params = {}) {
1915
+ /**
1916
+ * @method
1917
+ * @name blofin#setLeverage
1918
+ * @description set the level of leverage for a market
1919
+ * @see https://blofin.com/docs#set-leverage
1920
+ * @param {int} leverage the rate of leverage
1921
+ * @param {string} symbol unified market symbol
1922
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1923
+ * @param {string} [params.marginMode] 'cross' or 'isolated'
1924
+ * @returns {object} response from the exchange
1925
+ */
1926
+ if (symbol === undefined) {
1927
+ throw new ArgumentsRequired(this.id + ' setLeverage() requires a symbol argument');
1928
+ }
1929
+ // WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
1930
+ // AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
1931
+ if ((leverage < 1) || (leverage > 125)) {
1932
+ throw new BadRequest(this.id + ' setLeverage() leverage should be between 1 and 125');
1933
+ }
1934
+ await this.loadMarkets();
1935
+ const market = this.market(symbol);
1936
+ let marginMode = undefined;
1937
+ [marginMode, params] = this.handleMarginModeAndParams('setLeverage', params, 'cross');
1938
+ if ((marginMode !== 'cross') && (marginMode !== 'isolated')) {
1939
+ throw new BadRequest(this.id + ' setLeverage() requires a marginMode parameter that must be either cross or isolated');
1940
+ }
1941
+ const request = {
1942
+ 'leverage': leverage,
1943
+ 'marginMode': marginMode,
1944
+ 'instId': market['id'],
1945
+ };
1946
+ const response = await this.privatePostAccountSetLeverage(this.extend(request, params));
1947
+ return response;
1948
+ }
1949
+ async closePosition(symbol, side = undefined, params = {}) {
1950
+ /**
1951
+ * @method
1952
+ * @name blofin#closePosition
1953
+ * @description closes open positions for a market
1954
+ * @see https://blofin.com/docs#close-positions
1955
+ * @param {string} symbol Unified CCXT market symbol
1956
+ * @param {string} [side] 'buy' or 'sell', leave as undefined in net mode
1957
+ * @param {object} [params] extra parameters specific to the blofin api endpoint
1958
+ * @param {string} [params.clientOrderId] a unique identifier for the order
1959
+ * @param {string} [params.marginMode] 'cross' or 'isolated', default is 'cross;
1960
+ * @param {string} [params.code] *required in the case of closing cross MARGIN position for Single-currency margin* margin currency
1961
+ *
1962
+ * EXCHANGE SPECIFIC PARAMETERS
1963
+ * @param {boolean} [params.autoCxl] whether any pending orders for closing out needs to be automatically canceled when close position via a market order. false or true, the default is false
1964
+ * @param {string} [params.tag] order tag a combination of case-sensitive alphanumerics, all numbers, or all letters of up to 16 characters
1965
+ * @returns {object[]} [A list of position structures]{@link https://docs.ccxt.com/#/?id=position-structure}
1966
+ */
1967
+ await this.loadMarkets();
1968
+ const market = this.market(symbol);
1969
+ const clientOrderId = this.safeString(params, 'clientOrderId');
1970
+ let marginMode = undefined;
1971
+ [marginMode, params] = this.handleMarginModeAndParams('closePosition', params, 'cross');
1972
+ const request = {
1973
+ 'instId': market['id'],
1974
+ 'marginMode': marginMode,
1975
+ };
1976
+ if (clientOrderId !== undefined) {
1977
+ request['clientOrderId'] = clientOrderId;
1978
+ }
1979
+ const response = await this.privatePostTradeClosePosition(this.extend(request, params));
1980
+ return this.safeValue(response, 'data');
1981
+ }
1982
+ async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1983
+ /**
1984
+ * @method
1985
+ * @name blofin#fetchClosedOrders
1986
+ * @description fetches information on multiple closed orders made by the user
1987
+ * @see https://blofin.com/docs#get-order-history
1988
+ * @see https://blofin.com/docs#get-tpsl-order-history
1989
+ * @param {string} symbol unified market symbol of the market orders were made in
1990
+ * @param {int} [since] the earliest time in ms to fetch orders for
1991
+ * @param {int} [limit] the maximum number of orde structures to retrieve
1992
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1993
+ * @param {bool} [params.stop] True if fetching trigger or conditional orders
1994
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1995
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1996
+ */
1997
+ await this.loadMarkets();
1998
+ let paginate = false;
1999
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchClosedOrders', 'paginate');
2000
+ if (paginate) {
2001
+ return await this.fetchPaginatedCallDynamic('fetchClosedOrders', symbol, since, limit, params);
2002
+ }
2003
+ const request = {};
2004
+ let market = undefined;
2005
+ if (symbol !== undefined) {
2006
+ market = this.market(symbol);
2007
+ request['instId'] = market['id'];
2008
+ }
2009
+ if (limit !== undefined) {
2010
+ request['limit'] = limit; // default 100, max 100
2011
+ }
2012
+ if (since !== undefined) {
2013
+ request['begin'] = since;
2014
+ }
2015
+ const isStop = this.safeValueN(params, ['stop', 'trigger', 'tpsl', 'TPSL'], false);
2016
+ let method = undefined;
2017
+ [method, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'method', 'privateGetTradeOrdersHistory');
2018
+ const query = this.omit(params, ['method', 'stop', 'trigger', 'tpsl', 'TPSL']);
2019
+ let response = undefined;
2020
+ if ((isStop) || (method === 'privateGetTradeOrdersTpslHistory')) {
2021
+ response = await this.privateGetTradeOrdersTpslHistory(this.extend(request, query));
2022
+ }
2023
+ else {
2024
+ response = await this.privateGetTradeOrdersHistory(this.extend(request, query));
2025
+ }
2026
+ const data = this.safeList(response, 'data', []);
2027
+ return this.parseOrders(data, market, since, limit);
2028
+ }
2029
+ handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
2030
+ if (response === undefined) {
2031
+ return undefined; // fallback to default error handler
2032
+ }
2033
+ //
2034
+ // {"code":"152002","msg":"Parameter bar error."}
2035
+ //
2036
+ const code = this.safeString(response, 'code');
2037
+ const message = this.safeString(response, 'msg');
2038
+ const feedback = this.id + ' ' + body;
2039
+ if (code !== undefined && code !== '0') {
2040
+ this.throwExactlyMatchedException(this.exceptions['exact'], message, feedback);
2041
+ this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);
2042
+ this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
2043
+ throw new ExchangeError(feedback); // unknown message
2044
+ }
2045
+ //
2046
+ // {
2047
+ // orderId: null,
2048
+ // clientOrderId: '',
2049
+ // msg: 'Order failed. Insufficient USDT margin in account',
2050
+ // code: '103003'
2051
+ // }
2052
+ //
2053
+ const data = this.safeList(response, 'data');
2054
+ const first = this.safeDict(data, 0);
2055
+ const insideMsg = this.safeString(first, 'msg');
2056
+ const insideCode = this.safeString(first, 'code');
2057
+ if (insideCode !== undefined && insideCode !== '0') {
2058
+ this.throwExactlyMatchedException(this.exceptions['exact'], insideCode, feedback);
2059
+ this.throwExactlyMatchedException(this.exceptions['exact'], insideMsg, feedback);
2060
+ this.throwBroadlyMatchedException(this.exceptions['broad'], insideMsg, feedback);
2061
+ }
2062
+ return undefined;
2063
+ }
2064
+ sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
2065
+ let request = '/api/' + this.version + '/' + this.implodeParams(path, params);
2066
+ const query = this.omit(params, this.extractParams(path));
2067
+ let url = this.implodeHostname(this.urls['api']['rest']) + request;
2068
+ // const type = this.getPathAuthenticationType (path);
2069
+ if (api === 'public') {
2070
+ if (!this.isEmpty(query)) {
2071
+ url += '?' + this.urlencode(query);
2072
+ }
2073
+ }
2074
+ else if (api === 'private') {
2075
+ this.checkRequiredCredentials();
2076
+ const timestamp = this.milliseconds().toString();
2077
+ headers = {
2078
+ 'ACCESS-KEY': this.apiKey,
2079
+ 'ACCESS-PASSPHRASE': this.password,
2080
+ 'ACCESS-TIMESTAMP': timestamp,
2081
+ 'ACCESS-NONCE': timestamp,
2082
+ };
2083
+ let sign_body = '';
2084
+ if (method === 'GET') {
2085
+ if (!this.isEmpty(query)) {
2086
+ const urlencodedQuery = '?' + this.urlencode(query);
2087
+ url += urlencodedQuery;
2088
+ request += urlencodedQuery;
2089
+ }
2090
+ }
2091
+ else {
2092
+ if (!this.isEmpty(query)) {
2093
+ body = this.json(query);
2094
+ sign_body = body;
2095
+ }
2096
+ headers['Content-Type'] = 'application/json';
2097
+ }
2098
+ const auth = request + method + timestamp + timestamp + sign_body;
2099
+ const signature = this.stringToBase64(this.hmac(this.encode(auth), this.encode(this.secret), sha256));
2100
+ headers['ACCESS-SIGN'] = signature;
2101
+ }
2102
+ return { 'url': url, 'method': method, 'body': body, 'headers': headers };
2103
+ }
2104
+ }