ccxt 4.4.90__py2.py3-none-any.whl → 4.4.92__py2.py3-none-any.whl

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