ccxt-look 1.81.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/.cache/eslintcache +1 -0
  2. package/.dockerignore +6 -0
  3. package/.eslintignore +1 -0
  4. package/.gitattributes +5 -0
  5. package/.readthedocs.yaml +16 -0
  6. package/CONTRIBUTING.md +1049 -0
  7. package/LICENSE.txt +21 -0
  8. package/README.md +537 -0
  9. package/SECURITY.md +5 -0
  10. package/build/cleanup-old-tags.js +94 -0
  11. package/build/countries.js +256 -0
  12. package/build/export-exchanges.js +520 -0
  13. package/build/fs.js +51 -0
  14. package/build/transpile.js +1772 -0
  15. package/build/vss.js +78 -0
  16. package/ccxt.browser.js +7 -0
  17. package/ccxt.d.ts +692 -0
  18. package/ccxt.js +171 -0
  19. package/cleanup.sh +2 -0
  20. package/composer-install.sh +20 -0
  21. package/dist/ccxt.browser.js +208383 -0
  22. package/gource.sh +3 -0
  23. package/index.html +7 -0
  24. package/js/.eslintrc +87 -0
  25. package/js/aax.js +2686 -0
  26. package/js/ascendex.js +2584 -0
  27. package/js/base/.eslintrc.js +43 -0
  28. package/js/base/Exchange.js +2371 -0
  29. package/js/base/Precise.js +283 -0
  30. package/js/base/errorHierarchy.js +47 -0
  31. package/js/base/errors.js +55 -0
  32. package/js/base/functions/crypto.js +158 -0
  33. package/js/base/functions/encode.js +118 -0
  34. package/js/base/functions/generic.js +270 -0
  35. package/js/base/functions/misc.js +138 -0
  36. package/js/base/functions/number.js +329 -0
  37. package/js/base/functions/platform.js +38 -0
  38. package/js/base/functions/string.js +21 -0
  39. package/js/base/functions/throttle.js +79 -0
  40. package/js/base/functions/time.js +210 -0
  41. package/js/base/functions/type.js +66 -0
  42. package/js/base/functions.js +28 -0
  43. package/js/bequant.js +32 -0
  44. package/js/bibox.js +1407 -0
  45. package/js/bigone.js +1366 -0
  46. package/js/binance.js +5652 -0
  47. package/js/binancecoinm.js +46 -0
  48. package/js/binanceus.js +46 -0
  49. package/js/binanceusdm.js +49 -0
  50. package/js/bit2c.js +535 -0
  51. package/js/bitbank.js +842 -0
  52. package/js/bitbay.js +16 -0
  53. package/js/bitbns.js +1073 -0
  54. package/js/bitcoincom.js +15 -0
  55. package/js/bitfinex.js +1433 -0
  56. package/js/bitfinex2.js +2025 -0
  57. package/js/bitflyer.js +840 -0
  58. package/js/bitforex.js +614 -0
  59. package/js/bitget.js +2397 -0
  60. package/js/bithumb.js +980 -0
  61. package/js/bitmart.js +2516 -0
  62. package/js/bitmex.js +1809 -0
  63. package/js/bitopro.js +1443 -0
  64. package/js/bitpanda.js +1782 -0
  65. package/js/bitrue.js +1747 -0
  66. package/js/bitso.js +1062 -0
  67. package/js/bitstamp.js +1757 -0
  68. package/js/bitstamp1.js +343 -0
  69. package/js/bittrex.js +1876 -0
  70. package/js/bitvavo.js +1579 -0
  71. package/js/bkex.js +1233 -0
  72. package/js/bl3p.js +346 -0
  73. package/js/blockchaincom.js +969 -0
  74. package/js/btcalpha.js +680 -0
  75. package/js/btcbox.js +477 -0
  76. package/js/btcmarkets.js +1022 -0
  77. package/js/btctradeua.js +466 -0
  78. package/js/btcturk.js +734 -0
  79. package/js/buda.js +946 -0
  80. package/js/bw.js +1265 -0
  81. package/js/bybit.js +3372 -0
  82. package/js/bytetrade.js +1336 -0
  83. package/js/cdax.js +1646 -0
  84. package/js/cex.js +1410 -0
  85. package/js/coinbase.js +1342 -0
  86. package/js/coinbaseprime.js +31 -0
  87. package/js/coinbasepro.js +1466 -0
  88. package/js/coincheck.js +755 -0
  89. package/js/coinex.js +3400 -0
  90. package/js/coinfalcon.js +880 -0
  91. package/js/coinmate.js +794 -0
  92. package/js/coinone.js +816 -0
  93. package/js/coinspot.js +345 -0
  94. package/js/crex24.js +1636 -0
  95. package/js/cryptocom.js +1832 -0
  96. package/js/currencycom.js +1748 -0
  97. package/js/delta.js +1547 -0
  98. package/js/deribit.js +2148 -0
  99. package/js/digifinex.js +1585 -0
  100. package/js/eqonex.js +1660 -0
  101. package/js/exmo.js +1670 -0
  102. package/js/fairdesk.js +1231 -0
  103. package/js/flowbtc.js +35 -0
  104. package/js/fmfwio.js +34 -0
  105. package/js/ftx.js +2751 -0
  106. package/js/ftxus.js +38 -0
  107. package/js/gateio.js +4174 -0
  108. package/js/gemini.js +1397 -0
  109. package/js/hitbtc.js +1343 -0
  110. package/js/hitbtc3.js +2329 -0
  111. package/js/hollaex.js +1486 -0
  112. package/js/huobi.js +5706 -0
  113. package/js/huobijp.js +1710 -0
  114. package/js/huobipro.js +18 -0
  115. package/js/idex.js +1439 -0
  116. package/js/independentreserve.js +649 -0
  117. package/js/indodax.js +742 -0
  118. package/js/itbit.js +722 -0
  119. package/js/kraken.js +2179 -0
  120. package/js/kucoin.js +2571 -0
  121. package/js/kucoinfutures.js +1771 -0
  122. package/js/kuna.js +809 -0
  123. package/js/latoken.js +1445 -0
  124. package/js/lbank.js +760 -0
  125. package/js/liquid.js +1432 -0
  126. package/js/luno.js +873 -0
  127. package/js/lykke.js +1147 -0
  128. package/js/mercado.js +771 -0
  129. package/js/mexc.js +3151 -0
  130. package/js/ndax.js +2233 -0
  131. package/js/novadax.js +1318 -0
  132. package/js/oceanex.js +816 -0
  133. package/js/okcoin.js +3841 -0
  134. package/js/okex.js +16 -0
  135. package/js/okex5.js +16 -0
  136. package/js/okx.js +4795 -0
  137. package/js/paymium.js +498 -0
  138. package/js/phemex.js +2957 -0
  139. package/js/poloniex.js +1674 -0
  140. package/js/probit.js +1346 -0
  141. package/js/qtrade.js +1588 -0
  142. package/js/ripio.js +1061 -0
  143. package/js/static_dependencies/BN/bn.js +3526 -0
  144. package/js/static_dependencies/README.md +1 -0
  145. package/js/static_dependencies/crypto-js/crypto-js.js +5988 -0
  146. package/js/static_dependencies/elliptic/lib/elliptic/curve/base.js +375 -0
  147. package/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js +433 -0
  148. package/js/static_dependencies/elliptic/lib/elliptic/curve/index.js +8 -0
  149. package/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js +180 -0
  150. package/js/static_dependencies/elliptic/lib/elliptic/curve/short.js +938 -0
  151. package/js/static_dependencies/elliptic/lib/elliptic/curves.js +204 -0
  152. package/js/static_dependencies/elliptic/lib/elliptic/ec/index.js +240 -0
  153. package/js/static_dependencies/elliptic/lib/elliptic/ec/key.js +119 -0
  154. package/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js +24 -0
  155. package/js/static_dependencies/elliptic/lib/elliptic/eddsa/index.js +145 -0
  156. package/js/static_dependencies/elliptic/lib/elliptic/eddsa/key.js +100 -0
  157. package/js/static_dependencies/elliptic/lib/elliptic/eddsa/signature.js +65 -0
  158. package/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js +780 -0
  159. package/js/static_dependencies/elliptic/lib/elliptic/utils.js +214 -0
  160. package/js/static_dependencies/elliptic/lib/elliptic.js +22 -0
  161. package/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js +114 -0
  162. package/js/static_dependencies/fetch-ponyfill/fetch-node.js +39 -0
  163. package/js/static_dependencies/node-fetch/index.js +1564 -0
  164. package/js/static_dependencies/node-rsa/NodeRSA.js +223 -0
  165. package/js/static_dependencies/node-rsa/asn1/ber/errors.js +13 -0
  166. package/js/static_dependencies/node-rsa/asn1/ber/index.js +21 -0
  167. package/js/static_dependencies/node-rsa/asn1/ber/reader.js +262 -0
  168. package/js/static_dependencies/node-rsa/asn1/ber/types.js +36 -0
  169. package/js/static_dependencies/node-rsa/asn1/index.js +17 -0
  170. package/js/static_dependencies/node-rsa/encryptEngines/js.js +34 -0
  171. package/js/static_dependencies/node-rsa/formats/components.js +71 -0
  172. package/js/static_dependencies/node-rsa/formats/formats.js +31 -0
  173. package/js/static_dependencies/node-rsa/formats/pkcs1.js +148 -0
  174. package/js/static_dependencies/node-rsa/formats/pkcs8.js +187 -0
  175. package/js/static_dependencies/node-rsa/libs/jsbn.js +1252 -0
  176. package/js/static_dependencies/node-rsa/libs/rsa.js +147 -0
  177. package/js/static_dependencies/node-rsa/schemes/pkcs1.js +176 -0
  178. package/js/static_dependencies/node-rsa/schemes/schemes.js +21 -0
  179. package/js/static_dependencies/node-rsa/utils.js +98 -0
  180. package/js/static_dependencies/qs/formats.js +18 -0
  181. package/js/static_dependencies/qs/index.js +11 -0
  182. package/js/static_dependencies/qs/parse.js +242 -0
  183. package/js/static_dependencies/qs/stringify.js +269 -0
  184. package/js/static_dependencies/qs/utils.js +230 -0
  185. package/js/stex.js +1925 -0
  186. package/js/test/.eslintrc.js +42 -0
  187. package/js/test/Exchange/test.balance.js +61 -0
  188. package/js/test/Exchange/test.borrowRate.js +32 -0
  189. package/js/test/Exchange/test.currency.js +52 -0
  190. package/js/test/Exchange/test.fetchBalance.js +23 -0
  191. package/js/test/Exchange/test.fetchBorrowInterest.js +59 -0
  192. package/js/test/Exchange/test.fetchBorrowRate.js +32 -0
  193. package/js/test/Exchange/test.fetchBorrowRates.js +28 -0
  194. package/js/test/Exchange/test.fetchClosedOrders.js +32 -0
  195. package/js/test/Exchange/test.fetchCurrencies.js +35 -0
  196. package/js/test/Exchange/test.fetchDeposits.js +31 -0
  197. package/js/test/Exchange/test.fetchFundingFees.js +19 -0
  198. package/js/test/Exchange/test.fetchFundingRateHistory.js +40 -0
  199. package/js/test/Exchange/test.fetchL2OrderBook.js +23 -0
  200. package/js/test/Exchange/test.fetchLedger.js +42 -0
  201. package/js/test/Exchange/test.fetchLeverageTiers.js +33 -0
  202. package/js/test/Exchange/test.fetchMarketLeverageTiers.js +22 -0
  203. package/js/test/Exchange/test.fetchMarkets.js +33 -0
  204. package/js/test/Exchange/test.fetchMyTrades.js +42 -0
  205. package/js/test/Exchange/test.fetchOHLCV.js +46 -0
  206. package/js/test/Exchange/test.fetchOpenOrders.js +36 -0
  207. package/js/test/Exchange/test.fetchOrderBook.js +25 -0
  208. package/js/test/Exchange/test.fetchOrderBooks.js +35 -0
  209. package/js/test/Exchange/test.fetchOrders.js +41 -0
  210. package/js/test/Exchange/test.fetchPositions.js +47 -0
  211. package/js/test/Exchange/test.fetchStatus.js +35 -0
  212. package/js/test/Exchange/test.fetchTicker.js +38 -0
  213. package/js/test/Exchange/test.fetchTickers.js +49 -0
  214. package/js/test/Exchange/test.fetchTrades.js +39 -0
  215. package/js/test/Exchange/test.fetchTradingFee.js +18 -0
  216. package/js/test/Exchange/test.fetchTradingFees.js +22 -0
  217. package/js/test/Exchange/test.fetchTransactions.js +31 -0
  218. package/js/test/Exchange/test.fetchWithdrawals.js +31 -0
  219. package/js/test/Exchange/test.ledgerItem.js +46 -0
  220. package/js/test/Exchange/test.leverageTier.js +33 -0
  221. package/js/test/Exchange/test.loadMarkets.js +35 -0
  222. package/js/test/Exchange/test.market.js +129 -0
  223. package/js/test/Exchange/test.ohlcv.js +33 -0
  224. package/js/test/Exchange/test.order.js +62 -0
  225. package/js/test/Exchange/test.orderbook.js +61 -0
  226. package/js/test/Exchange/test.position.js +21 -0
  227. package/js/test/Exchange/test.throttle.js +94 -0
  228. package/js/test/Exchange/test.ticker.js +95 -0
  229. package/js/test/Exchange/test.trade.js +68 -0
  230. package/js/test/Exchange/test.tradingFee.js +34 -0
  231. package/js/test/Exchange/test.transaction.js +35 -0
  232. package/js/test/base/.eslintrc +38 -0
  233. package/js/test/base/functions/test.crypto.js +110 -0
  234. package/js/test/base/functions/test.datetime.js +62 -0
  235. package/js/test/base/functions/test.generic.js +152 -0
  236. package/js/test/base/functions/test.number.js +362 -0
  237. package/js/test/base/functions/test.time.js +56 -0
  238. package/js/test/base/functions/test.type.js +53 -0
  239. package/js/test/base/test.base.js +193 -0
  240. package/js/test/errors/test.InsufficientFunds.js +86 -0
  241. package/js/test/errors/test.InvalidNonce.js +64 -0
  242. package/js/test/errors/test.InvalidOrder.js +35 -0
  243. package/js/test/errors/test.OrderNotFound.js +39 -0
  244. package/js/test/test.js +426 -0
  245. package/js/test/test.timeout_hang.js +12 -0
  246. package/js/therock.js +1431 -0
  247. package/js/tidebit.js +632 -0
  248. package/js/tidex.js +939 -0
  249. package/js/timex.js +1283 -0
  250. package/js/upbit.js +1622 -0
  251. package/js/vcc.js +1353 -0
  252. package/js/wavesexchange.js +2185 -0
  253. package/js/wazirx.js +732 -0
  254. package/js/whitebit.js +1352 -0
  255. package/js/woo.js +1577 -0
  256. package/js/xena.js +1948 -0
  257. package/js/yobit.js +1129 -0
  258. package/js/zaif.js +647 -0
  259. package/js/zb.js +4088 -0
  260. package/js/zipmex.js +40 -0
  261. package/js/zonda.js +1497 -0
  262. package/multilang.sh +159 -0
  263. package/package.json +591 -0
  264. package/postinstall.js +103 -0
