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