ccxt 4.4.42__py2.py3-none-any.whl → 4.4.43__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.
@@ -1,3625 +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.bitfinex2 import ImplicitAPI
8
- import asyncio
9
- import hashlib
10
- from ccxt.base.types import Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, 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 BadRequest
17
- from ccxt.base.errors import BadSymbol
18
- from ccxt.base.errors import InsufficientFunds
19
- from ccxt.base.errors import InvalidOrder
20
- from ccxt.base.errors import OrderNotFound
21
- from ccxt.base.errors import NotSupported
22
- from ccxt.base.errors import RateLimitExceeded
23
- from ccxt.base.errors import ExchangeNotAvailable
24
- from ccxt.base.errors import OnMaintenance
25
- from ccxt.base.errors import InvalidNonce
26
- from ccxt.base.decimal_to_precision import ROUND
27
- from ccxt.base.decimal_to_precision import TRUNCATE
28
- from ccxt.base.decimal_to_precision import DECIMAL_PLACES
29
- from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS
30
- from ccxt.base.precise import Precise
31
-
32
-
33
- class bitfinex2(Exchange, ImplicitAPI):
34
-
35
- def describe(self):
36
- return self.deep_extend(super(bitfinex2, self).describe(), {
37
- 'id': 'bitfinex2',
38
- 'name': 'Bitfinex',
39
- 'countries': ['VG'],
40
- 'version': 'v2',
41
- 'certified': False,
42
- 'pro': True,
43
- # new metainfo interface
44
- 'has': {
45
- 'CORS': None,
46
- 'spot': True,
47
- 'margin': True,
48
- 'swap': True,
49
- 'future': False,
50
- 'option': False,
51
- 'addMargin': False,
52
- 'borrowCrossMargin': False,
53
- 'borrowIsolatedMargin': False,
54
- 'cancelAllOrders': True,
55
- 'cancelOrder': True,
56
- 'cancelOrders': True,
57
- 'createDepositAddress': True,
58
- 'createLimitOrder': True,
59
- 'createMarketOrder': True,
60
- 'createOrder': True,
61
- 'createPostOnlyOrder': True,
62
- 'createReduceOnlyOrder': True,
63
- 'createStopLimitOrder': True,
64
- 'createStopMarketOrder': True,
65
- 'createStopOrder': True,
66
- 'createTrailingAmountOrder': True,
67
- 'createTrailingPercentOrder': False,
68
- 'createTriggerOrder': True,
69
- 'editOrder': True,
70
- 'fetchBalance': True,
71
- 'fetchBorrowInterest': False,
72
- 'fetchBorrowRate': False,
73
- 'fetchBorrowRateHistories': False,
74
- 'fetchBorrowRateHistory': False,
75
- 'fetchBorrowRates': False,
76
- 'fetchBorrowRatesPerSymbol': False,
77
- 'fetchClosedOrder': True,
78
- 'fetchClosedOrders': True,
79
- 'fetchCrossBorrowRate': False,
80
- 'fetchCrossBorrowRates': False,
81
- 'fetchCurrencies': True,
82
- 'fetchDepositAddress': True,
83
- 'fetchDepositAddresses': False,
84
- 'fetchDepositAddressesByNetwork': False,
85
- 'fetchDepositsWithdrawals': True,
86
- 'fetchFundingHistory': False,
87
- 'fetchFundingRate': 'emulated', # emulated in exchange
88
- 'fetchFundingRateHistory': True,
89
- 'fetchFundingRates': True,
90
- 'fetchIndexOHLCV': False,
91
- 'fetchIsolatedBorrowRate': False,
92
- 'fetchIsolatedBorrowRates': False,
93
- 'fetchLedger': True,
94
- 'fetchLeverage': False,
95
- 'fetchLeverageTiers': False,
96
- 'fetchLiquidations': True,
97
- 'fetchMarginMode': False,
98
- 'fetchMarketLeverageTiers': False,
99
- 'fetchMarkOHLCV': False,
100
- 'fetchMyTrades': True,
101
- 'fetchOHLCV': True,
102
- 'fetchOpenInterest': True,
103
- 'fetchOpenInterestHistory': True,
104
- 'fetchOpenOrder': True,
105
- 'fetchOpenOrders': True,
106
- 'fetchOrder': True,
107
- 'fetchOrderBook': True,
108
- 'fetchOrderBooks': False,
109
- 'fetchOrderTrades': True,
110
- 'fetchPosition': False,
111
- 'fetchPositionMode': False,
112
- 'fetchPositions': True,
113
- 'fetchPremiumIndexOHLCV': False,
114
- 'fetchStatus': True,
115
- 'fetchTickers': True,
116
- 'fetchTime': False,
117
- 'fetchTradingFee': False,
118
- 'fetchTradingFees': True,
119
- 'fetchTransactionFees': None,
120
- 'fetchTransactions': 'emulated',
121
- 'reduceMargin': False,
122
- 'repayCrossMargin': False,
123
- 'repayIsolatedMargin': False,
124
- 'setLeverage': False,
125
- 'setMargin': True,
126
- 'setMarginMode': False,
127
- 'setPositionMode': False,
128
- 'signIn': False,
129
- 'transfer': True,
130
- 'withdraw': True,
131
- },
132
- 'timeframes': {
133
- '1m': '1m',
134
- '5m': '5m',
135
- '15m': '15m',
136
- '30m': '30m',
137
- '1h': '1h',
138
- '3h': '3h',
139
- '4h': '4h',
140
- '6h': '6h',
141
- '12h': '12h',
142
- '1d': '1D',
143
- '1w': '7D',
144
- '2w': '14D',
145
- '1M': '1M',
146
- },
147
- # cheapest endpoint is 240 requests per minute => ~ 4 requests per second =>( 1000ms / 4 ) = 250ms between requests on average
148
- 'rateLimit': 250,
149
- 'urls': {
150
- 'logo': 'https://github.com/user-attachments/assets/4a8e947f-ab46-481a-a8ae-8b20e9b03178',
151
- 'api': {
152
- 'v1': 'https://api.bitfinex.com',
153
- 'public': 'https://api-pub.bitfinex.com',
154
- 'private': 'https://api.bitfinex.com',
155
- },
156
- 'www': 'https://www.bitfinex.com',
157
- 'doc': [
158
- 'https://docs.bitfinex.com/v2/docs/',
159
- 'https://github.com/bitfinexcom/bitfinex-api-node',
160
- ],
161
- 'fees': 'https://www.bitfinex.com/fees',
162
- },
163
- 'api': {
164
- 'public': {
165
- 'get': {
166
- 'conf/{config}': 2.7, # 90 requests a minute, 90/60 = 1.5, 1000 / (250 * 2.66) = 1.503, use 2.7 instead of 2.66 to ensure rateLimitExceeded is not triggered
167
- 'conf/pub:{action}:{object}': 2.7,
168
- 'conf/pub:{action}:{object}:{detail}': 2.7,
169
- 'conf/pub:map:{object}': 2.7,
170
- 'conf/pub:map:{object}:{detail}': 2.7,
171
- 'conf/pub:map:currency:{detail}': 2.7,
172
- 'conf/pub:map:currency:sym': 2.7, # maps symbols to their API symbols, BAB > BCH
173
- 'conf/pub:map:currency:label': 2.7, # verbose friendly names, BNT > Bancor
174
- 'conf/pub:map:currency:unit': 2.7, # maps symbols to unit of measure where applicable
175
- 'conf/pub:map:currency:undl': 2.7, # maps derivatives symbols to their underlying currency
176
- 'conf/pub:map:currency:pool': 2.7, # maps symbols to underlying network/protocol they operate on
177
- 'conf/pub:map:currency:explorer': 2.7, # maps symbols to their recognised block explorer URLs
178
- 'conf/pub:map:currency:tx:fee': 2.7, # maps currencies to their withdrawal fees https://github.com/ccxt/ccxt/issues/7745
179
- 'conf/pub:map:tx:method': 2.7,
180
- 'conf/pub:list:{object}': 2.7,
181
- 'conf/pub:list:{object}:{detail}': 2.7,
182
- 'conf/pub:list:currency': 2.7,
183
- 'conf/pub:list:pair:exchange': 2.7,
184
- 'conf/pub:list:pair:margin': 2.7,
185
- 'conf/pub:list:pair:futures': 2.7,
186
- 'conf/pub:list:competitions': 2.7,
187
- 'conf/pub:info:{object}': 2.7,
188
- 'conf/pub:info:{object}:{detail}': 2.7,
189
- 'conf/pub:info:pair': 2.7,
190
- 'conf/pub:info:pair:futures': 2.7,
191
- 'conf/pub:info:tx:status': 2.7, # [deposit, withdrawal] statuses 1 = active, 0 = maintenance
192
- 'conf/pub:fees': 2.7,
193
- 'platform/status': 8, # 30 requests per minute = 0.5 requests per second =>( 1000ms / rateLimit ) / 0.5 = 8
194
- 'tickers': 2.7, # 90 requests a minute = 1.5 requests per second =>( 1000 / rateLimit ) / 1.5 = 2.666666666
195
- 'ticker/{symbol}': 2.7,
196
- 'tickers/hist': 2.7,
197
- 'trades/{symbol}/hist': 2.7,
198
- 'book/{symbol}/{precision}': 1, # 240 requests a minute
199
- 'book/{symbol}/P0': 1,
200
- 'book/{symbol}/P1': 1,
201
- 'book/{symbol}/P2': 1,
202
- 'book/{symbol}/P3': 1,
203
- 'book/{symbol}/R0': 1,
204
- 'stats1/{key}:{size}:{symbol}:{side}/{section}': 2.7,
205
- 'stats1/{key}:{size}:{symbol}:{side}/last': 2.7,
206
- 'stats1/{key}:{size}:{symbol}:{side}/hist': 2.7,
207
- 'stats1/{key}:{size}:{symbol}/{section}': 2.7,
208
- 'stats1/{key}:{size}:{symbol}/last': 2.7,
209
- 'stats1/{key}:{size}:{symbol}/hist': 2.7,
210
- 'stats1/{key}:{size}:{symbol}:long/last': 2.7,
211
- 'stats1/{key}:{size}:{symbol}:long/hist': 2.7,
212
- 'stats1/{key}:{size}:{symbol}:short/last': 2.7,
213
- 'stats1/{key}:{size}:{symbol}:short/hist': 2.7,
214
- 'candles/trade:{timeframe}:{symbol}:{period}/{section}': 2.7,
215
- 'candles/trade:{timeframe}:{symbol}/{section}': 2.7,
216
- 'candles/trade:{timeframe}:{symbol}/last': 2.7,
217
- 'candles/trade:{timeframe}:{symbol}/hist': 2.7,
218
- 'status/{type}': 2.7,
219
- 'status/deriv': 2.7,
220
- 'status/deriv/{symbol}/hist': 2.7,
221
- 'liquidations/hist': 80, # 3 requests a minute = 0.05 requests a second =>( 1000ms / rateLimit ) / 0.05 = 80
222
- 'rankings/{key}:{timeframe}:{symbol}/{section}': 2.7,
223
- 'rankings/{key}:{timeframe}:{symbol}/hist': 2.7,
224
- 'pulse/hist': 2.7,
225
- 'pulse/profile/{nickname}': 2.7,
226
- 'funding/stats/{symbol}/hist': 10, # ratelimit not in docs
227
- 'ext/vasps': 1,
228
- },
229
- 'post': {
230
- 'calc/trade/avg': 2.7,
231
- 'calc/fx': 2.7,
232
- },
233
- },
234
- 'private': {
235
- 'post': {
236
- # 'auth/r/orders/{symbol}/new', # outdated
237
- # 'auth/r/stats/perf:{timeframe}/hist', # outdated
238
- 'auth/r/wallets': 2.7,
239
- 'auth/r/wallets/hist': 2.7,
240
- 'auth/r/orders': 2.7,
241
- 'auth/r/orders/{symbol}': 2.7,
242
- 'auth/w/order/submit': 2.7,
243
- 'auth/w/order/update': 2.7,
244
- 'auth/w/order/cancel': 2.7,
245
- 'auth/w/order/multi': 2.7,
246
- 'auth/w/order/cancel/multi': 2.7,
247
- 'auth/r/orders/{symbol}/hist': 2.7,
248
- 'auth/r/orders/hist': 2.7,
249
- 'auth/r/order/{symbol}:{id}/trades': 2.7,
250
- 'auth/r/trades/{symbol}/hist': 2.7,
251
- 'auth/r/trades/hist': 2.7,
252
- 'auth/r/ledgers/{currency}/hist': 2.7,
253
- 'auth/r/ledgers/hist': 2.7,
254
- 'auth/r/info/margin/{key}': 2.7,
255
- 'auth/r/info/margin/base': 2.7,
256
- 'auth/r/info/margin/sym_all': 2.7,
257
- 'auth/r/positions': 2.7,
258
- 'auth/w/position/claim': 2.7,
259
- 'auth/w/position/increase:': 2.7,
260
- 'auth/r/position/increase/info': 2.7,
261
- 'auth/r/positions/hist': 2.7,
262
- 'auth/r/positions/audit': 2.7,
263
- 'auth/r/positions/snap': 2.7,
264
- 'auth/w/deriv/collateral/set': 2.7,
265
- 'auth/w/deriv/collateral/limits': 2.7,
266
- 'auth/r/funding/offers': 2.7,
267
- 'auth/r/funding/offers/{symbol}': 2.7,
268
- 'auth/w/funding/offer/submit': 2.7,
269
- 'auth/w/funding/offer/cancel': 2.7,
270
- 'auth/w/funding/offer/cancel/all': 2.7,
271
- 'auth/w/funding/close': 2.7,
272
- 'auth/w/funding/auto': 2.7,
273
- 'auth/w/funding/keep': 2.7,
274
- 'auth/r/funding/offers/{symbol}/hist': 2.7,
275
- 'auth/r/funding/offers/hist': 2.7,
276
- 'auth/r/funding/loans': 2.7,
277
- 'auth/r/funding/loans/hist': 2.7,
278
- 'auth/r/funding/loans/{symbol}': 2.7,
279
- 'auth/r/funding/loans/{symbol}/hist': 2.7,
280
- 'auth/r/funding/credits': 2.7,
281
- 'auth/r/funding/credits/hist': 2.7,
282
- 'auth/r/funding/credits/{symbol}': 2.7,
283
- 'auth/r/funding/credits/{symbol}/hist': 2.7,
284
- 'auth/r/funding/trades/{symbol}/hist': 2.7,
285
- 'auth/r/funding/trades/hist': 2.7,
286
- 'auth/r/info/funding/{key}': 2.7,
287
- 'auth/r/info/user': 2.7,
288
- 'auth/r/summary': 2.7,
289
- 'auth/r/logins/hist': 2.7,
290
- 'auth/r/permissions': 2.7,
291
- 'auth/w/token': 2.7,
292
- 'auth/r/audit/hist': 2.7,
293
- 'auth/w/transfer': 2.7, # ratelimit not in docs...
294
- 'auth/w/deposit/address': 24, # 10 requests a minute = 0.166 requests per second =>( 1000ms / rateLimit ) / 0.166 = 24
295
- 'auth/w/deposit/invoice': 24, # ratelimit not in docs
296
- 'auth/w/withdraw': 24, # ratelimit not in docs
297
- 'auth/r/movements/{currency}/hist': 2.7,
298
- 'auth/r/movements/hist': 2.7,
299
- 'auth/r/alerts': 5.34, # 45 requests a minute = 0.75 requests per second =>( 1000ms / rateLimit ) / 0.749 => 5.34
300
- 'auth/w/alert/set': 2.7,
301
- 'auth/w/alert/price:{symbol}:{price}/del': 2.7,
302
- 'auth/w/alert/{type}:{symbol}:{price}/del': 2.7,
303
- 'auth/calc/order/avail': 2.7,
304
- 'auth/w/settings/set': 2.7,
305
- 'auth/r/settings': 2.7,
306
- 'auth/w/settings/del': 2.7,
307
- 'auth/r/pulse/hist': 2.7,
308
- 'auth/w/pulse/add': 16, # 15 requests a minute = 0.25 requests per second =>( 1000ms / rateLimit ) / 0.25 => 16
309
- 'auth/w/pulse/del': 2.7,
310
- },
311
- },
312
- },
313
- 'fees': {
314
- 'trading': {
315
- 'feeSide': 'get',
316
- 'percentage': True,
317
- 'tierBased': True,
318
- 'maker': self.parse_number('0.001'),
319
- 'taker': self.parse_number('0.002'),
320
- 'tiers': {
321
- 'taker': [
322
- [self.parse_number('0'), self.parse_number('0.002')],
323
- [self.parse_number('500000'), self.parse_number('0.002')],
324
- [self.parse_number('1000000'), self.parse_number('0.002')],
325
- [self.parse_number('2500000'), self.parse_number('0.002')],
326
- [self.parse_number('5000000'), self.parse_number('0.002')],
327
- [self.parse_number('7500000'), self.parse_number('0.002')],
328
- [self.parse_number('10000000'), self.parse_number('0.0018')],
329
- [self.parse_number('15000000'), self.parse_number('0.0016')],
330
- [self.parse_number('20000000'), self.parse_number('0.0014')],
331
- [self.parse_number('25000000'), self.parse_number('0.0012')],
332
- [self.parse_number('30000000'), self.parse_number('0.001')],
333
- ],
334
- 'maker': [
335
- [self.parse_number('0'), self.parse_number('0.001')],
336
- [self.parse_number('500000'), self.parse_number('0.0008')],
337
- [self.parse_number('1000000'), self.parse_number('0.0006')],
338
- [self.parse_number('2500000'), self.parse_number('0.0004')],
339
- [self.parse_number('5000000'), self.parse_number('0.0002')],
340
- [self.parse_number('7500000'), self.parse_number('0')],
341
- [self.parse_number('10000000'), self.parse_number('0')],
342
- [self.parse_number('15000000'), self.parse_number('0')],
343
- [self.parse_number('20000000'), self.parse_number('0')],
344
- [self.parse_number('25000000'), self.parse_number('0')],
345
- [self.parse_number('30000000'), self.parse_number('0')],
346
- ],
347
- },
348
- },
349
- 'funding': {
350
- 'withdraw': {},
351
- },
352
- },
353
- 'precisionMode': SIGNIFICANT_DIGITS,
354
- 'options': {
355
- 'precision': 'R0', # P0, P1, P2, P3, P4, R0
356
- # convert 'EXCHANGE MARKET' to lowercase 'market'
357
- # convert 'EXCHANGE LIMIT' to lowercase 'limit'
358
- # everything else remains uppercase
359
- 'exchangeTypes': {
360
- 'MARKET': 'market',
361
- 'EXCHANGE MARKET': 'market',
362
- 'LIMIT': 'limit',
363
- 'EXCHANGE LIMIT': 'limit',
364
- # 'STOP': None,
365
- 'EXCHANGE STOP': 'market',
366
- # 'TRAILING STOP': None,
367
- # 'EXCHANGE TRAILING STOP': None,
368
- # 'FOK': None,
369
- 'EXCHANGE FOK': 'limit',
370
- # 'STOP LIMIT': None,
371
- 'EXCHANGE STOP LIMIT': 'limit',
372
- # 'IOC': None,
373
- 'EXCHANGE IOC': 'limit',
374
- },
375
- # convert 'market' to 'EXCHANGE MARKET'
376
- # convert 'limit' 'EXCHANGE LIMIT'
377
- # everything else remains
378
- 'orderTypes': {
379
- 'market': 'EXCHANGE MARKET',
380
- 'limit': 'EXCHANGE LIMIT',
381
- },
382
- 'fiat': {
383
- 'USD': 'USD',
384
- 'EUR': 'EUR',
385
- 'JPY': 'JPY',
386
- 'GBP': 'GBP',
387
- 'CHN': 'CHN',
388
- },
389
- # actually the correct names unlike the v1
390
- # we don't want to self.extend self with accountsByType in v1
391
- 'v2AccountsByType': {
392
- 'spot': 'exchange',
393
- 'exchange': 'exchange',
394
- 'funding': 'funding',
395
- 'margin': 'margin',
396
- 'derivatives': 'margin',
397
- 'future': 'margin',
398
- 'swap': 'margin',
399
- },
400
- 'withdraw': {
401
- 'includeFee': False,
402
- },
403
- 'networks': {
404
- 'BTC': 'BITCOIN',
405
- 'LTC': 'LITECOIN',
406
- 'ERC20': 'ETHEREUM',
407
- 'OMNI': 'TETHERUSO',
408
- 'LIQUID': 'TETHERUSL',
409
- 'TRC20': 'TETHERUSX',
410
- 'EOS': 'TETHERUSS',
411
- 'AVAX': 'TETHERUSDTAVAX',
412
- 'SOL': 'TETHERUSDTSOL',
413
- 'ALGO': 'TETHERUSDTALG',
414
- 'BCH': 'TETHERUSDTBCH',
415
- 'KSM': 'TETHERUSDTKSM',
416
- 'DVF': 'TETHERUSDTDVF',
417
- 'OMG': 'TETHERUSDTOMG',
418
- },
419
- 'networksById': {
420
- 'TETHERUSE': 'ERC20',
421
- },
422
- },
423
- 'exceptions': {
424
- 'exact': {
425
- '11010': RateLimitExceeded,
426
- '10001': PermissionDenied, # api_key: permission invalid(#10001)
427
- '10020': BadRequest,
428
- '10100': AuthenticationError,
429
- '10114': InvalidNonce,
430
- '20060': OnMaintenance,
431
- # {"code":503,"error":"temporarily_unavailable","error_description":"Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info."}
432
- 'temporarily_unavailable': ExchangeNotAvailable,
433
- },
434
- 'broad': {
435
- 'available balance is only': InsufficientFunds,
436
- 'not enough exchange balance': InsufficientFunds,
437
- 'Order not found': OrderNotFound,
438
- 'symbol: invalid': BadSymbol,
439
- },
440
- },
441
- 'commonCurrencies': {
442
- 'UST': 'USDT',
443
- 'EUTF0': 'EURT',
444
- 'USTF0': 'USDT',
445
- 'ALG': 'ALGO', # https://github.com/ccxt/ccxt/issues/6034
446
- 'AMP': 'AMPL',
447
- 'ATO': 'ATOM', # https://github.com/ccxt/ccxt/issues/5118
448
- 'BCHABC': 'XEC',
449
- 'BCHN': 'BCH',
450
- 'DAT': 'DATA',
451
- 'DOG': 'MDOGE',
452
- 'DSH': 'DASH',
453
- 'EDO': 'PNT',
454
- 'EUS': 'EURS',
455
- 'EUT': 'EURT',
456
- 'HTX': 'HT',
457
- 'IDX': 'ID',
458
- 'IOT': 'IOTA',
459
- 'IQX': 'IQ',
460
- 'LUNA': 'LUNC',
461
- 'LUNA2': 'LUNA',
462
- 'MNA': 'MANA',
463
- 'ORS': 'ORS Group', # conflict with Origin Sport #3230
464
- 'PAS': 'PASS',
465
- 'QSH': 'QASH',
466
- 'QTM': 'QTUM',
467
- 'RBT': 'RBTC',
468
- 'SNG': 'SNGLS',
469
- 'STJ': 'STORJ',
470
- 'TERRAUST': 'USTC',
471
- 'TSD': 'TUSD',
472
- 'YGG': 'YEED', # conflict with Yield Guild Games
473
- 'YYW': 'YOYOW',
474
- 'UDC': 'USDC',
475
- 'VSY': 'VSYS',
476
- 'WAX': 'WAXP',
477
- 'XCH': 'XCHF',
478
- 'ZBT': 'ZB',
479
- },
480
- })
481
-
482
- def is_fiat(self, code):
483
- return(code in self.options['fiat'])
484
-
485
- def get_currency_id(self, code):
486
- return 'f' + code
487
-
488
- def get_currency_name(self, code):
489
- # temporary fix for transpiler recognition, even though self is in parent class
490
- if code in self.options['currencyNames']:
491
- return self.options['currencyNames'][code]
492
- raise NotSupported(self.id + ' ' + code + ' not supported for withdrawal')
493
-
494
- def amount_to_precision(self, symbol, amount):
495
- # https://docs.bitfinex.com/docs/introduction#amount-precision
496
- # The amount field allows up to 8 decimals.
497
- # Anything exceeding self will be rounded to the 8th decimal.
498
- symbol = self.safe_symbol(symbol)
499
- return self.decimal_to_precision(amount, TRUNCATE, self.markets[symbol]['precision']['amount'], DECIMAL_PLACES)
500
-
501
- def price_to_precision(self, symbol, price):
502
- symbol = self.safe_symbol(symbol)
503
- price = self.decimal_to_precision(price, ROUND, self.markets[symbol]['precision']['price'], self.precisionMode)
504
- # https://docs.bitfinex.com/docs/introduction#price-precision
505
- # The precision level of all trading prices is based on significant figures.
506
- # 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).
507
- # Prices submit with a precision larger than 5 will be cut by the API.
508
- return self.decimal_to_precision(price, TRUNCATE, 8, DECIMAL_PLACES)
509
-
510
- async def fetch_status(self, params={}):
511
- """
512
- the latest known information on the availability of the exchange API
513
-
514
- https://docs.bitfinex.com/reference/rest-public-platform-status
515
-
516
- :param dict [params]: extra parameters specific to the exchange API endpoint
517
- :returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
518
- """
519
- #
520
- # [1] # operative
521
- # [0] # maintenance
522
- #
523
- response = await self.publicGetPlatformStatus(params)
524
- statusRaw = self.safe_string(response, 0)
525
- return {
526
- 'status': self.safe_string({'0': 'maintenance', '1': 'ok'}, statusRaw, statusRaw),
527
- 'updated': None,
528
- 'eta': None,
529
- 'url': None,
530
- 'info': response,
531
- }
532
-
533
- async def fetch_markets(self, params={}) -> List[Market]:
534
- """
535
- retrieves data on all markets for bitfinex2
536
-
537
- https://docs.bitfinex.com/reference/rest-public-conf
538
-
539
- :param dict [params]: extra parameters specific to the exchange API endpoint
540
- :returns dict[]: an array of objects representing market data
541
- """
542
- spotMarketsInfoPromise = self.publicGetConfPubInfoPair(params)
543
- futuresMarketsInfoPromise = self.publicGetConfPubInfoPairFutures(params)
544
- marginIdsPromise = self.publicGetConfPubListPairMargin(params)
545
- spotMarketsInfo, futuresMarketsInfo, marginIds = await asyncio.gather(*[spotMarketsInfoPromise, futuresMarketsInfoPromise, marginIdsPromise])
546
- spotMarketsInfo = self.safe_list(spotMarketsInfo, 0, [])
547
- futuresMarketsInfo = self.safe_list(futuresMarketsInfo, 0, [])
548
- markets = self.array_concat(spotMarketsInfo, futuresMarketsInfo)
549
- marginIds = self.safe_value(marginIds, 0, [])
550
- #
551
- # [
552
- # "1INCH:USD",
553
- # [
554
- # null,
555
- # null,
556
- # null,
557
- # "2.0",
558
- # "100000.0",
559
- # null,
560
- # null,
561
- # null,
562
- # null,
563
- # null,
564
- # null,
565
- # null
566
- # ]
567
- # ]
568
- #
569
- result = []
570
- for i in range(0, len(markets)):
571
- pair = markets[i]
572
- id = self.safe_string_upper(pair, 0)
573
- market = self.safe_value(pair, 1, {})
574
- spot = True
575
- if id.find('F0') >= 0:
576
- spot = False
577
- swap = not spot
578
- baseId = None
579
- quoteId = None
580
- if id.find(':') >= 0:
581
- parts = id.split(':')
582
- baseId = parts[0]
583
- quoteId = parts[1]
584
- else:
585
- baseId = id[0:3]
586
- quoteId = id[3:6]
587
- base = self.safe_currency_code(baseId)
588
- quote = self.safe_currency_code(quoteId)
589
- splitBase = base.split('F0')
590
- splitQuote = quote.split('F0')
591
- base = self.safe_string(splitBase, 0)
592
- quote = self.safe_string(splitQuote, 0)
593
- symbol = base + '/' + quote
594
- baseId = self.get_currency_id(baseId)
595
- quoteId = self.get_currency_id(quoteId)
596
- settle = None
597
- settleId = None
598
- if swap:
599
- settle = quote
600
- settleId = quote
601
- symbol = symbol + ':' + settle
602
- minOrderSizeString = self.safe_string(market, 3)
603
- maxOrderSizeString = self.safe_string(market, 4)
604
- margin = False
605
- if spot and self.in_array(id, marginIds):
606
- margin = True
607
- result.append({
608
- 'id': 't' + id,
609
- 'symbol': symbol,
610
- 'base': base,
611
- 'quote': quote,
612
- 'settle': settle,
613
- 'baseId': baseId,
614
- 'quoteId': quoteId,
615
- 'settleId': settleId,
616
- 'type': 'spot' if spot else 'swap',
617
- 'spot': spot,
618
- 'margin': margin,
619
- 'swap': swap,
620
- 'future': False,
621
- 'option': False,
622
- 'active': True,
623
- 'contract': swap,
624
- 'linear': True if swap else None,
625
- 'inverse': False if swap else None,
626
- 'contractSize': self.parse_number('1') if swap else None,
627
- 'expiry': None,
628
- 'expiryDatetime': None,
629
- 'strike': None,
630
- 'optionType': None,
631
- 'precision': {
632
- 'amount': int('8'), # https://github.com/ccxt/ccxt/issues/7310
633
- 'price': int('5'),
634
- },
635
- 'limits': {
636
- 'leverage': {
637
- 'min': None,
638
- 'max': None,
639
- },
640
- 'amount': {
641
- 'min': self.parse_number(minOrderSizeString),
642
- 'max': self.parse_number(maxOrderSizeString),
643
- },
644
- 'price': {
645
- 'min': self.parse_number('1e-8'),
646
- 'max': None,
647
- },
648
- 'cost': {
649
- 'min': None,
650
- 'max': None,
651
- },
652
- },
653
- 'created': None, # todo: the api needs revision for extra params & endpoints for possibility of returning a timestamp for self
654
- 'info': market,
655
- })
656
- return result
657
-
658
- async def fetch_currencies(self, params={}) -> Currencies:
659
- """
660
- fetches all available currencies on an exchange
661
-
662
- https://docs.bitfinex.com/reference/rest-public-conf
663
-
664
- :param dict [params]: extra parameters specific to the exchange API endpoint
665
- :returns dict: an associative dictionary of currencies
666
- """
667
- labels = [
668
- 'pub:list:currency',
669
- 'pub:map:currency:sym', # maps symbols to their API symbols, BAB > BCH
670
- 'pub:map:currency:label', # verbose friendly names, BNT > Bancor
671
- 'pub:map:currency:unit', # maps symbols to unit of measure where applicable
672
- 'pub:map:currency:undl', # maps derivatives symbols to their underlying currency
673
- 'pub:map:currency:pool', # maps symbols to underlying network/protocol they operate on
674
- 'pub:map:currency:explorer', # maps symbols to their recognised block explorer URLs
675
- 'pub:map:currency:tx:fee', # maps currencies to their withdrawal fees https://github.com/ccxt/ccxt/issues/7745,
676
- 'pub:map:tx:method', # maps withdrawal/deposit methods to their API symbols
677
- ]
678
- config = ','.join(labels)
679
- request: dict = {
680
- 'config': config,
681
- }
682
- response = await self.publicGetConfConfig(self.extend(request, params))
683
- #
684
- # [
685
- #
686
- # a list of symbols
687
- # ["AAA","ABS","ADA"],
688
- #
689
- # # sym
690
- # # maps symbols to their API symbols, BAB > BCH
691
- # [
692
- # ["BAB", "BCH"],
693
- # ["CNHT", "CNHt"],
694
- # ["DSH", "DASH"],
695
- # ["IOT", "IOTA"],
696
- # ["LES", "LEO-EOS"],
697
- # ["LET", "LEO-ERC20"],
698
- # ["STJ", "STORJ"],
699
- # ["TSD", "TUSD"],
700
- # ["UDC", "USDC"],
701
- # ["USK", "USDK"],
702
- # ["UST", "USDt"],
703
- # ["USTF0", "USDt0"],
704
- # ["XCH", "XCHF"],
705
- # ["YYW", "YOYOW"],
706
- # # ...
707
- # ],
708
- # # label
709
- # # verbose friendly names, BNT > Bancor
710
- # [
711
- # ["BAB", "Bitcoin Cash"],
712
- # ["BCH", "Bitcoin Cash"],
713
- # ["LEO", "Unus Sed LEO"],
714
- # ["LES", "Unus Sed LEO(EOS)"],
715
- # ["LET", "Unus Sed LEO(ERC20)"],
716
- # # ...
717
- # ],
718
- # # unit
719
- # # maps symbols to unit of measure where applicable
720
- # [
721
- # ["IOT", "Mi|MegaIOTA"],
722
- # ],
723
- # # undl
724
- # # maps derivatives symbols to their underlying currency
725
- # [
726
- # ["USTF0", "UST"],
727
- # ["BTCF0", "BTC"],
728
- # ["ETHF0", "ETH"],
729
- # ],
730
- # # pool
731
- # # maps symbols to underlying network/protocol they operate on
732
- # [
733
- # ['SAN', 'ETH'], ['OMG', 'ETH'], ['AVT', 'ETH'], ["EDO", "ETH"],
734
- # ['ESS', 'ETH'], ['ATD', 'EOS'], ['ADD', 'EOS'], ["MTO", "EOS"],
735
- # ['PNK', 'ETH'], ['BAB', 'BCH'], ['WLO', 'XLM'], ["VLD", "ETH"],
736
- # ['BTT', 'TRX'], ['IMP', 'ETH'], ['SCR', 'ETH'], ["GNO", "ETH"],
737
- # # ...
738
- # ],
739
- # # explorer
740
- # # maps symbols to their recognised block explorer URLs
741
- # [
742
- # [
743
- # "AIO",
744
- # [
745
- # "https://mainnet.aion.network",
746
- # "https://mainnet.aion.network/#/account/VAL",
747
- # "https://mainnet.aion.network/#/transaction/VAL"
748
- # ]
749
- # ],
750
- # # ...
751
- # ],
752
- # # fee
753
- # # maps currencies to their withdrawal fees
754
- # [
755
- # ["AAA",[0,0]],
756
- # ["ABS",[0,131.3]],
757
- # ["ADA",[0,0.3]],
758
- # ],
759
- # ]
760
- #
761
- indexed: dict = {
762
- 'sym': self.index_by(self.safe_value(response, 1, []), 0),
763
- 'label': self.index_by(self.safe_value(response, 2, []), 0),
764
- 'unit': self.index_by(self.safe_value(response, 3, []), 0),
765
- 'undl': self.index_by(self.safe_value(response, 4, []), 0),
766
- 'pool': self.index_by(self.safe_value(response, 5, []), 0),
767
- 'explorer': self.index_by(self.safe_value(response, 6, []), 0),
768
- 'fees': self.index_by(self.safe_value(response, 7, []), 0),
769
- }
770
- ids = self.safe_value(response, 0, [])
771
- result: dict = {}
772
- for i in range(0, len(ids)):
773
- id = ids[i]
774
- if id.find('F0') >= 0:
775
- # we get a lot of F0 currencies, skip those
776
- continue
777
- code = self.safe_currency_code(id)
778
- label = self.safe_value(indexed['label'], id, [])
779
- name = self.safe_string(label, 1)
780
- pool = self.safe_value(indexed['pool'], id, [])
781
- rawType = self.safe_string(pool, 1)
782
- isCryptoCoin = (rawType is not None) or (id in indexed['explorer']) # "hacky" solution
783
- type = None
784
- if isCryptoCoin:
785
- type = 'crypto'
786
- feeValues = self.safe_value(indexed['fees'], id, [])
787
- fees = self.safe_value(feeValues, 1, [])
788
- fee = self.safe_number(fees, 1)
789
- undl = self.safe_value(indexed['undl'], id, [])
790
- precision = '8' # default precision, todo: fix "magic constants"
791
- fid = 'f' + id
792
- result[code] = {
793
- 'id': fid,
794
- 'uppercaseId': id,
795
- 'code': code,
796
- 'info': [id, label, pool, feeValues, undl],
797
- 'type': type,
798
- 'name': name,
799
- 'active': True,
800
- 'deposit': None,
801
- 'withdraw': None,
802
- 'fee': fee,
803
- 'precision': int(precision),
804
- 'limits': {
805
- 'amount': {
806
- 'min': self.parse_number(self.parse_precision(precision)),
807
- 'max': None,
808
- },
809
- 'withdraw': {
810
- 'min': fee,
811
- 'max': None,
812
- },
813
- },
814
- 'networks': {},
815
- }
816
- networks: dict = {}
817
- currencyNetworks = self.safe_value(response, 8, [])
818
- cleanId = id.replace('F0', '')
819
- for j in range(0, len(currencyNetworks)):
820
- pair = currencyNetworks[j]
821
- networkId = self.safe_string(pair, 0)
822
- currencyId = self.safe_string(self.safe_value(pair, 1, []), 0)
823
- if currencyId == cleanId:
824
- network = self.network_id_to_code(networkId)
825
- networks[network] = {
826
- 'info': networkId,
827
- 'id': networkId.lower(),
828
- 'network': networkId,
829
- 'active': None,
830
- 'deposit': None,
831
- 'withdraw': None,
832
- 'fee': None,
833
- 'precision': None,
834
- 'limits': {
835
- 'withdraw': {
836
- 'min': None,
837
- 'max': None,
838
- },
839
- },
840
- }
841
- keysNetworks = list(networks.keys())
842
- networksLength = len(keysNetworks)
843
- if networksLength > 0:
844
- result[code]['networks'] = networks
845
- return result
846
-
847
- async def fetch_balance(self, params={}) -> Balances:
848
- """
849
- query for balance and get the amount of funds available for trading or funds locked in orders
850
-
851
- https://docs.bitfinex.com/reference/rest-auth-wallets
852
-
853
- :param dict [params]: extra parameters specific to the exchange API endpoint
854
- :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
855
- """
856
- # self api call does not return the 'used' amount - use the v1 version instead(which also returns zero balances)
857
- # there is a difference between self and the v1 api, namely trading wallet is called margin in v2
858
- await self.load_markets()
859
- accountsByType = self.safe_value(self.options, 'v2AccountsByType', {})
860
- requestedType = self.safe_string(params, 'type', 'exchange')
861
- accountType = self.safe_string(accountsByType, requestedType, requestedType)
862
- if accountType is None:
863
- keys = list(accountsByType.keys())
864
- raise ExchangeError(self.id + ' fetchBalance() type parameter must be one of ' + ', '.join(keys))
865
- isDerivative = requestedType == 'derivatives'
866
- query = self.omit(params, 'type')
867
- response = await self.privatePostAuthRWallets(query)
868
- result: dict = {'info': response}
869
- for i in range(0, len(response)):
870
- balance = response[i]
871
- account = self.account()
872
- interest = self.safe_string(balance, 3)
873
- if interest != '0':
874
- account['debt'] = interest
875
- type = self.safe_string(balance, 0)
876
- currencyId = self.safe_string_lower(balance, 1, '')
877
- start = len(currencyId) - 2
878
- isDerivativeCode = currencyId[start:] == 'f0'
879
- # self will only filter the derivative codes if the requestedType is 'derivatives'
880
- derivativeCondition = (not isDerivative or isDerivativeCode)
881
- if (accountType == type) and derivativeCondition:
882
- code = self.safe_currency_code(currencyId)
883
- account['total'] = self.safe_string(balance, 2)
884
- account['free'] = self.safe_string(balance, 4)
885
- result[code] = account
886
- return self.safe_balance(result)
887
-
888
- async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
889
- """
890
- transfer currency internally between wallets on the same account
891
-
892
- https://docs.bitfinex.com/reference/rest-auth-transfer
893
-
894
- :param str code: unified currency code
895
- :param float amount: amount to transfer
896
- :param str fromAccount: account to transfer from
897
- :param str toAccount: account to transfer to
898
- :param dict [params]: extra parameters specific to the exchange API endpoint
899
- :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
900
- """
901
- # transferring between derivatives wallet and regular wallet is not documented in their API
902
- # however we support it in CCXT(from just looking at web inspector)
903
- await self.load_markets()
904
- accountsByType = self.safe_value(self.options, 'v2AccountsByType', {})
905
- fromId = self.safe_string(accountsByType, fromAccount)
906
- if fromId is None:
907
- keys = list(accountsByType.keys())
908
- raise ArgumentsRequired(self.id + ' transfer() fromAccount must be one of ' + ', '.join(keys))
909
- toId = self.safe_string(accountsByType, toAccount)
910
- if toId is None:
911
- keys = list(accountsByType.keys())
912
- raise ArgumentsRequired(self.id + ' transfer() toAccount must be one of ' + ', '.join(keys))
913
- currency = self.currency(code)
914
- fromCurrencyId = self.convert_derivatives_id(currency, fromAccount)
915
- toCurrencyId = self.convert_derivatives_id(currency, toAccount)
916
- requestedAmount = self.currency_to_precision(code, amount)
917
- # self request is slightly different from v1 fromAccount -> from
918
- request: dict = {
919
- 'amount': requestedAmount,
920
- 'currency': fromCurrencyId,
921
- 'currency_to': toCurrencyId,
922
- 'from': fromId,
923
- 'to': toId,
924
- }
925
- response = await self.privatePostAuthWTransfer(self.extend(request, params))
926
- #
927
- # [
928
- # 1616451183763,
929
- # "acc_tf",
930
- # null,
931
- # null,
932
- # [
933
- # 1616451183763,
934
- # "exchange",
935
- # "margin",
936
- # null,
937
- # "UST",
938
- # "UST",
939
- # null,
940
- # 1
941
- # ],
942
- # null,
943
- # "SUCCESS",
944
- # "1.0 Tether USDt transfered from Exchange to Margin"
945
- # ]
946
- #
947
- error = self.safe_string(response, 0)
948
- if error == 'error':
949
- message = self.safe_string(response, 2, '')
950
- # same message v1
951
- self.throw_exactly_matched_exception(self.exceptions['exact'], message, self.id + ' ' + message)
952
- raise ExchangeError(self.id + ' ' + message)
953
- return self.parse_transfer({'result': response}, currency)
954
-
955
- def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
956
- #
957
- # transfer
958
- #
959
- # [
960
- # 1616451183763,
961
- # "acc_tf",
962
- # null,
963
- # null,
964
- # [
965
- # 1616451183763,
966
- # "exchange",
967
- # "margin",
968
- # null,
969
- # "UST",
970
- # "UST",
971
- # null,
972
- # 1
973
- # ],
974
- # null,
975
- # "SUCCESS",
976
- # "1.0 Tether USDt transfered from Exchange to Margin"
977
- # ]
978
- #
979
- result = self.safe_list(transfer, 'result')
980
- timestamp = self.safe_integer(result, 0)
981
- info = self.safe_value(result, 4)
982
- fromAccount = self.safe_string(info, 1)
983
- toAccount = self.safe_string(info, 2)
984
- currencyId = self.safe_string(info, 5)
985
- status = self.safe_string(result, 6)
986
- return {
987
- 'id': None,
988
- 'timestamp': timestamp,
989
- 'datetime': self.iso8601(timestamp),
990
- 'status': self.parse_transfer_status(status),
991
- 'amount': self.safe_number(info, 7),
992
- 'currency': self.safe_currency_code(currencyId, currency),
993
- 'fromAccount': fromAccount,
994
- 'toAccount': toAccount,
995
- 'info': result,
996
- }
997
-
998
- def parse_transfer_status(self, status: Str) -> Str:
999
- statuses: dict = {
1000
- 'SUCCESS': 'ok',
1001
- 'ERROR': 'failed',
1002
- 'FAILURE': 'failed',
1003
- }
1004
- return self.safe_string(statuses, status, status)
1005
-
1006
- def convert_derivatives_id(self, currency, type):
1007
- # there is a difference between self and the v1 api, namely trading wallet is called margin in v2
1008
- # {
1009
- # "id": "fUSTF0",
1010
- # "code": "USTF0",
1011
- # "info": ['USTF0', [], [], [], ["USTF0", "UST"]],
1012
- info = self.safe_value(currency, 'info')
1013
- transferId = self.safe_string(info, 0)
1014
- underlying = self.safe_value(info, 4, [])
1015
- currencyId = None
1016
- if type == 'derivatives':
1017
- currencyId = self.safe_string(underlying, 0, transferId)
1018
- start = len(currencyId) - 2
1019
- isDerivativeCode = currencyId[start:] == 'F0'
1020
- if not isDerivativeCode:
1021
- currencyId = currencyId + 'F0'
1022
- elif type != 'margin':
1023
- currencyId = self.safe_string(underlying, 1, transferId)
1024
- else:
1025
- currencyId = transferId
1026
- return currencyId
1027
-
1028
- async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
1029
- """
1030
- fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1031
-
1032
- https://docs.bitfinex.com/reference/rest-public-book
1033
-
1034
- :param str symbol: unified symbol of the market to fetch the order book for
1035
- :param int [limit]: the maximum amount of order book entries to return, bitfinex only allows 1, 25, or 100
1036
- :param dict [params]: extra parameters specific to the exchange API endpoint
1037
- :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
1038
- """
1039
- await self.load_markets()
1040
- precision = self.safe_value(self.options, 'precision', 'R0')
1041
- market = self.market(symbol)
1042
- request: dict = {
1043
- 'symbol': market['id'],
1044
- 'precision': precision,
1045
- }
1046
- if limit is not None:
1047
- request['len'] = limit
1048
- fullRequest = self.extend(request, params)
1049
- orderbook = await self.publicGetBookSymbolPrecision(fullRequest)
1050
- timestamp = self.milliseconds()
1051
- result: dict = {
1052
- 'symbol': market['symbol'],
1053
- 'bids': [],
1054
- 'asks': [],
1055
- 'timestamp': timestamp,
1056
- 'datetime': self.iso8601(timestamp),
1057
- 'nonce': None,
1058
- }
1059
- priceIndex = 1 if (fullRequest['precision'] == 'R0') else 0
1060
- for i in range(0, len(orderbook)):
1061
- order = orderbook[i]
1062
- price = self.safe_number(order, priceIndex)
1063
- signedAmount = self.safe_string(order, 2)
1064
- amount = Precise.string_abs(signedAmount)
1065
- side = 'bids' if Precise.string_gt(signedAmount, '0') else 'asks'
1066
- result[side].append([price, self.parse_number(amount)])
1067
- result['bids'] = self.sort_by(result['bids'], 0, True)
1068
- result['asks'] = self.sort_by(result['asks'], 0)
1069
- return result
1070
-
1071
- def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
1072
- #
1073
- # on trading pairs(ex. tBTCUSD)
1074
- #
1075
- # {
1076
- # 'result': [
1077
- # SYMBOL,
1078
- # BID,
1079
- # BID_SIZE,
1080
- # ASK,
1081
- # ASK_SIZE,
1082
- # DAILY_CHANGE,
1083
- # DAILY_CHANGE_RELATIVE,
1084
- # LAST_PRICE,
1085
- # VOLUME,
1086
- # HIGH,
1087
- # LOW
1088
- # ]
1089
- # }
1090
- #
1091
- #
1092
- # on funding currencies(ex. fUSD)
1093
- #
1094
- # {
1095
- # 'result': [
1096
- # SYMBOL,
1097
- # FRR,
1098
- # BID,
1099
- # BID_PERIOD,
1100
- # BID_SIZE,
1101
- # ASK,
1102
- # ASK_PERIOD,
1103
- # ASK_SIZE,
1104
- # DAILY_CHANGE,
1105
- # DAILY_CHANGE_RELATIVE,
1106
- # LAST_PRICE,
1107
- # VOLUME,
1108
- # HIGH,
1109
- # LOW,
1110
- # _PLACEHOLDER,
1111
- # _PLACEHOLDER,
1112
- # FRR_AMOUNT_AVAILABLE
1113
- # ]
1114
- # }
1115
- #
1116
- result = self.safe_list(ticker, 'result')
1117
- symbol = self.safe_symbol(None, market)
1118
- length = len(result)
1119
- last = self.safe_string(result, length - 4)
1120
- percentage = self.safe_string(result, length - 5)
1121
- return self.safe_ticker({
1122
- 'symbol': symbol,
1123
- 'timestamp': None,
1124
- 'datetime': None,
1125
- 'high': self.safe_string(result, length - 2),
1126
- 'low': self.safe_string(result, length - 1),
1127
- 'bid': self.safe_string(result, length - 10),
1128
- 'bidVolume': self.safe_string(result, length - 9),
1129
- 'ask': self.safe_string(result, length - 8),
1130
- 'askVolume': self.safe_string(result, length - 7),
1131
- 'vwap': None,
1132
- 'open': None,
1133
- 'close': last,
1134
- 'last': last,
1135
- 'previousClose': None,
1136
- 'change': self.safe_string(result, length - 6),
1137
- 'percentage': Precise.string_mul(percentage, '100'),
1138
- 'average': None,
1139
- 'baseVolume': self.safe_string(result, length - 3),
1140
- 'quoteVolume': None,
1141
- 'info': result,
1142
- }, market)
1143
-
1144
- async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1145
- """
1146
- fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
1147
-
1148
- https://docs.bitfinex.com/reference/rest-public-tickers
1149
-
1150
- :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
1151
- :param dict [params]: extra parameters specific to the exchange API endpoint
1152
- :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
1153
- """
1154
- await self.load_markets()
1155
- symbols = self.market_symbols(symbols)
1156
- request: dict = {}
1157
- if symbols is not None:
1158
- ids = self.market_ids(symbols)
1159
- request['symbols'] = ','.join(ids)
1160
- else:
1161
- request['symbols'] = 'ALL'
1162
- tickers = await self.publicGetTickers(self.extend(request, params))
1163
- #
1164
- # [
1165
- # # on trading pairs(ex. tBTCUSD)
1166
- # [
1167
- # SYMBOL,
1168
- # BID,
1169
- # BID_SIZE,
1170
- # ASK,
1171
- # ASK_SIZE,
1172
- # DAILY_CHANGE,
1173
- # DAILY_CHANGE_RELATIVE,
1174
- # LAST_PRICE,
1175
- # VOLUME,
1176
- # HIGH,
1177
- # LOW
1178
- # ],
1179
- # # on funding currencies(ex. fUSD)
1180
- # [
1181
- # SYMBOL,
1182
- # FRR,
1183
- # BID,
1184
- # BID_PERIOD,
1185
- # BID_SIZE,
1186
- # ASK,
1187
- # ASK_PERIOD,
1188
- # ASK_SIZE,
1189
- # DAILY_CHANGE,
1190
- # DAILY_CHANGE_RELATIVE,
1191
- # LAST_PRICE,
1192
- # VOLUME,
1193
- # HIGH,
1194
- # LOW,
1195
- # _PLACEHOLDER,
1196
- # _PLACEHOLDER,
1197
- # FRR_AMOUNT_AVAILABLE
1198
- # ],
1199
- # ...
1200
- # ]
1201
- #
1202
- result: dict = {}
1203
- for i in range(0, len(tickers)):
1204
- ticker = tickers[i]
1205
- marketId = self.safe_string(ticker, 0)
1206
- market = self.safe_market(marketId)
1207
- symbol = market['symbol']
1208
- result[symbol] = self.parse_ticker({'result': ticker}, market)
1209
- return self.filter_by_array_tickers(result, 'symbol', symbols)
1210
-
1211
- async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
1212
- """
1213
- fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1214
-
1215
- https://docs.bitfinex.com/reference/rest-public-ticker
1216
-
1217
- :param str symbol: unified symbol of the market to fetch the ticker for
1218
- :param dict [params]: extra parameters specific to the exchange API endpoint
1219
- :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1220
- """
1221
- await self.load_markets()
1222
- market = self.market(symbol)
1223
- request: dict = {
1224
- 'symbol': market['id'],
1225
- }
1226
- ticker = await self.publicGetTickerSymbol(self.extend(request, params))
1227
- result: dict = {'result': ticker}
1228
- return self.parse_ticker(result, market)
1229
-
1230
- def parse_trade(self, trade: dict, market: Market = None) -> Trade:
1231
- #
1232
- # fetchTrades(public)
1233
- #
1234
- # [
1235
- # ID,
1236
- # MTS, # timestamp
1237
- # AMOUNT,
1238
- # PRICE
1239
- # ]
1240
- #
1241
- # fetchMyTrades(private)
1242
- #
1243
- # [
1244
- # ID,
1245
- # PAIR,
1246
- # MTS_CREATE,
1247
- # ORDER_ID,
1248
- # EXEC_AMOUNT,
1249
- # EXEC_PRICE,
1250
- # ORDER_TYPE,
1251
- # ORDER_PRICE,
1252
- # MAKER,
1253
- # FEE,
1254
- # FEE_CURRENCY,
1255
- # ...
1256
- # ]
1257
- #
1258
- tradeList = self.safe_list(trade, 'result', [])
1259
- tradeLength = len(tradeList)
1260
- isPrivate = (tradeLength > 5)
1261
- id = self.safe_string(tradeList, 0)
1262
- amountIndex = 4 if isPrivate else 2
1263
- side = None
1264
- amountString = self.safe_string(tradeList, amountIndex)
1265
- priceIndex = 5 if isPrivate else 3
1266
- priceString = self.safe_string(tradeList, priceIndex)
1267
- if amountString[0] == '-':
1268
- side = 'sell'
1269
- amountString = Precise.string_abs(amountString)
1270
- else:
1271
- side = 'buy'
1272
- orderId = None
1273
- takerOrMaker = None
1274
- type = None
1275
- fee = None
1276
- symbol = self.safe_symbol(None, market)
1277
- timestampIndex = 2 if isPrivate else 1
1278
- timestamp = self.safe_integer(tradeList, timestampIndex)
1279
- if isPrivate:
1280
- marketId = tradeList[1]
1281
- symbol = self.safe_symbol(marketId)
1282
- orderId = self.safe_string(tradeList, 3)
1283
- maker = self.safe_integer(tradeList, 8)
1284
- takerOrMaker = 'maker' if (maker == 1) else 'taker'
1285
- feeCostString = self.safe_string(tradeList, 9)
1286
- feeCostString = Precise.string_neg(feeCostString)
1287
- feeCurrencyId = self.safe_string(tradeList, 10)
1288
- feeCurrency = self.safe_currency_code(feeCurrencyId)
1289
- fee = {
1290
- 'cost': feeCostString,
1291
- 'currency': feeCurrency,
1292
- }
1293
- orderType = tradeList[6]
1294
- type = self.safe_string(self.options['exchangeTypes'], orderType)
1295
- return self.safe_trade({
1296
- 'id': id,
1297
- 'timestamp': timestamp,
1298
- 'datetime': self.iso8601(timestamp),
1299
- 'symbol': symbol,
1300
- 'order': orderId,
1301
- 'side': side,
1302
- 'type': type,
1303
- 'takerOrMaker': takerOrMaker,
1304
- 'price': priceString,
1305
- 'amount': amountString,
1306
- 'cost': None,
1307
- 'fee': fee,
1308
- 'info': tradeList,
1309
- }, market)
1310
-
1311
- async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1312
- """
1313
- get the list of most recent trades for a particular symbol
1314
-
1315
- https://docs.bitfinex.com/reference/rest-public-trades
1316
-
1317
- :param str symbol: unified symbol of the market to fetch trades for
1318
- :param int [since]: timestamp in ms of the earliest trade to fetch
1319
- :param int [limit]: the maximum amount of trades to fetch, default 120, max 10000
1320
- :param dict [params]: extra parameters specific to the exchange API endpoint
1321
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1322
- :param int [params.until]: the latest time in ms to fetch entries for
1323
- :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1324
- """
1325
- await self.load_markets()
1326
- paginate = False
1327
- paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
1328
- if paginate:
1329
- return await self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params, 10000)
1330
- market = self.market(symbol)
1331
- sort = '-1'
1332
- request: dict = {
1333
- 'symbol': market['id'],
1334
- }
1335
- if since is not None:
1336
- request['start'] = since
1337
- sort = '1'
1338
- if limit is not None:
1339
- request['limit'] = min(limit, 10000) # default 120, max 10000
1340
- request['sort'] = sort
1341
- request, params = self.handle_until_option('end', request, params)
1342
- response = await self.publicGetTradesSymbolHist(self.extend(request, params))
1343
- #
1344
- # [
1345
- # [
1346
- # ID,
1347
- # MTS, # timestamp
1348
- # AMOUNT,
1349
- # PRICE
1350
- # ]
1351
- # ]
1352
- #
1353
- trades = self.sort_by(response, 1)
1354
- tradesList = []
1355
- for i in range(0, len(trades)):
1356
- tradesList.append({'result': trades[i]}) # convert to array of dicts to match parseOrder signature
1357
- return self.parse_trades(tradesList, market, None, limit)
1358
-
1359
- async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = 100, params={}) -> List[list]:
1360
- """
1361
- fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1362
-
1363
- https://docs.bitfinex.com/reference/rest-public-candles
1364
-
1365
- :param str symbol: unified symbol of the market to fetch OHLCV data for
1366
- :param str timeframe: the length of time each candle represents
1367
- :param int [since]: timestamp in ms of the earliest candle to fetch
1368
- :param int [limit]: the maximum amount of candles to fetch, default 100 max 10000
1369
- :param dict [params]: extra parameters specific to the exchange API endpoint
1370
- :returns int[][]: A list of candles ordered, open, high, low, close, volume
1371
- :param int [params.until]: timestamp in ms of the latest candle to fetch
1372
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1373
- """
1374
- await self.load_markets()
1375
- paginate = False
1376
- paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
1377
- if paginate:
1378
- return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 10000)
1379
- market = self.market(symbol)
1380
- if limit is None:
1381
- limit = 10000
1382
- else:
1383
- limit = min(limit, 10000)
1384
- request: dict = {
1385
- 'symbol': market['id'],
1386
- 'timeframe': self.safe_string(self.timeframes, timeframe, timeframe),
1387
- 'sort': 1,
1388
- 'limit': limit,
1389
- }
1390
- if since is not None:
1391
- request['start'] = since
1392
- request, params = self.handle_until_option('end', request, params)
1393
- response = await self.publicGetCandlesTradeTimeframeSymbolHist(self.extend(request, params))
1394
- #
1395
- # [
1396
- # [1591503840000,0.025069,0.025068,0.025069,0.025068,1.97828998],
1397
- # [1591504500000,0.025065,0.025065,0.025065,0.025065,1.0164],
1398
- # [1591504620000,0.025062,0.025062,0.025062,0.025062,0.5],
1399
- # ]
1400
- #
1401
- return self.parse_ohlcvs(response, market, timeframe, since, limit)
1402
-
1403
- def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
1404
- #
1405
- # [
1406
- # 1457539800000,
1407
- # 0.02594,
1408
- # 0.02594,
1409
- # 0.02594,
1410
- # 0.02594,
1411
- # 0.1
1412
- # ]
1413
- #
1414
- return [
1415
- self.safe_integer(ohlcv, 0),
1416
- self.safe_number(ohlcv, 1),
1417
- self.safe_number(ohlcv, 3),
1418
- self.safe_number(ohlcv, 4),
1419
- self.safe_number(ohlcv, 2),
1420
- self.safe_number(ohlcv, 5),
1421
- ]
1422
-
1423
- def parse_order_status(self, status: Str):
1424
- if status is None:
1425
- return status
1426
- parts = status.split(' ')
1427
- state = self.safe_string(parts, 0)
1428
- statuses: dict = {
1429
- 'ACTIVE': 'open',
1430
- 'PARTIALLY': 'open',
1431
- 'EXECUTED': 'closed',
1432
- 'CANCELED': 'canceled',
1433
- 'INSUFFICIENT': 'canceled',
1434
- 'POSTONLY CANCELED': 'canceled',
1435
- 'RSN_DUST': 'rejected',
1436
- 'RSN_PAUSE': 'rejected',
1437
- 'IOC CANCELED': 'canceled',
1438
- 'FILLORKILL CANCELED': 'canceled',
1439
- }
1440
- return self.safe_string(statuses, state, status)
1441
-
1442
- def parse_order_flags(self, flags):
1443
- # flags can be added to each other...
1444
- flagValues: dict = {
1445
- '1024': ['reduceOnly'],
1446
- '4096': ['postOnly'],
1447
- '5120': ['reduceOnly', 'postOnly'],
1448
- # '64': 'hidden', # The hidden order option ensures an order does not appear in the order book
1449
- # '512': 'close', # Close position if position present.
1450
- # '16384': 'OCO', # The one cancels other order option allows you to place a pair of orders stipulating that if one order is executed fully or partially, then the other is automatically canceled.
1451
- # '524288': 'No Var Rates' # Excludes variable rate funding offers from matching against self order, if on margin
1452
- }
1453
- return self.safe_value(flagValues, flags, None)
1454
-
1455
- def parse_time_in_force(self, orderType):
1456
- orderTypes: dict = {
1457
- 'EXCHANGE IOC': 'IOC',
1458
- 'EXCHANGE FOK': 'FOK',
1459
- 'IOC': 'IOC', # Margin
1460
- 'FOK': 'FOK', # Margin
1461
- }
1462
- return self.safe_string(orderTypes, orderType, 'GTC')
1463
-
1464
- def parse_order(self, order: dict, market: Market = None) -> Order:
1465
- orderList = self.safe_list(order, 'result')
1466
- id = self.safe_string(orderList, 0)
1467
- marketId = self.safe_string(orderList, 3)
1468
- symbol = self.safe_symbol(marketId)
1469
- # https://github.com/ccxt/ccxt/issues/6686
1470
- # timestamp = self.safe_timestamp(orderObject, 5)
1471
- timestamp = self.safe_integer(orderList, 5)
1472
- remaining = Precise.string_abs(self.safe_string(orderList, 6))
1473
- signedAmount = self.safe_string(orderList, 7)
1474
- amount = Precise.string_abs(signedAmount)
1475
- side = 'sell' if Precise.string_lt(signedAmount, '0') else 'buy'
1476
- orderType = self.safe_string(orderList, 8)
1477
- type = self.safe_string(self.safe_value(self.options, 'exchangeTypes'), orderType)
1478
- timeInForce = self.parse_time_in_force(orderType)
1479
- rawFlags = self.safe_string(orderList, 12)
1480
- flags = self.parse_order_flags(rawFlags)
1481
- postOnly = False
1482
- if flags is not None:
1483
- for i in range(0, len(flags)):
1484
- if flags[i] == 'postOnly':
1485
- postOnly = True
1486
- price = self.safe_string(orderList, 16)
1487
- stopPrice = None
1488
- if (orderType == 'EXCHANGE STOP') or (orderType == 'EXCHANGE STOP LIMIT'):
1489
- price = None
1490
- stopPrice = self.safe_string(orderList, 16)
1491
- if orderType == 'EXCHANGE STOP LIMIT':
1492
- price = self.safe_string(orderList, 19)
1493
- status = None
1494
- statusString = self.safe_string(orderList, 13)
1495
- if statusString is not None:
1496
- parts = statusString.split(' @ ')
1497
- status = self.parse_order_status(self.safe_string(parts, 0))
1498
- average = self.safe_string(orderList, 17)
1499
- clientOrderId = self.safe_string(orderList, 2)
1500
- return self.safe_order({
1501
- 'info': orderList,
1502
- 'id': id,
1503
- 'clientOrderId': clientOrderId,
1504
- 'timestamp': timestamp,
1505
- 'datetime': self.iso8601(timestamp),
1506
- 'lastTradeTimestamp': None,
1507
- 'symbol': symbol,
1508
- 'type': type,
1509
- 'timeInForce': timeInForce,
1510
- 'postOnly': postOnly,
1511
- 'side': side,
1512
- 'price': price,
1513
- 'stopPrice': stopPrice,
1514
- 'triggerPrice': stopPrice,
1515
- 'amount': amount,
1516
- 'cost': None,
1517
- 'average': average,
1518
- 'filled': None,
1519
- 'remaining': remaining,
1520
- 'status': status,
1521
- 'fee': None,
1522
- 'trades': None,
1523
- }, market)
1524
-
1525
- def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1526
- """
1527
- @ignore
1528
- helper function to build an order request
1529
- :param str symbol: unified symbol of the market to create an order in
1530
- :param str type: 'market' or 'limit'
1531
- :param str side: 'buy' or 'sell'
1532
- :param float amount: how much you want to trade in units of the base currency
1533
- :param float [price]: the price of the order, in units of the quote currency, ignored in market orders
1534
- :param dict [params]: extra parameters specific to the exchange API endpoint
1535
- :param float [params.stopPrice]: The price at which a trigger order is triggered at
1536
- :param str [params.timeInForce]: "GTC", "IOC", "FOK", or "PO"
1537
- :param bool [params.postOnly]:
1538
- :param bool [params.reduceOnly]: Ensures that the executed order does not flip the opened position.
1539
- :param int [params.flags]: additional order parameters: 4096(Post Only), 1024(Reduce Only), 16384(OCO), 64(Hidden), 512(Close), 524288(No Var Rates)
1540
- :param int [params.lev]: leverage for a derivative order, supported by derivative symbol orders only. The value should be between 1 and 100 inclusive.
1541
- :param str [params.price_traling]: The trailing price for a trailing stop order
1542
- :param str [params.price_aux_limit]: Order price for stop limit orders
1543
- :param str [params.price_oco_stop]: OCO stop price
1544
- :returns dict: an `order structure <https://github.com/ccxt/ccxt/wiki/Manual#order-structure>`
1545
- """
1546
- market = self.market(symbol)
1547
- amountString = self.amount_to_precision(symbol, amount)
1548
- amountString = amountString if (side == 'buy') else Precise.string_neg(amountString)
1549
- request: dict = {
1550
- 'symbol': market['id'],
1551
- 'amount': amountString,
1552
- }
1553
- stopPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice')
1554
- trailingAmount = self.safe_string(params, 'trailingAmount')
1555
- timeInForce = self.safe_string(params, 'timeInForce')
1556
- postOnlyParam = self.safe_bool(params, 'postOnly', False)
1557
- reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1558
- clientOrderId = self.safe_value_2(params, 'cid', 'clientOrderId')
1559
- orderType = type.upper()
1560
- if trailingAmount is not None:
1561
- orderType = 'TRAILING STOP'
1562
- request['price_trailing'] = trailingAmount
1563
- elif stopPrice is not None:
1564
- # request['price'] is taken for stop orders
1565
- request['price'] = self.price_to_precision(symbol, stopPrice)
1566
- if type == 'limit':
1567
- orderType = 'STOP LIMIT'
1568
- request['price_aux_limit'] = self.price_to_precision(symbol, price)
1569
- else:
1570
- orderType = 'STOP'
1571
- ioc = (timeInForce == 'IOC')
1572
- fok = (timeInForce == 'FOK')
1573
- postOnly = (postOnlyParam or (timeInForce == 'PO'))
1574
- if (ioc or fok) and (price is None):
1575
- raise InvalidOrder(self.id + ' createOrder() requires a price argument with IOC and FOK orders')
1576
- if (ioc or fok) and (type == 'market'):
1577
- raise InvalidOrder(self.id + ' createOrder() does not allow market IOC and FOK orders')
1578
- if (type != 'market') and (stopPrice is None):
1579
- request['price'] = self.price_to_precision(symbol, price)
1580
- if ioc:
1581
- orderType = 'IOC'
1582
- elif fok:
1583
- orderType = 'FOK'
1584
- marginMode = None
1585
- marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
1586
- if market['spot'] and (marginMode is None):
1587
- # The EXCHANGE prefix is only required for non margin spot markets
1588
- orderType = 'EXCHANGE ' + orderType
1589
- request['type'] = orderType
1590
- # flag values may be summed to combine flags
1591
- flags = 0
1592
- if postOnly:
1593
- flags = self.sum(flags, 4096)
1594
- if reduceOnly:
1595
- flags = self.sum(flags, 1024)
1596
- if flags != 0:
1597
- request['flags'] = flags
1598
- if clientOrderId is not None:
1599
- request['cid'] = clientOrderId
1600
- params = self.omit(params, ['triggerPrice', 'stopPrice', 'timeInForce', 'postOnly', 'reduceOnly', 'trailingAmount', 'clientOrderId'])
1601
- return self.extend(request, params)
1602
-
1603
- async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1604
- """
1605
- create an order on the exchange
1606
-
1607
- https://docs.bitfinex.com/reference/rest-auth-submit-order
1608
-
1609
- :param str symbol: unified CCXT market symbol
1610
- :param str type: 'limit' or 'market'
1611
- :param str side: 'buy' or 'sell'
1612
- :param float amount: the amount of currency to trade
1613
- :param float [price]: price of the order
1614
- :param dict [params]: extra parameters specific to the exchange API endpoint
1615
- :param float [params.stopPrice]: the price that triggers a trigger order
1616
- :param str [params.timeInForce]: "GTC", "IOC", "FOK", or "PO"
1617
- :param boolean [params.postOnly]: set to True if you want to make a post only order
1618
- :param boolean [params.reduceOnly]: indicates that the order is to reduce the size of a position
1619
- :param int [params.flags]: additional order parameters: 4096(Post Only), 1024(Reduce Only), 16384(OCO), 64(Hidden), 512(Close), 524288(No Var Rates)
1620
- :param int [params.lev]: leverage for a derivative order, supported by derivative symbol orders only. The value should be between 1 and 100 inclusive.
1621
- :param str [params.price_aux_limit]: order price for stop limit orders
1622
- :param str [params.price_oco_stop]: OCO stop price
1623
- :param str [params.trailingAmount]: *swap only* the quote amount to trail away from the current market price
1624
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1625
- """
1626
- await self.load_markets()
1627
- market = self.market(symbol)
1628
- request = self.create_order_request(symbol, type, side, amount, price, params)
1629
- response = await self.privatePostAuthWOrderSubmit(request)
1630
- #
1631
- # [
1632
- # 1653325121, # Timestamp in milliseconds
1633
- # "on-req", # Purpose of notification('on-req', 'oc-req', "uca", 'fon-req', "foc-req")
1634
- # null, # unique ID of the message
1635
- # null,
1636
- # [
1637
- # [
1638
- # 95412102131, # Order ID
1639
- # null, # Group ID
1640
- # 1653325121798, # Client Order ID
1641
- # "tDOGE:UST", # Market ID
1642
- # 1653325121798, # Millisecond timestamp of creation
1643
- # 1653325121798, # Millisecond timestamp of update
1644
- # -10, # Amount(Positive means buy, negative means sell)
1645
- # -10, # Original amount
1646
- # "EXCHANGE LIMIT", # Type of the order: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET, STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT, TRAILING STOP, EXCHANGE TRAILING STOP, FOK, EXCHANGE FOK, IOC, EXCHANGE IOC.
1647
- # null, # Previous order type(stop-limit orders are converted to limit orders so for them previous type is always STOP)
1648
- # null, # Millisecond timestamp of Time-In-Force: automatic order cancellation
1649
- # null, # _PLACEHOLDER
1650
- # 4096, # Flags, see parseOrderFlags()
1651
- # "ACTIVE", # Order Status, see parseOrderStatus()
1652
- # null, # _PLACEHOLDER
1653
- # null, # _PLACEHOLDER
1654
- # 0.071, # Price(Stop Price for stop-limit orders, Limit Price for limit orders)
1655
- # 0, # Average Price
1656
- # 0, # Trailing Price
1657
- # 0, # Auxiliary Limit price(for STOP LIMIT)
1658
- # null, # _PLACEHOLDER
1659
- # null, # _PLACEHOLDER
1660
- # null, # _PLACEHOLDER
1661
- # 0, # Hidden(0 if False, 1 if True)
1662
- # 0, # Placed ID(If another order caused self order to be placed(OCO) self will be that other order's ID)
1663
- # null, # _PLACEHOLDER
1664
- # null, # _PLACEHOLDER
1665
- # null, # _PLACEHOLDER
1666
- # "API>BFX", # Routing, indicates origin of action: BFX, ETHFX, API>BFX, API>ETHFX
1667
- # null, # _PLACEHOLDER
1668
- # null, # _PLACEHOLDER
1669
- # {"$F7":1} # additional meta information about the order( $F7 = IS_POST_ONLY(0 if False, 1 if True), $F33 = Leverage(int))
1670
- # ]
1671
- # ],
1672
- # null, # CODE(work in progress)
1673
- # "SUCCESS", # Status of the request
1674
- # "Submitting 1 orders." # Message
1675
- # ]
1676
- #
1677
- status = self.safe_string(response, 6)
1678
- if status != 'SUCCESS':
1679
- errorCode = response[5]
1680
- errorText = response[7]
1681
- raise ExchangeError(self.id + ' ' + response[6] + ': ' + errorText + '(#' + errorCode + ')')
1682
- orders = self.safe_list(response, 4, [])
1683
- order = self.safe_list(orders, 0)
1684
- newOrder = {'result': order}
1685
- return self.parse_order(newOrder, market)
1686
-
1687
- async def create_orders(self, orders: List[OrderRequest], params={}):
1688
- """
1689
- create a list of trade orders
1690
-
1691
- https://docs.bitfinex.com/reference/rest-auth-order-multi
1692
-
1693
- :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
1694
- :param dict [params]: extra parameters specific to the exchange API endpoint
1695
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1696
- """
1697
- await self.load_markets()
1698
- ordersRequests = []
1699
- for i in range(0, len(orders)):
1700
- rawOrder = orders[i]
1701
- symbol = self.safe_string(rawOrder, 'symbol')
1702
- type = self.safe_string(rawOrder, 'type')
1703
- side = self.safe_string(rawOrder, 'side')
1704
- amount = self.safe_number(rawOrder, 'amount')
1705
- price = self.safe_number(rawOrder, 'price')
1706
- orderParams = self.safe_dict(rawOrder, 'params', {})
1707
- orderRequest = self.create_order_request(symbol, type, side, amount, price, orderParams)
1708
- ordersRequests.append(['on', orderRequest])
1709
- request: dict = {
1710
- 'ops': ordersRequests,
1711
- }
1712
- response = await self.privatePostAuthWOrderMulti(request)
1713
- #
1714
- # [
1715
- # 1706762515553,
1716
- # "ox_multi-req",
1717
- # null,
1718
- # null,
1719
- # [
1720
- # [
1721
- # 1706762515,
1722
- # "on-req",
1723
- # null,
1724
- # null,
1725
- # [
1726
- # [139567428547,null,1706762515551,"tBTCUST",1706762515551,1706762515551,0.0001,0.0001,"EXCHANGE LIMIT",null,null,null,0,"ACTIVE",null,null,35000,0,0,0,null,null,null,0,0,null,null,null,"API>BFX",null,null,{}]
1727
- # ],
1728
- # null,
1729
- # "SUCCESS",
1730
- # "Submitting 1 orders."
1731
- # ],
1732
- # ],
1733
- # null,
1734
- # "SUCCESS",
1735
- # "Submitting 2 order operations."
1736
- # ]
1737
- #
1738
- results = []
1739
- data = self.safe_list(response, 4, [])
1740
- for i in range(0, len(data)):
1741
- entry = data[i]
1742
- individualOrder = entry[4]
1743
- results.append({'result': individualOrder[0]})
1744
- return self.parse_orders(results)
1745
-
1746
- async def cancel_all_orders(self, symbol: Str = None, params={}):
1747
- """
1748
- cancel all open orders
1749
-
1750
- https://docs.bitfinex.com/reference/rest-auth-cancel-orders-multiple
1751
-
1752
- :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1753
- :param dict [params]: extra parameters specific to the exchange API endpoint
1754
- :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1755
- """
1756
- await self.load_markets()
1757
- request: dict = {
1758
- 'all': 1,
1759
- }
1760
- response = await self.privatePostAuthWOrderCancelMulti(self.extend(request, params))
1761
- orders = self.safe_list(response, 4, [])
1762
- ordersList = []
1763
- for i in range(0, len(orders)):
1764
- ordersList.append({'result': orders[i]})
1765
- return self.parse_orders(ordersList)
1766
-
1767
- async def cancel_order(self, id: str, symbol: Str = None, params={}):
1768
- """
1769
- cancels an open order
1770
-
1771
- https://docs.bitfinex.com/reference/rest-auth-cancel-order
1772
-
1773
- :param str id: order id
1774
- :param str symbol: Not used by bitfinex2 cancelOrder()
1775
- :param dict [params]: extra parameters specific to the exchange API endpoint
1776
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1777
- """
1778
- await self.load_markets()
1779
- cid = self.safe_value_2(params, 'cid', 'clientOrderId') # client order id
1780
- request = None
1781
- market = None
1782
- if symbol is not None:
1783
- market = self.market(symbol)
1784
- if cid is not None:
1785
- cidDate = self.safe_value(params, 'cidDate') # client order id date
1786
- if cidDate is None:
1787
- raise InvalidOrder(self.id + " canceling an order by clientOrderId('cid') requires both 'cid' and 'cid_date'('YYYY-MM-DD')")
1788
- request = {
1789
- 'cid': cid,
1790
- 'cid_date': cidDate,
1791
- }
1792
- params = self.omit(params, ['cid', 'clientOrderId'])
1793
- else:
1794
- request = {
1795
- 'id': int(id),
1796
- }
1797
- response = await self.privatePostAuthWOrderCancel(self.extend(request, params))
1798
- order = self.safe_value(response, 4)
1799
- newOrder = {'result': order}
1800
- return self.parse_order(newOrder, market)
1801
-
1802
- async def cancel_orders(self, ids, symbol: Str = None, params={}):
1803
- """
1804
- cancel multiple orders at the same time
1805
-
1806
- https://docs.bitfinex.com/reference/rest-auth-cancel-orders-multiple
1807
-
1808
- :param str[] ids: order ids
1809
- :param str symbol: unified market symbol, default is None
1810
- :param dict [params]: extra parameters specific to the exchange API endpoint
1811
- :returns dict: an array of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1812
- """
1813
- await self.load_markets()
1814
- for i in range(0, len(ids)):
1815
- ids[i] = self.parse_to_numeric(ids[i])
1816
- request: dict = {
1817
- 'id': ids,
1818
- }
1819
- market = None
1820
- if symbol is not None:
1821
- market = self.market(symbol)
1822
- response = await self.privatePostAuthWOrderCancelMulti(self.extend(request, params))
1823
- #
1824
- # [
1825
- # 1706740198811,
1826
- # "oc_multi-req",
1827
- # null,
1828
- # null,
1829
- # [
1830
- # [
1831
- # 139530205057,
1832
- # null,
1833
- # 1706740132275,
1834
- # "tBTCF0:USTF0",
1835
- # 1706740132276,
1836
- # 1706740132276,
1837
- # 0.0001,
1838
- # 0.0001,
1839
- # "LIMIT",
1840
- # null,
1841
- # null,
1842
- # null,
1843
- # 0,
1844
- # "ACTIVE",
1845
- # null,
1846
- # null,
1847
- # 39000,
1848
- # 0,
1849
- # 0,
1850
- # 0,
1851
- # null,
1852
- # null,
1853
- # null,
1854
- # 0,
1855
- # 0,
1856
- # null,
1857
- # null,
1858
- # null,
1859
- # "API>BFX",
1860
- # null,
1861
- # null,
1862
- # {
1863
- # "lev": 10,
1864
- # "$F33": 10
1865
- # }
1866
- # ],
1867
- # ],
1868
- # null,
1869
- # "SUCCESS",
1870
- # "Submitting 2 order cancellations."
1871
- # ]
1872
- #
1873
- orders = self.safe_list(response, 4, [])
1874
- ordersList = []
1875
- for i in range(0, len(orders)):
1876
- ordersList.append({'result': orders[i]})
1877
- return self.parse_orders(ordersList, market)
1878
-
1879
- async def fetch_open_order(self, id: str, symbol: Str = None, params={}):
1880
- """
1881
- fetch an open order by it's id
1882
-
1883
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1884
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
1885
-
1886
- :param str id: order id
1887
- :param str symbol: unified market symbol, default is None
1888
- :param dict [params]: extra parameters specific to the exchange API endpoint
1889
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1890
- """
1891
- request: dict = {
1892
- 'id': [int(id)],
1893
- }
1894
- orders = await self.fetch_open_orders(symbol, None, None, self.extend(request, params))
1895
- order = self.safe_value(orders, 0)
1896
- if order is None:
1897
- raise OrderNotFound(self.id + ' order ' + id + ' not found')
1898
- return order
1899
-
1900
- async def fetch_closed_order(self, id: str, symbol: Str = None, params={}):
1901
- """
1902
- fetch an open order by it's id
1903
-
1904
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1905
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
1906
-
1907
- :param str id: order id
1908
- :param str symbol: unified market symbol, default is None
1909
- :param dict [params]: extra parameters specific to the exchange API endpoint
1910
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1911
- """
1912
- request: dict = {
1913
- 'id': [int(id)],
1914
- }
1915
- orders = await self.fetch_closed_orders(symbol, None, None, self.extend(request, params))
1916
- order = self.safe_value(orders, 0)
1917
- if order is None:
1918
- raise OrderNotFound(self.id + ' order ' + id + ' not found')
1919
- return order
1920
-
1921
- async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1922
- """
1923
- fetch all unfilled currently open orders
1924
-
1925
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1926
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
1927
-
1928
- :param str symbol: unified market symbol
1929
- :param int [since]: the earliest time in ms to fetch open orders for
1930
- :param int [limit]: the maximum number of open orders structures to retrieve
1931
- :param dict [params]: extra parameters specific to the exchange API endpoint
1932
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1933
- """
1934
- await self.load_markets()
1935
- request: dict = {}
1936
- market = None
1937
- response = None
1938
- if symbol is None:
1939
- response = await self.privatePostAuthROrders(self.extend(request, params))
1940
- else:
1941
- market = self.market(symbol)
1942
- request['symbol'] = market['id']
1943
- response = await self.privatePostAuthROrdersSymbol(self.extend(request, params))
1944
- #
1945
- # [
1946
- # [
1947
- # 95408916206, # Order ID
1948
- # null, # Group Order ID
1949
- # 1653322349926, # Client Order ID
1950
- # "tDOGE:UST", # Market ID
1951
- # 1653322349926, # Created Timestamp in milliseconds
1952
- # 1653322349927, # Updated Timestamp in milliseconds
1953
- # -10, # Amount remaining(Positive means buy, negative means sell)
1954
- # -10, # Original amount
1955
- # "EXCHANGE LIMIT", # Order type
1956
- # null, # Previous Order Type
1957
- # null, # _PLACEHOLDER
1958
- # null, # _PLACEHOLDER
1959
- # 0, # Flags, see parseOrderFlags()
1960
- # "ACTIVE", # Order Status, see parseOrderStatus()
1961
- # null, # _PLACEHOLDER
1962
- # null, # _PLACEHOLDER
1963
- # 0.11, # Price
1964
- # 0, # Average Price
1965
- # 0, # Trailing Price
1966
- # 0, # Auxiliary Limit price(for STOP LIMIT)
1967
- # null, # _PLACEHOLDER
1968
- # null, # _PLACEHOLDER
1969
- # null, # _PLACEHOLDER
1970
- # 0, # Hidden(0 if False, 1 if True)
1971
- # 0, # Placed ID(If another order caused self order to be placed(OCO) self will be that other order's ID)
1972
- # null, # _PLACEHOLDER
1973
- # null, # _PLACEHOLDER
1974
- # null, # _PLACEHOLDER
1975
- # "API>BFX", # Routing, indicates origin of action: BFX, ETHFX, API>BFX, API>ETHFX
1976
- # null, # _PLACEHOLDER
1977
- # null, # _PLACEHOLDER
1978
- # {"$F7":1} # additional meta information about the order( $F7 = IS_POST_ONLY(0 if False, 1 if True), $F33 = Leverage(int))
1979
- # ],
1980
- # ]
1981
- #
1982
- ordersList = []
1983
- for i in range(0, len(response)):
1984
- ordersList.append({'result': response[i]})
1985
- return self.parse_orders(ordersList, market, since, limit)
1986
-
1987
- async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1988
- """
1989
- fetches information on multiple closed orders made by the user
1990
-
1991
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1992
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
1993
-
1994
- :param str symbol: unified market symbol of the market orders were made in
1995
- :param int [since]: the earliest time in ms to fetch orders for
1996
- :param int [limit]: the maximum number of order structures to retrieve
1997
- :param dict [params]: extra parameters specific to the exchange API endpoint
1998
- :param int [params.until]: the latest time in ms to fetch entries for
1999
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
2000
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2001
- """
2002
- # returns the most recent closed or canceled orders up to circa two weeks ago
2003
- await self.load_markets()
2004
- paginate = False
2005
- paginate, params = self.handle_option_and_params(params, 'fetchClosedOrders', 'paginate')
2006
- if paginate:
2007
- return await self.fetch_paginated_call_dynamic('fetchClosedOrders', symbol, since, limit, params)
2008
- request: dict = {}
2009
- if since is not None:
2010
- request['start'] = since
2011
- if limit is not None:
2012
- request['limit'] = limit # default 25, max 2500
2013
- request, params = self.handle_until_option('end', request, params)
2014
- market = None
2015
- response = None
2016
- if symbol is None:
2017
- response = await self.privatePostAuthROrdersHist(self.extend(request, params))
2018
- else:
2019
- market = self.market(symbol)
2020
- request['symbol'] = market['id']
2021
- response = await self.privatePostAuthROrdersSymbolHist(self.extend(request, params))
2022
- #
2023
- # [
2024
- # [
2025
- # 95412102131, # Order ID
2026
- # null, # Group Order ID
2027
- # 1653325121798, # Client Order ID
2028
- # "tDOGE:UST", # Market ID
2029
- # 1653325122000, # Created Timestamp in milliseconds
2030
- # 1653325122000, # Updated Timestamp in milliseconds
2031
- # -10, # Amount remaining(Positive means buy, negative means sell)
2032
- # -10, # Original amount
2033
- # "EXCHANGE LIMIT", # Order type
2034
- # null, # Previous Order Type
2035
- # null, # Millisecond timestamp of Time-In-Force: automatic order cancellation
2036
- # null, # _PLACEHOLDER
2037
- # "4096", # Flags, see parseOrderFlags()
2038
- # "POSTONLY CANCELED", # Order Status, see parseOrderStatus()
2039
- # null, # _PLACEHOLDER
2040
- # null, # _PLACEHOLDER
2041
- # 0.071, # Price
2042
- # 0, # Average Price
2043
- # 0, # Trailing Price
2044
- # 0, # Auxiliary Limit price(for STOP LIMIT)
2045
- # null, # _PLACEHOLDER
2046
- # null, # _PLACEHOLDER
2047
- # null, # _PLACEHOLDER
2048
- # 0, # Notify(0 if False, 1 if True)
2049
- # 0, # Hidden(0 if False, 1 if True)
2050
- # null, # Placed ID(If another order caused self order to be placed(OCO) self will be that other order's ID)
2051
- # null, # _PLACEHOLDER
2052
- # null, # _PLACEHOLDER
2053
- # "API>BFX", # Routing, indicates origin of action: BFX, ETHFX, API>BFX, API>ETHFX
2054
- # null, # _PLACEHOLDER
2055
- # null, # _PLACEHOLDER
2056
- # {"_$F7":1} # additional meta information about the order( _$F7 = IS_POST_ONLY(0 if False, 1 if True), _$F33 = Leverage(int))
2057
- # ]
2058
- # ]
2059
- #
2060
- ordersList = []
2061
- for i in range(0, len(response)):
2062
- ordersList.append({'result': response[i]})
2063
- return self.parse_orders(ordersList, market, since, limit)
2064
-
2065
- async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2066
- """
2067
- fetch all the trades made from a single order
2068
-
2069
- https://docs.bitfinex.com/reference/rest-auth-order-trades
2070
-
2071
- :param str id: order id
2072
- :param str symbol: unified market symbol
2073
- :param int [since]: the earliest time in ms to fetch trades for
2074
- :param int [limit]: the maximum number of trades to retrieve
2075
- :param dict [params]: extra parameters specific to the exchange API endpoint
2076
- :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2077
- """
2078
- if symbol is None:
2079
- raise ArgumentsRequired(self.id + ' fetchOrderTrades() requires a symbol argument')
2080
- await self.load_markets()
2081
- market = self.market(symbol)
2082
- orderId = int(id)
2083
- request: dict = {
2084
- 'id': orderId,
2085
- 'symbol': market['id'],
2086
- }
2087
- # valid for trades upto 10 days old
2088
- response = await self.privatePostAuthROrderSymbolIdTrades(self.extend(request, params))
2089
- tradesList = []
2090
- for i in range(0, len(response)):
2091
- tradesList.append({'result': response[i]}) # convert to array of dicts to match parseOrder signature
2092
- return self.parse_trades(tradesList, market, since, limit)
2093
-
2094
- async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2095
- """
2096
- fetch all trades made by the user
2097
-
2098
- https://docs.bitfinex.com/reference/rest-auth-trades
2099
- https://docs.bitfinex.com/reference/rest-auth-trades-by-symbol
2100
-
2101
- :param str symbol: unified market symbol
2102
- :param int [since]: the earliest time in ms to fetch trades for
2103
- :param int [limit]: the maximum number of trades structures to retrieve
2104
- :param dict [params]: extra parameters specific to the exchange API endpoint
2105
- :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2106
- """
2107
- await self.load_markets()
2108
- market = None
2109
- request: dict = {
2110
- 'end': self.milliseconds(),
2111
- }
2112
- if since is not None:
2113
- request['start'] = since
2114
- if limit is not None:
2115
- request['limit'] = limit # default 25, max 1000
2116
- response = None
2117
- if symbol is not None:
2118
- market = self.market(symbol)
2119
- request['symbol'] = market['id']
2120
- response = await self.privatePostAuthRTradesSymbolHist(self.extend(request, params))
2121
- else:
2122
- response = await self.privatePostAuthRTradesHist(self.extend(request, params))
2123
- tradesList = []
2124
- for i in range(0, len(response)):
2125
- tradesList.append({'result': response[i]}) # convert to array of dicts to match parseOrder signature
2126
- return self.parse_trades(tradesList, market, since, limit)
2127
-
2128
- async def create_deposit_address(self, code: str, params={}):
2129
- """
2130
- create a currency deposit address
2131
-
2132
- https://docs.bitfinex.com/reference/rest-auth-deposit-address
2133
-
2134
- :param str code: unified currency code of the currency for the deposit address
2135
- :param dict [params]: extra parameters specific to the exchange API endpoint
2136
- :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
2137
- """
2138
- await self.load_markets()
2139
- request: dict = {
2140
- 'op_renew': 1,
2141
- }
2142
- return await self.fetch_deposit_address(code, self.extend(request, params))
2143
-
2144
- async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
2145
- """
2146
- fetch the deposit address for a currency associated with self account
2147
-
2148
- https://docs.bitfinex.com/reference/rest-auth-deposit-address
2149
-
2150
- :param str code: unified currency code
2151
- :param dict [params]: extra parameters specific to the exchange API endpoint
2152
- :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
2153
- """
2154
- await self.load_markets()
2155
- currency = self.currency(code)
2156
- # if not provided explicitly we will try to match using the currency name
2157
- network = self.safe_string(params, 'network', code)
2158
- currencyNetworks = self.safe_value(currency, 'networks', {})
2159
- currencyNetwork = self.safe_value(currencyNetworks, network)
2160
- networkId = self.safe_string(currencyNetwork, 'id')
2161
- if networkId is None:
2162
- raise ArgumentsRequired(self.id + " fetchDepositAddress() could not find a network for '" + code + "'. You can specify it by providing the 'network' value inside params")
2163
- wallet = self.safe_string(params, 'wallet', 'exchange') # 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively
2164
- params = self.omit(params, 'network', 'wallet')
2165
- request: dict = {
2166
- 'method': networkId,
2167
- 'wallet': wallet,
2168
- 'op_renew': 0, # a value of 1 will generate a new address
2169
- }
2170
- response = await self.privatePostAuthWDepositAddress(self.extend(request, params))
2171
- #
2172
- # [
2173
- # 1582269616687, # MTS Millisecond Time Stamp of the update
2174
- # "acc_dep", # TYPE Purpose of notification "acc_dep" for account deposit
2175
- # null, # MESSAGE_ID unique ID of the message
2176
- # null, # not documented
2177
- # [
2178
- # null, # PLACEHOLDER
2179
- # "BITCOIN", # METHOD Method of deposit
2180
- # "BTC", # CURRENCY_CODE Currency code of new address
2181
- # null, # PLACEHOLDER
2182
- # "1BC9PZqpUmjyEB54uggn8TFKj49zSDYzqG", # ADDRESS
2183
- # null, # POOL_ADDRESS
2184
- # ],
2185
- # null, # CODE null or integer work in progress
2186
- # "SUCCESS", # STATUS Status of the notification, SUCCESS, ERROR, FAILURE
2187
- # "success", # TEXT Text of the notification
2188
- # ]
2189
- #
2190
- result = self.safe_value(response, 4, [])
2191
- poolAddress = self.safe_string(result, 5)
2192
- address = self.safe_string(result, 4) if (poolAddress is None) else poolAddress
2193
- tag = None if (poolAddress is None) else self.safe_string(result, 4)
2194
- self.check_address(address)
2195
- return {
2196
- 'currency': code,
2197
- 'address': address,
2198
- 'tag': tag,
2199
- 'network': None,
2200
- 'info': response,
2201
- }
2202
-
2203
- def parse_transaction_status(self, status: Str):
2204
- statuses: dict = {
2205
- 'SUCCESS': 'ok',
2206
- 'COMPLETED': 'ok',
2207
- 'ERROR': 'failed',
2208
- 'FAILURE': 'failed',
2209
- 'CANCELED': 'canceled',
2210
- 'PENDING APPROVAL': 'pending',
2211
- 'PENDING': 'pending',
2212
- 'PENDING REVIEW': 'pending',
2213
- 'PENDING CANCELLATION': 'pending',
2214
- 'SENDING': 'pending',
2215
- 'USER APPROVED': 'pending',
2216
- }
2217
- return self.safe_string(statuses, status, status)
2218
-
2219
- def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
2220
- #
2221
- # withdraw
2222
- #
2223
- # [
2224
- # 1582271520931, # MTS Millisecond Time Stamp of the update
2225
- # "acc_wd-req", # TYPE Purpose of notification "acc_wd-req" account withdrawal request
2226
- # null, # MESSAGE_ID unique ID of the message
2227
- # null, # not documented
2228
- # [
2229
- # 0, # WITHDRAWAL_ID Unique Withdrawal ID
2230
- # null, # PLACEHOLDER
2231
- # "bitcoin", # METHOD Method of withdrawal
2232
- # null, # PAYMENT_ID Payment ID if relevant
2233
- # "exchange", # WALLET Sending wallet
2234
- # 1, # AMOUNT Amount of Withdrawal less fee
2235
- # null, # PLACEHOLDER
2236
- # null, # PLACEHOLDER
2237
- # 0.0004, # WITHDRAWAL_FEE Fee on withdrawal
2238
- # ],
2239
- # null, # CODE null or integer Work in progress
2240
- # "SUCCESS", # STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE
2241
- # "Invalid bitcoin address(abcdef)", # TEXT Text of the notification
2242
- # ]
2243
- #
2244
- # fetchDepositsWithdrawals
2245
- #
2246
- # [
2247
- # 13293039, # ID
2248
- # "ETH", # CURRENCY
2249
- # "ETHEREUM", # CURRENCY_NAME
2250
- # null,
2251
- # null,
2252
- # 1574175052000, # MTS_STARTED
2253
- # 1574181326000, # MTS_UPDATED
2254
- # null,
2255
- # null,
2256
- # "CANCELED", # STATUS
2257
- # null,
2258
- # null,
2259
- # -0.24, # AMOUNT, negative for withdrawals
2260
- # -0.00135, # FEES
2261
- # null,
2262
- # null,
2263
- # "0x38110e0Fc932CB2BE...........", # DESTINATION_ADDRESS
2264
- # null,
2265
- # null,
2266
- # null,
2267
- # "0x523ec8945500.....................................", # TRANSACTION_ID
2268
- # "Purchase of 100 pizzas", # WITHDRAW_TRANSACTION_NOTE, might also be: null
2269
- # ]
2270
- #
2271
- transactionLength = len(transaction)
2272
- timestamp = None
2273
- updated = None
2274
- code = None
2275
- amount = None
2276
- id = None
2277
- status = None
2278
- tag = None
2279
- type = None
2280
- feeCost = None
2281
- txid = None
2282
- addressTo = None
2283
- network = None
2284
- comment = None
2285
- if transactionLength == 8:
2286
- data = self.safe_value(transaction, 4, [])
2287
- timestamp = self.safe_integer(transaction, 0)
2288
- if currency is not None:
2289
- code = currency['code']
2290
- feeCost = self.safe_string(data, 8)
2291
- if feeCost is not None:
2292
- feeCost = Precise.string_abs(feeCost)
2293
- amount = self.safe_number(data, 5)
2294
- id = self.safe_integer(data, 0)
2295
- status = 'ok'
2296
- if id == 0:
2297
- id = None
2298
- status = 'failed'
2299
- tag = self.safe_string(data, 3)
2300
- type = 'withdrawal'
2301
- networkId = self.safe_string(data, 2)
2302
- network = self.network_id_to_code(networkId.upper()) # withdraw returns in lowercase
2303
- elif transactionLength == 22:
2304
- id = self.safe_string(transaction, 0)
2305
- currencyId = self.safe_string(transaction, 1)
2306
- code = self.safe_currency_code(currencyId, currency)
2307
- networkId = self.safe_string(transaction, 2)
2308
- network = self.network_id_to_code(networkId)
2309
- timestamp = self.safe_integer(transaction, 5)
2310
- updated = self.safe_integer(transaction, 6)
2311
- status = self.parse_transaction_status(self.safe_string(transaction, 9))
2312
- signedAmount = self.safe_string(transaction, 12)
2313
- amount = Precise.string_abs(signedAmount)
2314
- if signedAmount is not None:
2315
- if Precise.string_lt(signedAmount, '0'):
2316
- type = 'withdrawal'
2317
- else:
2318
- type = 'deposit'
2319
- feeCost = self.safe_string(transaction, 13)
2320
- if feeCost is not None:
2321
- feeCost = Precise.string_abs(feeCost)
2322
- addressTo = self.safe_string(transaction, 16)
2323
- txid = self.safe_string(transaction, 20)
2324
- comment = self.safe_string(transaction, 21)
2325
- return {
2326
- 'info': transaction,
2327
- 'id': id,
2328
- 'txid': txid,
2329
- 'type': type,
2330
- 'currency': code,
2331
- 'network': network,
2332
- 'amount': self.parse_number(amount),
2333
- 'status': status,
2334
- 'timestamp': timestamp,
2335
- 'datetime': self.iso8601(timestamp),
2336
- 'address': addressTo, # self is actually the tag for XRP transfers(the address is missing)
2337
- 'addressFrom': None,
2338
- 'addressTo': addressTo,
2339
- 'tag': tag, # refix it properly for the tag from description
2340
- 'tagFrom': None,
2341
- 'tagTo': tag,
2342
- 'updated': updated,
2343
- 'comment': comment,
2344
- 'internal': None,
2345
- 'fee': {
2346
- 'currency': code,
2347
- 'cost': self.parse_number(feeCost),
2348
- 'rate': None,
2349
- },
2350
- }
2351
-
2352
- async def fetch_trading_fees(self, params={}) -> TradingFees:
2353
- """
2354
- fetch the trading fees for multiple markets
2355
-
2356
- https://docs.bitfinex.com/reference/rest-auth-summary
2357
-
2358
- :param dict [params]: extra parameters specific to the exchange API endpoint
2359
- :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
2360
- """
2361
- await self.load_markets()
2362
- response = await self.privatePostAuthRSummary(params)
2363
- #
2364
- # Response Spec:
2365
- # [
2366
- # PLACEHOLDER,
2367
- # PLACEHOLDER,
2368
- # PLACEHOLDER,
2369
- # PLACEHOLDER,
2370
- # [
2371
- # [
2372
- # MAKER_FEE,
2373
- # MAKER_FEE,
2374
- # MAKER_FEE,
2375
- # PLACEHOLDER,
2376
- # PLACEHOLDER,
2377
- # DERIV_REBATE
2378
- # ],
2379
- # [
2380
- # TAKER_FEE_TO_CRYPTO,
2381
- # TAKER_FEE_TO_STABLE,
2382
- # TAKER_FEE_TO_FIAT,
2383
- # PLACEHOLDER,
2384
- # PLACEHOLDER,
2385
- # DERIV_TAKER_FEE
2386
- # ]
2387
- # ],
2388
- # PLACEHOLDER,
2389
- # PLACEHOLDER,
2390
- # PLACEHOLDER,
2391
- # PLACEHOLDER,
2392
- # {
2393
- # LEO_LEV,
2394
- # LEO_AMOUNT_AVG
2395
- # }
2396
- # ]
2397
- #
2398
- # Example response:
2399
- #
2400
- # [
2401
- # null,
2402
- # null,
2403
- # null,
2404
- # null,
2405
- # [
2406
- # [0.001, 0.001, 0.001, null, null, 0.0002],
2407
- # [0.002, 0.002, 0.002, null, null, 0.00065]
2408
- # ],
2409
- # [
2410
- # [
2411
- # {
2412
- # "curr": "Total(USD)",
2413
- # "vol": "0",
2414
- # "vol_safe": "0",
2415
- # "vol_maker": "0",
2416
- # "vol_BFX": "0",
2417
- # "vol_BFX_safe": "0",
2418
- # "vol_BFX_maker": "0"
2419
- # }
2420
- # ],
2421
- # {},
2422
- # 0
2423
- # ],
2424
- # [null, {}, 0],
2425
- # null,
2426
- # null,
2427
- # {leo_lev: "0", leo_amount_avg: "0"}
2428
- # ]
2429
- #
2430
- result: dict = {}
2431
- fiat = self.safe_value(self.options, 'fiat', {})
2432
- feeData = self.safe_value(response, 4, [])
2433
- makerData = self.safe_value(feeData, 0, [])
2434
- takerData = self.safe_value(feeData, 1, [])
2435
- makerFee = self.safe_number(makerData, 0)
2436
- makerFeeFiat = self.safe_number(makerData, 2)
2437
- makerFeeDeriv = self.safe_number(makerData, 5)
2438
- takerFee = self.safe_number(takerData, 0)
2439
- takerFeeFiat = self.safe_number(takerData, 2)
2440
- takerFeeDeriv = self.safe_number(takerData, 5)
2441
- for i in range(0, len(self.symbols)):
2442
- symbol = self.symbols[i]
2443
- market = self.market(symbol)
2444
- fee = {
2445
- 'info': response,
2446
- 'symbol': symbol,
2447
- 'percentage': True,
2448
- 'tierBased': True,
2449
- }
2450
- if market['quote'] in fiat:
2451
- fee['maker'] = makerFeeFiat
2452
- fee['taker'] = takerFeeFiat
2453
- elif market['contract']:
2454
- fee['maker'] = makerFeeDeriv
2455
- fee['taker'] = takerFeeDeriv
2456
- else: # TODO check if stable coin
2457
- fee['maker'] = makerFee
2458
- fee['taker'] = takerFee
2459
- result[symbol] = fee
2460
- return result
2461
-
2462
- async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2463
- """
2464
- fetch history of deposits and withdrawals
2465
-
2466
- https://docs.bitfinex.com/reference/movement-info
2467
- https://docs.bitfinex.com/reference/rest-auth-movements
2468
-
2469
- :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
2470
- :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
2471
- :param int [limit]: max number of deposit/withdrawals to return, default is None
2472
- :param dict [params]: extra parameters specific to the exchange API endpoint
2473
- :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2474
- """
2475
- await self.load_markets()
2476
- currency = None
2477
- request: dict = {}
2478
- if since is not None:
2479
- request['start'] = since
2480
- if limit is not None:
2481
- request['limit'] = limit # max 1000
2482
- response = None
2483
- if code is not None:
2484
- currency = self.currency(code)
2485
- request['currency'] = currency['uppercaseId']
2486
- response = await self.privatePostAuthRMovementsCurrencyHist(self.extend(request, params))
2487
- else:
2488
- response = await self.privatePostAuthRMovementsHist(self.extend(request, params))
2489
- #
2490
- # [
2491
- # [
2492
- # 13293039, # ID
2493
- # "ETH", # CURRENCY
2494
- # "ETHEREUM", # CURRENCY_NAME
2495
- # null,
2496
- # null,
2497
- # 1574175052000, # MTS_STARTED
2498
- # 1574181326000, # MTS_UPDATED
2499
- # null,
2500
- # null,
2501
- # "CANCELED", # STATUS
2502
- # null,
2503
- # null,
2504
- # -0.24, # AMOUNT, negative for withdrawals
2505
- # -0.00135, # FEES
2506
- # null,
2507
- # null,
2508
- # "0x38110e0Fc932CB2BE...........", # DESTINATION_ADDRESS
2509
- # null,
2510
- # null,
2511
- # null,
2512
- # "0x523ec8945500.....................................", # TRANSACTION_ID
2513
- # "Purchase of 100 pizzas", # WITHDRAW_TRANSACTION_NOTE, might also be: null
2514
- # ]
2515
- # ]
2516
- #
2517
- return self.parse_transactions(response, currency, since, limit)
2518
-
2519
- async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
2520
- """
2521
- make a withdrawal
2522
-
2523
- https://docs.bitfinex.com/reference/rest-auth-withdraw
2524
-
2525
- :param str code: unified currency code
2526
- :param float amount: the amount to withdraw
2527
- :param str address: the address to withdraw to
2528
- :param str tag:
2529
- :param dict [params]: extra parameters specific to the exchange API endpoint
2530
- :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2531
- """
2532
- self.check_address(address)
2533
- await self.load_markets()
2534
- currency = self.currency(code)
2535
- # if not provided explicitly we will try to match using the currency name
2536
- network = self.safe_string(params, 'network', code)
2537
- params = self.omit(params, 'network')
2538
- currencyNetworks = self.safe_value(currency, 'networks', {})
2539
- currencyNetwork = self.safe_value(currencyNetworks, network)
2540
- networkId = self.safe_string(currencyNetwork, 'id')
2541
- if networkId is None:
2542
- raise ArgumentsRequired(self.id + " withdraw() could not find a network for '" + code + "'. You can specify it by providing the 'network' value inside params")
2543
- wallet = self.safe_string(params, 'wallet', 'exchange') # 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively
2544
- params = self.omit(params, 'network', 'wallet')
2545
- request: dict = {
2546
- 'method': networkId,
2547
- 'wallet': wallet,
2548
- 'amount': self.number_to_string(amount),
2549
- 'address': address,
2550
- }
2551
- if tag is not None:
2552
- request['payment_id'] = tag
2553
- withdrawOptions = self.safe_value(self.options, 'withdraw', {})
2554
- includeFee = self.safe_bool(withdrawOptions, 'includeFee', False)
2555
- if includeFee:
2556
- request['fee_deduct'] = 1
2557
- response = await self.privatePostAuthWWithdraw(self.extend(request, params))
2558
- #
2559
- # [
2560
- # 1582271520931, # MTS Millisecond Time Stamp of the update
2561
- # "acc_wd-req", # TYPE Purpose of notification "acc_wd-req" account withdrawal request
2562
- # null, # MESSAGE_ID unique ID of the message
2563
- # null, # not documented
2564
- # [
2565
- # 0, # WITHDRAWAL_ID Unique Withdrawal ID
2566
- # null, # PLACEHOLDER
2567
- # "bitcoin", # METHOD Method of withdrawal
2568
- # null, # PAYMENT_ID Payment ID if relevant
2569
- # "exchange", # WALLET Sending wallet
2570
- # 1, # AMOUNT Amount of Withdrawal less fee
2571
- # null, # PLACEHOLDER
2572
- # null, # PLACEHOLDER
2573
- # 0.0004, # WITHDRAWAL_FEE Fee on withdrawal
2574
- # ],
2575
- # null, # CODE null or integer Work in progress
2576
- # "SUCCESS", # STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE
2577
- # "Invalid bitcoin address(abcdef)", # TEXT Text of the notification
2578
- # ]
2579
- #
2580
- # in case of failure:
2581
- #
2582
- # [
2583
- # "error",
2584
- # 10001,
2585
- # "Momentary balance check. Please wait few seconds and try the transfer again."
2586
- # ]
2587
- #
2588
- statusMessage = self.safe_string(response, 0)
2589
- if statusMessage == 'error':
2590
- feedback = self.id + ' ' + response
2591
- message = self.safe_string(response, 2, '')
2592
- # same message v1
2593
- self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
2594
- self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
2595
- raise ExchangeError(feedback) # unknown message
2596
- text = self.safe_string(response, 7)
2597
- if text != 'success':
2598
- self.throw_broadly_matched_exception(self.exceptions['broad'], text, text)
2599
- return self.parse_transaction(response, currency)
2600
-
2601
- async def fetch_positions(self, symbols: Strings = None, params={}):
2602
- """
2603
- fetch all open positions
2604
-
2605
- https://docs.bitfinex.com/reference/rest-auth-positions
2606
-
2607
- :param str[]|None symbols: list of unified market symbols
2608
- :param dict [params]: extra parameters specific to the exchange API endpoint
2609
- :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2610
- """
2611
- await self.load_markets()
2612
- symbols = self.market_symbols(symbols)
2613
- response = await self.privatePostAuthRPositions(params)
2614
- #
2615
- # [
2616
- # [
2617
- # "tBTCUSD", # SYMBOL
2618
- # "ACTIVE", # STATUS
2619
- # 0.0195, # AMOUNT
2620
- # 8565.0267019, # BASE_PRICE
2621
- # 0, # MARGIN_FUNDING
2622
- # 0, # MARGIN_FUNDING_TYPE
2623
- # -0.33455568705000516, # PL
2624
- # -0.0003117550117425625, # PL_PERC
2625
- # 7045.876419249083, # PRICE_LIQ
2626
- # 3.0673001895895604, # LEVERAGE
2627
- # null, # _PLACEHOLDER
2628
- # 142355652, # POSITION_ID
2629
- # 1574002216000, # MTS_CREATE
2630
- # 1574002216000, # MTS_UPDATE
2631
- # null, # _PLACEHOLDER
2632
- # 0, # TYPE
2633
- # null, # _PLACEHOLDER
2634
- # 0, # COLLATERAL
2635
- # 0, # COLLATERAL_MIN
2636
- # # META
2637
- # {
2638
- # "reason":"TRADE",
2639
- # "order_id":34271018124,
2640
- # "liq_stage":null,
2641
- # "trade_price":"8565.0267019",
2642
- # "trade_amount":"0.0195",
2643
- # "order_id_oppo":34277498022
2644
- # }
2645
- # ]
2646
- # ]
2647
- #
2648
- positionsList = []
2649
- for i in range(0, len(response)):
2650
- positionsList.append({'result': response[i]})
2651
- return self.parse_positions(positionsList, symbols)
2652
-
2653
- def parse_position(self, position: dict, market: Market = None):
2654
- #
2655
- # [
2656
- # "tBTCUSD", # SYMBOL
2657
- # "ACTIVE", # STATUS
2658
- # 0.0195, # AMOUNT
2659
- # 8565.0267019, # BASE_PRICE
2660
- # 0, # MARGIN_FUNDING
2661
- # 0, # MARGIN_FUNDING_TYPE
2662
- # -0.33455568705000516, # PL
2663
- # -0.0003117550117425625, # PL_PERC
2664
- # 7045.876419249083, # PRICE_LIQ
2665
- # 3.0673001895895604, # LEVERAGE
2666
- # null, # _PLACEHOLDER
2667
- # 142355652, # POSITION_ID
2668
- # 1574002216000, # MTS_CREATE
2669
- # 1574002216000, # MTS_UPDATE
2670
- # null, # _PLACEHOLDER
2671
- # 0, # TYPE
2672
- # null, # _PLACEHOLDER
2673
- # 0, # COLLATERAL
2674
- # 0, # COLLATERAL_MIN
2675
- # # META
2676
- # {
2677
- # "reason": "TRADE",
2678
- # "order_id": 34271018124,
2679
- # "liq_stage": null,
2680
- # "trade_price": "8565.0267019",
2681
- # "trade_amount": "0.0195",
2682
- # "order_id_oppo": 34277498022
2683
- # }
2684
- # ]
2685
- #
2686
- positionList = self.safe_list(position, 'result')
2687
- marketId = self.safe_string(positionList, 0)
2688
- amount = self.safe_string(positionList, 2)
2689
- timestamp = self.safe_integer(positionList, 12)
2690
- meta = self.safe_string(positionList, 19)
2691
- tradePrice = self.safe_string(meta, 'trade_price')
2692
- tradeAmount = self.safe_string(meta, 'trade_amount')
2693
- return self.safe_position({
2694
- 'info': positionList,
2695
- 'id': self.safe_string(positionList, 11),
2696
- 'symbol': self.safe_symbol(marketId, market),
2697
- 'notional': self.parse_number(amount),
2698
- 'marginMode': 'isolated', # derivatives use isolated, margin uses cross, https://support.bitfinex.com/hc/en-us/articles/360035475374-Derivatives-Trading-on-Bitfinex
2699
- 'liquidationPrice': self.safe_number(positionList, 8),
2700
- 'entryPrice': self.safe_number(positionList, 3),
2701
- 'unrealizedPnl': self.safe_number(positionList, 6),
2702
- 'percentage': self.safe_number(positionList, 7),
2703
- 'contracts': None,
2704
- 'contractSize': None,
2705
- 'markPrice': None,
2706
- 'lastPrice': None,
2707
- 'side': 'long' if Precise.string_gt(amount, '0') else 'short',
2708
- 'hedged': None,
2709
- 'timestamp': timestamp,
2710
- 'datetime': self.iso8601(timestamp),
2711
- 'lastUpdateTimestamp': self.safe_integer(positionList, 13),
2712
- 'maintenanceMargin': self.safe_number(positionList, 18),
2713
- 'maintenanceMarginPercentage': None,
2714
- 'collateral': self.safe_number(positionList, 17),
2715
- 'initialMargin': self.parse_number(Precise.string_mul(tradeAmount, tradePrice)),
2716
- 'initialMarginPercentage': None,
2717
- 'leverage': self.safe_number(positionList, 9),
2718
- 'marginRatio': None,
2719
- 'stopLossPrice': None,
2720
- 'takeProfitPrice': None,
2721
- })
2722
-
2723
- def nonce(self):
2724
- return self.milliseconds()
2725
-
2726
- def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
2727
- request = '/' + self.implode_params(path, params)
2728
- query = self.omit(params, self.extract_params(path))
2729
- if api == 'v1':
2730
- request = api + request
2731
- else:
2732
- request = self.version + request
2733
- url = self.urls['api'][api] + '/' + request
2734
- if api == 'public':
2735
- if query:
2736
- url += '?' + self.urlencode(query)
2737
- if api == 'private':
2738
- self.check_required_credentials()
2739
- nonce = str(self.nonce())
2740
- body = self.json(query)
2741
- auth = '/api/' + request + nonce + body
2742
- signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha384)
2743
- headers = {
2744
- 'bfx-nonce': nonce,
2745
- 'bfx-apikey': self.apiKey,
2746
- 'bfx-signature': signature,
2747
- 'Content-Type': 'application/json',
2748
- }
2749
- return {'url': url, 'method': method, 'body': body, 'headers': headers}
2750
-
2751
- def handle_errors(self, statusCode, statusText, url, method, headers, body, response, requestHeaders, requestBody):
2752
- # ["error", 11010, "ratelimit: error"]
2753
- if response is not None:
2754
- if not isinstance(response, list):
2755
- message = self.safe_string_2(response, 'message', 'error')
2756
- feedback = self.id + ' ' + body
2757
- self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
2758
- self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
2759
- raise ExchangeError(self.id + ' ' + body)
2760
- elif response == '':
2761
- raise ExchangeError(self.id + ' returned empty response')
2762
- if statusCode == 429:
2763
- raise RateLimitExceeded(self.id + ' ' + body)
2764
- if statusCode == 500:
2765
- # See https://docs.bitfinex.com/docs/abbreviations-glossary#section-errorinfo-codes
2766
- errorCode = self.safe_string(response, 1, '')
2767
- errorText = self.safe_string(response, 2, '')
2768
- feedback = self.id + ' ' + errorText
2769
- self.throw_broadly_matched_exception(self.exceptions['broad'], errorText, feedback)
2770
- self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
2771
- self.throw_exactly_matched_exception(self.exceptions['exact'], errorText, feedback)
2772
- raise ExchangeError(self.id + ' ' + errorText + '(#' + errorCode + ')')
2773
- return response
2774
-
2775
- def parse_ledger_entry_type(self, type: Str):
2776
- if type is None:
2777
- return None
2778
- elif type.find('fee') >= 0 or type.find('charged') >= 0:
2779
- return 'fee'
2780
- elif type.find('rebate') >= 0:
2781
- return 'rebate'
2782
- elif type.find('deposit') >= 0 or type.find('withdrawal') >= 0:
2783
- return 'transaction'
2784
- elif type.find('transfer') >= 0:
2785
- return 'transfer'
2786
- elif type.find('payment') >= 0:
2787
- return 'payout'
2788
- elif type.find('exchange') >= 0 or type.find('position') >= 0:
2789
- return 'trade'
2790
- else:
2791
- return type
2792
-
2793
- def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
2794
- #
2795
- # [
2796
- # [
2797
- # 2531822314, # ID: Ledger identifier
2798
- # "USD", # CURRENCY: The symbol of the currency(ex. "BTC")
2799
- # null, # PLACEHOLDER
2800
- # 1573521810000, # MTS: Timestamp in milliseconds
2801
- # null, # PLACEHOLDER
2802
- # 0.01644445, # AMOUNT: Amount of funds moved
2803
- # 0, # BALANCE: New balance
2804
- # null, # PLACEHOLDER
2805
- # "Settlement @ 185.79 on wallet margin" # DESCRIPTION: Description of ledger transaction
2806
- # ]
2807
- # ]
2808
- #
2809
- itemList = self.safe_list(item, 'result', [])
2810
- type = None
2811
- id = self.safe_string(itemList, 0)
2812
- currencyId = self.safe_string(itemList, 1)
2813
- code = self.safe_currency_code(currencyId, currency)
2814
- currency = self.safe_currency(currencyId, currency)
2815
- timestamp = self.safe_integer(itemList, 3)
2816
- amount = self.safe_number(itemList, 5)
2817
- after = self.safe_number(itemList, 6)
2818
- description = self.safe_string(itemList, 8)
2819
- if description is not None:
2820
- parts = description.split(' @ ')
2821
- first = self.safe_string_lower(parts, 0)
2822
- type = self.parse_ledger_entry_type(first)
2823
- return self.safe_ledger_entry({
2824
- 'info': item,
2825
- 'id': id,
2826
- 'direction': None,
2827
- 'account': None,
2828
- 'referenceId': id,
2829
- 'referenceAccount': None,
2830
- 'type': type,
2831
- 'currency': code,
2832
- 'amount': amount,
2833
- 'timestamp': timestamp,
2834
- 'datetime': self.iso8601(timestamp),
2835
- 'before': None,
2836
- 'after': after,
2837
- 'status': None,
2838
- 'fee': None,
2839
- }, currency)
2840
-
2841
- async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
2842
- """
2843
- fetch the history of changes, actions done by the user or operations that altered the balance of the user
2844
-
2845
- https://docs.bitfinex.com/reference/rest-auth-ledgers
2846
-
2847
- :param str [code]: unified currency code, default is None
2848
- :param int [since]: timestamp in ms of the earliest ledger entry, default is None
2849
- :param int [limit]: max number of ledger entries to return, default is None, max is 2500
2850
- :param dict [params]: extra parameters specific to the exchange API endpoint
2851
- :param int [params.until]: timestamp in ms of the latest ledger entry
2852
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
2853
- :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger-structure>`
2854
- """
2855
- await self.load_markets()
2856
- paginate = False
2857
- paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
2858
- if paginate:
2859
- return await self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params, 2500)
2860
- currency = None
2861
- request: dict = {}
2862
- if since is not None:
2863
- request['start'] = since
2864
- if limit is not None:
2865
- request['limit'] = limit
2866
- request, params = self.handle_until_option('end', request, params)
2867
- response = None
2868
- if code is not None:
2869
- currency = self.currency(code)
2870
- request['currency'] = currency['uppercaseId']
2871
- response = await self.privatePostAuthRLedgersCurrencyHist(self.extend(request, params))
2872
- else:
2873
- response = await self.privatePostAuthRLedgersHist(self.extend(request, params))
2874
- #
2875
- # [
2876
- # [
2877
- # 2531822314, # ID: Ledger identifier
2878
- # "USD", # CURRENCY: The symbol of the currency(ex. "BTC")
2879
- # null, # PLACEHOLDER
2880
- # 1573521810000, # MTS: Timestamp in milliseconds
2881
- # null, # PLACEHOLDER
2882
- # 0.01644445, # AMOUNT: Amount of funds moved
2883
- # 0, # BALANCE: New balance
2884
- # null, # PLACEHOLDER
2885
- # "Settlement @ 185.79 on wallet margin" # DESCRIPTION: Description of ledger transaction
2886
- # ]
2887
- # ]
2888
- #
2889
- ledgerObjects = []
2890
- for i in range(0, len(response)):
2891
- item = response[i]
2892
- ledgerObjects.append({'result': item})
2893
- return self.parse_ledger(ledgerObjects, currency, since, limit)
2894
-
2895
- async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
2896
- """
2897
- fetch the current funding rate for multiple symbols
2898
-
2899
- https://docs.bitfinex.com/reference/rest-public-derivatives-status
2900
-
2901
- :param str[] symbols: list of unified market symbols
2902
- :param dict [params]: extra parameters specific to the exchange API endpoint
2903
- :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
2904
- """
2905
- if symbols is None:
2906
- raise ArgumentsRequired(self.id + ' fetchFundingRates() requires a symbols argument')
2907
- await self.load_markets()
2908
- marketIds = self.market_ids(symbols)
2909
- request: dict = {
2910
- 'keys': ','.join(marketIds),
2911
- }
2912
- response = await self.publicGetStatusDeriv(self.extend(request, params))
2913
- #
2914
- # [
2915
- # [
2916
- # "tBTCF0:USTF0",
2917
- # 1691165059000,
2918
- # null,
2919
- # 29297.851276225,
2920
- # 29277.5,
2921
- # null,
2922
- # 36950860.76010306,
2923
- # null,
2924
- # 1691193600000,
2925
- # 0.00000527,
2926
- # 82,
2927
- # null,
2928
- # 0.00014548,
2929
- # null,
2930
- # null,
2931
- # 29278.8925,
2932
- # null,
2933
- # null,
2934
- # 9636.07644994,
2935
- # null,
2936
- # null,
2937
- # null,
2938
- # 0.0005,
2939
- # 0.0025
2940
- # ]
2941
- # ]
2942
- #
2943
- return self.parse_funding_rates(response)
2944
-
2945
- async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2946
- """
2947
- fetches historical funding rate prices
2948
-
2949
- https://docs.bitfinex.com/reference/rest-public-derivatives-status-history
2950
-
2951
- :param str symbol: unified market symbol
2952
- :param int [since]: timestamp in ms of the earliest funding rate entry
2953
- :param int [limit]: max number of funding rate entrys to return
2954
- :param dict [params]: extra parameters specific to the exchange API endpoint
2955
- :param int [params.until]: timestamp in ms of the latest funding rate
2956
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
2957
- :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
2958
- """
2959
- if symbol is None:
2960
- raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
2961
- await self.load_markets()
2962
- paginate = False
2963
- paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
2964
- if paginate:
2965
- return await self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params, 5000)
2966
- market = self.market(symbol)
2967
- request: dict = {
2968
- 'symbol': market['id'],
2969
- }
2970
- if since is not None:
2971
- request['start'] = since
2972
- request, params = self.handle_until_option('end', request, params)
2973
- response = await self.publicGetStatusDerivSymbolHist(self.extend(request, params))
2974
- #
2975
- # [
2976
- # [
2977
- # "tBTCF0:USTF0",
2978
- # 1691165059000,
2979
- # null,
2980
- # 29297.851276225,
2981
- # 29277.5,
2982
- # null,
2983
- # 36950860.76010306,
2984
- # null,
2985
- # 1691193600000,
2986
- # 0.00000527,
2987
- # 82,
2988
- # null,
2989
- # 0.00014548,
2990
- # null,
2991
- # null,
2992
- # 29278.8925,
2993
- # null,
2994
- # null,
2995
- # 9636.07644994,
2996
- # null,
2997
- # null,
2998
- # null,
2999
- # 0.0005,
3000
- # 0.0025
3001
- # ]
3002
- # ]
3003
- #
3004
- rates = []
3005
- for i in range(0, len(response)):
3006
- fr = response[i]
3007
- rate = self.parse_funding_rate_history(fr, market)
3008
- rates.append(rate)
3009
- reversedArray = []
3010
- rawRates = self.filter_by_symbol_since_limit(rates, symbol, since, limit)
3011
- ratesLength = len(rawRates)
3012
- for i in range(0, ratesLength):
3013
- index = ratesLength - i - 1
3014
- valueAtIndex = rawRates[index]
3015
- reversedArray.append(valueAtIndex)
3016
- return reversedArray
3017
-
3018
- def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
3019
- #
3020
- # [
3021
- # "tBTCF0:USTF0",
3022
- # 1691165059000,
3023
- # null,
3024
- # 29297.851276225,
3025
- # 29277.5,
3026
- # null,
3027
- # 36950860.76010306,
3028
- # null,
3029
- # 1691193600000,
3030
- # 0.00000527,
3031
- # 82,
3032
- # null,
3033
- # 0.00014548,
3034
- # null,
3035
- # null,
3036
- # 29278.8925,
3037
- # null,
3038
- # null,
3039
- # 9636.07644994,
3040
- # null,
3041
- # null,
3042
- # null,
3043
- # 0.0005,
3044
- # 0.0025
3045
- # ]
3046
- #
3047
- marketId = self.safe_string(contract, 0)
3048
- timestamp = self.safe_integer(contract, 1)
3049
- nextFundingTimestamp = self.safe_integer(contract, 8)
3050
- return {
3051
- 'info': contract,
3052
- 'symbol': self.safe_symbol(marketId, market),
3053
- 'markPrice': self.safe_number(contract, 15),
3054
- 'indexPrice': self.safe_number(contract, 3),
3055
- 'interestRate': None,
3056
- 'estimatedSettlePrice': None,
3057
- 'timestamp': timestamp,
3058
- 'datetime': self.iso8601(timestamp),
3059
- 'fundingRate': self.safe_number(contract, 12),
3060
- 'fundingTimestamp': None,
3061
- 'fundingDatetime': None,
3062
- 'nextFundingRate': self.safe_number(contract, 9),
3063
- 'nextFundingTimestamp': nextFundingTimestamp,
3064
- 'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
3065
- 'previousFundingRate': None,
3066
- 'previousFundingTimestamp': None,
3067
- 'previousFundingDatetime': None,
3068
- 'interval': None,
3069
- }
3070
-
3071
- def parse_funding_rate_history(self, contract, market: Market = None):
3072
- #
3073
- # [
3074
- # 1691165494000,
3075
- # null,
3076
- # 29278.95838065,
3077
- # 29260.5,
3078
- # null,
3079
- # 36950860.76010305,
3080
- # null,
3081
- # 1691193600000,
3082
- # 0.00001449,
3083
- # 222,
3084
- # null,
3085
- # 0.00014548,
3086
- # null,
3087
- # null,
3088
- # 29260.005,
3089
- # null,
3090
- # null,
3091
- # 9635.86484562,
3092
- # null,
3093
- # null,
3094
- # null,
3095
- # 0.0005,
3096
- # 0.0025
3097
- # ]
3098
- #
3099
- timestamp = self.safe_integer(contract, 0)
3100
- nextFundingTimestamp = self.safe_integer(contract, 7)
3101
- return {
3102
- 'info': contract,
3103
- 'symbol': self.safe_symbol(None, market),
3104
- 'markPrice': self.safe_number(contract, 14),
3105
- 'indexPrice': self.safe_number(contract, 2),
3106
- 'interestRate': None,
3107
- 'estimatedSettlePrice': None,
3108
- 'timestamp': timestamp,
3109
- 'datetime': self.iso8601(timestamp),
3110
- 'fundingRate': self.safe_number(contract, 11),
3111
- 'fundingTimestamp': None,
3112
- 'fundingDatetime': None,
3113
- 'nextFundingRate': self.safe_number(contract, 8),
3114
- 'nextFundingTimestamp': nextFundingTimestamp,
3115
- 'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
3116
- 'previousFundingRate': None,
3117
- 'previousFundingTimestamp': None,
3118
- 'previousFundingDatetime': None,
3119
- }
3120
-
3121
- async def fetch_open_interest(self, symbol: str, params={}):
3122
- """
3123
- retrieves the open interest of a contract trading pair
3124
-
3125
- https://docs.bitfinex.com/reference/rest-public-derivatives-status
3126
-
3127
- :param str symbol: unified CCXT market symbol
3128
- :param dict [params]: exchange specific parameters
3129
- :returns dict: an `open interest structure <https://docs.ccxt.com/#/?id=open-interest-structure>`
3130
- """
3131
- await self.load_markets()
3132
- market = self.market(symbol)
3133
- request: dict = {
3134
- 'keys': market['id'],
3135
- }
3136
- response = await self.publicGetStatusDeriv(self.extend(request, params))
3137
- #
3138
- # [
3139
- # [
3140
- # "tXRPF0:USTF0", # market id
3141
- # 1706256986000, # millisecond timestamp
3142
- # null,
3143
- # 0.512705, # derivative mid price
3144
- # 0.512395, # underlying spot mid price
3145
- # null,
3146
- # 37671483.04, # insurance fund balance
3147
- # null,
3148
- # 1706284800000, # timestamp of next funding
3149
- # 0.00002353, # accrued funding for next period
3150
- # 317, # next funding step
3151
- # null,
3152
- # 0, # current funding
3153
- # null,
3154
- # null,
3155
- # 0.5123016, # mark price
3156
- # null,
3157
- # null,
3158
- # 2233562.03115, # open interest in contracts
3159
- # null,
3160
- # null,
3161
- # null,
3162
- # 0.0005, # average spread without funding payment
3163
- # 0.0025 # funding payment cap
3164
- # ]
3165
- # ]
3166
- #
3167
- oi = self.safe_list(response, 0)
3168
- return self.parse_open_interest(oi, market)
3169
-
3170
- async def fetch_open_interest_history(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
3171
- """
3172
- retrieves the open interest history of a currency
3173
-
3174
- https://docs.bitfinex.com/reference/rest-public-derivatives-status-history
3175
-
3176
- :param str symbol: unified CCXT market symbol
3177
- :param str timeframe: the time period of each row of data, not used by bitfinex2
3178
- :param int [since]: the time in ms of the earliest record to retrieve unix timestamp
3179
- :param int [limit]: the number of records in the response
3180
- :param dict [params]: exchange specific parameters
3181
- :param int [params.until]: the time in ms of the latest record to retrieve unix timestamp
3182
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
3183
- :returns: An array of `open interest structures <https://docs.ccxt.com/#/?id=open-interest-structure>`
3184
- """
3185
- await self.load_markets()
3186
- paginate = False
3187
- paginate, params = self.handle_option_and_params(params, 'fetchOpenInterestHistory', 'paginate')
3188
- if paginate:
3189
- return await self.fetch_paginated_call_deterministic('fetchOpenInterestHistory', symbol, since, limit, '8h', params, 5000)
3190
- market = self.market(symbol)
3191
- request: dict = {
3192
- 'symbol': market['id'],
3193
- }
3194
- if since is not None:
3195
- request['start'] = since
3196
- if limit is not None:
3197
- request['limit'] = limit
3198
- request, params = self.handle_until_option('end', request, params)
3199
- response = await self.publicGetStatusDerivSymbolHist(self.extend(request, params))
3200
- #
3201
- # [
3202
- # [
3203
- # 1706295191000, # timestamp
3204
- # null,
3205
- # 42152.425382, # derivative mid price
3206
- # 42133, # spot mid price
3207
- # null,
3208
- # 37671589.7853521, # insurance fund balance
3209
- # null,
3210
- # 1706313600000, # timestamp of next funding
3211
- # 0.00018734, # accrued funding for next period
3212
- # 3343, # next funding step
3213
- # null,
3214
- # 0.00007587, # current funding
3215
- # null,
3216
- # null,
3217
- # 42134.1, # mark price
3218
- # null,
3219
- # null,
3220
- # 5775.20348804, # open interest number of contracts
3221
- # null,
3222
- # null,
3223
- # null,
3224
- # 0.0005, # average spread without funding payment
3225
- # 0.0025 # funding payment cap
3226
- # ],
3227
- # ]
3228
- #
3229
- return self.parse_open_interests(response, market, since, limit)
3230
-
3231
- def parse_open_interest(self, interest, market: Market = None):
3232
- #
3233
- # fetchOpenInterest:
3234
- #
3235
- # [
3236
- # "tXRPF0:USTF0", # market id
3237
- # 1706256986000, # millisecond timestamp
3238
- # null,
3239
- # 0.512705, # derivative mid price
3240
- # 0.512395, # underlying spot mid price
3241
- # null,
3242
- # 37671483.04, # insurance fund balance
3243
- # null,
3244
- # 1706284800000, # timestamp of next funding
3245
- # 0.00002353, # accrued funding for next period
3246
- # 317, # next funding step
3247
- # null,
3248
- # 0, # current funding
3249
- # null,
3250
- # null,
3251
- # 0.5123016, # mark price
3252
- # null,
3253
- # null,
3254
- # 2233562.03115, # open interest in contracts
3255
- # null,
3256
- # null,
3257
- # null,
3258
- # 0.0005, # average spread without funding payment
3259
- # 0.0025 # funding payment cap
3260
- # ]
3261
- #
3262
- # fetchOpenInterestHistory:
3263
- #
3264
- # [
3265
- # 1706295191000, # timestamp
3266
- # null,
3267
- # 42152.425382, # derivative mid price
3268
- # 42133, # spot mid price
3269
- # null,
3270
- # 37671589.7853521, # insurance fund balance
3271
- # null,
3272
- # 1706313600000, # timestamp of next funding
3273
- # 0.00018734, # accrued funding for next period
3274
- # 3343, # next funding step
3275
- # null,
3276
- # 0.00007587, # current funding
3277
- # null,
3278
- # null,
3279
- # 42134.1, # mark price
3280
- # null,
3281
- # null,
3282
- # 5775.20348804, # open interest number of contracts
3283
- # null,
3284
- # null,
3285
- # null,
3286
- # 0.0005, # average spread without funding payment
3287
- # 0.0025 # funding payment cap
3288
- # ]
3289
- #
3290
- interestLength = len(interest)
3291
- openInterestIndex = 17 if (interestLength == 23) else 18
3292
- timestamp = self.safe_integer(interest, 1)
3293
- marketId = self.safe_string(interest, 0)
3294
- return self.safe_open_interest({
3295
- 'symbol': self.safe_symbol(marketId, market, None, 'swap'),
3296
- 'openInterestAmount': self.safe_number(interest, openInterestIndex),
3297
- 'openInterestValue': None,
3298
- 'timestamp': timestamp,
3299
- 'datetime': self.iso8601(timestamp),
3300
- 'info': interest,
3301
- }, market)
3302
-
3303
- async def fetch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
3304
- """
3305
- retrieves the public liquidations of a trading pair
3306
-
3307
- https://docs.bitfinex.com/reference/rest-public-liquidations
3308
-
3309
- :param str symbol: unified CCXT market symbol
3310
- :param int [since]: the earliest time in ms to fetch liquidations for
3311
- :param int [limit]: the maximum number of liquidation structures to retrieve
3312
- :param dict [params]: exchange specific parameters
3313
- :param int [params.until]: timestamp in ms of the latest liquidation
3314
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
3315
- :returns dict: an array of `liquidation structures <https://docs.ccxt.com/#/?id=liquidation-structure>`
3316
- """
3317
- await self.load_markets()
3318
- paginate = False
3319
- paginate, params = self.handle_option_and_params(params, 'fetchLiquidations', 'paginate')
3320
- if paginate:
3321
- return await self.fetch_paginated_call_deterministic('fetchLiquidations', symbol, since, limit, '8h', params, 500)
3322
- market = self.market(symbol)
3323
- request: dict = {}
3324
- if since is not None:
3325
- request['start'] = since
3326
- if limit is not None:
3327
- request['limit'] = limit
3328
- request, params = self.handle_until_option('end', request, params)
3329
- response = await self.publicGetLiquidationsHist(self.extend(request, params))
3330
- #
3331
- # [
3332
- # [
3333
- # [
3334
- # "pos",
3335
- # 171085137,
3336
- # 1706395919788,
3337
- # null,
3338
- # "tAVAXF0:USTF0",
3339
- # -8,
3340
- # 32.868,
3341
- # null,
3342
- # 1,
3343
- # 1,
3344
- # null,
3345
- # 33.255
3346
- # ]
3347
- # ],
3348
- # ]
3349
- #
3350
- return self.parse_liquidations(response, market, since, limit)
3351
-
3352
- def parse_liquidation(self, liquidation, market: Market = None):
3353
- #
3354
- # [
3355
- # [
3356
- # "pos",
3357
- # 171085137, # position id
3358
- # 1706395919788, # timestamp
3359
- # null,
3360
- # "tAVAXF0:USTF0", # market id
3361
- # -8, # amount in contracts
3362
- # 32.868, # base price
3363
- # null,
3364
- # 1,
3365
- # 1,
3366
- # null,
3367
- # 33.255 # acquired price
3368
- # ]
3369
- # ]
3370
- #
3371
- entry = liquidation[0]
3372
- timestamp = self.safe_integer(entry, 2)
3373
- marketId = self.safe_string(entry, 4)
3374
- contracts = Precise.string_abs(self.safe_string(entry, 5))
3375
- contractSize = self.safe_string(market, 'contractSize')
3376
- baseValue = Precise.string_mul(contracts, contractSize)
3377
- price = self.safe_string(entry, 11)
3378
- return self.safe_liquidation({
3379
- 'info': entry,
3380
- 'symbol': self.safe_symbol(marketId, market, None, 'contract'),
3381
- 'contracts': self.parse_number(contracts),
3382
- 'contractSize': self.parse_number(contractSize),
3383
- 'price': self.parse_number(price),
3384
- 'baseValue': self.parse_number(baseValue),
3385
- 'quoteValue': self.parse_number(Precise.string_mul(baseValue, price)),
3386
- 'timestamp': timestamp,
3387
- 'datetime': self.iso8601(timestamp),
3388
- })
3389
-
3390
- async def set_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
3391
- """
3392
- either adds or reduces margin in a swap position in order to set the margin to a specific value
3393
-
3394
- https://docs.bitfinex.com/reference/rest-auth-deriv-pos-collateral-set
3395
-
3396
- :param str symbol: unified market symbol of the market to set margin in
3397
- :param float amount: the amount to set the margin to
3398
- :param dict [params]: parameters specific to the exchange API endpoint
3399
- :returns dict: A `margin structure <https://github.com/ccxt/ccxt/wiki/Manual#add-margin-structure>`
3400
- """
3401
- await self.load_markets()
3402
- market = self.market(symbol)
3403
- if not market['swap']:
3404
- raise NotSupported(self.id + ' setMargin() only support swap markets')
3405
- request: dict = {
3406
- 'symbol': market['id'],
3407
- 'collateral': self.parse_to_numeric(amount),
3408
- }
3409
- response = await self.privatePostAuthWDerivCollateralSet(self.extend(request, params))
3410
- #
3411
- # [
3412
- # [
3413
- # 1
3414
- # ]
3415
- # ]
3416
- #
3417
- data = self.safe_value(response, 0)
3418
- return self.parse_margin_modification(data, market)
3419
-
3420
- def parse_margin_modification(self, data, market=None) -> MarginModification:
3421
- #
3422
- # setMargin
3423
- #
3424
- # [
3425
- # [
3426
- # 1
3427
- # ]
3428
- # ]
3429
- #
3430
- marginStatusRaw = data[0]
3431
- marginStatus = 'ok' if (marginStatusRaw == 1) else 'failed'
3432
- return {
3433
- 'info': data,
3434
- 'symbol': market['symbol'],
3435
- 'type': None,
3436
- 'marginMode': 'isolated',
3437
- 'amount': None,
3438
- 'total': None,
3439
- 'code': None,
3440
- 'status': marginStatus,
3441
- 'timestamp': None,
3442
- 'datetime': None,
3443
- }
3444
-
3445
- async def fetch_order(self, id: str, symbol: Str = None, params={}):
3446
- """
3447
- fetches information on an order made by the user
3448
-
3449
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
3450
- https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
3451
-
3452
- :param str id: the order id
3453
- :param str [symbol]: unified symbol of the market the order was made in
3454
- :param dict [params]: extra parameters specific to the exchange API endpoint
3455
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
3456
- """
3457
- await self.load_markets()
3458
- request: dict = {
3459
- 'id': [self.parse_to_numeric(id)],
3460
- }
3461
- market = None
3462
- response = None
3463
- if symbol is None:
3464
- response = await self.privatePostAuthROrders(self.extend(request, params))
3465
- else:
3466
- market = self.market(symbol)
3467
- request['symbol'] = market['id']
3468
- response = await self.privatePostAuthROrdersSymbol(self.extend(request, params))
3469
- #
3470
- # [
3471
- # [
3472
- # 139658969116,
3473
- # null,
3474
- # 1706843908637,
3475
- # "tBTCUST",
3476
- # 1706843908637,
3477
- # 1706843908638,
3478
- # 0.0001,
3479
- # 0.0001,
3480
- # "EXCHANGE LIMIT",
3481
- # null,
3482
- # null,
3483
- # null,
3484
- # 0,
3485
- # "ACTIVE",
3486
- # null,
3487
- # null,
3488
- # 35000,
3489
- # 0,
3490
- # 0,
3491
- # 0,
3492
- # null,
3493
- # null,
3494
- # null,
3495
- # 0,
3496
- # 0,
3497
- # null,
3498
- # null,
3499
- # null,
3500
- # "API>BFX",
3501
- # null,
3502
- # null,
3503
- # {}
3504
- # ]
3505
- # ]
3506
- #
3507
- order = self.safe_list(response, 0)
3508
- newOrder = {'result': order}
3509
- return self.parse_order(newOrder, market)
3510
-
3511
- async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
3512
- """
3513
- edit a trade order
3514
-
3515
- https://docs.bitfinex.com/reference/rest-auth-update-order
3516
-
3517
- :param str id: edit order id
3518
- :param str symbol: unified symbol of the market to edit an order in
3519
- :param str type: 'market' or 'limit'
3520
- :param str side: 'buy' or 'sell'
3521
- :param float amount: how much you want to trade in units of the base currency
3522
- :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
3523
- :param dict [params]: extra parameters specific to the exchange API endpoint
3524
- :param float [params.stopPrice]: the price that triggers a trigger order
3525
- :param boolean [params.postOnly]: set to True if you want to make a post only order
3526
- :param boolean [params.reduceOnly]: indicates that the order is to reduce the size of a position
3527
- :param int [params.flags]: additional order parameters: 4096(Post Only), 1024(Reduce Only), 16384(OCO), 64(Hidden), 512(Close), 524288(No Var Rates)
3528
- :param int [params.leverage]: leverage for a derivative order, supported by derivative symbol orders only, the value should be between 1 and 100 inclusive
3529
- :param int [params.clientOrderId]: a unique client order id for the order
3530
- :param float [params.trailingAmount]: *swap only* the quote amount to trail away from the current market price
3531
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
3532
- """
3533
- await self.load_markets()
3534
- market = self.market(symbol)
3535
- request: dict = {
3536
- 'id': self.parse_to_numeric(id),
3537
- }
3538
- if amount is not None:
3539
- amountString = self.amount_to_precision(symbol, amount)
3540
- amountString = amountString if (side == 'buy') else Precise.string_neg(amountString)
3541
- request['amount'] = amountString
3542
- stopPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice')
3543
- trailingAmount = self.safe_string(params, 'trailingAmount')
3544
- timeInForce = self.safe_string(params, 'timeInForce')
3545
- postOnlyParam = self.safe_bool(params, 'postOnly', False)
3546
- reduceOnly = self.safe_bool(params, 'reduceOnly', False)
3547
- clientOrderId = self.safe_integer_2(params, 'cid', 'clientOrderId')
3548
- if trailingAmount is not None:
3549
- request['price_trailing'] = trailingAmount
3550
- elif stopPrice is not None:
3551
- # request['price'] is taken for stop orders
3552
- request['price'] = self.price_to_precision(symbol, stopPrice)
3553
- if type == 'limit':
3554
- request['price_aux_limit'] = self.price_to_precision(symbol, price)
3555
- postOnly = (postOnlyParam or (timeInForce == 'PO'))
3556
- if (type != 'market') and (stopPrice is None):
3557
- request['price'] = self.price_to_precision(symbol, price)
3558
- # flag values may be summed to combine flags
3559
- flags = 0
3560
- if postOnly:
3561
- flags = self.sum(flags, 4096)
3562
- if reduceOnly:
3563
- flags = self.sum(flags, 1024)
3564
- if flags != 0:
3565
- request['flags'] = flags
3566
- if clientOrderId is not None:
3567
- request['cid'] = clientOrderId
3568
- leverage = self.safe_integer_2(params, 'leverage', 'lev')
3569
- if leverage is not None:
3570
- request['lev'] = leverage
3571
- params = self.omit(params, ['triggerPrice', 'stopPrice', 'timeInForce', 'postOnly', 'reduceOnly', 'trailingAmount', 'clientOrderId', 'leverage'])
3572
- response = await self.privatePostAuthWOrderUpdate(self.extend(request, params))
3573
- #
3574
- # [
3575
- # 1706845376402,
3576
- # "ou-req",
3577
- # null,
3578
- # null,
3579
- # [
3580
- # 139658969116,
3581
- # null,
3582
- # 1706843908637,
3583
- # "tBTCUST",
3584
- # 1706843908637,
3585
- # 1706843908638,
3586
- # 0.0002,
3587
- # 0.0002,
3588
- # "EXCHANGE LIMIT",
3589
- # null,
3590
- # null,
3591
- # null,
3592
- # 0,
3593
- # "ACTIVE",
3594
- # null,
3595
- # null,
3596
- # 35000,
3597
- # 0,
3598
- # 0,
3599
- # 0,
3600
- # null,
3601
- # null,
3602
- # null,
3603
- # 0,
3604
- # 0,
3605
- # null,
3606
- # null,
3607
- # null,
3608
- # "API>BFX",
3609
- # null,
3610
- # null,
3611
- # {}
3612
- # ],
3613
- # null,
3614
- # "SUCCESS",
3615
- # "Submitting update to exchange limit buy order for 0.0002 BTC."
3616
- # ]
3617
- #
3618
- status = self.safe_string(response, 6)
3619
- if status != 'SUCCESS':
3620
- errorCode = response[5]
3621
- errorText = response[7]
3622
- raise ExchangeError(self.id + ' ' + response[6] + ': ' + errorText + '(#' + errorCode + ')')
3623
- order = self.safe_list(response, 4, [])
3624
- newOrder = {'result': order}
3625
- return self.parse_order(newOrder, market)