package/js/bitfinex.js ADDED
@@ -0,0 +1,1433 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+
5
+ const Exchange = require ('./base/Exchange');
6
+ const { NotSupported, RateLimitExceeded, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, InvalidNonce, BadSymbol } = require ('./base/errors');
7
+ const { SIGNIFICANT_DIGITS, DECIMAL_PLACES, TRUNCATE, ROUND } = require ('./base/functions/number');
8
+ const Precise = require ('./base/Precise');
9
+
10
+ // ---------------------------------------------------------------------------
11
+
12
+ module.exports = class bitfinex extends Exchange {
13
+ describe () {
14
+ return this.deepExtend (super.describe (), {
15
+ 'id': 'bitfinex',
16
+ 'name': 'Bitfinex',
17
+ 'countries': [ 'VG' ],
18
+ 'version': 'v1',
19
+ // cheapest is 90 requests a minute = 1.5 requests per second on average => ( 1000ms / 1.5) = 666.666 ms between requests on average
20
+ 'rateLimit': 666.666,
21
+ 'pro': true,
22
+ // new metainfo interface
23
+ 'has': {
24
+ 'CORS': undefined,
25
+ 'spot': true,
26
+ 'margin': undefined, // has but unimplemented
27
+ 'swap': undefined, // has but unimplemented
28
+ 'future': undefined,
29
+ 'option': undefined,
30
+ 'cancelAllOrders': true,
31
+ 'cancelOrder': true,
32
+ 'createDepositAddress': true,
33
+ 'createOrder': true,
34
+ 'editOrder': true,
35
+ 'fetchBalance': true,
36
+ 'fetchClosedOrders': true,
37
+ 'fetchDepositAddress': true,
38
+ 'fetchDeposits': undefined,
39
+ 'fetchFundingFees': true,
40
+ 'fetchIndexOHLCV': false,
41
+ 'fetchLeverageTiers': false,
42
+ 'fetchMarkets': true,
43
+ 'fetchMarkOHLCV': false,
44
+ 'fetchMyTrades': true,
45
+ 'fetchOHLCV': true,
46
+ 'fetchOpenOrders': true,
47
+ 'fetchOrder': true,
48
+ 'fetchOrderBook': true,
49
+ 'fetchPositions': true,
50
+ 'fetchPremiumIndexOHLCV': false,
51
+ 'fetchTicker': true,
52
+ 'fetchTickers': true,
53
+ 'fetchTime': false,
54
+ 'fetchTrades': true,
55
+ 'fetchTradingFee': false,
56
+ 'fetchTradingFees': true,
57
+ 'fetchTransactions': true,
58
+ 'fetchWithdrawals': undefined,
59
+ 'transfer': true,
60
+ 'withdraw': true,
61
+ },
62
+ 'timeframes': {
63
+ '1m': '1m',
64
+ '5m': '5m',
65
+ '15m': '15m',
66
+ '30m': '30m',
67
+ '1h': '1h',
68
+ '3h': '3h',
69
+ '4h': '4h',
70
+ '6h': '6h',
71
+ '12h': '12h',
72
+ '1d': '1D',
73
+ '1w': '7D',
74
+ '2w': '14D',
75
+ '1M': '1M',
76
+ },
77
+ 'urls': {
78
+ 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg',
79
+ 'api': {
80
+ 'v2': 'https://api-pub.bitfinex.com', // https://github.com/ccxt/ccxt/issues/5109
81
+ 'public': 'https://api.bitfinex.com',
82
+ 'private': 'https://api.bitfinex.com',
83
+ },
84
+ 'www': 'https://www.bitfinex.com',
85
+ 'referral': 'https://www.bitfinex.com/?refcode=P61eYxFL',
86
+ 'doc': [
87
+ 'https://docs.bitfinex.com/v1/docs',
88
+ 'https://github.com/bitfinexcom/bitfinex-api-node',
89
+ ],
90
+ },
91
+ 'api': {
92
+ // v2 symbol ids require a 't' prefix
93
+ // just the public part of it (use bitfinex2 for everything else)
94
+ 'v2': {
95
+ 'get': {
96
+ 'platform/status': 3, // 30 requests per minute
97
+ 'tickers': 1, // 90 requests a minute
98
+ 'ticker/{symbol}': 1,
99
+ 'tickers/hist': 1,
100
+ 'trades/{symbol}/hist': 1,
101
+ 'book/{symbol}/{precision}': 0.375, // 240 requests per minute = 4 requests per second (1000ms / rateLimit) / 4 = 0.37500375
102
+ 'book/{symbol}/P0': 0.375,
103
+ 'book/{symbol}/P1': 0.375,
104
+ 'book/{symbol}/P2': 0.375,
105
+ 'book/{symbol}/P3': 0.375,
106
+ 'book/{symbol}/R0': 0.375,
107
+ 'stats1/{key}:{size}:{symbol}:{side}/{section}': 1, // 90 requests a minute
108
+ 'stats1/{key}:{size}:{symbol}/{section}': 1,
109
+ 'stats1/{key}:{size}:{symbol}:long/last': 1,
110
+ 'stats1/{key}:{size}:{symbol}:long/hist': 1,
111
+ 'stats1/{key}:{size}:{symbol}:short/last': 1,
112
+ 'stats1/{key}:{size}:{symbol}:short/hist': 1,
113
+ 'candles/trade:{timeframe}:{symbol}/{section}': 1, // 90 requests a minute
114
+ 'candles/trade:{timeframe}:{symbol}/last': 1,
115
+ 'candles/trade:{timeframe}:{symbol}/hist': 1,
116
+ },
117
+ },
118
+ 'public': {
119
+ 'get': {
120
+ 'book/{symbol}': 1, // 90 requests a minute
121
+ // 'candles/{symbol}':0,
122
+ 'lendbook/{currency}': 6, // 15 requests a minute
123
+ 'lends/{currency}': 3, // 30 requests a minute
124
+ 'pubticker/{symbol}': 3, // 30 requests a minute = 0.5 requests per second => (1000ms / rateLimit) / 0.5 = 3.00003
125
+ 'stats/{symbol}': 6, // 15 requests a minute = 0.25 requests per second => (1000ms / rateLimit ) /0.25 = 6.00006 (endpoint returns red html... or 'unknown symbol')
126
+ 'symbols': 18, // 5 requests a minute = 0.08333 requests per second => (1000ms / rateLimit) / 0.08333 = 18.0009
127
+ 'symbols_details': 18, // 5 requests a minute
128
+ 'tickers': 1, // endpoint not mentioned in v1 docs... but still responds
129
+ 'trades/{symbol}': 3, // 60 requests a minute = 1 request per second => (1000ms / rateLimit) / 1 = 1.5 ... but only works if set to 3
130
+ },
131
+ },
132
+ 'private': {
133
+ 'post': {
134
+ 'account_fees': 18,
135
+ 'account_infos': 6,
136
+ 'balances': 9.036, // 10 requests a minute = 0.166 requests per second => (1000ms / rateLimit) / 0.166 = 9.036
137
+ 'basket_manage': 6,
138
+ 'credits': 6,
139
+ 'deposit/new': 18,
140
+ 'funding/close': 6,
141
+ 'history': 6, // 15 requests a minute
142
+ 'history/movements': 6,
143
+ 'key_info': 6,
144
+ 'margin_infos': 3, // 30 requests a minute
145
+ 'mytrades': 3,
146
+ 'mytrades_funding': 6,
147
+ 'offer/cancel': 6,
148
+ 'offer/new': 6,
149
+ 'offer/status': 6,
150
+ 'offers': 6,
151
+ 'offers/hist': 90.03, // one request per minute
152
+ 'order/cancel': 0.2,
153
+ 'order/cancel/all': 0.2,
154
+ 'order/cancel/multi': 0.2,
155
+ 'order/cancel/replace': 0.2,
156
+ 'order/new': 0.2, // 450 requests a minute = 7.5 request a second => (1000ms / rateLimit) / 7.5 = 0.2000002
157
+ 'order/new/multi': 0.2,
158
+ 'order/status': 0.2,
159
+ 'orders': 0.2,
160
+ 'orders/hist': 90.03, // one request per minute = 0.1666 => (1000ms / rateLimit) / 0.01666 = 90.03
161
+ 'position/claim': 18,
162
+ 'position/close': 18,
163
+ 'positions': 18,
164
+ 'summary': 18,
165
+ 'taken_funds': 6,
166
+ 'total_taken_funds': 6,
167
+ 'transfer': 18,
168
+ 'unused_taken_funds': 6,
169
+ 'withdraw': 18,
170
+ },
171
+ },
172
+ },
173
+ 'fees': {
174
+ 'trading': {
175
+ 'feeSide': 'get',
176
+ 'tierBased': true,
177
+ 'percentage': true,
178
+ 'maker': this.parseNumber ('0.001'),
179
+ 'taker': this.parseNumber ('0.002'),
180
+ 'tiers': {
181
+ 'taker': [
182
+ [ this.parseNumber ('0'), this.parseNumber ('0.002') ],
183
+ [ this.parseNumber ('500000'), this.parseNumber ('0.002') ],
184
+ [ this.parseNumber ('1000000'), this.parseNumber ('0.002') ],
185
+ [ this.parseNumber ('2500000'), this.parseNumber ('0.002') ],
186
+ [ this.parseNumber ('5000000'), this.parseNumber ('0.002') ],
187
+ [ this.parseNumber ('7500000'), this.parseNumber ('0.002') ],
188
+ [ this.parseNumber ('10000000'), this.parseNumber ('0.0018') ],
189
+ [ this.parseNumber ('15000000'), this.parseNumber ('0.0016') ],
190
+ [ this.parseNumber ('20000000'), this.parseNumber ('0.0014') ],
191
+ [ this.parseNumber ('25000000'), this.parseNumber ('0.0012') ],
192
+ [ this.parseNumber ('30000000'), this.parseNumber ('0.001') ],
193
+ ],
194
+ 'maker': [
195
+ [ this.parseNumber ('0'), this.parseNumber ('0.001') ],
196
+ [ this.parseNumber ('500000'), this.parseNumber ('0.0008') ],
197
+ [ this.parseNumber ('1000000'), this.parseNumber ('0.0006') ],
198
+ [ this.parseNumber ('2500000'), this.parseNumber ('0.0004') ],
199
+ [ this.parseNumber ('5000000'), this.parseNumber ('0.0002') ],
200
+ [ this.parseNumber ('7500000'), this.parseNumber ('0') ],
201
+ [ this.parseNumber ('10000000'), this.parseNumber ('0') ],
202
+ [ this.parseNumber ('15000000'), this.parseNumber ('0') ],
203
+ [ this.parseNumber ('20000000'), this.parseNumber ('0') ],
204
+ [ this.parseNumber ('25000000'), this.parseNumber ('0') ],
205
+ [ this.parseNumber ('30000000'), this.parseNumber ('0') ],
206
+ ],
207
+ },
208
+ },
209
+ 'funding': {
210
+ 'tierBased': false, // true for tier-based/progressive
211
+ 'percentage': false, // fixed commission
212
+ // Actually deposit fees are free for larger deposits (> $1000 USD equivalent)
213
+ // these values below are deprecated, we should not hardcode fees and limits anymore
214
+ // to be reimplemented with bitfinex funding fees from their API or web endpoints
215
+ 'deposit': {},
216
+ 'withdraw': {},
217
+ },
218
+ },
219
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
220
+ 'commonCurrencies': {
221
+ 'ALG': 'ALGO', // https://github.com/ccxt/ccxt/issues/6034
222
+ 'AMP': 'AMPL',
223
+ 'ATO': 'ATOM', // https://github.com/ccxt/ccxt/issues/5118
224
+ 'BCHABC': 'XEC',
225
+ 'BCHN': 'BCH',
226
+ 'DAT': 'DATA',
227
+ 'DOG': 'MDOGE',
228
+ 'DSH': 'DASH',
229
+ // https://github.com/ccxt/ccxt/issues/7399
230
+ // https://coinmarketcap.com/currencies/pnetwork/
231
+ // https://en.cryptonomist.ch/blog/eidoo/the-edo-to-pnt-upgrade-what-you-need-to-know-updated/
232
+ 'EDO': 'PNT',
233
+ 'EUS': 'EURS',
234
+ 'EUT': 'EURT',
235
+ 'IDX': 'ID',
236
+ 'IOT': 'IOTA',
237
+ 'IQX': 'IQ',
238
+ 'MNA': 'MANA',
239
+ 'ORS': 'ORS Group', // conflict with Origin Sport #3230
240
+ 'PAS': 'PASS',
241
+ 'QSH': 'QASH',
242
+ 'QTM': 'QTUM',
243
+ 'RBT': 'RBTC',
244
+ 'SNG': 'SNGLS',
245
+ 'STJ': 'STORJ',
246
+ 'TERRAUST': 'UST',
247
+ 'TSD': 'TUSD',
248
+ 'YGG': 'YEED', // conflict with Yield Guild Games
249
+ 'YYW': 'YOYOW',
250
+ 'UDC': 'USDC',
251
+ 'UST': 'USDT',
252
+ 'VSY': 'VSYS',
253
+ 'WAX': 'WAXP',
254
+ 'XCH': 'XCHF',
255
+ 'ZBT': 'ZB',
256
+ },
257
+ 'exceptions': {
258
+ 'exact': {
259
+ 'temporarily_unavailable': ExchangeNotAvailable, // Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info.
260
+ 'Order could not be cancelled.': OrderNotFound, // non-existent order
261
+ 'No such order found.': OrderNotFound, // ?
262
+ 'Order price must be positive.': InvalidOrder, // on price <= 0
263
+ 'Could not find a key matching the given X-BFX-APIKEY.': AuthenticationError,
264
+ 'Key price should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (price)
265
+ 'Key amount should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (amount)
266
+ 'ERR_RATE_LIMIT': RateLimitExceeded,
267
+ 'Ratelimit': RateLimitExceeded,
268
+ 'Nonce is too small.': InvalidNonce,
269
+ 'No summary found.': ExchangeError, // fetchTradingFees (summary) endpoint can give this vague error message
270
+ 'Cannot evaluate your available balance, please try again': ExchangeNotAvailable,
271
+ 'Unknown symbol': BadSymbol,
272
+ 'Cannot complete transfer. Exchange balance insufficient.': InsufficientFunds,
273
+ 'Momentary balance check. Please wait few seconds and try the transfer again.': ExchangeError,
274
+ },
275
+ 'broad': {
276
+ 'Invalid X-BFX-SIGNATURE': AuthenticationError,
277
+ 'This API key does not have permission': PermissionDenied, // authenticated but not authorized
278
+ 'not enough exchange balance for ': InsufficientFunds, // when buying cost is greater than the available quote currency
279
+ 'minimum size for ': InvalidOrder, // when amount below limits.amount.min
280
+ 'Invalid order': InvalidOrder, // ?
281
+ 'The available balance is only': InsufficientFunds, // {"status":"error","message":"Cannot withdraw 1.0027 ETH from your exchange wallet. The available balance is only 0.0 ETH. If you have limit orders, open positions, unused or active margin funding, this will decrease your available balance. To increase it, you can cancel limit orders or reduce/close your positions.","withdrawal_id":0,"fees":"0.0027"}
282
+ },
283
+ },
284
+ 'precisionMode': SIGNIFICANT_DIGITS,
285
+ 'options': {
286
+ 'currencyNames': {
287
+ 'AGI': 'agi',
288
+ 'AID': 'aid',
289
+ 'AIO': 'aio',
290
+ 'ANT': 'ant',
291
+ 'AVT': 'aventus', // #1811
292
+ 'BAT': 'bat',
293
+ // https://github.com/ccxt/ccxt/issues/5833
294
+ 'BCH': 'bab', // undocumented
295
+ // 'BCH': 'bcash', // undocumented
296
+ 'BCI': 'bci',
297
+ 'BFT': 'bft',
298
+ 'BSV': 'bsv',
299
+ 'BTC': 'bitcoin',
300
+ 'BTG': 'bgold',
301
+ 'CFI': 'cfi',
302
+ 'COMP': 'comp',
303
+ 'DAI': 'dai',
304
+ 'DADI': 'dad',
305
+ 'DASH': 'dash',
306
+ 'DATA': 'datacoin',
307
+ 'DTH': 'dth',
308
+ 'EDO': 'eidoo', // #1811
309
+ 'ELF': 'elf',
310
+ 'EOS': 'eos',
311
+ 'ETC': 'ethereumc',
312
+ 'ETH': 'ethereum',
313
+ 'ETP': 'metaverse',
314
+ 'FUN': 'fun',
315
+ 'GNT': 'golem',
316
+ 'IOST': 'ios',
317
+ 'IOTA': 'iota',
318
+ // https://github.com/ccxt/ccxt/issues/5833
319
+ 'LEO': 'let', // ETH chain
320
+ // 'LEO': 'les', // EOS chain
321
+ 'LINK': 'link',
322
+ 'LRC': 'lrc',
323
+ 'LTC': 'litecoin',
324
+ 'LYM': 'lym',
325
+ 'MANA': 'mna',
326
+ 'MIT': 'mit',
327
+ 'MKR': 'mkr',
328
+ 'MTN': 'mtn',
329
+ 'NEO': 'neo',
330
+ 'ODE': 'ode',
331
+ 'OMG': 'omisego',
332
+ 'OMNI': 'mastercoin',
333
+ 'QASH': 'qash',
334
+ 'QTUM': 'qtum', // #1811
335
+ 'RCN': 'rcn',
336
+ 'RDN': 'rdn',
337
+ 'REP': 'rep',
338
+ 'REQ': 'req',
339
+ 'RLC': 'rlc',
340
+ 'SAN': 'santiment',
341
+ 'SNGLS': 'sng',
342
+ 'SNT': 'status',
343
+ 'SPANK': 'spk',
344
+ 'STORJ': 'stj',
345
+ 'TNB': 'tnb',
346
+ 'TRX': 'trx',
347
+ 'TUSD': 'tsd',
348
+ 'USD': 'wire',
349
+ 'USDC': 'udc', // https://github.com/ccxt/ccxt/issues/5833
350
+ 'UTK': 'utk',
351
+ 'USDT': 'tetheruso', // Tether on Omni
352
+ // 'USDT': 'tetheruse', // Tether on ERC20
353
+ // 'USDT': 'tetherusl', // Tether on Liquid
354
+ // 'USDT': 'tetherusx', // Tether on Tron
355
+ // 'USDT': 'tetheruss', // Tether on EOS
356
+ 'VEE': 'vee',
357
+ 'WAX': 'wax',
358
+ 'XLM': 'xlm',
359
+ 'XMR': 'monero',
360
+ 'XRP': 'ripple',
361
+ 'XVG': 'xvg',
362
+ 'YOYOW': 'yoyow',
363
+ 'ZEC': 'zcash',
364
+ 'ZRX': 'zrx',
365
+ 'XTZ': 'xtz',
366
+ },
367
+ 'orderTypes': {
368
+ 'limit': 'exchange limit',
369
+ 'market': 'exchange market',
370
+ },
371
+ 'fiat': {
372
+ 'USD': 'USD',
373
+ 'EUR': 'EUR',
374
+ 'JPY': 'JPY',
375
+ 'GBP': 'GBP',
376
+ 'CNH': 'CNH',
377
+ },
378
+ 'accountsByType': {
379
+ 'spot': 'exchange',
380
+ 'margin': 'trading',
381
+ 'funding': 'deposit',
382
+ 'swap': 'trading',
383
+ },
384
+ },
385
+ });
386
+ }
387
+
388
+ async fetchFundingFees (params = {}) {
389
+ await this.loadMarkets ();
390
+ const response = await this.privatePostAccountFees (params);
391
+ const fees = response['withdraw'];
392
+ const withdraw = {};
393
+ const ids = Object.keys (fees);
394
+ for (let i = 0; i < ids.length; i++) {
395
+ const id = ids[i];
396
+ const code = this.safeCurrencyCode (id);
397
+ withdraw[code] = this.safeNumber (fees, id);
398
+ }
399
+ return {
400
+ 'info': response,
401
+ 'withdraw': withdraw,
402
+ 'deposit': withdraw, // only for deposits of less than $1000
403
+ };
404
+ }
405
+
406
+ async fetchTradingFees (params = {}) {
407
+ await this.loadMarkets ();
408
+ const response = await this.privatePostSummary (params);
409
+ //
410
+ // {
411
+ // time: '2022-02-23T16:05:47.659000Z',
412
+ // status: { resid_hint: null, login_last: '2022-02-23T16:05:48Z' },
413
+ // is_locked: false,
414
+ // leo_lev: '0',
415
+ // leo_amount_avg: '0.0',
416
+ // trade_vol_30d: [
417
+ // {
418
+ // curr: 'Total (USD)',
419
+ // vol: '0.0',
420
+ // vol_safe: '0.0',
421
+ // vol_maker: '0.0',
422
+ // vol_BFX: '0.0',
423
+ // vol_BFX_safe: '0.0',
424
+ // vol_BFX_maker: '0.0'
425
+ // }
426
+ // ],
427
+ // fees_funding_30d: {},
428
+ // fees_funding_total_30d: '0',
429
+ // fees_trading_30d: {},
430
+ // fees_trading_total_30d: '0',
431
+ // rebates_trading_30d: {},
432
+ // rebates_trading_total_30d: '0',
433
+ // maker_fee: '0.001',
434
+ // taker_fee: '0.002',
435
+ // maker_fee_2crypto: '0.001',
436
+ // maker_fee_2stablecoin: '0.001',
437
+ // maker_fee_2fiat: '0.001',
438
+ // maker_fee_2deriv: '0.0002',
439
+ // taker_fee_2crypto: '0.002',
440
+ // taker_fee_2stablecoin: '0.002',
441
+ // taker_fee_2fiat: '0.002',
442
+ // taker_fee_2deriv: '0.00065',
443
+ // deriv_maker_rebate: '0.0002',
444
+ // deriv_taker_fee: '0.00065',
445
+ // trade_last: null
446
+ // }
447
+ //
448
+ const result = {};
449
+ const fiat = this.safeValue (this.options, 'fiat', {});
450
+ const makerFee = this.safeNumber (response, 'maker_fee');
451
+ const takerFee = this.safeNumber (response, 'taker_fee');
452
+ const makerFee2Fiat = this.safeNumber (response, 'maker_fee_2fiat');
453
+ const takerFee2Fiat = this.safeNumber (response, 'taker_fee_2fiat');
454
+ const makerFee2Deriv = this.safeNumber (response, 'maker_fee_2deriv');
455
+ const takerFee2Deriv = this.safeNumber (response, 'taker_fee_2deriv');
456
+ for (let i = 0; i < this.symbols.length; i++) {
457
+ const symbol = this.symbols[i];
458
+ const market = this.market (symbol);
459
+ const fee = {
460
+ 'info': response,
461
+ 'symbol': symbol,
462
+ 'percentage': true,
463
+ 'tierBased': true,
464
+ };
465
+ if (market['quote'] in fiat) {
466
+ fee['maker'] = makerFee2Fiat;
467
+ fee['taker'] = takerFee2Fiat;
468
+ } else if (market['contract']) {
469
+ fee['maker'] = makerFee2Deriv;
470
+ fee['taker'] = takerFee2Deriv;
471
+ } else {
472
+ fee['maker'] = makerFee;
473
+ fee['taker'] = takerFee;
474
+ }
475
+ result[symbol] = fee;
476
+ }
477
+ return result;
478
+ }
479
+
480
+ async fetchMarkets (params = {}) {
481
+ const ids = await this.publicGetSymbols ();
482
+ //
483
+ // [ "btcusd", "ltcusd", "ltcbtc" ]
484
+ //
485
+ const details = await this.publicGetSymbolsDetails ();
486
+ //
487
+ // [
488
+ // {
489
+ // "pair":"btcusd",
490
+ // "price_precision":5,
491
+ // "initial_margin":"10.0",
492
+ // "minimum_margin":"5.0",
493
+ // "maximum_order_size":"2000.0",
494
+ // "minimum_order_size":"0.0002",
495
+ // "expiration":"NA",
496
+ // "margin":true
497
+ // },
498
+ // ]
499
+ //
500
+ const result = [];
501
+ for (let i = 0; i < details.length; i++) {
502
+ const market = details[i];
503
+ let id = this.safeString (market, 'pair');
504
+ if (!this.inArray (id, ids)) {
505
+ continue;
506
+ }
507
+ id = id.toUpperCase ();
508
+ let baseId = undefined;
509
+ let quoteId = undefined;
510
+ if (id.indexOf (':') >= 0) {
511
+ const parts = id.split (':');
512
+ baseId = parts[0];
513
+ quoteId = parts[1];
514
+ } else {
515
+ baseId = id.slice (0, 3);
516
+ quoteId = id.slice (3, 6);
517
+ }
518
+ const base = this.safeCurrencyCode (baseId);
519
+ const quote = this.safeCurrencyCode (quoteId);
520
+ result.push ({
521
+ 'id': id,
522
+ 'symbol': base + '/' + quote,
523
+ 'base': base,
524
+ 'quote': quote,
525
+ 'settle': undefined,
526
+ 'baseId': baseId,
527
+ 'quoteId': quoteId,
528
+ 'settleId': undefined,
529
+ 'type': 'spot',
530
+ 'spot': true,
531
+ 'margin': this.safeValue (market, 'margin'),
532
+ 'swap': false,
533
+ 'future': false,
534
+ 'option': false,
535
+ 'active': true,
536
+ 'contract': false,
537
+ 'linear': undefined,
538
+ 'inverse': undefined,
539
+ 'contractSize': undefined,
540
+ 'expiry': undefined,
541
+ 'expiryDatetime': undefined,
542
+ 'strike': undefined,
543
+ 'optionType': undefined,
544
+ 'precision': {
545
+ // https://docs.bitfinex.com/docs/introduction#amount-precision
546
+ // The amount field allows up to 8 decimals.
547
+ // Anything exceeding this will be rounded to the 8th decimal.
548
+ 'amount': parseInt ('8'),
549
+ 'price': this.safeInteger (market, 'price_precision'),
550
+ },
551
+ 'limits': {
552
+ 'leverage': {
553
+ 'min': undefined,
554
+ 'max': undefined,
555
+ },
556
+ 'amount': {
557
+ 'min': this.safeNumber (market, 'minimum_order_size'),
558
+ 'max': this.safeNumber (market, 'maximum_order_size'),
559
+ },
560
+ 'price': {
561
+ 'min': this.parseNumber ('1e-8'),
562
+ 'max': undefined,
563
+ },
564
+ 'cost': {
565
+ 'min': undefined,
566
+ 'max': undefined,
567
+ },
568
+ },
569
+ 'info': market,
570
+ });
571
+ }
572
+ return result;
573
+ }
574
+
575
+ amountToPrecision (symbol, amount) {
576
+ // https://docs.bitfinex.com/docs/introduction#amount-precision
577
+ // The amount field allows up to 8 decimals.
578
+ // Anything exceeding this will be rounded to the 8th decimal.
579
+ return this.decimalToPrecision (amount, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES);
580
+ }
581
+
582
+ priceToPrecision (symbol, price) {
583
+ price = this.decimalToPrecision (price, ROUND, this.markets[symbol]['precision']['price'], this.precisionMode);
584
+ // https://docs.bitfinex.com/docs/introduction#price-precision
585
+ // The precision level of all trading prices is based on significant figures.
586
+ // 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).
587
+ // Prices submit with a precision larger than 5 will be cut by the API.
588
+ return this.decimalToPrecision (price, TRUNCATE, 8, DECIMAL_PLACES);
589
+ }
590
+
591
+ async fetchBalance (params = {}) {
592
+ await this.loadMarkets ();
593
+ const accountsByType = this.safeValue (this.options, 'accountsByType', {});
594
+ const requestedType = this.safeString (params, 'type', 'exchange');
595
+ const accountType = this.safeString (accountsByType, requestedType, requestedType);
596
+ if (accountType === undefined) {
597
+ const keys = Object.keys (accountsByType);
598
+ throw new ExchangeError (this.id + ' fetchBalance() type parameter must be one of ' + keys.join (', '));
599
+ }
600
+ const query = this.omit (params, 'type');
601
+ const response = await this.privatePostBalances (query);
602
+ // [ { type: 'deposit',
603
+ // currency: 'btc',
604
+ // amount: '0.00116721',
605
+ // available: '0.00116721' },
606
+ // { type: 'exchange',
607
+ // currency: 'ust',
608
+ // amount: '0.0000002',
609
+ // available: '0.0000002' },
610
+ // { type: 'trading',
611
+ // currency: 'btc',
612
+ // amount: '0.0005',
613
+ // available: '0.0005' } ],
614
+ const result = { 'info': response };
615
+ const isDerivative = requestedType === 'derivatives';
616
+ for (let i = 0; i < response.length; i++) {
617
+ const balance = response[i];
618
+ const type = this.safeString (balance, 'type');
619
+ const currencyId = this.safeStringLower (balance, 'currency', '');
620
+ const start = currencyId.length - 2;
621
+ const isDerivativeCode = currencyId.slice (start) === 'f0';
622
+ // this will only filter the derivative codes if the requestedType is 'derivatives'
623
+ const derivativeCondition = (!isDerivative || isDerivativeCode);
624
+ if ((accountType === type) && derivativeCondition) {
625
+ const code = this.safeCurrencyCode (currencyId);
626
+ // bitfinex had BCH previously, now it's BAB, but the old
627
+ // BCH symbol is kept for backward-compatibility
628
+ // we need a workaround here so that the old BCH balance
629
+ // would not override the new BAB balance (BAB is unified to BCH)
630
+ // https://github.com/ccxt/ccxt/issues/4989
631
+ if (!(code in result)) {
632
+ const account = this.account ();
633
+ account['free'] = this.safeString (balance, 'available');
634
+ account['total'] = this.safeString (balance, 'amount');
635
+ result[code] = account;
636
+ }
637
+ }
638
+ }
639
+ return this.safeBalance (result);
640
+ }
641
+
642
+ async transfer (code, amount, fromAccount, toAccount, params = {}) {
643
+ // transferring between derivatives wallet and regular wallet is not documented in their API
644
+ // however we support it in CCXT (from just looking at web inspector)
645
+ await this.loadMarkets ();
646
+ const accountsByType = this.safeValue (this.options, 'accountsByType', {});
647
+ const fromId = this.safeString (accountsByType, fromAccount, fromAccount);
648
+ const toId = this.safeString (accountsByType, toAccount, toAccount);
649
+ const currency = this.currency (code);
650
+ const fromCurrencyId = this.convertDerivativesId (currency['id'], fromAccount);
651
+ const toCurrencyId = this.convertDerivativesId (currency['id'], toAccount);
652
+ const requestedAmount = this.currencyToPrecision (code, amount);
653
+ const request = {
654
+ 'amount': requestedAmount,
655
+ 'currency': fromCurrencyId,
656
+ 'currency_to': toCurrencyId,
657
+ 'walletfrom': fromId,
658
+ 'walletto': toId,
659
+ };
660
+ const response = await this.privatePostTransfer (this.extend (request, params));
661
+ //
662
+ // [
663
+ // {
664
+ // status: 'success',
665
+ // message: '0.0001 Bitcoin transfered from Margin to Exchange'
666
+ // }
667
+ // ]
668
+ //
669
+ const result = this.safeValue (response, 0);
670
+ const message = this.safeString (result, 'message');
671
+ if (message === undefined) {
672
+ throw new ExchangeError (this.id + ' transfer failed');
673
+ }
674
+ return this.extend (this.parseTransfer (result, currency), {
675
+ 'fromAccount': fromAccount,
676
+ 'toAccount': toAccount,
677
+ 'amount': this.parseNumber (requestedAmount),
678
+ });
679
+ }
680
+
681
+ parseTransfer (transfer, currency = undefined) {
682
+ //
683
+ // {
684
+ // status: 'success',
685
+ // message: '0.0001 Bitcoin transfered from Margin to Exchange'
686
+ // }
687
+ //
688
+ const timestamp = this.milliseconds ();
689
+ return {
690
+ 'info': transfer,
691
+ 'id': undefined,
692
+ 'timestamp': timestamp,
693
+ 'datetime': this.iso8601 (timestamp),
694
+ 'currency': this.safeCurrencyCode (undefined, currency),
695
+ 'amount': undefined,
696
+ 'fromAccount': undefined,
697
+ 'toAccount': undefined,
698
+ 'status': this.parseTransferStatus (this.safeString (transfer, 'status')),
699
+ };
700
+ }
701
+
702
+ parseTransferStatus (status) {
703
+ const statuses = {
704
+ 'SUCCESS': 'ok',
705
+ };
706
+ return this.safeString (statuses, status, status);
707
+ }
708
+
709
+ convertDerivativesId (currencyId, type) {
710
+ const start = currencyId.length - 2;
711
+ const isDerivativeCode = currencyId.slice (start) === 'F0';
712
+ if ((type !== 'derivatives' && type !== 'trading' && type !== 'margin') && isDerivativeCode) {
713
+ currencyId = currencyId.slice (0, start);
714
+ } else if (type === 'derivatives' && !isDerivativeCode) {
715
+ currencyId = currencyId + 'F0';
716
+ }
717
+ return currencyId;
718
+ }
719
+
720
+ async fetchOrderBook (symbol, limit = undefined, params = {}) {
721
+ await this.loadMarkets ();
722
+ const request = {
723
+ 'symbol': this.marketId (symbol),
724
+ };
725
+ if (limit !== undefined) {
726
+ request['limit_bids'] = limit;
727
+ request['limit_asks'] = limit;
728
+ }
729
+ const response = await this.publicGetBookSymbol (this.extend (request, params));
730
+ return this.parseOrderBook (response, symbol, undefined, 'bids', 'asks', 'price', 'amount');
731
+ }
732
+
733
+ async fetchTickers (symbols = undefined, params = {}) {
734
+ await this.loadMarkets ();
735
+ const response = await this.publicGetTickers (params);
736
+ const result = {};
737
+ for (let i = 0; i < response.length; i++) {
738
+ const ticker = this.parseTicker (response[i]);
739
+ const symbol = ticker['symbol'];
740
+ result[symbol] = ticker;
741
+ }
742
+ return this.filterByArray (result, 'symbol', symbols);
743
+ }
744
+
745
+ async fetchTicker (symbol, params = {}) {
746
+ await this.loadMarkets ();
747
+ const market = this.market (symbol);
748
+ const request = {
749
+ 'symbol': market['id'],
750
+ };
751
+ const ticker = await this.publicGetPubtickerSymbol (this.extend (request, params));
752
+ return this.parseTicker (ticker, market);
753
+ }
754
+
755
+ parseTicker (ticker, market = undefined) {
756
+ const timestamp = this.safeTimestamp (ticker, 'timestamp');
757
+ let symbol = undefined;
758
+ if (market !== undefined) {
759
+ symbol = market['symbol'];
760
+ } else if ('pair' in ticker) {
761
+ const marketId = this.safeString (ticker, 'pair');
762
+ if (marketId !== undefined) {
763
+ if (marketId in this.markets_by_id) {
764
+ market = this.markets_by_id[marketId];
765
+ symbol = market['symbol'];
766
+ } else {
767
+ const baseId = marketId.slice (0, 3);
768
+ const quoteId = marketId.slice (3, 6);
769
+ const base = this.safeCurrencyCode (baseId);
770
+ const quote = this.safeCurrencyCode (quoteId);
771
+ symbol = base + '/' + quote;
772
+ }
773
+ }
774
+ }
775
+ const last = this.safeString (ticker, 'last_price');
776
+ return this.safeTicker ({
777
+ 'symbol': symbol,
778
+ 'timestamp': timestamp,
779
+ 'datetime': this.iso8601 (timestamp),
780
+ 'high': this.safeString (ticker, 'high'),
781
+ 'low': this.safeString (ticker, 'low'),
782
+ 'bid': this.safeString (ticker, 'bid'),
783
+ 'bidVolume': undefined,
784
+ 'ask': this.safeString (ticker, 'ask'),
785
+ 'askVolume': undefined,
786
+ 'vwap': undefined,
787
+ 'open': undefined,
788
+ 'close': last,
789
+ 'last': last,
790
+ 'previousClose': undefined,
791
+ 'change': undefined,
792
+ 'percentage': undefined,
793
+ 'average': this.safeString (ticker, 'mid'),
794
+ 'baseVolume': this.safeString (ticker, 'volume'),
795
+ 'quoteVolume': undefined,
796
+ 'info': ticker,
797
+ }, market, false);
798
+ }
799
+
800
+ parseTrade (trade, market = undefined) {
801
+ //
802
+ // fetchTrades (public) v1
803
+ //
804
+ // {
805
+ // "timestamp":1637258380,
806
+ // "tid":894452833,
807
+ // "price":"0.99941",
808
+ // "amount":"261.38",
809
+ // "exchange":"bitfinex",
810
+ // "type":"sell"
811
+ // }
812
+ //
813
+ // { "timestamp":1637258238,
814
+ // "tid":894452800,
815
+ // "price":"0.99958",
816
+ // "amount":"261.90514",
817
+ // "exchange":"bitfinex",
818
+ // "type":"buy"
819
+ // }
820
+ //
821
+ // fetchMyTrades (private) v1
822
+ //
823
+ // {
824
+ // "price":"0.99941",
825
+ // "amount":"261.38",
826
+ // "timestamp":"1637258380.0",
827
+ // "type":"Sell",
828
+ // "fee_currency":"UST",
829
+ // "fee_amount":"-0.52245157",
830
+ // "tid":894452833,
831
+ // "order_id":78819731373
832
+ // }
833
+ //
834
+ // {
835
+ // "price":"0.99958",
836
+ // "amount":"261.90514",
837
+ // "timestamp":"1637258238.0",
838
+ // "type":"Buy",
839
+ // "fee_currency":"UDC",
840
+ // "fee_amount":"-0.52381028",
841
+ // "tid":894452800,
842
+ // "order_id":78819504838
843
+ // }
844
+ //
845
+ const id = this.safeString (trade, 'tid');
846
+ const timestamp = this.safeTimestamp (trade, 'timestamp');
847
+ const type = undefined;
848
+ const side = this.safeStringLower (trade, 'type');
849
+ const orderId = this.safeString (trade, 'order_id');
850
+ const priceString = this.safeString (trade, 'price');
851
+ const amountString = this.safeString (trade, 'amount');
852
+ let fee = undefined;
853
+ if ('fee_amount' in trade) {
854
+ const feeCostString = Precise.stringNeg (this.safeString (trade, 'fee_amount'));
855
+ const feeCurrencyId = this.safeString (trade, 'fee_currency');
856
+ const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
857
+ fee = {
858
+ 'cost': feeCostString,
859
+ 'currency': feeCurrencyCode,
860
+ };
861
+ }
862
+ return this.safeTrade ({
863
+ 'id': id,
864
+ 'info': trade,
865
+ 'timestamp': timestamp,
866
+ 'datetime': this.iso8601 (timestamp),
867
+ 'symbol': market['symbol'],
868
+ 'type': type,
869
+ 'order': orderId,
870
+ 'side': side,
871
+ 'takerOrMaker': undefined,
872
+ 'price': priceString,
873
+ 'amount': amountString,
874
+ 'cost': undefined,
875
+ 'fee': fee,
876
+ }, market);
877
+ }
878
+
879
+ async fetchTrades (symbol, since = undefined, limit = 50, params = {}) {
880
+ await this.loadMarkets ();
881
+ const market = this.market (symbol);
882
+ const request = {
883
+ 'symbol': market['id'],
884
+ 'limit_trades': limit,
885
+ };
886
+ if (since !== undefined) {
887
+ request['timestamp'] = parseInt (since / 1000);
888
+ }
889
+ const response = await this.publicGetTradesSymbol (this.extend (request, params));
890
+ return this.parseTrades (response, market, since, limit);
891
+ }
892
+
893
+ async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
894
+ if (symbol === undefined) {
895
+ throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a `symbol` argument');
896
+ }
897
+ await this.loadMarkets ();
898
+ const market = this.market (symbol);
899
+ const request = {
900
+ 'symbol': market['id'],
901
+ };
902
+ if (limit !== undefined) {
903
+ request['limit_trades'] = limit;
904
+ }
905
+ if (since !== undefined) {
906
+ request['timestamp'] = parseInt (since / 1000);
907
+ }
908
+ const response = await this.privatePostMytrades (this.extend (request, params));
909
+ return this.parseTrades (response, market, since, limit);
910
+ }
911
+
912
+ async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
913
+ await this.loadMarkets ();
914
+ const postOnly = this.safeValue (params, 'postOnly', false);
915
+ params = this.omit (params, [ 'postOnly' ]);
916
+ const request = {
917
+ 'symbol': this.marketId (symbol),
918
+ 'side': side,
919
+ 'amount': this.amountToPrecision (symbol, amount),
920
+ 'type': this.safeString (this.options['orderTypes'], type, type),
921
+ 'ocoorder': false,
922
+ 'buy_price_oco': 0,
923
+ 'sell_price_oco': 0,
924
+ };
925
+ if (type === 'market') {
926
+ request['price'] = this.nonce ().toString ();
927
+ } else {
928
+ request['price'] = this.priceToPrecision (symbol, price);
929
+ }
930
+ if (postOnly) {
931
+ request['is_postonly'] = true;
932
+ }
933
+ const response = await this.privatePostOrderNew (this.extend (request, params));
934
+ return this.parseOrder (response);
935
+ }
936
+
937
+ async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
938
+ await this.loadMarkets ();
939
+ const order = {
940
+ 'order_id': parseInt (id),
941
+ };
942
+ if (price !== undefined) {
943
+ order['price'] = this.priceToPrecision (symbol, price);
944
+ }
945
+ if (amount !== undefined) {
946
+ order['amount'] = this.numberToString (amount);
947
+ }
948
+ if (symbol !== undefined) {
949
+ order['symbol'] = this.marketId (symbol);
950
+ }
951
+ if (side !== undefined) {
952
+ order['side'] = side;
953
+ }
954
+ if (type !== undefined) {
955
+ order['type'] = this.safeString (this.options['orderTypes'], type, type);
956
+ }
957
+ const response = await this.privatePostOrderCancelReplace (this.extend (order, params));
958
+ return this.parseOrder (response);
959
+ }
960
+
961
+ async cancelOrder (id, symbol = undefined, params = {}) {
962
+ await this.loadMarkets ();
963
+ const request = {
964
+ 'order_id': parseInt (id),
965
+ };
966
+ return await this.privatePostOrderCancel (this.extend (request, params));
967
+ }
968
+
969
+ async cancelAllOrders (symbol = undefined, params = {}) {
970
+ return await this.privatePostOrderCancelAll (params);
971
+ }
972
+
973
+ parseOrder (order, market = undefined) {
974
+ //
975
+ // {
976
+ // id: 57334010955,
977
+ // cid: 1611584840966,
978
+ // cid_date: null,
979
+ // gid: null,
980
+ // symbol: 'ltcbtc',
981
+ // exchange: null,
982
+ // price: '0.0042125',
983
+ // avg_execution_price: '0.0042097',
984
+ // side: 'sell',
985
+ // type: 'exchange market',
986
+ // timestamp: '1611584841.0',
987
+ // is_live: false,
988
+ // is_cancelled: false,
989
+ // is_hidden: 0,
990
+ // oco_order: 0,
991
+ // was_forced: false,
992
+ // original_amount: '0.205176',
993
+ // remaining_amount: '0.0',
994
+ // executed_amount: '0.205176',
995
+ // src: 'web'
996
+ // }
997
+ //
998
+ const side = this.safeString (order, 'side');
999
+ const open = this.safeValue (order, 'is_live');
1000
+ const canceled = this.safeValue (order, 'is_cancelled');
1001
+ let status = undefined;
1002
+ if (open) {
1003
+ status = 'open';
1004
+ } else if (canceled) {
1005
+ status = 'canceled';
1006
+ } else {
1007
+ status = 'closed';
1008
+ }
1009
+ const marketId = this.safeStringUpper (order, 'symbol');
1010
+ const symbol = this.safeSymbol (marketId, market);
1011
+ let orderType = this.safeString (order, 'type', '');
1012
+ const exchange = orderType.indexOf ('exchange ') >= 0;
1013
+ if (exchange) {
1014
+ const parts = order['type'].split (' ');
1015
+ orderType = parts[1];
1016
+ }
1017
+ const timestamp = this.safeTimestamp (order, 'timestamp');
1018
+ const id = this.safeString (order, 'id');
1019
+ return this.safeOrder ({
1020
+ 'info': order,
1021
+ 'id': id,
1022
+ 'clientOrderId': undefined,
1023
+ 'timestamp': timestamp,
1024
+ 'datetime': this.iso8601 (timestamp),
1025
+ 'lastTradeTimestamp': undefined,
1026
+ 'symbol': symbol,
1027
+ 'type': orderType,
1028
+ 'timeInForce': undefined,
1029
+ 'postOnly': undefined,
1030
+ 'side': side,
1031
+ 'price': this.safeString (order, 'price'),
1032
+ 'stopPrice': undefined,
1033
+ 'average': this.safeString (order, 'avg_execution_price'),
1034
+ 'amount': this.safeString (order, 'original_amount'),
1035
+ 'remaining': this.safeString (order, 'remaining_amount'),
1036
+ 'filled': this.safeString (order, 'executed_amount'),
1037
+ 'status': status,
1038
+ 'fee': undefined,
1039
+ 'cost': undefined,
1040
+ 'trades': undefined,
1041
+ }, market);
1042
+ }
1043
+
1044
+ async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
1045
+ await this.loadMarkets ();
1046
+ if (symbol !== undefined) {
1047
+ if (!(symbol in this.markets)) {
1048
+ throw new ExchangeError (this.id + ' has no symbol ' + symbol);
1049
+ }
1050
+ }
1051
+ const response = await this.privatePostOrders (params);
1052
+ let orders = this.parseOrders (response, undefined, since, limit);
1053
+ if (symbol !== undefined) {
1054
+ orders = this.filterBy (orders, 'symbol', symbol);
1055
+ }
1056
+ return orders;
1057
+ }
1058
+
1059
+ async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
1060
+ await this.loadMarkets ();
1061
+ const request = {};
1062
+ if (limit !== undefined) {
1063
+ request['limit'] = limit;
1064
+ }
1065
+ const response = await this.privatePostOrdersHist (this.extend (request, params));
1066
+ let orders = this.parseOrders (response, undefined, since, limit);
1067
+ if (symbol !== undefined) {
1068
+ orders = this.filterBy (orders, 'symbol', symbol);
1069
+ }
1070
+ orders = this.filterByArray (orders, 'status', [ 'closed', 'canceled' ], false);
1071
+ return orders;
1072
+ }
1073
+
1074
+ async fetchOrder (id, symbol = undefined, params = {}) {
1075
+ await this.loadMarkets ();
1076
+ const request = {
1077
+ 'order_id': parseInt (id),
1078
+ };
1079
+ const response = await this.privatePostOrderStatus (this.extend (request, params));
1080
+ return this.parseOrder (response);
1081
+ }
1082
+
1083
+ parseOHLCV (ohlcv, market = undefined) {
1084
+ //
1085
+ // [
1086
+ // 1457539800000,
1087
+ // 0.02594,
1088
+ // 0.02594,
1089
+ // 0.02594,
1090
+ // 0.02594,
1091
+ // 0.1
1092
+ // ]
1093
+ //
1094
+ return [
1095
+ this.safeInteger (ohlcv, 0),
1096
+ this.safeNumber (ohlcv, 1),
1097
+ this.safeNumber (ohlcv, 3),
1098
+ this.safeNumber (ohlcv, 4),
1099
+ this.safeNumber (ohlcv, 2),
1100
+ this.safeNumber (ohlcv, 5),
1101
+ ];
1102
+ }
1103
+
1104
+ async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
1105
+ await this.loadMarkets ();
1106
+ if (limit === undefined) {
1107
+ limit = 100;
1108
+ }
1109
+ const market = this.market (symbol);
1110
+ const v2id = 't' + market['id'];
1111
+ const request = {
1112
+ 'symbol': v2id,
1113
+ 'timeframe': this.timeframes[timeframe],
1114
+ 'sort': 1,
1115
+ 'limit': limit,
1116
+ };
1117
+ if (since !== undefined) {
1118
+ request['start'] = since;
1119
+ }
1120
+ const response = await this.v2GetCandlesTradeTimeframeSymbolHist (this.extend (request, params));
1121
+ //
1122
+ // [
1123
+ // [1457539800000,0.02594,0.02594,0.02594,0.02594,0.1],
1124
+ // [1457547300000,0.02577,0.02577,0.02577,0.02577,0.01],
1125
+ // [1457550240000,0.0255,0.0253,0.0255,0.0252,3.2640000000000002],
1126
+ // ]
1127
+ //
1128
+ return this.parseOHLCVs (response, market, timeframe, since, limit);
1129
+ }
1130
+
1131
+ getCurrencyName (code) {
1132
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1133
+ if (code in this.options['currencyNames']) {
1134
+ return this.options['currencyNames'][code];
1135
+ }
1136
+ throw new NotSupported (this.id + ' ' + code + ' not supported for withdrawal');
1137
+ }
1138
+
1139
+ async createDepositAddress (code, params = {}) {
1140
+ await this.loadMarkets ();
1141
+ const request = {
1142
+ 'renew': 1,
1143
+ };
1144
+ const response = await this.fetchDepositAddress (code, this.extend (request, params));
1145
+ return response;
1146
+ }
1147
+
1148
+ async fetchDepositAddress (code, params = {}) {
1149
+ await this.loadMarkets ();
1150
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1151
+ const name = this.getCurrencyName (code);
1152
+ const request = {
1153
+ 'method': name,
1154
+ 'wallet_name': 'exchange',
1155
+ 'renew': 0, // a value of 1 will generate a new address
1156
+ };
1157
+ const response = await this.privatePostDepositNew (this.extend (request, params));
1158
+ let address = this.safeValue (response, 'address');
1159
+ let tag = undefined;
1160
+ if ('address_pool' in response) {
1161
+ tag = address;
1162
+ address = response['address_pool'];
1163
+ }
1164
+ this.checkAddress (address);
1165
+ return {
1166
+ 'currency': code,
1167
+ 'address': address,
1168
+ 'tag': tag,
1169
+ 'network': undefined,
1170
+ 'info': response,
1171
+ };
1172
+ }
1173
+
1174
+ async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) {
1175
+ await this.loadMarkets ();
1176
+ let currencyId = this.safeString (params, 'currency');
1177
+ const query = this.omit (params, 'currency');
1178
+ let currency = undefined;
1179
+ if (currencyId === undefined) {
1180
+ if (code === undefined) {
1181
+ throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument or a `currency` parameter');
1182
+ } else {
1183
+ currency = this.currency (code);
1184
+ currencyId = currency['id'];
1185
+ }
1186
+ }
1187
+ query['currency'] = currencyId;
1188
+ if (since !== undefined) {
1189
+ query['since'] = parseInt (since / 1000);
1190
+ }
1191
+ const response = await this.privatePostHistoryMovements (this.extend (query, params));
1192
+ //
1193
+ // [
1194
+ // {
1195
+ // "id":581183,
1196
+ // "txid": 123456,
1197
+ // "currency":"BTC",
1198
+ // "method":"BITCOIN",
1199
+ // "type":"WITHDRAWAL",
1200
+ // "amount":".01",
1201
+ // "description":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ, offchain transfer ",
1202
+ // "address":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ",
1203
+ // "status":"COMPLETED",
1204
+ // "timestamp":"1443833327.0",
1205
+ // "timestamp_created": "1443833327.1",
1206
+ // "fee": 0.1,
1207
+ // }
1208
+ // ]
1209
+ //
1210
+ return this.parseTransactions (response, currency, since, limit);
1211
+ }
1212
+
1213
+ parseTransaction (transaction, currency = undefined) {
1214
+ //
1215
+ // crypto
1216
+ //
1217
+ // {
1218
+ // "id": 12042490,
1219
+ // "fee": "-0.02",
1220
+ // "txid": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D",
1221
+ // "type": "DEPOSIT",
1222
+ // "amount": "2099.849999",
1223
+ // "method": "RIPPLE",
1224
+ // "status": "COMPLETED",
1225
+ // "address": "2505189261",
1226
+ // "currency": "XRP",
1227
+ // "timestamp": "1551730524.0",
1228
+ // "description": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D",
1229
+ // "timestamp_created": "1551730523.0"
1230
+ // }
1231
+ //
1232
+ // fiat
1233
+ //
1234
+ // {
1235
+ // "id": 12725095,
1236
+ // "fee": "-60.0",
1237
+ // "txid": null,
1238
+ // "type": "WITHDRAWAL",
1239
+ // "amount": "9943.0",
1240
+ // "method": "WIRE",
1241
+ // "status": "SENDING",
1242
+ // "address": null,
1243
+ // "currency": "EUR",
1244
+ // "timestamp": "1561802484.0",
1245
+ // "description": "Name: bob, AccountAddress: some address, Account: someaccountno, Bank: bank address, SWIFT: foo, Country: UK, Details of Payment: withdrawal name, Intermediary Bank Name: , Intermediary Bank Address: , Intermediary Bank City: , Intermediary Bank Country: , Intermediary Bank Account: , Intermediary Bank SWIFT: , Fee: -60.0",
1246
+ // "timestamp_created": "1561716066.0"
1247
+ // }
1248
+ //
1249
+ // withdraw
1250
+ //
1251
+ // {
1252
+ // "status":"success",
1253
+ // "message":"Your withdrawal request has been successfully submitted.",
1254
+ // "withdrawal_id":586829
1255
+ // }
1256
+ //
1257
+ const timestamp = this.safeTimestamp (transaction, 'timestamp_created');
1258
+ const updated = this.safeTimestamp (transaction, 'timestamp');
1259
+ const currencyId = this.safeString (transaction, 'currency');
1260
+ const code = this.safeCurrencyCode (currencyId, currency);
1261
+ const type = this.safeStringLower (transaction, 'type'); // DEPOSIT or WITHDRAWAL
1262
+ const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
1263
+ let feeCost = this.safeNumber (transaction, 'fee');
1264
+ if (feeCost !== undefined) {
1265
+ feeCost = Math.abs (feeCost);
1266
+ }
1267
+ const tag = this.safeString (transaction, 'description');
1268
+ return {
1269
+ 'info': transaction,
1270
+ 'id': this.safeString2 (transaction, 'id', 'withdrawal_id'),
1271
+ 'txid': this.safeString (transaction, 'txid'),
1272
+ 'timestamp': timestamp,
1273
+ 'datetime': this.iso8601 (timestamp),
1274
+ 'network': undefined,
1275
+ 'address': this.safeString (transaction, 'address'), // todo: this is actually the tag for XRP transfers (the address is missing)
1276
+ 'addressTo': undefined,
1277
+ 'addressFrom': undefined,
1278
+ 'tag': tag,
1279
+ 'tagTo': undefined,
1280
+ 'tagFrom': undefined,
1281
+ 'type': type,
1282
+ 'amount': this.safeNumber (transaction, 'amount'),
1283
+ 'currency': code,
1284
+ 'status': status,
1285
+ 'updated': updated,
1286
+ 'fee': {
1287
+ 'currency': code,
1288
+ 'cost': feeCost,
1289
+ 'rate': undefined,
1290
+ },
1291
+ };
1292
+ }
1293
+
1294
+ parseTransactionStatus (status) {
1295
+ const statuses = {
1296
+ 'SENDING': 'pending',
1297
+ 'CANCELED': 'canceled',
1298
+ 'ZEROCONFIRMED': 'failed', // ZEROCONFIRMED happens e.g. in a double spend attempt (I had one in my movements!)
1299
+ 'COMPLETED': 'ok',
1300
+ };
1301
+ return this.safeString (statuses, status, status);
1302
+ }
1303
+
1304
+ async withdraw (code, amount, address, tag = undefined, params = {}) {
1305
+ [ tag, params ] = this.handleWithdrawTagAndParams (tag, params);
1306
+ this.checkAddress (address);
1307
+ await this.loadMarkets ();
1308
+ // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1309
+ const name = this.getCurrencyName (code);
1310
+ const currency = this.currency (code);
1311
+ const request = {
1312
+ 'withdraw_type': name,
1313
+ 'walletselected': 'exchange',
1314
+ 'amount': this.numberToString (amount),
1315
+ 'address': address,
1316
+ };
1317
+ if (tag !== undefined) {
1318
+ request['payment_id'] = tag;
1319
+ }
1320
+ const responses = await this.privatePostWithdraw (this.extend (request, params));
1321
+ //
1322
+ // [
1323
+ // {
1324
+ // "status":"success",
1325
+ // "message":"Your withdrawal request has been successfully submitted.",
1326
+ // "withdrawal_id":586829
1327
+ // }
1328
+ // ]
1329
+ //
1330
+ const response = this.safeValue (responses, 0, {});
1331
+ const id = this.safeString (response, 'withdrawal_id');
1332
+ const message = this.safeString (response, 'message');
1333
+ const errorMessage = this.findBroadlyMatchedKey (this.exceptions['broad'], message);
1334
+ if (id === 0) {
1335
+ if (errorMessage !== undefined) {
1336
+ const ExceptionClass = this.exceptions['broad'][errorMessage];
1337
+ throw new ExceptionClass (this.id + ' ' + message);
1338
+ }
1339
+ throw new ExchangeError (this.id + ' withdraw returned an id of zero: ' + this.json (response));
1340
+ }
1341
+ return this.parseTransaction (response, currency);
1342
+ }
1343
+
1344
+ async fetchPositions (symbols = undefined, params = {}) {
1345
+ await this.loadMarkets ();
1346
+ const response = await this.privatePostPositions (params);
1347
+ //
1348
+ // [
1349
+ // {
1350
+ // "id":943715,
1351
+ // "symbol":"btcusd",
1352
+ // "status":"ACTIVE",
1353
+ // "base":"246.94",
1354
+ // "amount":"1.0",
1355
+ // "timestamp":"1444141857.0",
1356
+ // "swap":"0.0",
1357
+ // "pl":"-2.22042"
1358
+ // }
1359
+ // ]
1360
+ //
1361
+ // todo unify parsePosition/parsePositions
1362
+ return response;
1363
+ }
1364
+
1365
+ nonce () {
1366
+ return this.milliseconds ();
1367
+ }
1368
+
1369
+ sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
1370
+ let request = '/' + this.implodeParams (path, params);
1371
+ if (api === 'v2') {
1372
+ request = '/' + api + request;
1373
+ } else {
1374
+ request = '/' + this.version + request;
1375
+ }
1376
+ let query = this.omit (params, this.extractParams (path));
1377
+ let url = this.urls['api'][api] + request;
1378
+ if ((api === 'public') || (path.indexOf ('/hist') >= 0)) {
1379
+ if (Object.keys (query).length) {
1380
+ const suffix = '?' + this.urlencode (query);
1381
+ url += suffix;
1382
+ request += suffix;
1383
+ }
1384
+ }
1385
+ if (api === 'private') {
1386
+ this.checkRequiredCredentials ();
1387
+ const nonce = this.nonce ();
1388
+ query = this.extend ({
1389
+ 'nonce': nonce.toString (),
1390
+ 'request': request,
1391
+ }, query);
1392
+ body = this.json (query);
1393
+ const payload = this.stringToBase64 (body);
1394
+ const secret = this.encode (this.secret);
1395
+ const signature = this.hmac (payload, secret, 'sha384');
1396
+ headers = {
1397
+ 'X-BFX-APIKEY': this.apiKey,
1398
+ 'X-BFX-PAYLOAD': this.decode (payload),
1399
+ 'X-BFX-SIGNATURE': signature,
1400
+ 'Content-Type': 'application/json',
1401
+ };
1402
+ }
1403
+ return { 'url': url, 'method': method, 'body': body, 'headers': headers };
1404
+ }
1405
+
1406
+ handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
1407
+ if (response === undefined) {
1408
+ return;
1409
+ }
1410
+ let throwError = false;
1411
+ if (code >= 400) {
1412
+ const firstChar = this.safeString (body, 0);
1413
+ if (firstChar === '{') {
1414
+ throwError = true;
1415
+ }
1416
+ } else {
1417
+ // json response with error, i.e:
1418
+ // [{"status":"error","message":"Momentary balance check. Please wait few seconds and try the transfer again."}]
1419
+ const responseObject = this.safeValue (response, 0, {});
1420
+ const status = this.safeString (responseObject, 'status', '');
1421
+ if (status === 'error') {
1422
+ throwError = true;
1423
+ }
1424
+ }
1425
+ if (throwError) {
1426
+ const feedback = this.id + ' ' + body;
1427
+ const message = this.safeString2 (response, 'message', 'error');
1428
+ this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
1429
+ this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
1430
+ throw new ExchangeError (feedback); // unknown message
1431
+ }
1432
+ }
1433
+ };