ccxt 4.4.36 → 4.4.37

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 (38) hide show
  1. package/README.md +7 -7
  2. package/dist/ccxt.browser.min.js +2 -2
  3. package/dist/cjs/ccxt.js +6 -6
  4. package/dist/cjs/src/abstract/bitfinex1.js +9 -0
  5. package/dist/cjs/src/base/Exchange.js +6 -1
  6. package/dist/cjs/src/bitfinex.js +3178 -1161
  7. package/dist/cjs/src/bitfinex1.js +1760 -0
  8. package/dist/cjs/src/coinbase.js +87 -0
  9. package/dist/cjs/src/gate.js +1 -1
  10. package/dist/cjs/src/hyperliquid.js +125 -14
  11. package/dist/cjs/src/paradex.js +4 -2
  12. package/dist/cjs/src/pro/bitfinex.js +760 -271
  13. package/dist/cjs/src/pro/bitfinex1.js +675 -0
  14. package/dist/cjs/src/pro/probit.js +1 -0
  15. package/js/ccxt.d.ts +8 -8
  16. package/js/ccxt.js +6 -6
  17. package/js/src/abstract/bitfinex.d.ts +135 -64
  18. package/js/src/abstract/bitfinex1.d.ts +72 -0
  19. package/js/src/base/Exchange.js +6 -1
  20. package/js/src/bitfinex.d.ts +316 -106
  21. package/js/src/bitfinex.js +3179 -1162
  22. package/js/src/bitfinex1.d.ts +296 -0
  23. package/js/src/bitfinex1.js +1761 -0
  24. package/js/src/coinbase.d.ts +33 -0
  25. package/js/src/coinbase.js +87 -0
  26. package/js/src/gate.js +1 -1
  27. package/js/src/hyperliquid.d.ts +6 -1
  28. package/js/src/hyperliquid.js +125 -14
  29. package/js/src/paradex.d.ts +2 -0
  30. package/js/src/paradex.js +4 -2
  31. package/js/src/pro/bitfinex.d.ts +42 -10
  32. package/js/src/pro/bitfinex.js +761 -272
  33. package/js/src/pro/bitfinex1.d.ts +67 -0
  34. package/js/src/pro/bitfinex1.js +676 -0
  35. package/js/src/pro/probit.js +1 -0
  36. package/package.json +1 -1
  37. package/js/src/abstract/bitfinex2.d.ts +0 -143
  38. /package/js/src/abstract/{bitfinex2.js → bitfinex1.js} +0 -0
@@ -0,0 +1,1760 @@
1
+ 'use strict';
2
+
3
+ var bitfinex1$1 = require('./abstract/bitfinex1.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 sha512 = require('./static_dependencies/noble-hashes/sha512.js');
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * @class bitfinex1
13
+ * @augments Exchange
14
+ */
15
+ class bitfinex1 extends bitfinex1$1 {
16
+ describe() {
17
+ return this.deepExtend(super.describe(), {
18
+ 'id': 'bitfinex1',
19
+ 'name': 'Bitfinex',
20
+ 'countries': ['VG'],
21
+ 'version': 'v1',
22
+ // cheapest is 90 requests a minute = 1.5 requests per second on average => ( 1000ms / 1.5) = 666.666 ms between requests on average
23
+ 'rateLimit': 666.666,
24
+ 'pro': true,
25
+ // new metainfo interface
26
+ 'has': {
27
+ 'CORS': undefined,
28
+ 'spot': true,
29
+ 'margin': undefined,
30
+ 'swap': undefined,
31
+ 'future': undefined,
32
+ 'option': undefined,
33
+ 'cancelAllOrders': true,
34
+ 'cancelOrder': true,
35
+ 'createDepositAddress': true,
36
+ 'createOrder': true,
37
+ 'editOrder': true,
38
+ 'fetchBalance': true,
39
+ 'fetchClosedOrders': true,
40
+ 'fetchDepositAddress': true,
41
+ 'fetchDepositAddresses': false,
42
+ 'fetchDepositAddressesByNetwork': false,
43
+ 'fetchDeposits': false,
44
+ 'fetchDepositsWithdrawals': true,
45
+ 'fetchDepositWithdrawFee': 'emulated',
46
+ 'fetchDepositWithdrawFees': true,
47
+ 'fetchFundingHistory': false,
48
+ 'fetchFundingRate': false,
49
+ 'fetchFundingRateHistory': false,
50
+ 'fetchFundingRates': false,
51
+ 'fetchIndexOHLCV': false,
52
+ 'fetchLeverageTiers': false,
53
+ 'fetchMarginMode': false,
54
+ 'fetchMarkets': true,
55
+ 'fetchMarkOHLCV': false,
56
+ 'fetchMyTrades': true,
57
+ 'fetchOHLCV': true,
58
+ 'fetchOpenOrders': true,
59
+ 'fetchOrder': true,
60
+ 'fetchOrderBook': true,
61
+ 'fetchPositionMode': false,
62
+ 'fetchPositions': true,
63
+ 'fetchPremiumIndexOHLCV': false,
64
+ 'fetchTicker': true,
65
+ 'fetchTickers': true,
66
+ 'fetchTime': false,
67
+ 'fetchTrades': true,
68
+ 'fetchTradingFee': false,
69
+ 'fetchTradingFees': true,
70
+ 'fetchTransactionFees': true,
71
+ 'fetchTransactions': 'emulated',
72
+ 'transfer': true,
73
+ 'withdraw': true,
74
+ },
75
+ 'timeframes': {
76
+ '1m': '1m',
77
+ '5m': '5m',
78
+ '15m': '15m',
79
+ '30m': '30m',
80
+ '1h': '1h',
81
+ '3h': '3h',
82
+ '4h': '4h',
83
+ '6h': '6h',
84
+ '12h': '12h',
85
+ '1d': '1D',
86
+ '1w': '7D',
87
+ '2w': '14D',
88
+ '1M': '1M',
89
+ },
90
+ 'urls': {
91
+ 'logo': 'https://github.com/user-attachments/assets/9147c6c5-7197-481e-827b-7483672bb0e9',
92
+ 'api': {
93
+ 'v2': 'https://api-pub.bitfinex.com',
94
+ 'public': 'https://api.bitfinex.com',
95
+ 'private': 'https://api.bitfinex.com',
96
+ },
97
+ 'www': 'https://www.bitfinex.com',
98
+ 'referral': 'https://www.bitfinex.com/?refcode=P61eYxFL',
99
+ 'doc': [
100
+ 'https://docs.bitfinex.com/v1/docs',
101
+ 'https://github.com/bitfinexcom/bitfinex-api-node',
102
+ ],
103
+ },
104
+ 'api': {
105
+ // v2 symbol ids require a 't' prefix
106
+ // just the public part of it (use bitfinex2 for everything else)
107
+ 'v2': {
108
+ 'get': {
109
+ 'platform/status': 3,
110
+ 'tickers': 1,
111
+ 'ticker/{symbol}': 1,
112
+ 'tickers/hist': 1,
113
+ 'trades/{symbol}/hist': 1,
114
+ 'book/{symbol}/{precision}': 0.375,
115
+ 'book/{symbol}/P0': 0.375,
116
+ 'book/{symbol}/P1': 0.375,
117
+ 'book/{symbol}/P2': 0.375,
118
+ 'book/{symbol}/P3': 0.375,
119
+ 'book/{symbol}/R0': 0.375,
120
+ 'stats1/{key}:{size}:{symbol}:{side}/{section}': 1,
121
+ 'stats1/{key}:{size}:{symbol}/{section}': 1,
122
+ 'stats1/{key}:{size}:{symbol}:long/last': 1,
123
+ 'stats1/{key}:{size}:{symbol}:long/hist': 1,
124
+ 'stats1/{key}:{size}:{symbol}:short/last': 1,
125
+ 'stats1/{key}:{size}:{symbol}:short/hist': 1,
126
+ 'candles/trade:{timeframe}:{symbol}/{section}': 1,
127
+ 'candles/trade:{timeframe}:{symbol}/last': 1,
128
+ 'candles/trade:{timeframe}:{symbol}/hist': 1,
129
+ },
130
+ },
131
+ 'public': {
132
+ 'get': {
133
+ 'book/{symbol}': 1,
134
+ // 'candles/{symbol}':0,
135
+ 'lendbook/{currency}': 6,
136
+ 'lends/{currency}': 3,
137
+ 'pubticker/{symbol}': 3,
138
+ 'stats/{symbol}': 6,
139
+ 'symbols': 18,
140
+ 'symbols_details': 18,
141
+ 'tickers': 1,
142
+ 'trades/{symbol}': 3, // 60 requests a minute = 1 request per second => (1000ms / rateLimit) / 1 = 1.5 ... but only works if set to 3
143
+ },
144
+ },
145
+ 'private': {
146
+ 'post': {
147
+ 'account_fees': 18,
148
+ 'account_infos': 6,
149
+ 'balances': 9.036,
150
+ 'basket_manage': 6,
151
+ 'credits': 6,
152
+ 'deposit/new': 18,
153
+ 'funding/close': 6,
154
+ 'history': 6,
155
+ 'history/movements': 6,
156
+ 'key_info': 6,
157
+ 'margin_infos': 3,
158
+ 'mytrades': 3,
159
+ 'mytrades_funding': 6,
160
+ 'offer/cancel': 6,
161
+ 'offer/new': 6,
162
+ 'offer/status': 6,
163
+ 'offers': 6,
164
+ 'offers/hist': 90.03,
165
+ 'order/cancel': 0.2,
166
+ 'order/cancel/all': 0.2,
167
+ 'order/cancel/multi': 0.2,
168
+ 'order/cancel/replace': 0.2,
169
+ 'order/new': 0.2,
170
+ 'order/new/multi': 0.2,
171
+ 'order/status': 0.2,
172
+ 'orders': 0.2,
173
+ 'orders/hist': 90.03,
174
+ 'position/claim': 18,
175
+ 'position/close': 18,
176
+ 'positions': 18,
177
+ 'summary': 18,
178
+ 'taken_funds': 6,
179
+ 'total_taken_funds': 6,
180
+ 'transfer': 18,
181
+ 'unused_taken_funds': 6,
182
+ 'withdraw': 18,
183
+ },
184
+ },
185
+ },
186
+ 'fees': {
187
+ 'trading': {
188
+ 'feeSide': 'get',
189
+ 'tierBased': true,
190
+ 'percentage': true,
191
+ 'maker': this.parseNumber('0.001'),
192
+ 'taker': this.parseNumber('0.002'),
193
+ 'tiers': {
194
+ 'taker': [
195
+ [this.parseNumber('0'), this.parseNumber('0.002')],
196
+ [this.parseNumber('500000'), this.parseNumber('0.002')],
197
+ [this.parseNumber('1000000'), this.parseNumber('0.002')],
198
+ [this.parseNumber('2500000'), this.parseNumber('0.002')],
199
+ [this.parseNumber('5000000'), this.parseNumber('0.002')],
200
+ [this.parseNumber('7500000'), this.parseNumber('0.002')],
201
+ [this.parseNumber('10000000'), this.parseNumber('0.0018')],
202
+ [this.parseNumber('15000000'), this.parseNumber('0.0016')],
203
+ [this.parseNumber('20000000'), this.parseNumber('0.0014')],
204
+ [this.parseNumber('25000000'), this.parseNumber('0.0012')],
205
+ [this.parseNumber('30000000'), this.parseNumber('0.001')],
206
+ ],
207
+ 'maker': [
208
+ [this.parseNumber('0'), this.parseNumber('0.001')],
209
+ [this.parseNumber('500000'), this.parseNumber('0.0008')],
210
+ [this.parseNumber('1000000'), this.parseNumber('0.0006')],
211
+ [this.parseNumber('2500000'), this.parseNumber('0.0004')],
212
+ [this.parseNumber('5000000'), this.parseNumber('0.0002')],
213
+ [this.parseNumber('7500000'), this.parseNumber('0')],
214
+ [this.parseNumber('10000000'), this.parseNumber('0')],
215
+ [this.parseNumber('15000000'), this.parseNumber('0')],
216
+ [this.parseNumber('20000000'), this.parseNumber('0')],
217
+ [this.parseNumber('25000000'), this.parseNumber('0')],
218
+ [this.parseNumber('30000000'), this.parseNumber('0')],
219
+ ],
220
+ },
221
+ },
222
+ 'funding': {
223
+ 'tierBased': false,
224
+ 'percentage': false,
225
+ // Actually deposit fees are free for larger deposits (> $1000 USD equivalent)
226
+ // these values below are deprecated, we should not hardcode fees and limits anymore
227
+ // to be reimplemented with bitfinex funding fees from their API or web endpoints
228
+ 'deposit': {},
229
+ 'withdraw': {},
230
+ },
231
+ },
232
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
233
+ 'commonCurrencies': {
234
+ 'ALG': 'ALGO',
235
+ 'AMP': 'AMPL',
236
+ 'ATO': 'ATOM',
237
+ 'BCHABC': 'XEC',
238
+ 'BCHN': 'BCH',
239
+ 'DAT': 'DATA',
240
+ 'DOG': 'MDOGE',
241
+ 'DSH': 'DASH',
242
+ // https://github.com/ccxt/ccxt/issues/7399
243
+ // https://coinmarketcap.com/currencies/pnetwork/
244
+ // https://en.cryptonomist.ch/blog/eidoo/the-edo-to-pnt-upgrade-what-you-need-to-know-updated/
245
+ 'EDO': 'PNT',
246
+ 'EUS': 'EURS',
247
+ 'EUT': 'EURT',
248
+ 'IDX': 'ID',
249
+ 'IOT': 'IOTA',
250
+ 'IQX': 'IQ',
251
+ 'LUNA': 'LUNC',
252
+ 'LUNA2': 'LUNA',
253
+ 'MNA': 'MANA',
254
+ 'ORS': 'ORS Group',
255
+ 'PAS': 'PASS',
256
+ 'QSH': 'QASH',
257
+ 'QTM': 'QTUM',
258
+ 'RBT': 'RBTC',
259
+ 'SNG': 'SNGLS',
260
+ 'STJ': 'STORJ',
261
+ 'TERRAUST': 'USTC',
262
+ 'TSD': 'TUSD',
263
+ 'YGG': 'YEED',
264
+ 'YYW': 'YOYOW',
265
+ 'UDC': 'USDC',
266
+ 'UST': 'USDT',
267
+ 'VSY': 'VSYS',
268
+ 'WAX': 'WAXP',
269
+ 'XCH': 'XCHF',
270
+ 'ZBT': 'ZB',
271
+ },
272
+ 'exceptions': {
273
+ 'exact': {
274
+ 'temporarily_unavailable': errors.ExchangeNotAvailable,
275
+ 'Order could not be cancelled.': errors.OrderNotFound,
276
+ 'No such order found.': errors.OrderNotFound,
277
+ 'Order price must be positive.': errors.InvalidOrder,
278
+ 'Could not find a key matching the given X-BFX-APIKEY.': errors.AuthenticationError,
279
+ 'Key price should be a decimal number, e.g. "123.456"': errors.InvalidOrder,
280
+ 'Key amount should be a decimal number, e.g. "123.456"': errors.InvalidOrder,
281
+ 'ERR_RATE_LIMIT': errors.RateLimitExceeded,
282
+ 'Ratelimit': errors.RateLimitExceeded,
283
+ 'Nonce is too small.': errors.InvalidNonce,
284
+ 'No summary found.': errors.ExchangeError,
285
+ 'Cannot evaluate your available balance, please try again': errors.ExchangeNotAvailable,
286
+ 'Unknown symbol': errors.BadSymbol,
287
+ 'Cannot complete transfer. Exchange balance insufficient.': errors.InsufficientFunds,
288
+ 'Momentary balance check. Please wait few seconds and try the transfer again.': errors.ExchangeError,
289
+ },
290
+ 'broad': {
291
+ 'Invalid X-BFX-SIGNATURE': errors.AuthenticationError,
292
+ 'This API key does not have permission': errors.PermissionDenied,
293
+ 'not enough exchange balance for ': errors.InsufficientFunds,
294
+ 'minimum size for ': errors.InvalidOrder,
295
+ 'Invalid order': errors.InvalidOrder,
296
+ 'The available balance is only': errors.InsufficientFunds, // {"status":"error","message":"Cannot withdraw 1.0027 ETH from your exchange wallet. The available balance is only 0.0 ETH. If you have limit orders, open positions, unused or active margin funding, this will decrease your available balance. To increase it, you can cancel limit orders or reduce/close your positions.","withdrawal_id":0,"fees":"0.0027"}
297
+ },
298
+ },
299
+ 'precisionMode': number.SIGNIFICANT_DIGITS,
300
+ 'options': {
301
+ 'currencyNames': {
302
+ 'AGI': 'agi',
303
+ 'AID': 'aid',
304
+ 'AIO': 'aio',
305
+ 'ANT': 'ant',
306
+ 'AVT': 'aventus',
307
+ 'BAT': 'bat',
308
+ // https://github.com/ccxt/ccxt/issues/5833
309
+ 'BCH': 'bab',
310
+ // 'BCH': 'bcash', // undocumented
311
+ 'BCI': 'bci',
312
+ 'BFT': 'bft',
313
+ 'BSV': 'bsv',
314
+ 'BTC': 'bitcoin',
315
+ 'BTG': 'bgold',
316
+ 'CFI': 'cfi',
317
+ 'COMP': 'comp',
318
+ 'DAI': 'dai',
319
+ 'DADI': 'dad',
320
+ 'DASH': 'dash',
321
+ 'DATA': 'datacoin',
322
+ 'DTH': 'dth',
323
+ 'EDO': 'eidoo',
324
+ 'ELF': 'elf',
325
+ 'EOS': 'eos',
326
+ 'ETC': 'ethereumc',
327
+ 'ETH': 'ethereum',
328
+ 'ETP': 'metaverse',
329
+ 'FUN': 'fun',
330
+ 'GNT': 'golem',
331
+ 'IOST': 'ios',
332
+ 'IOTA': 'iota',
333
+ // https://github.com/ccxt/ccxt/issues/5833
334
+ 'LEO': 'let',
335
+ // 'LEO': 'les', // EOS chain
336
+ 'LINK': 'link',
337
+ 'LRC': 'lrc',
338
+ 'LTC': 'litecoin',
339
+ 'LYM': 'lym',
340
+ 'MANA': 'mna',
341
+ 'MIT': 'mit',
342
+ 'MKR': 'mkr',
343
+ 'MTN': 'mtn',
344
+ 'NEO': 'neo',
345
+ 'ODE': 'ode',
346
+ 'OMG': 'omisego',
347
+ 'OMNI': 'mastercoin',
348
+ 'QASH': 'qash',
349
+ 'QTUM': 'qtum',
350
+ 'RCN': 'rcn',
351
+ 'RDN': 'rdn',
352
+ 'REP': 'rep',
353
+ 'REQ': 'req',
354
+ 'RLC': 'rlc',
355
+ 'SAN': 'santiment',
356
+ 'SNGLS': 'sng',
357
+ 'SNT': 'status',
358
+ 'SPANK': 'spk',
359
+ 'STORJ': 'stj',
360
+ 'TNB': 'tnb',
361
+ 'TRX': 'trx',
362
+ 'TUSD': 'tsd',
363
+ 'USD': 'wire',
364
+ 'USDC': 'udc',
365
+ 'UTK': 'utk',
366
+ 'USDT': 'tetheruso',
367
+ // 'USDT': 'tetheruse', // Tether on ERC20
368
+ // 'USDT': 'tetherusl', // Tether on Liquid
369
+ // 'USDT': 'tetherusx', // Tether on Tron
370
+ // 'USDT': 'tetheruss', // Tether on EOS
371
+ 'VEE': 'vee',
372
+ 'WAX': 'wax',
373
+ 'XLM': 'xlm',
374
+ 'XMR': 'monero',
375
+ 'XRP': 'ripple',
376
+ 'XVG': 'xvg',
377
+ 'YOYOW': 'yoyow',
378
+ 'ZEC': 'zcash',
379
+ 'ZRX': 'zrx',
380
+ 'XTZ': 'xtz',
381
+ },
382
+ 'orderTypes': {
383
+ 'limit': 'exchange limit',
384
+ 'market': 'exchange market',
385
+ },
386
+ 'fiat': {
387
+ 'USD': 'USD',
388
+ 'EUR': 'EUR',
389
+ 'JPY': 'JPY',
390
+ 'GBP': 'GBP',
391
+ 'CNH': 'CNH',
392
+ },
393
+ 'accountsByType': {
394
+ 'spot': 'exchange',
395
+ 'margin': 'trading',
396
+ 'funding': 'deposit',
397
+ 'swap': 'trading',
398
+ },
399
+ },
400
+ });
401
+ }
402
+ /**
403
+ * @method
404
+ * @name bitfinex#fetchTransactionFees
405
+ * @deprecated
406
+ * @description please use fetchDepositWithdrawFees instead
407
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-fees
408
+ * @param {string[]|undefined} codes list of unified currency codes
409
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
410
+ * @returns {object[]} a list of [fees structures]{@link https://docs.ccxt.com/#/?id=fee-structure}
411
+ */
412
+ async fetchTransactionFees(codes = undefined, params = {}) {
413
+ await this.loadMarkets();
414
+ const result = {};
415
+ const response = await this.privatePostAccountFees(params);
416
+ //
417
+ // {
418
+ // "withdraw": {
419
+ // "BTC": "0.0004",
420
+ // }
421
+ // }
422
+ //
423
+ const fees = this.safeDict(response, 'withdraw', {});
424
+ const ids = Object.keys(fees);
425
+ for (let i = 0; i < ids.length; i++) {
426
+ const id = ids[i];
427
+ const code = this.safeCurrencyCode(id);
428
+ if ((codes !== undefined) && !this.inArray(code, codes)) {
429
+ continue;
430
+ }
431
+ result[code] = {
432
+ 'withdraw': this.safeNumber(fees, id),
433
+ 'deposit': {},
434
+ 'info': this.safeNumber(fees, id),
435
+ };
436
+ }
437
+ return result;
438
+ }
439
+ /**
440
+ * @method
441
+ * @name bitfinex#fetchDepositWithdrawFees
442
+ * @description fetch deposit and withdraw fees
443
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-fees
444
+ * @param {string[]|undefined} codes list of unified currency codes
445
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
446
+ * @returns {object[]} a list of [fees structures]{@link https://docs.ccxt.com/#/?id=fee-structure}
447
+ */
448
+ async fetchDepositWithdrawFees(codes = undefined, params = {}) {
449
+ await this.loadMarkets();
450
+ const response = await this.privatePostAccountFees(params);
451
+ //
452
+ // {
453
+ // "withdraw": {
454
+ // "BTC": "0.0004",
455
+ // ...
456
+ // }
457
+ // }
458
+ //
459
+ const withdraw = this.safeList(response, 'withdraw');
460
+ return this.parseDepositWithdrawFees(withdraw, codes);
461
+ }
462
+ parseDepositWithdrawFee(fee, currency = undefined) {
463
+ //
464
+ // '0.0004'
465
+ //
466
+ return {
467
+ 'withdraw': {
468
+ 'fee': this.parseNumber(fee),
469
+ 'percentage': undefined,
470
+ },
471
+ 'deposit': {
472
+ 'fee': undefined,
473
+ 'percentage': undefined,
474
+ },
475
+ 'networks': {},
476
+ 'info': fee,
477
+ };
478
+ }
479
+ /**
480
+ * @method
481
+ * @name bitfinex#fetchTradingFees
482
+ * @description fetch the trading fees for multiple markets
483
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-summary
484
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
485
+ * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
486
+ */
487
+ async fetchTradingFees(params = {}) {
488
+ await this.loadMarkets();
489
+ const response = await this.privatePostSummary(params);
490
+ //
491
+ // {
492
+ // "time": "2022-02-23T16:05:47.659000Z",
493
+ // "status": { resid_hint: null, login_last: "2022-02-23T16:05:48Z" },
494
+ // "is_locked": false,
495
+ // "leo_lev": "0",
496
+ // "leo_amount_avg": "0.0",
497
+ // "trade_vol_30d": [
498
+ // {
499
+ // "curr": "Total (USD)",
500
+ // "vol": "0.0",
501
+ // "vol_safe": "0.0",
502
+ // "vol_maker": "0.0",
503
+ // "vol_BFX": "0.0",
504
+ // "vol_BFX_safe": "0.0",
505
+ // "vol_BFX_maker": "0.0"
506
+ // }
507
+ // ],
508
+ // "fees_funding_30d": {},
509
+ // "fees_funding_total_30d": "0",
510
+ // "fees_trading_30d": {},
511
+ // "fees_trading_total_30d": "0",
512
+ // "rebates_trading_30d": {},
513
+ // "rebates_trading_total_30d": "0",
514
+ // "maker_fee": "0.001",
515
+ // "taker_fee": "0.002",
516
+ // "maker_fee_2crypto": "0.001",
517
+ // "maker_fee_2stablecoin": "0.001",
518
+ // "maker_fee_2fiat": "0.001",
519
+ // "maker_fee_2deriv": "0.0002",
520
+ // "taker_fee_2crypto": "0.002",
521
+ // "taker_fee_2stablecoin": "0.002",
522
+ // "taker_fee_2fiat": "0.002",
523
+ // "taker_fee_2deriv": "0.00065",
524
+ // "deriv_maker_rebate": "0.0002",
525
+ // "deriv_taker_fee": "0.00065",
526
+ // "trade_last": null
527
+ // }
528
+ //
529
+ const result = {};
530
+ const fiat = this.safeDict(this.options, 'fiat', {});
531
+ const makerFee = this.safeNumber(response, 'maker_fee');
532
+ const takerFee = this.safeNumber(response, 'taker_fee');
533
+ const makerFee2Fiat = this.safeNumber(response, 'maker_fee_2fiat');
534
+ const takerFee2Fiat = this.safeNumber(response, 'taker_fee_2fiat');
535
+ const makerFee2Deriv = this.safeNumber(response, 'maker_fee_2deriv');
536
+ const takerFee2Deriv = this.safeNumber(response, 'taker_fee_2deriv');
537
+ for (let i = 0; i < this.symbols.length; i++) {
538
+ const symbol = this.symbols[i];
539
+ const market = this.market(symbol);
540
+ const fee = {
541
+ 'info': response,
542
+ 'symbol': symbol,
543
+ 'percentage': true,
544
+ 'tierBased': true,
545
+ };
546
+ if (market['quote'] in fiat) {
547
+ fee['maker'] = makerFee2Fiat;
548
+ fee['taker'] = takerFee2Fiat;
549
+ }
550
+ else if (market['contract']) {
551
+ fee['maker'] = makerFee2Deriv;
552
+ fee['taker'] = takerFee2Deriv;
553
+ }
554
+ else {
555
+ fee['maker'] = makerFee;
556
+ fee['taker'] = takerFee;
557
+ }
558
+ result[symbol] = fee;
559
+ }
560
+ return result;
561
+ }
562
+ /**
563
+ * @method
564
+ * @name bitfinex#fetchMarkets
565
+ * @description retrieves data on all markets for bitfinex
566
+ * @see https://docs.bitfinex.com/v1/reference/rest-public-symbols
567
+ * @see https://docs.bitfinex.com/v1/reference/rest-public-symbol-details
568
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
569
+ * @returns {object[]} an array of objects representing market data
570
+ */
571
+ async fetchMarkets(params = {}) {
572
+ const idsPromise = this.publicGetSymbols();
573
+ //
574
+ // [ "btcusd", "ltcusd", "ltcbtc" ]
575
+ //
576
+ const detailsPromise = this.publicGetSymbolsDetails();
577
+ //
578
+ // [
579
+ // {
580
+ // "pair":"btcusd",
581
+ // "price_precision":5,
582
+ // "initial_margin":"10.0",
583
+ // "minimum_margin":"5.0",
584
+ // "maximum_order_size":"2000.0",
585
+ // "minimum_order_size":"0.0002",
586
+ // "expiration":"NA",
587
+ // "margin":true
588
+ // },
589
+ // ]
590
+ //
591
+ const [ids, details] = await Promise.all([idsPromise, detailsPromise]);
592
+ const result = [];
593
+ for (let i = 0; i < details.length; i++) {
594
+ const market = details[i];
595
+ let id = this.safeString(market, 'pair');
596
+ if (!this.inArray(id, ids)) {
597
+ continue;
598
+ }
599
+ id = id.toUpperCase();
600
+ let baseId = undefined;
601
+ let quoteId = undefined;
602
+ if (id.indexOf(':') >= 0) {
603
+ const parts = id.split(':');
604
+ baseId = parts[0];
605
+ quoteId = parts[1];
606
+ }
607
+ else {
608
+ baseId = id.slice(0, 3);
609
+ quoteId = id.slice(3, 6);
610
+ }
611
+ const base = this.safeCurrencyCode(baseId);
612
+ const quote = this.safeCurrencyCode(quoteId);
613
+ const symbol = base + '/' + quote;
614
+ let type = 'spot';
615
+ if (id.indexOf('F0') > -1) {
616
+ type = 'swap';
617
+ }
618
+ result.push({
619
+ 'id': id,
620
+ 'symbol': symbol,
621
+ 'base': base,
622
+ 'quote': quote,
623
+ 'settle': undefined,
624
+ 'baseId': baseId,
625
+ 'quoteId': quoteId,
626
+ 'settleId': undefined,
627
+ 'type': type,
628
+ 'spot': (type === 'spot'),
629
+ 'margin': this.safeBool(market, 'margin'),
630
+ 'swap': (type === 'swap'),
631
+ 'future': false,
632
+ 'option': false,
633
+ 'active': true,
634
+ 'contract': (type === 'swap'),
635
+ 'linear': undefined,
636
+ 'inverse': undefined,
637
+ 'contractSize': undefined,
638
+ 'expiry': undefined,
639
+ 'expiryDatetime': undefined,
640
+ 'strike': undefined,
641
+ 'optionType': undefined,
642
+ 'precision': {
643
+ // https://docs.bitfinex.com/docs/introduction#amount-precision
644
+ // The amount field allows up to 8 decimals.
645
+ // Anything exceeding this will be rounded to the 8th decimal.
646
+ 'amount': parseInt('8'),
647
+ 'price': this.safeInteger(market, 'price_precision'),
648
+ },
649
+ 'limits': {
650
+ 'leverage': {
651
+ 'min': undefined,
652
+ 'max': undefined,
653
+ },
654
+ 'amount': {
655
+ 'min': this.safeNumber(market, 'minimum_order_size'),
656
+ 'max': this.safeNumber(market, 'maximum_order_size'),
657
+ },
658
+ 'price': {
659
+ 'min': this.parseNumber('1e-8'),
660
+ 'max': undefined,
661
+ },
662
+ 'cost': {
663
+ 'min': undefined,
664
+ 'max': undefined,
665
+ },
666
+ },
667
+ 'created': undefined,
668
+ 'info': market,
669
+ });
670
+ }
671
+ return result;
672
+ }
673
+ amountToPrecision(symbol, amount) {
674
+ // https://docs.bitfinex.com/docs/introduction#amount-precision
675
+ // The amount field allows up to 8 decimals.
676
+ // Anything exceeding this will be rounded to the 8th decimal.
677
+ symbol = this.safeSymbol(symbol);
678
+ return this.decimalToPrecision(amount, number.TRUNCATE, this.markets[symbol]['precision']['amount'], number.DECIMAL_PLACES);
679
+ }
680
+ priceToPrecision(symbol, price) {
681
+ symbol = this.safeSymbol(symbol);
682
+ price = this.decimalToPrecision(price, number.ROUND, this.markets[symbol]['precision']['price'], this.precisionMode);
683
+ // https://docs.bitfinex.com/docs/introduction#price-precision
684
+ // The precision level of all trading prices is based on significant figures.
685
+ // All pairs on Bitfinex use up to 5 significant digits and up to 8 decimals (e.g. 1.2345, 123.45, 1234.5, 0.00012345).
686
+ // Prices submit with a precision larger than 5 will be cut by the API.
687
+ return this.decimalToPrecision(price, number.TRUNCATE, 8, number.DECIMAL_PLACES);
688
+ }
689
+ /**
690
+ * @method
691
+ * @name bitfinex#fetchBalance
692
+ * @description query for balance and get the amount of funds available for trading or funds locked in orders
693
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-wallet-balances
694
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
695
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
696
+ */
697
+ async fetchBalance(params = {}) {
698
+ await this.loadMarkets();
699
+ const accountsByType = this.safeDict(this.options, 'accountsByType', {});
700
+ const requestedType = this.safeString(params, 'type', 'exchange');
701
+ const accountType = this.safeString(accountsByType, requestedType, requestedType);
702
+ if (accountType === undefined) {
703
+ const keys = Object.keys(accountsByType);
704
+ throw new errors.ExchangeError(this.id + ' fetchBalance() type parameter must be one of ' + keys.join(', '));
705
+ }
706
+ const query = this.omit(params, 'type');
707
+ const response = await this.privatePostBalances(query);
708
+ // [ { type: "deposit",
709
+ // "currency": "btc",
710
+ // "amount": "0.00116721",
711
+ // "available": "0.00116721" },
712
+ // { type: "exchange",
713
+ // "currency": "ust",
714
+ // "amount": "0.0000002",
715
+ // "available": "0.0000002" },
716
+ // { type: "trading",
717
+ // "currency": "btc",
718
+ // "amount": "0.0005",
719
+ // "available": "0.0005" } ],
720
+ const result = { 'info': response };
721
+ const isDerivative = requestedType === 'derivatives';
722
+ for (let i = 0; i < response.length; i++) {
723
+ const balance = response[i];
724
+ const type = this.safeString(balance, 'type');
725
+ const currencyId = this.safeStringLower(balance, 'currency', '');
726
+ const start = currencyId.length - 2;
727
+ const isDerivativeCode = currencyId.slice(start) === 'f0';
728
+ // this will only filter the derivative codes if the requestedType is 'derivatives'
729
+ const derivativeCondition = (!isDerivative || isDerivativeCode);
730
+ if ((accountType === type) && derivativeCondition) {
731
+ const code = this.safeCurrencyCode(currencyId);
732
+ // bitfinex had BCH previously, now it's BAB, but the old
733
+ // BCH symbol is kept for backward-compatibility
734
+ // we need a workaround here so that the old BCH balance
735
+ // would not override the new BAB balance (BAB is unified to BCH)
736
+ // https://github.com/ccxt/ccxt/issues/4989
737
+ if (!(code in result)) {
738
+ const account = this.account();
739
+ account['free'] = this.safeString(balance, 'available');
740
+ account['total'] = this.safeString(balance, 'amount');
741
+ result[code] = account;
742
+ }
743
+ }
744
+ }
745
+ return this.safeBalance(result);
746
+ }
747
+ /**
748
+ * @method
749
+ * @name bitfinex#transfer
750
+ * @description transfer currency internally between wallets on the same account
751
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-transfer-between-wallets
752
+ * @param {string} code unified currency code
753
+ * @param {float} amount amount to transfer
754
+ * @param {string} fromAccount account to transfer from
755
+ * @param {string} toAccount account to transfer to
756
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
757
+ * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
758
+ */
759
+ async transfer(code, amount, fromAccount, toAccount, params = {}) {
760
+ // transferring between derivatives wallet and regular wallet is not documented in their API
761
+ // however we support it in CCXT (from just looking at web inspector)
762
+ await this.loadMarkets();
763
+ const accountsByType = this.safeDict(this.options, 'accountsByType', {});
764
+ const fromId = this.safeString(accountsByType, fromAccount, fromAccount);
765
+ const toId = this.safeString(accountsByType, toAccount, toAccount);
766
+ const currency = this.currency(code);
767
+ const fromCurrencyId = this.convertDerivativesId(currency['id'], fromAccount);
768
+ const toCurrencyId = this.convertDerivativesId(currency['id'], toAccount);
769
+ const requestedAmount = this.currencyToPrecision(code, amount);
770
+ const request = {
771
+ 'amount': requestedAmount,
772
+ 'currency': fromCurrencyId,
773
+ 'currency_to': toCurrencyId,
774
+ 'walletfrom': fromId,
775
+ 'walletto': toId,
776
+ };
777
+ const response = await this.privatePostTransfer(this.extend(request, params));
778
+ //
779
+ // [
780
+ // {
781
+ // "status": "success",
782
+ // "message": "0.0001 Bitcoin transfered from Margin to Exchange"
783
+ // }
784
+ // ]
785
+ //
786
+ const result = this.safeValue(response, 0);
787
+ const message = this.safeString(result, 'message');
788
+ if (message === undefined) {
789
+ throw new errors.ExchangeError(this.id + ' transfer failed');
790
+ }
791
+ return this.extend(this.parseTransfer(result, currency), {
792
+ 'fromAccount': fromAccount,
793
+ 'toAccount': toAccount,
794
+ 'amount': this.parseNumber(requestedAmount),
795
+ });
796
+ }
797
+ parseTransfer(transfer, currency = undefined) {
798
+ //
799
+ // {
800
+ // "status": "success",
801
+ // "message": "0.0001 Bitcoin transfered from Margin to Exchange"
802
+ // }
803
+ //
804
+ return {
805
+ 'info': transfer,
806
+ 'id': undefined,
807
+ 'timestamp': undefined,
808
+ 'datetime': undefined,
809
+ 'currency': this.safeCurrencyCode(undefined, currency),
810
+ 'amount': undefined,
811
+ 'fromAccount': undefined,
812
+ 'toAccount': undefined,
813
+ 'status': this.parseTransferStatus(this.safeString(transfer, 'status')),
814
+ };
815
+ }
816
+ parseTransferStatus(status) {
817
+ const statuses = {
818
+ 'SUCCESS': 'ok',
819
+ };
820
+ return this.safeString(statuses, status, status);
821
+ }
822
+ convertDerivativesId(currencyId, type) {
823
+ const start = currencyId.length - 2;
824
+ const isDerivativeCode = currencyId.slice(start) === 'F0';
825
+ if ((type !== 'derivatives' && type !== 'trading' && type !== 'margin') && isDerivativeCode) {
826
+ currencyId = currencyId.slice(0, start);
827
+ }
828
+ else if (type === 'derivatives' && !isDerivativeCode) {
829
+ currencyId = currencyId + 'F0';
830
+ }
831
+ return currencyId;
832
+ }
833
+ /**
834
+ * @method
835
+ * @name bitfinex#fetchOrderBook
836
+ * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
837
+ * @see https://docs.bitfinex.com/v1/reference/rest-public-orderbook
838
+ * @param {string} symbol unified symbol of the market to fetch the order book for
839
+ * @param {int} [limit] the maximum amount of order book entries to return
840
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
841
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
842
+ */
843
+ async fetchOrderBook(symbol, limit = undefined, params = {}) {
844
+ await this.loadMarkets();
845
+ const market = this.market(symbol);
846
+ const request = {
847
+ 'symbol': market['id'],
848
+ };
849
+ if (limit !== undefined) {
850
+ request['limit_bids'] = limit;
851
+ request['limit_asks'] = limit;
852
+ }
853
+ const response = await this.publicGetBookSymbol(this.extend(request, params));
854
+ return this.parseOrderBook(response, market['symbol'], undefined, 'bids', 'asks', 'price', 'amount');
855
+ }
856
+ /**
857
+ * @method
858
+ * @name bitfinex#fetchTickers
859
+ * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
860
+ * @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
861
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
862
+ * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
863
+ */
864
+ async fetchTickers(symbols = undefined, params = {}) {
865
+ await this.loadMarkets();
866
+ symbols = this.marketSymbols(symbols);
867
+ const response = await this.publicGetTickers(params);
868
+ const result = {};
869
+ for (let i = 0; i < response.length; i++) {
870
+ const ticker = this.parseTicker(response[i]);
871
+ const symbol = ticker['symbol'];
872
+ result[symbol] = ticker;
873
+ }
874
+ return this.filterByArrayTickers(result, 'symbol', symbols);
875
+ }
876
+ /**
877
+ * @method
878
+ * @name bitfinex#fetchTicker
879
+ * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
880
+ * @see https://docs.bitfinex.com/v1/reference/rest-public-ticker
881
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
882
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
883
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
884
+ */
885
+ async fetchTicker(symbol, params = {}) {
886
+ await this.loadMarkets();
887
+ const market = this.market(symbol);
888
+ const request = {
889
+ 'symbol': market['id'],
890
+ };
891
+ const ticker = await this.publicGetPubtickerSymbol(this.extend(request, params));
892
+ //
893
+ // {
894
+ // mid: '63560.5',
895
+ // bid: '63560.0',
896
+ // ask: '63561.0',
897
+ // last_price: '63547.0',
898
+ // low: '62812.0',
899
+ // high: '64480.0',
900
+ // volume: '517.25634977',
901
+ // timestamp: '1715102384.9849467'
902
+ // }
903
+ //
904
+ return this.parseTicker(ticker, market);
905
+ }
906
+ parseTicker(ticker, market = undefined) {
907
+ //
908
+ // {
909
+ // mid: '63560.5',
910
+ // bid: '63560.0',
911
+ // ask: '63561.0',
912
+ // last_price: '63547.0',
913
+ // low: '62812.0',
914
+ // high: '64480.0',
915
+ // volume: '517.25634977',
916
+ // timestamp: '1715102384.9849467'
917
+ // }
918
+ //
919
+ const timestamp = this.safeTimestamp(ticker, 'timestamp');
920
+ const marketId = this.safeString(ticker, 'pair');
921
+ market = this.safeMarket(marketId, market);
922
+ const symbol = market['symbol'];
923
+ const last = this.safeString(ticker, 'last_price');
924
+ return this.safeTicker({
925
+ 'symbol': symbol,
926
+ 'timestamp': timestamp,
927
+ 'datetime': this.iso8601(timestamp),
928
+ 'high': this.safeString(ticker, 'high'),
929
+ 'low': this.safeString(ticker, 'low'),
930
+ 'bid': this.safeString(ticker, 'bid'),
931
+ 'bidVolume': undefined,
932
+ 'ask': this.safeString(ticker, 'ask'),
933
+ 'askVolume': undefined,
934
+ 'vwap': undefined,
935
+ 'open': undefined,
936
+ 'close': last,
937
+ 'last': last,
938
+ 'previousClose': undefined,
939
+ 'change': undefined,
940
+ 'percentage': undefined,
941
+ 'average': this.safeString(ticker, 'mid'),
942
+ 'baseVolume': this.safeString(ticker, 'volume'),
943
+ 'quoteVolume': undefined,
944
+ 'info': ticker,
945
+ }, market);
946
+ }
947
+ parseTrade(trade, market = undefined) {
948
+ //
949
+ // fetchTrades (public) v1
950
+ //
951
+ // {
952
+ // "timestamp":1637258380,
953
+ // "tid":894452833,
954
+ // "price":"0.99941",
955
+ // "amount":"261.38",
956
+ // "exchange":"bitfinex",
957
+ // "type":"sell"
958
+ // }
959
+ //
960
+ // fetchMyTrades (private) v1
961
+ //
962
+ // {
963
+ // "price":"0.99941",
964
+ // "amount":"261.38",
965
+ // "timestamp":"1637258380.0",
966
+ // "type":"Sell",
967
+ // "fee_currency":"UST",
968
+ // "fee_amount":"-0.52245157",
969
+ // "tid":894452833,
970
+ // "order_id":78819731373
971
+ // }
972
+ //
973
+ // {
974
+ // "price":"0.99958",
975
+ // "amount":"261.90514",
976
+ // "timestamp":"1637258238.0",
977
+ // "type":"Buy",
978
+ // "fee_currency":"UDC",
979
+ // "fee_amount":"-0.52381028",
980
+ // "tid":894452800,
981
+ // "order_id":78819504838
982
+ // }
983
+ //
984
+ const id = this.safeString(trade, 'tid');
985
+ const timestamp = this.safeTimestamp(trade, 'timestamp');
986
+ const type = undefined;
987
+ const side = this.safeStringLower(trade, 'type');
988
+ const orderId = this.safeString(trade, 'order_id');
989
+ const priceString = this.safeString(trade, 'price');
990
+ const amountString = this.safeString(trade, 'amount');
991
+ let fee = undefined;
992
+ if ('fee_amount' in trade) {
993
+ const feeCostString = Precise["default"].stringNeg(this.safeString(trade, 'fee_amount'));
994
+ const feeCurrencyId = this.safeString(trade, 'fee_currency');
995
+ const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
996
+ fee = {
997
+ 'cost': feeCostString,
998
+ 'currency': feeCurrencyCode,
999
+ };
1000
+ }
1001
+ return this.safeTrade({
1002
+ 'id': id,
1003
+ 'info': trade,
1004
+ 'timestamp': timestamp,
1005
+ 'datetime': this.iso8601(timestamp),
1006
+ 'symbol': market['symbol'],
1007
+ 'type': type,
1008
+ 'order': orderId,
1009
+ 'side': side,
1010
+ 'takerOrMaker': undefined,
1011
+ 'price': priceString,
1012
+ 'amount': amountString,
1013
+ 'cost': undefined,
1014
+ 'fee': fee,
1015
+ }, market);
1016
+ }
1017
+ /**
1018
+ * @method
1019
+ * @name bitfinex#fetchTrades
1020
+ * @description get the list of most recent trades for a particular symbol
1021
+ * @see https://docs.bitfinex.com/v1/reference/rest-public-trades
1022
+ * @param {string} symbol unified symbol of the market to fetch trades for
1023
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
1024
+ * @param {int} [limit] the maximum amount of trades to fetch
1025
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1026
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
1027
+ */
1028
+ async fetchTrades(symbol, since = undefined, limit = 50, params = {}) {
1029
+ await this.loadMarkets();
1030
+ const market = this.market(symbol);
1031
+ const request = {
1032
+ 'symbol': market['id'],
1033
+ 'limit_trades': limit,
1034
+ };
1035
+ if (since !== undefined) {
1036
+ request['timestamp'] = this.parseToInt(since / 1000);
1037
+ }
1038
+ const response = await this.publicGetTradesSymbol(this.extend(request, params));
1039
+ //
1040
+ // [
1041
+ // {
1042
+ // "timestamp": "1694284565",
1043
+ // "tid": "1415415034",
1044
+ // "price": "25862.0",
1045
+ // "amount": "0.00020685",
1046
+ // "exchange": "bitfinex",
1047
+ // "type": "buy"
1048
+ // },
1049
+ // ]
1050
+ //
1051
+ return this.parseTrades(response, market, since, limit);
1052
+ }
1053
+ /**
1054
+ * @method
1055
+ * @name bitfinex#fetchMyTrades
1056
+ * @description fetch all trades made by the user
1057
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-past-trades
1058
+ * @param {string} symbol unified market symbol
1059
+ * @param {int} [since] the earliest time in ms to fetch trades for
1060
+ * @param {int} [limit] the maximum number of trades structures to retrieve
1061
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1062
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
1063
+ */
1064
+ async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1065
+ if (symbol === undefined) {
1066
+ throw new errors.ArgumentsRequired(this.id + ' fetchMyTrades() requires a symbol argument');
1067
+ }
1068
+ await this.loadMarkets();
1069
+ const market = this.market(symbol);
1070
+ const request = {
1071
+ 'symbol': market['id'],
1072
+ };
1073
+ if (limit !== undefined) {
1074
+ request['limit_trades'] = limit;
1075
+ }
1076
+ if (since !== undefined) {
1077
+ request['timestamp'] = this.parseToInt(since / 1000);
1078
+ }
1079
+ const response = await this.privatePostMytrades(this.extend(request, params));
1080
+ return this.parseTrades(response, market, since, limit);
1081
+ }
1082
+ /**
1083
+ * @method
1084
+ * @name bitfinex#createOrder
1085
+ * @description create a trade order
1086
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-new-order
1087
+ * @param {string} symbol unified symbol of the market to create an order in
1088
+ * @param {string} type 'market' or 'limit'
1089
+ * @param {string} side 'buy' or 'sell'
1090
+ * @param {float} amount how much of currency you want to trade in units of base currency
1091
+ * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1092
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1093
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1094
+ */
1095
+ async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
1096
+ await this.loadMarkets();
1097
+ const market = this.market(symbol);
1098
+ const postOnly = this.safeBool(params, 'postOnly', false);
1099
+ type = type.toLowerCase();
1100
+ params = this.omit(params, ['postOnly']);
1101
+ if (market['spot']) {
1102
+ // although they claim that type needs to be 'exchange limit' or 'exchange market'
1103
+ // in fact that's not the case for swap markets
1104
+ type = this.safeStringLower(this.options['orderTypes'], type, type);
1105
+ }
1106
+ const request = {
1107
+ 'symbol': market['id'],
1108
+ 'side': side,
1109
+ 'amount': this.amountToPrecision(symbol, amount),
1110
+ 'type': type,
1111
+ 'ocoorder': false,
1112
+ 'buy_price_oco': 0,
1113
+ 'sell_price_oco': 0,
1114
+ };
1115
+ if (type.indexOf('market') > -1) {
1116
+ request['price'] = this.nonce().toString();
1117
+ }
1118
+ else {
1119
+ request['price'] = this.priceToPrecision(symbol, price);
1120
+ }
1121
+ if (postOnly) {
1122
+ request['is_postonly'] = true;
1123
+ }
1124
+ const response = await this.privatePostOrderNew(this.extend(request, params));
1125
+ return this.parseOrder(response, market);
1126
+ }
1127
+ async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
1128
+ await this.loadMarkets();
1129
+ const order = {
1130
+ 'order_id': parseInt(id),
1131
+ };
1132
+ if (price !== undefined) {
1133
+ order['price'] = this.priceToPrecision(symbol, price);
1134
+ }
1135
+ if (amount !== undefined) {
1136
+ order['amount'] = this.numberToString(amount);
1137
+ }
1138
+ if (symbol !== undefined) {
1139
+ order['symbol'] = this.marketId(symbol);
1140
+ }
1141
+ if (side !== undefined) {
1142
+ order['side'] = side;
1143
+ }
1144
+ if (type !== undefined) {
1145
+ order['type'] = this.safeString(this.options['orderTypes'], type, type);
1146
+ }
1147
+ const response = await this.privatePostOrderCancelReplace(this.extend(order, params));
1148
+ return this.parseOrder(response);
1149
+ }
1150
+ /**
1151
+ * @method
1152
+ * @name bitfinex#cancelOrder
1153
+ * @description cancels an open order
1154
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-cancel-order
1155
+ * @param {string} id order id
1156
+ * @param {string} symbol not used by bitfinex cancelOrder ()
1157
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1158
+ * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1159
+ */
1160
+ async cancelOrder(id, symbol = undefined, params = {}) {
1161
+ await this.loadMarkets();
1162
+ const request = {
1163
+ 'order_id': parseInt(id),
1164
+ };
1165
+ const response = await this.privatePostOrderCancel(this.extend(request, params));
1166
+ //
1167
+ // {
1168
+ // id: '161236928925',
1169
+ // cid: '1720172026812',
1170
+ // cid_date: '2024-07-05',
1171
+ // gid: null,
1172
+ // symbol: 'adaust',
1173
+ // exchange: 'bitfinex',
1174
+ // price: '0.33',
1175
+ // avg_execution_price: '0.0',
1176
+ // side: 'buy',
1177
+ // type: 'exchange limit',
1178
+ // timestamp: '1720172026.813',
1179
+ // is_live: true,
1180
+ // is_cancelled: false,
1181
+ // is_hidden: false,
1182
+ // oco_order: null,
1183
+ // was_forced: false,
1184
+ // original_amount: '10.0',
1185
+ // remaining_amount: '10.0',
1186
+ // executed_amount: '0.0',
1187
+ // src: 'api',
1188
+ // meta: {}
1189
+ // }
1190
+ //
1191
+ return this.parseOrder(response);
1192
+ }
1193
+ /**
1194
+ * @method
1195
+ * @name bitfinex#cancelAllOrders
1196
+ * @description cancel all open orders
1197
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-cancel-all-orders
1198
+ * @param {string} symbol not used by bitfinex cancelAllOrders
1199
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1200
+ * @returns {object} response from exchange
1201
+ */
1202
+ async cancelAllOrders(symbol = undefined, params = {}) {
1203
+ const response = await this.privatePostOrderCancelAll(params);
1204
+ //
1205
+ // { result: 'Submitting 1 order cancellations.' }
1206
+ //
1207
+ return [
1208
+ this.safeOrder({
1209
+ 'info': response,
1210
+ }),
1211
+ ];
1212
+ }
1213
+ parseOrder(order, market = undefined) {
1214
+ //
1215
+ // {
1216
+ // "id": 57334010955,
1217
+ // "cid": 1611584840966,
1218
+ // "cid_date": null,
1219
+ // "gid": null,
1220
+ // "symbol": "ltcbtc",
1221
+ // "exchange": null,
1222
+ // "price": "0.0042125",
1223
+ // "avg_execution_price": "0.0042097",
1224
+ // "side": "sell",
1225
+ // "type": "exchange market",
1226
+ // "timestamp": "1611584841.0",
1227
+ // "is_live": false,
1228
+ // "is_cancelled": false,
1229
+ // "is_hidden": 0,
1230
+ // "oco_order": 0,
1231
+ // "was_forced": false,
1232
+ // "original_amount": "0.205176",
1233
+ // "remaining_amount": "0.0",
1234
+ // "executed_amount": "0.205176",
1235
+ // "src": "web"
1236
+ // }
1237
+ //
1238
+ const side = this.safeString(order, 'side');
1239
+ const open = this.safeBool(order, 'is_live');
1240
+ const canceled = this.safeBool(order, 'is_cancelled');
1241
+ let status = undefined;
1242
+ if (open) {
1243
+ status = 'open';
1244
+ }
1245
+ else if (canceled) {
1246
+ status = 'canceled';
1247
+ }
1248
+ else {
1249
+ status = 'closed';
1250
+ }
1251
+ const marketId = this.safeStringUpper(order, 'symbol');
1252
+ const symbol = this.safeSymbol(marketId, market);
1253
+ let orderType = this.safeString(order, 'type', '');
1254
+ const exchange = orderType.indexOf('exchange ') >= 0;
1255
+ if (exchange) {
1256
+ const parts = order['type'].split(' ');
1257
+ orderType = parts[1];
1258
+ }
1259
+ const timestamp = this.safeTimestamp(order, 'timestamp');
1260
+ const id = this.safeString(order, 'id');
1261
+ return this.safeOrder({
1262
+ 'info': order,
1263
+ 'id': id,
1264
+ 'clientOrderId': undefined,
1265
+ 'timestamp': timestamp,
1266
+ 'datetime': this.iso8601(timestamp),
1267
+ 'lastTradeTimestamp': undefined,
1268
+ 'symbol': symbol,
1269
+ 'type': orderType,
1270
+ 'timeInForce': undefined,
1271
+ 'postOnly': undefined,
1272
+ 'side': side,
1273
+ 'price': this.safeString(order, 'price'),
1274
+ 'stopPrice': undefined,
1275
+ 'triggerPrice': undefined,
1276
+ 'average': this.safeString(order, 'avg_execution_price'),
1277
+ 'amount': this.safeString(order, 'original_amount'),
1278
+ 'remaining': this.safeString(order, 'remaining_amount'),
1279
+ 'filled': this.safeString(order, 'executed_amount'),
1280
+ 'status': status,
1281
+ 'fee': undefined,
1282
+ 'cost': undefined,
1283
+ 'trades': undefined,
1284
+ }, market);
1285
+ }
1286
+ /**
1287
+ * @method
1288
+ * @name bitfinex#fetchOpenOrders
1289
+ * @description fetch all unfilled currently open orders
1290
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-active-orders
1291
+ * @param {string} symbol unified market symbol
1292
+ * @param {int} [since] the earliest time in ms to fetch open orders for
1293
+ * @param {int} [limit] the maximum number of open orders structures to retrieve
1294
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1295
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1296
+ */
1297
+ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1298
+ await this.loadMarkets();
1299
+ if (symbol !== undefined) {
1300
+ if (!(symbol in this.markets)) {
1301
+ throw new errors.ExchangeError(this.id + ' has no symbol ' + symbol);
1302
+ }
1303
+ }
1304
+ const response = await this.privatePostOrders(params);
1305
+ let orders = this.parseOrders(response, undefined, since, limit);
1306
+ if (symbol !== undefined) {
1307
+ orders = this.filterBy(orders, 'symbol', symbol);
1308
+ }
1309
+ return orders;
1310
+ }
1311
+ /**
1312
+ * @method
1313
+ * @name bitfinex#fetchClosedOrders
1314
+ * @description fetches information on multiple closed orders made by the user
1315
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-orders-history
1316
+ * @param {string} symbol unified market symbol of the market orders were made in
1317
+ * @param {int} [since] the earliest time in ms to fetch orders for
1318
+ * @param {int} [limit] the maximum number of order structures to retrieve
1319
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1320
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1321
+ */
1322
+ async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1323
+ await this.loadMarkets();
1324
+ symbol = this.symbol(symbol);
1325
+ const request = {};
1326
+ if (limit !== undefined) {
1327
+ request['limit'] = limit;
1328
+ }
1329
+ const response = await this.privatePostOrdersHist(this.extend(request, params));
1330
+ let orders = this.parseOrders(response, undefined, since, limit);
1331
+ if (symbol !== undefined) {
1332
+ orders = this.filterBy(orders, 'symbol', symbol);
1333
+ }
1334
+ orders = this.filterByArray(orders, 'status', ['closed', 'canceled'], false);
1335
+ return orders;
1336
+ }
1337
+ /**
1338
+ * @method
1339
+ * @name bitfinex#fetchOrder
1340
+ * @description fetches information on an order made by the user
1341
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-order-status
1342
+ * @param {string} id the order id
1343
+ * @param {string} symbol not used by bitfinex fetchOrder
1344
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1345
+ * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1346
+ */
1347
+ async fetchOrder(id, symbol = undefined, params = {}) {
1348
+ await this.loadMarkets();
1349
+ const request = {
1350
+ 'order_id': parseInt(id),
1351
+ };
1352
+ const response = await this.privatePostOrderStatus(this.extend(request, params));
1353
+ return this.parseOrder(response);
1354
+ }
1355
+ parseOHLCV(ohlcv, market = undefined) {
1356
+ //
1357
+ // [
1358
+ // 1457539800000,
1359
+ // 0.02594,
1360
+ // 0.02594,
1361
+ // 0.02594,
1362
+ // 0.02594,
1363
+ // 0.1
1364
+ // ]
1365
+ //
1366
+ return [
1367
+ this.safeInteger(ohlcv, 0),
1368
+ this.safeNumber(ohlcv, 1),
1369
+ this.safeNumber(ohlcv, 3),
1370
+ this.safeNumber(ohlcv, 4),
1371
+ this.safeNumber(ohlcv, 2),
1372
+ this.safeNumber(ohlcv, 5),
1373
+ ];
1374
+ }
1375
+ /**
1376
+ * @method
1377
+ * @name bitfinex#fetchOHLCV
1378
+ * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1379
+ * @see https://docs.bitfinex.com/reference/rest-public-candles#aggregate-funding-currency-candles
1380
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
1381
+ * @param {string} timeframe the length of time each candle represents
1382
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
1383
+ * @param {int} [limit] the maximum amount of candles to fetch
1384
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1385
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
1386
+ */
1387
+ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
1388
+ await this.loadMarkets();
1389
+ if (limit === undefined) {
1390
+ limit = 100;
1391
+ }
1392
+ else {
1393
+ limit = Math.min(limit, 10000);
1394
+ }
1395
+ const market = this.market(symbol);
1396
+ const v2id = 't' + market['id'];
1397
+ const request = {
1398
+ 'symbol': v2id,
1399
+ 'timeframe': this.safeString(this.timeframes, timeframe, timeframe),
1400
+ 'sort': 1,
1401
+ 'limit': limit,
1402
+ };
1403
+ if (since !== undefined) {
1404
+ request['start'] = since;
1405
+ }
1406
+ const response = await this.v2GetCandlesTradeTimeframeSymbolHist(this.extend(request, params));
1407
+ //
1408
+ // [
1409
+ // [1457539800000,0.02594,0.02594,0.02594,0.02594,0.1],
1410
+ // [1457547300000,0.02577,0.02577,0.02577,0.02577,0.01],
1411
+ // [1457550240000,0.0255,0.0253,0.0255,0.0252,3.2640000000000002],
1412
+ // ]
1413
+ //
1414
+ return this.parseOHLCVs(response, market, timeframe, since, limit);
1415
+ }
1416
+ getCurrencyName(code) {
1417
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1418
+ if (code in this.options['currencyNames']) {
1419
+ return this.options['currencyNames'][code];
1420
+ }
1421
+ throw new errors.NotSupported(this.id + ' ' + code + ' not supported for withdrawal');
1422
+ }
1423
+ /**
1424
+ * @method
1425
+ * @name bitfinex#createDepositAddress
1426
+ * @description create a currency deposit address
1427
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-deposit
1428
+ * @param {string} code unified currency code of the currency for the deposit address
1429
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1430
+ * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
1431
+ */
1432
+ async createDepositAddress(code, params = {}) {
1433
+ await this.loadMarkets();
1434
+ const request = {
1435
+ 'renew': 1,
1436
+ };
1437
+ return await this.fetchDepositAddress(code, this.extend(request, params));
1438
+ }
1439
+ /**
1440
+ * @method
1441
+ * @name bitfinex#fetchDepositAddress
1442
+ * @description fetch the deposit address for a currency associated with this account
1443
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-deposit
1444
+ * @param {string} code unified currency code
1445
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1446
+ * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
1447
+ */
1448
+ async fetchDepositAddress(code, params = {}) {
1449
+ await this.loadMarkets();
1450
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1451
+ const name = this.getCurrencyName(code);
1452
+ const request = {
1453
+ 'method': name,
1454
+ 'wallet_name': 'exchange',
1455
+ 'renew': 0, // a value of 1 will generate a new address
1456
+ };
1457
+ const response = await this.privatePostDepositNew(this.extend(request, params));
1458
+ let address = this.safeValue(response, 'address');
1459
+ let tag = undefined;
1460
+ if ('address_pool' in response) {
1461
+ tag = address;
1462
+ address = response['address_pool'];
1463
+ }
1464
+ this.checkAddress(address);
1465
+ return {
1466
+ 'currency': code,
1467
+ 'address': address,
1468
+ 'tag': tag,
1469
+ 'network': undefined,
1470
+ 'info': response,
1471
+ };
1472
+ }
1473
+ /**
1474
+ * @method
1475
+ * @name bitfinex#fetchDepositsWithdrawals
1476
+ * @description fetch history of deposits and withdrawals
1477
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-deposit-withdrawal-history
1478
+ * @param {string} code unified currency code for the currency of the deposit/withdrawals
1479
+ * @param {int} [since] timestamp in ms of the earliest deposit/withdrawal, default is undefined
1480
+ * @param {int} [limit] max number of deposit/withdrawals to return, default is undefined
1481
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1482
+ * @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
1483
+ */
1484
+ async fetchDepositsWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
1485
+ await this.loadMarkets();
1486
+ let currencyId = this.safeString(params, 'currency');
1487
+ const query = this.omit(params, 'currency');
1488
+ let currency = undefined;
1489
+ if (currencyId === undefined) {
1490
+ if (code === undefined) {
1491
+ throw new errors.ArgumentsRequired(this.id + ' fetchDepositsWithdrawals() requires a currency `code` argument or a `currency` parameter');
1492
+ }
1493
+ else {
1494
+ currency = this.currency(code);
1495
+ currencyId = currency['id'];
1496
+ }
1497
+ }
1498
+ query['currency'] = currencyId;
1499
+ if (since !== undefined) {
1500
+ query['since'] = this.parseToInt(since / 1000);
1501
+ }
1502
+ const response = await this.privatePostHistoryMovements(this.extend(query, params));
1503
+ //
1504
+ // [
1505
+ // {
1506
+ // "id": 581183,
1507
+ // "txid": 123456,
1508
+ // "currency": "BTC",
1509
+ // "method": "BITCOIN",
1510
+ // "type": "WITHDRAWAL",
1511
+ // "amount": ".01",
1512
+ // "description": "3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ, offchain transfer ",
1513
+ // "address": "3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ",
1514
+ // "status": "COMPLETED",
1515
+ // "timestamp": "1443833327.0",
1516
+ // "timestamp_created": "1443833327.1",
1517
+ // "fee": 0.1,
1518
+ // }
1519
+ // ]
1520
+ //
1521
+ return this.parseTransactions(response, currency, since, limit);
1522
+ }
1523
+ parseTransaction(transaction, currency = undefined) {
1524
+ //
1525
+ // crypto
1526
+ //
1527
+ // {
1528
+ // "id": 12042490,
1529
+ // "fee": "-0.02",
1530
+ // "txid": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D",
1531
+ // "type": "DEPOSIT",
1532
+ // "amount": "2099.849999",
1533
+ // "method": "RIPPLE",
1534
+ // "status": "COMPLETED",
1535
+ // "address": "2505189261",
1536
+ // "currency": "XRP",
1537
+ // "timestamp": "1551730524.0",
1538
+ // "description": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D",
1539
+ // "timestamp_created": "1551730523.0"
1540
+ // }
1541
+ //
1542
+ // fiat
1543
+ //
1544
+ // {
1545
+ // "id": 12725095,
1546
+ // "fee": "-60.0",
1547
+ // "txid": null,
1548
+ // "type": "WITHDRAWAL",
1549
+ // "amount": "9943.0",
1550
+ // "method": "WIRE",
1551
+ // "status": "SENDING",
1552
+ // "address": null,
1553
+ // "currency": "EUR",
1554
+ // "timestamp": "1561802484.0",
1555
+ // "description": "Name: bob, AccountAddress: some address, Account: someaccountno, Bank: bank address, SWIFT: foo, Country: UK, Details of Payment: withdrawal name, Intermediary Bank Name: , Intermediary Bank Address: , Intermediary Bank City: , Intermediary Bank Country: , Intermediary Bank Account: , Intermediary Bank SWIFT: , Fee: -60.0",
1556
+ // "timestamp_created": "1561716066.0"
1557
+ // }
1558
+ //
1559
+ // withdraw
1560
+ //
1561
+ // {
1562
+ // "status": "success",
1563
+ // "message": "Your withdrawal request has been successfully submitted.",
1564
+ // "withdrawal_id": 586829
1565
+ // }
1566
+ //
1567
+ const timestamp = this.safeTimestamp(transaction, 'timestamp_created');
1568
+ const currencyId = this.safeString(transaction, 'currency');
1569
+ const code = this.safeCurrencyCode(currencyId, currency);
1570
+ let feeCost = this.safeString(transaction, 'fee');
1571
+ if (feeCost !== undefined) {
1572
+ feeCost = Precise["default"].stringAbs(feeCost);
1573
+ }
1574
+ return {
1575
+ 'info': transaction,
1576
+ 'id': this.safeString2(transaction, 'id', 'withdrawal_id'),
1577
+ 'txid': this.safeString(transaction, 'txid'),
1578
+ 'type': this.safeStringLower(transaction, 'type'),
1579
+ 'currency': code,
1580
+ 'network': undefined,
1581
+ 'amount': this.safeNumber(transaction, 'amount'),
1582
+ 'status': this.parseTransactionStatus(this.safeString(transaction, 'status')),
1583
+ 'timestamp': timestamp,
1584
+ 'datetime': this.iso8601(timestamp),
1585
+ 'address': this.safeString(transaction, 'address'),
1586
+ 'addressFrom': undefined,
1587
+ 'addressTo': undefined,
1588
+ 'tag': this.safeString(transaction, 'description'),
1589
+ 'tagFrom': undefined,
1590
+ 'tagTo': undefined,
1591
+ 'updated': this.safeTimestamp(transaction, 'timestamp'),
1592
+ 'comment': undefined,
1593
+ 'internal': undefined,
1594
+ 'fee': {
1595
+ 'currency': code,
1596
+ 'cost': this.parseNumber(feeCost),
1597
+ 'rate': undefined,
1598
+ },
1599
+ };
1600
+ }
1601
+ parseTransactionStatus(status) {
1602
+ const statuses = {
1603
+ 'SENDING': 'pending',
1604
+ 'CANCELED': 'canceled',
1605
+ 'ZEROCONFIRMED': 'failed',
1606
+ 'COMPLETED': 'ok',
1607
+ };
1608
+ return this.safeString(statuses, status, status);
1609
+ }
1610
+ /**
1611
+ * @method
1612
+ * @name bitfinex#withdraw
1613
+ * @description make a withdrawal
1614
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-withdrawal
1615
+ * @param {string} code unified currency code
1616
+ * @param {float} amount the amount to withdraw
1617
+ * @param {string} address the address to withdraw to
1618
+ * @param {string} tag
1619
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1620
+ * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
1621
+ */
1622
+ async withdraw(code, amount, address, tag = undefined, params = {}) {
1623
+ [tag, params] = this.handleWithdrawTagAndParams(tag, params);
1624
+ this.checkAddress(address);
1625
+ await this.loadMarkets();
1626
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1627
+ const name = this.getCurrencyName(code);
1628
+ const currency = this.currency(code);
1629
+ const request = {
1630
+ 'withdraw_type': name,
1631
+ 'walletselected': 'exchange',
1632
+ 'amount': this.numberToString(amount),
1633
+ 'address': address,
1634
+ };
1635
+ if (tag !== undefined) {
1636
+ request['payment_id'] = tag;
1637
+ }
1638
+ const responses = await this.privatePostWithdraw(this.extend(request, params));
1639
+ //
1640
+ // [
1641
+ // {
1642
+ // "status":"success",
1643
+ // "message":"Your withdrawal request has been successfully submitted.",
1644
+ // "withdrawal_id":586829
1645
+ // }
1646
+ // ]
1647
+ //
1648
+ const response = this.safeDict(responses, 0, {});
1649
+ const id = this.safeInteger(response, 'withdrawal_id');
1650
+ const message = this.safeString(response, 'message');
1651
+ const errorMessage = this.findBroadlyMatchedKey(this.exceptions['broad'], message);
1652
+ if (id === 0) {
1653
+ if (errorMessage !== undefined) {
1654
+ const ExceptionClass = this.exceptions['broad'][errorMessage];
1655
+ throw new ExceptionClass(this.id + ' ' + message);
1656
+ }
1657
+ throw new errors.ExchangeError(this.id + ' withdraw returned an id of zero: ' + this.json(response));
1658
+ }
1659
+ return this.parseTransaction(response, currency);
1660
+ }
1661
+ /**
1662
+ * @method
1663
+ * @name bitfinex#fetchPositions
1664
+ * @description fetch all open positions
1665
+ * @see https://docs.bitfinex.com/v1/reference/rest-auth-active-positions
1666
+ * @param {string[]|undefined} symbols list of unified market symbols
1667
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1668
+ * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
1669
+ */
1670
+ async fetchPositions(symbols = undefined, params = {}) {
1671
+ await this.loadMarkets();
1672
+ const response = await this.privatePostPositions(params);
1673
+ //
1674
+ // [
1675
+ // {
1676
+ // "id":943715,
1677
+ // "symbol":"btcusd",
1678
+ // "status":"ACTIVE",
1679
+ // "base":"246.94",
1680
+ // "amount":"1.0",
1681
+ // "timestamp":"1444141857.0",
1682
+ // "swap":"0.0",
1683
+ // "pl":"-2.22042"
1684
+ // }
1685
+ // ]
1686
+ //
1687
+ // todo unify parsePosition/parsePositions
1688
+ return response;
1689
+ }
1690
+ nonce() {
1691
+ return this.microseconds();
1692
+ }
1693
+ sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
1694
+ let request = '/' + this.implodeParams(path, params);
1695
+ if (api === 'v2') {
1696
+ request = '/' + api + request;
1697
+ }
1698
+ else {
1699
+ request = '/' + this.version + request;
1700
+ }
1701
+ let query = this.omit(params, this.extractParams(path));
1702
+ let url = this.urls['api'][api] + request;
1703
+ if ((api === 'public') || (path.indexOf('/hist') >= 0)) {
1704
+ if (Object.keys(query).length) {
1705
+ const suffix = '?' + this.urlencode(query);
1706
+ url += suffix;
1707
+ request += suffix;
1708
+ }
1709
+ }
1710
+ if (api === 'private') {
1711
+ this.checkRequiredCredentials();
1712
+ const nonce = this.nonce();
1713
+ query = this.extend({
1714
+ 'nonce': nonce.toString(),
1715
+ 'request': request,
1716
+ }, query);
1717
+ body = this.json(query);
1718
+ const payload = this.stringToBase64(body);
1719
+ const secret = this.encode(this.secret);
1720
+ const signature = this.hmac(this.encode(payload), secret, sha512.sha384);
1721
+ headers = {
1722
+ 'X-BFX-APIKEY': this.apiKey,
1723
+ 'X-BFX-PAYLOAD': payload,
1724
+ 'X-BFX-SIGNATURE': signature,
1725
+ 'Content-Type': 'application/json',
1726
+ };
1727
+ }
1728
+ return { 'url': url, 'method': method, 'body': body, 'headers': headers };
1729
+ }
1730
+ handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
1731
+ if (response === undefined) {
1732
+ return undefined;
1733
+ }
1734
+ let throwError = false;
1735
+ if (code >= 400) {
1736
+ if (body[0] === '{') {
1737
+ throwError = true;
1738
+ }
1739
+ }
1740
+ else {
1741
+ // json response with error, i.e:
1742
+ // [{"status":"error","message":"Momentary balance check. Please wait few seconds and try the transfer again."}]
1743
+ const responseObject = this.safeDict(response, 0, {});
1744
+ const status = this.safeString(responseObject, 'status', '');
1745
+ if (status === 'error') {
1746
+ throwError = true;
1747
+ }
1748
+ }
1749
+ if (throwError) {
1750
+ const feedback = this.id + ' ' + body;
1751
+ const message = this.safeString2(response, 'message', 'error');
1752
+ this.throwExactlyMatchedException(this.exceptions['exact'], message, feedback);
1753
+ this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
1754
+ throw new errors.ExchangeError(feedback); // unknown message
1755
+ }
1756
+ return undefined;
1757
+ }
1758
+ }
1759
+
1760
+ module.exports = bitfinex1;