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
@@ -0,0 +1,2371 @@
1
+ "use strict";
2
+
3
+ // ----------------------------------------------------------------------------
4
+
5
+ const functions = require ('./functions')
6
+
7
+ const {
8
+ isNode
9
+ , keys
10
+ , values
11
+ , deepExtend
12
+ , extend
13
+ , clone
14
+ , flatten
15
+ , unique
16
+ , indexBy
17
+ , sortBy
18
+ , sortBy2
19
+ , groupBy
20
+ , aggregate
21
+ , uuid
22
+ , unCamelCase
23
+ , precisionFromString
24
+ , throttle
25
+ , capitalize
26
+ , now
27
+ , timeout
28
+ , TimedOut
29
+ , buildOHLCVC
30
+ , decimalToPrecision
31
+ , defaultFetch
32
+ } = functions
33
+
34
+ const { // eslint-disable-line object-curly-newline
35
+ ExchangeError
36
+ , BadSymbol
37
+ , InvalidAddress
38
+ , InvalidOrder
39
+ , NotSupported
40
+ , AuthenticationError
41
+ , DDoSProtection
42
+ , RequestTimeout
43
+ , ExchangeNotAvailable
44
+ , RateLimitExceeded } = require ('./errors')
45
+
46
+ const { TRUNCATE, ROUND, DECIMAL_PLACES, NO_PADDING } = functions.precisionConstants
47
+
48
+ const BN = require ('../static_dependencies/BN/bn')
49
+ const Precise = require ('./Precise')
50
+
51
+ // ----------------------------------------------------------------------------
52
+
53
+ module.exports = class Exchange {
54
+
55
+ describe () {
56
+ return {
57
+ 'id': undefined,
58
+ 'name': undefined,
59
+ 'countries': undefined,
60
+ 'enableRateLimit': true,
61
+ 'rateLimit': 2000, // milliseconds = seconds * 1000
62
+ 'certified': false, // if certified by the CCXT dev team
63
+ 'pro': false, // if it is integrated with CCXT Pro for WebSocket support
64
+ 'alias': false, // whether this exchange is an alias to another exchange
65
+ 'has': {
66
+ 'publicAPI': true,
67
+ 'privateAPI': true,
68
+ 'CORS': undefined,
69
+ 'spot': undefined,
70
+ 'margin': undefined,
71
+ 'swap': undefined,
72
+ 'future': undefined,
73
+ 'option': undefined,
74
+ 'addMargin': undefined,
75
+ 'cancelAllOrders': undefined,
76
+ 'cancelOrder': true,
77
+ 'cancelOrders': undefined,
78
+ 'createDepositAddress': undefined,
79
+ 'createLimitOrder': true,
80
+ 'createMarketOrder': true,
81
+ 'createOrder': true,
82
+ 'createPostOnlyOrder': undefined,
83
+ 'createStopOrder': undefined,
84
+ 'createStopLimitOrder': undefined,
85
+ 'createStopMarketOrder': undefined,
86
+ 'editOrder': 'emulated',
87
+ 'fetchAccounts': undefined,
88
+ 'fetchBalance': true,
89
+ 'fetchBidsAsks': undefined,
90
+ 'fetchBorrowInterest': undefined,
91
+ 'fetchBorrowRate': undefined,
92
+ 'fetchBorrowRateHistory': undefined,
93
+ 'fetchBorrowRatesPerSymbol': undefined,
94
+ 'fetchBorrowRates': undefined,
95
+ 'fetchCanceledOrders': undefined,
96
+ 'fetchClosedOrder': undefined,
97
+ 'fetchClosedOrders': undefined,
98
+ 'fetchCurrencies': 'emulated',
99
+ 'fetchDeposit': undefined,
100
+ 'fetchDepositAddress': undefined,
101
+ 'fetchDepositAddresses': undefined,
102
+ 'fetchDepositAddressesByNetwork': undefined,
103
+ 'fetchDeposits': undefined,
104
+ 'fetchFundingFee': undefined,
105
+ 'fetchFundingFees': undefined,
106
+ 'fetchFundingHistory': undefined,
107
+ 'fetchFundingRate': undefined,
108
+ 'fetchFundingRateHistory': undefined,
109
+ 'fetchFundingRates': undefined,
110
+ 'fetchIndexOHLCV': undefined,
111
+ 'fetchL2OrderBook': true,
112
+ 'fetchLedger': undefined,
113
+ 'fetchLedgerEntry': undefined,
114
+ 'fetchLeverageTiers': undefined,
115
+ 'fetchMarketLeverageTiers': undefined,
116
+ 'fetchMarkets': true,
117
+ 'fetchMarkOHLCV': undefined,
118
+ 'fetchMyTrades': undefined,
119
+ 'fetchOHLCV': 'emulated',
120
+ 'fetchOpenOrder': undefined,
121
+ 'fetchOpenOrders': undefined,
122
+ 'fetchOrder': undefined,
123
+ 'fetchOrderBook': true,
124
+ 'fetchOrderBooks': undefined,
125
+ 'fetchOrders': undefined,
126
+ 'fetchOrderTrades': undefined,
127
+ 'fetchPermissions': undefined,
128
+ 'fetchPosition': undefined,
129
+ 'fetchPositions': undefined,
130
+ 'fetchPositionsRisk': undefined,
131
+ 'fetchPremiumIndexOHLCV': undefined,
132
+ 'fetchStatus': 'emulated',
133
+ 'fetchTicker': true,
134
+ 'fetchTickers': undefined,
135
+ 'fetchTime': undefined,
136
+ 'fetchTrades': true,
137
+ 'fetchTradingFee': undefined,
138
+ 'fetchTradingFees': undefined,
139
+ 'fetchTradingLimits': undefined,
140
+ 'fetchTransactions': undefined,
141
+ 'fetchTransfers': undefined,
142
+ 'fetchWithdrawal': undefined,
143
+ 'fetchWithdrawals': undefined,
144
+ 'loadMarkets': true,
145
+ 'reduceMargin': undefined,
146
+ 'setLeverage': undefined,
147
+ 'setMarginMode': undefined,
148
+ 'setPositionMode': undefined,
149
+ 'signIn': undefined,
150
+ 'transfer': undefined,
151
+ 'withdraw': undefined,
152
+ },
153
+ 'urls': {
154
+ 'logo': undefined,
155
+ 'api': undefined,
156
+ 'www': undefined,
157
+ 'doc': undefined,
158
+ 'fees': undefined,
159
+ },
160
+ 'api': undefined,
161
+ 'requiredCredentials': {
162
+ 'apiKey': true,
163
+ 'secret': true,
164
+ 'uid': false,
165
+ 'login': false,
166
+ 'password': false,
167
+ 'twofa': false, // 2-factor authentication (one-time password key)
168
+ 'privateKey': false, // a "0x"-prefixed hexstring private key for a wallet
169
+ 'walletAddress': false, // the wallet address "0x"-prefixed hexstring
170
+ 'token': false, // reserved for HTTP auth in some cases
171
+ },
172
+ 'markets': undefined, // to be filled manually or by fetchMarkets
173
+ 'currencies': {}, // to be filled manually or by fetchMarkets
174
+ 'timeframes': undefined, // redefine if the exchange has.fetchOHLCV
175
+ 'fees': {
176
+ 'trading': {
177
+ 'tierBased': undefined,
178
+ 'percentage': undefined,
179
+ 'taker': undefined,
180
+ 'maker': undefined,
181
+ },
182
+ 'funding': {
183
+ 'tierBased': undefined,
184
+ 'percentage': undefined,
185
+ 'withdraw': {},
186
+ 'deposit': {},
187
+ },
188
+ },
189
+ 'status': {
190
+ 'status': 'ok',
191
+ 'updated': undefined,
192
+ 'eta': undefined,
193
+ 'url': undefined,
194
+ },
195
+ 'exceptions': undefined,
196
+ 'httpExceptions': {
197
+ '422': ExchangeError,
198
+ '418': DDoSProtection,
199
+ '429': RateLimitExceeded,
200
+ '404': ExchangeNotAvailable,
201
+ '409': ExchangeNotAvailable,
202
+ '410': ExchangeNotAvailable,
203
+ '500': ExchangeNotAvailable,
204
+ '501': ExchangeNotAvailable,
205
+ '502': ExchangeNotAvailable,
206
+ '520': ExchangeNotAvailable,
207
+ '521': ExchangeNotAvailable,
208
+ '522': ExchangeNotAvailable,
209
+ '525': ExchangeNotAvailable,
210
+ '526': ExchangeNotAvailable,
211
+ '400': ExchangeNotAvailable,
212
+ '403': ExchangeNotAvailable,
213
+ '405': ExchangeNotAvailable,
214
+ '503': ExchangeNotAvailable,
215
+ '530': ExchangeNotAvailable,
216
+ '408': RequestTimeout,
217
+ '504': RequestTimeout,
218
+ '401': AuthenticationError,
219
+ '511': AuthenticationError,
220
+ },
221
+ 'commonCurrencies': { // gets extended/overwritten in subclasses
222
+ 'XBT': 'BTC',
223
+ 'BCC': 'BCH',
224
+ 'DRK': 'DASH',
225
+ 'BCHABC': 'BCH',
226
+ 'BCHSV': 'BSV',
227
+ },
228
+ 'precisionMode': DECIMAL_PLACES,
229
+ 'paddingMode': NO_PADDING,
230
+ 'limits': {
231
+ 'leverage': { 'min': undefined, 'max': undefined },
232
+ 'amount': { 'min': undefined, 'max': undefined },
233
+ 'price': { 'min': undefined, 'max': undefined },
234
+ 'cost': { 'min': undefined, 'max': undefined },
235
+ },
236
+ } // return
237
+ } // describe ()
238
+
239
+ constructor (userConfig = {}) {
240
+ Object.assign (this, functions)
241
+ // if (isNode) {
242
+ // this.nodeVersion = process.version.match (/\d+\.\d+\.\d+/)[0]
243
+ // this.userAgent = {
244
+ // 'User-Agent': 'ccxt/' + Exchange.ccxtVersion +
245
+ // ' (+https://github.com/ccxt/ccxt)' +
246
+ // ' Node.js/' + this.nodeVersion + ' (JavaScript)'
247
+ // }
248
+ // }
249
+
250
+ this.options = {} // exchange-specific options, if any
251
+
252
+ // fetch implementation options (JS only)
253
+ this.fetchOptions = {
254
+ // keepalive: true, // does not work in Chrome, https://github.com/ccxt/ccxt/issues/6368
255
+ }
256
+
257
+ this.userAgents = {
258
+ 'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
259
+ 'chrome39': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
260
+ 'chrome100': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36',
261
+ }
262
+
263
+ this.headers = {}
264
+
265
+ // prepended to URL, like https://proxy.com/https://exchange.com/api...
266
+ this.proxy = ''
267
+ this.origin = '*' // CORS origin
268
+
269
+ this.minFundingAddressLength = 1 // used in checkAddress
270
+ this.substituteCommonCurrencyCodes = true // reserved
271
+ this.quoteJsonNumbers = true // treat numbers in json as quoted precise strings
272
+ this.number = Number // or String (a pointer to a function)
273
+ this.handleContentTypeApplicationZip = false
274
+
275
+ // whether fees should be summed by currency code
276
+ this.reduceFees = true
277
+
278
+ // do not delete this line, it is needed for users to be able to define their own fetchImplementation
279
+ this.fetchImplementation = defaultFetch
280
+ this.validateServerSsl = true
281
+ this.validateClientSsl = false
282
+
283
+ this.timeout = 10000 // milliseconds
284
+ this.verbose = false
285
+ this.debug = false
286
+ this.userAgent = undefined
287
+ this.twofa = undefined // two-factor authentication (2FA)
288
+
289
+ this.apiKey = undefined
290
+ this.secret = undefined
291
+ this.uid = undefined
292
+ this.login = undefined
293
+ this.password = undefined
294
+ this.privateKey = undefined // a "0x"-prefixed hexstring private key for a wallet
295
+ this.walletAddress = undefined // a wallet address "0x"-prefixed hexstring
296
+ this.token = undefined // reserved for HTTP auth in some cases
297
+
298
+ this.balance = {}
299
+ this.orderbooks = {}
300
+ this.tickers = {}
301
+ this.orders = undefined
302
+ this.trades = {}
303
+ this.transactions = {}
304
+ this.ohlcvs = {}
305
+ this.myTrades = undefined
306
+ this.positions = {}
307
+
308
+ this.requiresWeb3 = false
309
+ this.requiresEddsa = false
310
+ this.precision = {}
311
+
312
+ this.enableLastJsonResponse = true
313
+ this.enableLastHttpResponse = true
314
+ this.enableLastResponseHeaders = true
315
+ this.last_http_response = undefined
316
+ this.last_json_response = undefined
317
+ this.last_response_headers = undefined
318
+
319
+ const unCamelCaseProperties = (obj = this) => {
320
+ if (obj !== null) {
321
+ const ownPropertyNames = Object.getOwnPropertyNames (obj)
322
+ for (let i = 0; i < ownPropertyNames.length; i++) {
323
+ const k = ownPropertyNames[i]
324
+ this[unCamelCase (k)] = this[k]
325
+ }
326
+ unCamelCaseProperties (Object.getPrototypeOf (obj))
327
+ }
328
+ }
329
+ unCamelCaseProperties ()
330
+
331
+ // merge to this
332
+ const configEntries = Object.entries (this.describe ()).concat (Object.entries (userConfig))
333
+ for (let i = 0; i < configEntries.length; i++) {
334
+ const [property, value] = configEntries[i]
335
+ if (value && Object.getPrototypeOf (value) === Object.prototype) {
336
+ this[property] = deepExtend (this[property], value)
337
+ } else {
338
+ this[property] = value
339
+ }
340
+ }
341
+
342
+ const agentOptions = {
343
+ 'keepAlive': true,
344
+ }
345
+
346
+ if (!this.validateServerSsl) {
347
+ agentOptions['rejectUnauthorized'] = false;
348
+ }
349
+
350
+ if (!this.httpAgent && defaultFetch.http && isNode) {
351
+ this.httpAgent = new defaultFetch.http.Agent (agentOptions)
352
+ }
353
+
354
+ if (!this.httpsAgent && defaultFetch.https && isNode) {
355
+ this.httpsAgent = new defaultFetch.https.Agent (agentOptions)
356
+ }
357
+
358
+ // generate old metainfo interface
359
+ const hasKeys = Object.keys (this.has)
360
+ for (let i = 0; i < hasKeys.length; i++) {
361
+ const k = hasKeys[i]
362
+ this['has' + capitalize (k)] = !!this.has[k] // converts 'emulated' to true
363
+ }
364
+
365
+ if (this.api) {
366
+ this.defineRestApi (this.api, 'request')
367
+ }
368
+
369
+ this.initRestRateLimiter ()
370
+
371
+ if (this.markets) {
372
+ this.setMarkets (this.markets)
373
+ }
374
+ }
375
+
376
+ defaults () {
377
+ return { /* override me */ }
378
+ }
379
+
380
+ nonce () {
381
+ return this.seconds ()
382
+ }
383
+
384
+ encodeURIComponent (...args) {
385
+ return encodeURIComponent (...args)
386
+ }
387
+
388
+ checkRequiredVersion (requiredVersion, error = true) {
389
+ let result = true
390
+ const [ major1, minor1, patch1 ] = requiredVersion.split ('.')
391
+ , [ major2, minor2, patch2 ] = Exchange.ccxtVersion.split ('.')
392
+ , intMajor1 = parseInt (major1)
393
+ , intMinor1 = parseInt (minor1)
394
+ , intPatch1 = parseInt (patch1)
395
+ , intMajor2 = parseInt (major2)
396
+ , intMinor2 = parseInt (minor2)
397
+ , intPatch2 = parseInt (patch2)
398
+ if (intMajor1 > intMajor2) {
399
+ result = false
400
+ }
401
+ if (intMajor1 === intMajor2) {
402
+ if (intMinor1 > intMinor2) {
403
+ result = false
404
+ } else if (intMinor1 === intMinor2 && intPatch1 > intPatch2) {
405
+ result = false
406
+ }
407
+ }
408
+ if (!result) {
409
+ if (error) {
410
+ throw new NotSupported ('Your current version of CCXT is ' + Exchange.ccxtVersion + ', a newer version ' + requiredVersion + ' is required, please, upgrade your version of CCXT')
411
+ } else {
412
+ return error
413
+ }
414
+ }
415
+ return result
416
+ }
417
+
418
+ checkRequiredCredentials (error = true) {
419
+ const keys = Object.keys (this.requiredCredentials)
420
+ for (let i = 0; i < keys.length; i++) {
421
+ const key = keys[i]
422
+ if (this.requiredCredentials[key] && !this[key]) {
423
+ if (error) {
424
+ throw new AuthenticationError (this.id + ' requires `' + key + '` credential')
425
+ } else {
426
+ return error
427
+ }
428
+ }
429
+ }
430
+ return true
431
+ }
432
+
433
+ checkAddress (address) {
434
+
435
+ if (address === undefined) {
436
+ throw new InvalidAddress (this.id + ' address is undefined')
437
+ }
438
+
439
+ // check the address is not the same letter like 'aaaaa' nor too short nor has a space
440
+ if ((unique (address).length === 1) || address.length < this.minFundingAddressLength || address.includes (' ')) {
441
+ throw new InvalidAddress (this.id + ' address is invalid or has less than ' + this.minFundingAddressLength.toString () + ' characters: "' + this.json (address) + '"')
442
+ }
443
+
444
+ return address
445
+ }
446
+
447
+ initRestRateLimiter () {
448
+
449
+ if (this.rateLimit === undefined) {
450
+ throw new Error (this.id + '.rateLimit property is not configured')
451
+ }
452
+
453
+ this.tokenBucket = this.extend ({
454
+ delay: 0.001,
455
+ capacity: 1,
456
+ cost: 1,
457
+ maxCapacity: 1000,
458
+ refillRate: (this.rateLimit > 0) ? 1 / this.rateLimit : Number.MAX_VALUE
459
+ }, this.tokenBucket)
460
+
461
+ this.throttle = throttle (this.tokenBucket)
462
+
463
+ this.executeRestRequest = (url, method = 'GET', headers = undefined, body = undefined) => {
464
+
465
+ // fetchImplementation cannot be called on this. in browsers:
466
+ // TypeError Failed to execute 'fetch' on 'Window': Illegal invocation
467
+ const fetchImplementation = this.fetchImplementation
468
+
469
+ const params = { method, headers, body, timeout: this.timeout }
470
+
471
+ if (this.agent) {
472
+ params['agent'] = this.agent
473
+ } else if (this.httpAgent && url.indexOf ('http://') === 0) {
474
+ params['agent'] = this.httpAgent
475
+ } else if (this.httpsAgent && url.indexOf ('https://') === 0) {
476
+ params['agent'] = this.httpsAgent
477
+ }
478
+
479
+ const promise =
480
+ fetchImplementation (url, this.extend (params, this.fetchOptions))
481
+ .catch ((e) => {
482
+ if (isNode) {
483
+ throw new ExchangeNotAvailable ([ this.id, method, url, e.type, e.message ].join (' '))
484
+ }
485
+ throw e // rethrow all unknown errors
486
+ })
487
+ .then ((response) => this.handleRestResponse (response, url, method, headers, body))
488
+
489
+ return timeout (this.timeout, promise).catch ((e) => {
490
+ if (e instanceof TimedOut) {
491
+ throw new RequestTimeout (this.id + ' ' + method + ' ' + url + ' request timed out (' + this.timeout + ' ms)')
492
+ }
493
+ throw e
494
+ })
495
+ }
496
+ }
497
+
498
+ setSandboxMode (enabled) {
499
+ if (!!enabled) { // eslint-disable-line no-extra-boolean-cast
500
+ if ('test' in this.urls) {
501
+ if (typeof this.urls['api'] === 'string') {
502
+ this.urls['apiBackup'] = this.urls['api']
503
+ this.urls['api'] = this.urls['test']
504
+ } else {
505
+ this.urls['apiBackup'] = clone (this.urls['api'])
506
+ this.urls['api'] = clone (this.urls['test'])
507
+ }
508
+ } else {
509
+ throw new NotSupported (this.id + ' does not have a sandbox URL')
510
+ }
511
+ } else if ('apiBackup' in this.urls) {
512
+ if (typeof this.urls['api'] === 'string') {
513
+ this.urls['api'] = this.urls['apiBackup']
514
+ } else {
515
+ this.urls['api'] = clone (this.urls['apiBackup'])
516
+ }
517
+ }
518
+ }
519
+
520
+ defineRestApiEndpoint (methodName, uppercaseMethod, lowercaseMethod, camelcaseMethod, path, paths, config = {}) {
521
+ const splitPath = path.split (/[^a-zA-Z0-9]/)
522
+ const camelcaseSuffix = splitPath.map (this.capitalize).join ('')
523
+ const underscoreSuffix = splitPath.map ((x) => x.trim ().toLowerCase ()).filter ((x) => x.length > 0).join ('_')
524
+ const camelcasePrefix = [ paths[0] ].concat (paths.slice (1).map (this.capitalize)).join ('')
525
+ const underscorePrefix = [ paths[0] ].concat (paths.slice (1).map ((x) => x.trim ()).filter ((x) => x.length > 0)).join ('_')
526
+ const camelcase = camelcasePrefix + camelcaseMethod + this.capitalize (camelcaseSuffix)
527
+ const underscore = underscorePrefix + '_' + lowercaseMethod + '_' + underscoreSuffix
528
+ const typeArgument = (paths.length > 1) ? paths : paths[0]
529
+ // handle call costs here
530
+ const partial = async (params = {}, context = {}) => this[methodName] (path, typeArgument, uppercaseMethod, params, undefined, undefined, config, context)
531
+ // const partial = async (params) => this[methodName] (path, typeArgument, uppercaseMethod, params || {})
532
+ this[camelcase] = partial
533
+ this[underscore] = partial
534
+ }
535
+
536
+ defineRestApi (api, methodName, paths = []) {
537
+ const keys = Object.keys (api)
538
+ for (let i = 0; i < keys.length; i++) {
539
+ const key = keys[i]
540
+ const value = api[key]
541
+ const uppercaseMethod = key.toUpperCase ()
542
+ const lowercaseMethod = key.toLowerCase ()
543
+ const camelcaseMethod = this.capitalize (lowercaseMethod)
544
+ if (Array.isArray (value)) {
545
+ for (let k = 0; k < value.length; k++) {
546
+ const path = value[k].trim ()
547
+ this.defineRestApiEndpoint (methodName, uppercaseMethod, lowercaseMethod, camelcaseMethod, path, paths)
548
+ }
549
+ // the options HTTP method conflicts with the 'options' API url path
550
+ // } else if (key.match (/^(?:get|post|put|delete|options|head|patch)$/i)) {
551
+ } else if (key.match (/^(?:get|post|put|delete|head|patch)$/i)) {
552
+ const endpoints = Object.keys (value);
553
+ for (let j = 0; j < endpoints.length; j++) {
554
+ const endpoint = endpoints[j]
555
+ const path = endpoint.trim ()
556
+ const config = value[endpoint]
557
+ if (typeof config === 'object') {
558
+ this.defineRestApiEndpoint (methodName, uppercaseMethod, lowercaseMethod, camelcaseMethod, path, paths, config)
559
+ } else if (typeof config === 'number') {
560
+ this.defineRestApiEndpoint (methodName, uppercaseMethod, lowercaseMethod, camelcaseMethod, path, paths, { cost: config })
561
+ } else {
562
+ throw new NotSupported (this.id + ' defineRestApi() API format not supported, API leafs must strings, objects or numbers');
563
+ }
564
+ }
565
+ } else {
566
+ this.defineRestApi (value, methodName, paths.concat ([ key ]))
567
+ }
568
+ }
569
+ }
570
+
571
+ log (... args) {
572
+ console.log (... args)
573
+ }
574
+
575
+ setHeaders (headers) {
576
+ return headers;
577
+ }
578
+
579
+ fetch (url, method = 'GET', headers = undefined, body = undefined) {
580
+
581
+ if (isNode && this.userAgent) {
582
+ if (typeof this.userAgent === 'string') {
583
+ headers = extend ({ 'User-Agent': this.userAgent }, headers)
584
+ } else if ((typeof this.userAgent === 'object') && ('User-Agent' in this.userAgent)) {
585
+ headers = extend (this.userAgent, headers)
586
+ }
587
+ }
588
+
589
+ if (typeof this.proxy === 'function') {
590
+
591
+ url = this.proxy (url)
592
+ if (isNode) {
593
+ headers = extend ({ 'Origin': this.origin }, headers)
594
+ }
595
+
596
+ } else if (typeof this.proxy === 'string') {
597
+
598
+ if (this.proxy.length && isNode) {
599
+ headers = extend ({ 'Origin': this.origin }, headers)
600
+ }
601
+
602
+ url = this.proxy + url
603
+ }
604
+
605
+ headers = extend (this.headers, headers)
606
+ headers = this.setHeaders (headers)
607
+
608
+ if (this.verbose) {
609
+ this.log ("fetch Request:\n", this.id, method, url, "\nRequestHeaders:\n", headers, "\nRequestBody:\n", body, "\n")
610
+ }
611
+
612
+ return this.executeRestRequest (url, method, headers, body)
613
+ }
614
+
615
+ // eslint-disable-next-line no-unused-vars
616
+ calculateRateLimiterCost (api, method, path, params, config = {}, context = {}) {
617
+ return this.safeValue (config, 'cost', 1);
618
+ }
619
+
620
+ async fetch2 (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined, config = {}, context = {}) {
621
+ if (this.enableRateLimit) {
622
+ const cost = this.calculateRateLimiterCost (type, method, path, params, config, context)
623
+ await this.throttle (cost)
624
+ }
625
+ const request = this.sign (path, type, method, params, headers, body)
626
+ return this.fetch (request.url, request.method, request.headers, request.body)
627
+ }
628
+
629
+ request (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined, config = {}, context = {}) {
630
+ return this.fetch2 (path, type, method, params, headers, body, config, context)
631
+ }
632
+
633
+ parseJson (jsonString) {
634
+ try {
635
+ if (this.isJsonEncodedObject (jsonString)) {
636
+ return JSON.parse (this.onJsonResponse (jsonString))
637
+ }
638
+ } catch (e) {
639
+ // SyntaxError
640
+ return undefined
641
+ }
642
+ }
643
+
644
+ throwExactlyMatchedException (exact, string, message) {
645
+ if (string in exact) {
646
+ throw new exact[string] (message)
647
+ }
648
+ }
649
+
650
+ throwBroadlyMatchedException (broad, string, message) {
651
+ const broadKey = this.findBroadlyMatchedKey (broad, string)
652
+ if (broadKey !== undefined) {
653
+ throw new broad[broadKey] (message)
654
+ }
655
+ }
656
+
657
+ // a helper for matching error strings exactly vs broadly
658
+ findBroadlyMatchedKey (broad, string) {
659
+ const keys = Object.keys (broad)
660
+ for (let i = 0; i < keys.length; i++) {
661
+ const key = keys[i]
662
+ if (string.indexOf (key) >= 0) {
663
+ return key
664
+ }
665
+ }
666
+ return undefined
667
+ }
668
+
669
+ handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) {
670
+ // override me
671
+ }
672
+
673
+ handleHttpStatusCode (code, reason, url, method, body) {
674
+ const codeAsString = code.toString ()
675
+ if (codeAsString in this.httpExceptions) {
676
+ const ErrorClass = this.httpExceptions[codeAsString]
677
+ throw new ErrorClass ([ this.id, method, url, code, reason, body ].join (' '))
678
+ }
679
+ }
680
+
681
+ getResponseHeaders (response) {
682
+ const result = {}
683
+ response.headers.forEach ((value, key) => {
684
+ key = key.split ('-').map ((word) => capitalize (word)).join ('-')
685
+ result[key] = value
686
+ })
687
+ return result
688
+ }
689
+
690
+ handleRestResponse (response, url, method = 'GET', requestHeaders = undefined, requestBody = undefined) {
691
+ const responseHeaders = this.getResponseHeaders (response)
692
+
693
+ if (this.handleContentTypeApplicationZip && (responseHeaders['Content-Type'] === 'application/zip')) {
694
+ const responseBuffer = response.buffer ();
695
+ if (this.enableLastResponseHeaders) {
696
+ this.last_response_headers = responseHeaders
697
+ }
698
+ if (this.enableLastHttpResponse) {
699
+ this.last_http_response = responseBuffer
700
+ }
701
+ if (this.verbose) {
702
+ this.log ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponseHeaders:\n", responseHeaders, "ZIP redacted", "\n")
703
+ }
704
+ // no error handler needed, because it would not be a zip response in case of an error
705
+ return responseBuffer;
706
+ }
707
+
708
+ return response.text ().then ((responseBody) => {
709
+ const bodyText = this.onRestResponse (response.status, response.statusText, url, method, responseHeaders, responseBody, requestHeaders, requestBody);
710
+ const json = this.parseJson (bodyText)
711
+ if (this.enableLastResponseHeaders) {
712
+ this.last_response_headers = responseHeaders
713
+ }
714
+ if (this.enableLastHttpResponse) {
715
+ this.last_http_response = responseBody
716
+ }
717
+ if (this.enableLastJsonResponse) {
718
+ this.last_json_response = json
719
+ }
720
+ if (this.verbose) {
721
+ this.log ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponseHeaders:\n", responseHeaders, "\nResponseBody:\n", responseBody, "\n")
722
+ }
723
+ const skipFurtherErrorHandling = this.handleErrors (response.status, response.statusText, url, method, responseHeaders, responseBody, json, requestHeaders, requestBody)
724
+ if (!skipFurtherErrorHandling) {
725
+ this.handleHttpStatusCode (response.status, response.statusText, url, method, responseBody)
726
+ }
727
+ return json || responseBody
728
+ })
729
+ }
730
+
731
+ onRestResponse (statusCode, statusText, url, method, responseHeaders, responseBody, requestHeaders, requestBody) {
732
+ return responseBody.trim ()
733
+ }
734
+
735
+ onJsonResponse (responseBody) {
736
+ return this.quoteJsonNumbers ? responseBody.replace (/":([+.0-9eE-]+)([,}])/g, '":"$1"$2') : responseBody;
737
+ }
738
+
739
+ setMarkets (markets, currencies = undefined) {
740
+ const values = Object.values (markets).map ((market) => deepExtend ({
741
+ 'id': undefined,
742
+ 'symbol': undefined,
743
+ 'base': undefined,
744
+ 'quote': undefined,
745
+ 'baseId': undefined,
746
+ 'quoteId': undefined,
747
+ 'active': undefined,
748
+ 'type': undefined,
749
+ 'linear': undefined,
750
+ 'inverse': undefined,
751
+ 'spot': false,
752
+ 'swap': false,
753
+ 'future': false,
754
+ 'option': false,
755
+ 'margin': false,
756
+ 'contract': false,
757
+ 'contractSize': undefined,
758
+ 'expiry': undefined,
759
+ 'expiryDatetime': undefined,
760
+ 'optionType': undefined,
761
+ 'strike': undefined,
762
+ 'settle': undefined,
763
+ 'settleId': undefined,
764
+ 'precision': this.precision,
765
+ 'limits': this.limits,
766
+ 'info': undefined,
767
+ }, this.fees['trading'], market))
768
+ this.markets = indexBy (values, 'symbol')
769
+ this.markets_by_id = indexBy (markets, 'id')
770
+ this.symbols = Object.keys (this.markets).sort ()
771
+ this.ids = Object.keys (this.markets_by_id).sort ()
772
+ if (currencies) {
773
+ this.currencies = deepExtend (this.currencies, currencies)
774
+ } else {
775
+ let baseCurrencies =
776
+ values.filter ((market) => 'base' in market)
777
+ .map ((market) => ({
778
+ id: market.baseId || market.base,
779
+ numericId: (market.baseNumericId !== undefined) ? market.baseNumericId : undefined,
780
+ code: market.base,
781
+ precision: market.precision ? (market.precision.base || market.precision.amount) : 8,
782
+ }))
783
+ let quoteCurrencies =
784
+ values.filter ((market) => 'quote' in market)
785
+ .map ((market) => ({
786
+ id: market.quoteId || market.quote,
787
+ numericId: (market.quoteNumericId !== undefined) ? market.quoteNumericId : undefined,
788
+ code: market.quote,
789
+ precision: market.precision ? (market.precision.quote || market.precision.price) : 8,
790
+ }))
791
+ baseCurrencies = sortBy (baseCurrencies, 'code')
792
+ quoteCurrencies = sortBy (quoteCurrencies, 'code')
793
+ this.baseCurrencies = indexBy (baseCurrencies, 'code')
794
+ this.quoteCurrencies = indexBy (quoteCurrencies, 'code')
795
+ const allCurrencies = baseCurrencies.concat (quoteCurrencies)
796
+ const groupedCurrencies = groupBy (allCurrencies, 'code')
797
+ const currencies = Object.keys (groupedCurrencies).map ((code) =>
798
+ groupedCurrencies[code].reduce ((previous, current) => // eslint-disable-line implicit-arrow-linebreak
799
+ ((previous.precision > current.precision) ? previous : current), groupedCurrencies[code][0])) // eslint-disable-line implicit-arrow-linebreak
800
+ const sortedCurrencies = sortBy (flatten (currencies), 'code')
801
+ this.currencies = deepExtend (this.currencies, indexBy (sortedCurrencies, 'code'))
802
+ }
803
+ this.currencies_by_id = indexBy (this.currencies, 'id')
804
+ this.codes = Object.keys (this.currencies).sort ()
805
+ return this.markets
806
+ }
807
+
808
+ async loadMarketsHelper (reload = false, params = {}) {
809
+ if (!reload && this.markets) {
810
+ if (!this.markets_by_id) {
811
+ return this.setMarkets (this.markets)
812
+ }
813
+ return this.markets
814
+ }
815
+ let currencies = undefined
816
+ // only call if exchange API provides endpoint (true), thus avoid emulated versions ('emulated')
817
+ if (this.has.fetchCurrencies === true) {
818
+ currencies = await this.fetchCurrencies ()
819
+ }
820
+ const markets = await this.fetchMarkets (params)
821
+ return this.setMarkets (markets, currencies)
822
+ }
823
+
824
+ async fetchPermissions (params = {}) {
825
+ throw new NotSupported (this.id + ' fetchPermissions() is not supported yet')
826
+ }
827
+
828
+ // is async (returns a promise)
829
+ loadMarkets (reload = false, params = {}) {
830
+ if ((reload && !this.reloadingMarkets) || !this.marketsLoading) {
831
+ this.reloadingMarkets = true
832
+ this.marketsLoading = this.loadMarketsHelper (reload, params).then ((resolved) => {
833
+ this.reloadingMarkets = false
834
+ return resolved
835
+ }, (error) => {
836
+ this.reloadingMarkets = false
837
+ throw error
838
+ })
839
+ }
840
+ return this.marketsLoading
841
+ }
842
+
843
+ async loadAccounts (reload = false, params = {}) {
844
+ if (reload) {
845
+ this.accounts = await this.fetchAccounts (params)
846
+ } else {
847
+ if (this.accounts) {
848
+ return this.accounts
849
+ } else {
850
+ this.accounts = await this.fetchAccounts (params)
851
+ }
852
+ }
853
+ this.accountsById = this.indexBy (this.accounts, 'id')
854
+ return this.accounts
855
+ }
856
+
857
+ fetchBidsAsks (symbols = undefined, params = {}) {
858
+ throw new NotSupported (this.id + ' fetchBidsAsks() is not supported yet')
859
+ }
860
+
861
+ async fetchOHLCVC (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
862
+ if (!this.has['fetchTrades']) {
863
+ throw new NotSupported (this.id + ' fetchOHLCV() is not supported yet')
864
+ }
865
+ await this.loadMarkets ()
866
+ const trades = await this.fetchTrades (symbol, since, limit, params)
867
+ const ohlcvc = buildOHLCVC (trades, timeframe, since, limit)
868
+ return ohlcvc
869
+ }
870
+
871
+ async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
872
+ if (!this.has['fetchTrades']) {
873
+ throw new NotSupported (this.id + ' fetchOHLCV() is not supported yet')
874
+ }
875
+ await this.loadMarkets ()
876
+ const trades = await this.fetchTrades (symbol, since, limit, params)
877
+ const ohlcvc = buildOHLCVC (trades, timeframe, since, limit)
878
+ return ohlcvc.map ((c) => c.slice (0, -1))
879
+ }
880
+
881
+ parseTradingViewOHLCV (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
882
+ const result = this.convertTradingViewToOHLCV (ohlcvs)
883
+ return this.parseOHLCVs (result, market, timeframe, since, limit)
884
+ }
885
+
886
+ convertTradingViewToOHLCV (ohlcvs, t = 't', o = 'o', h = 'h', l = 'l', c = 'c', v = 'v', ms = false) {
887
+ const result = [];
888
+ for (let i = 0; i < ohlcvs[t].length; i++) {
889
+ result.push ([
890
+ ms ? ohlcvs[t][i] : (ohlcvs[t][i] * 1000),
891
+ ohlcvs[o][i],
892
+ ohlcvs[h][i],
893
+ ohlcvs[l][i],
894
+ ohlcvs[c][i],
895
+ ohlcvs[v][i],
896
+ ])
897
+ }
898
+ return result
899
+ }
900
+
901
+ convertOHLCVToTradingView (ohlcvs, t = 't', o = 'o', h = 'h', l = 'l', c = 'c', v = 'v', ms = false) {
902
+ const result = {}
903
+ result[t] = []
904
+ result[o] = []
905
+ result[h] = []
906
+ result[l] = []
907
+ result[c] = []
908
+ result[v] = []
909
+ for (let i = 0; i < ohlcvs.length; i++) {
910
+ result[t].push (ms ? ohlcvs[i][0] : parseInt (ohlcvs[i][0] / 1000))
911
+ result[o].push (ohlcvs[i][1])
912
+ result[h].push (ohlcvs[i][2])
913
+ result[l].push (ohlcvs[i][3])
914
+ result[c].push (ohlcvs[i][4])
915
+ result[v].push (ohlcvs[i][5])
916
+ }
917
+ return result
918
+ }
919
+
920
+ async fetchTicker (symbol, params = {}) {
921
+ if (this.has['fetchTickers']) {
922
+ const tickers = await this.fetchTickers ([ symbol ], params);
923
+ const ticker = this.safeValue (tickers, symbol);
924
+ if (ticker === undefined) {
925
+ throw new InvalidAddress (this.id + ' fetchTickers() could not find a ticker for ' + symbol);
926
+ } else {
927
+ return ticker;
928
+ }
929
+ } else {
930
+ throw new NotSupported (this.id + ' fetchTicker() is not supported yet');
931
+ }
932
+ }
933
+
934
+ fetchTickers (symbols = undefined, params = {}) {
935
+ throw new NotSupported (this.id + ' fetchTickers() is not supported yet')
936
+ }
937
+
938
+ fetchOrder (id, symbol = undefined, params = {}) {
939
+ throw new NotSupported (this.id + ' fetchOrder() is not supported yet');
940
+ }
941
+
942
+ fetchUnifiedOrder (order, params = {}) {
943
+ return this.fetchOrder (this.safeValue (order, 'id'), this.safeValue (order, 'symbol'), params);
944
+ }
945
+
946
+ createOrder (symbol, type, side, amount, price = undefined, params = {}) {
947
+ throw new NotSupported (this.id + ' createOrder() is not supported yet');
948
+ }
949
+
950
+ cancelOrder (id, symbol = undefined, params = {}) {
951
+ throw new NotSupported (this.id + ' cancelOrder() is not supported yet');
952
+ }
953
+
954
+ cancelUnifiedOrder (order, params = {}) {
955
+ return this.cancelOrder (this.safeValue (order, 'id'), this.safeValue (order, 'symbol'), params);
956
+ }
957
+
958
+ fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
959
+ throw new NotSupported (this.id + ' fetchOrders() is not supported yet');
960
+ }
961
+
962
+ fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
963
+ throw new NotSupported (this.id + ' fetchOpenOrders() is not supported yet');
964
+ }
965
+
966
+ fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
967
+ throw new NotSupported (this.id + ' fetchClosedOrders() is not supported yet');
968
+ }
969
+
970
+ fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
971
+ throw new NotSupported (this.id + ' fetchMyTrades() is not supported yet');
972
+ }
973
+
974
+ fetchTransactions (symbol = undefined, since = undefined, limit = undefined, params = {}) {
975
+ throw new NotSupported (this.id + ' fetchTransactions() is not supported yet');
976
+ }
977
+
978
+ fetchDeposits (symbol = undefined, since = undefined, limit = undefined, params = {}) {
979
+ throw new NotSupported (this.id + ' fetchDeposits() is not supported yet');
980
+ }
981
+
982
+ fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) {
983
+ throw new NotSupported (this.id + ' fetchWithdrawals() is not supported yet');
984
+ }
985
+
986
+ async fetchDepositAddress (code, params = {}) {
987
+ if (this.has['fetchDepositAddresses']) {
988
+ const depositAddresses = await this.fetchDepositAddresses ([ code ], params);
989
+ const depositAddress = this.safeValue (depositAddresses, code);
990
+ if (depositAddress === undefined) {
991
+ throw new InvalidAddress (this.id + ' fetchDepositAddress() could not find a deposit address for ' + code + ', make sure you have created a corresponding deposit address in your wallet on the exchange website');
992
+ } else {
993
+ return depositAddress;
994
+ }
995
+ } else {
996
+ throw new NotSupported (this.id + ' fetchDepositAddress() not supported yet');
997
+ }
998
+ }
999
+
1000
+ fetchCurrencies (params = {}) {
1001
+ // markets are returned as a list
1002
+ // currencies are returned as a dict
1003
+ // this is for historical reasons
1004
+ // and may be changed for consistency later
1005
+ return new Promise ((resolve, reject) => resolve (this.currencies));
1006
+ }
1007
+
1008
+ fetchMarkets (params = {}) {
1009
+ // markets are returned as a list
1010
+ // currencies are returned as a dict
1011
+ // this is for historical reasons
1012
+ // and may be changed for consistency later
1013
+ return new Promise ((resolve, reject) => resolve (Object.values (this.markets)))
1014
+ }
1015
+
1016
+ async fetchOrderStatus (id, symbol = undefined, params = {}) {
1017
+ const order = await this.fetchOrder (id, symbol, params);
1018
+ return order['status'];
1019
+ }
1020
+
1021
+ account () {
1022
+ return {
1023
+ 'free': undefined,
1024
+ 'used': undefined,
1025
+ 'total': undefined,
1026
+ }
1027
+ }
1028
+
1029
+ commonCurrencyCode (currency) {
1030
+ if (!this.substituteCommonCurrencyCodes) {
1031
+ return currency
1032
+ }
1033
+ return this.safeString (this.commonCurrencies, currency, currency)
1034
+ }
1035
+
1036
+ currency (code) {
1037
+ if (this.currencies === undefined) {
1038
+ throw new ExchangeError (this.id + ' currencies not loaded')
1039
+ }
1040
+ if (typeof code === 'string') {
1041
+ if (code in this.currencies) {
1042
+ return this.currencies[code];
1043
+ } else if (code in this.currencies_by_id) {
1044
+ return this.currencies_by_id[code];
1045
+ }
1046
+ }
1047
+ throw new ExchangeError (this.id + ' does not have currency code ' + code)
1048
+ }
1049
+
1050
+ market (symbol) {
1051
+ if (this.markets === undefined) {
1052
+ throw new ExchangeError (this.id + ' markets not loaded')
1053
+ }
1054
+ if (this.markets_by_id === undefined) {
1055
+ throw new ExchangeError (this.id + ' markets not loaded')
1056
+ }
1057
+ if (typeof symbol === 'string') {
1058
+ if (symbol in this.markets) {
1059
+ return this.markets[symbol]
1060
+ } else if (symbol in this.markets_by_id) {
1061
+ return this.markets_by_id[symbol]
1062
+ }
1063
+ }
1064
+ throw new BadSymbol (this.id + ' does not have market symbol ' + symbol)
1065
+ }
1066
+
1067
+ marketId (symbol) {
1068
+ const market = this.market (symbol)
1069
+ return (market !== undefined ? market['id'] : symbol)
1070
+ }
1071
+
1072
+ marketIds (symbols) {
1073
+ return symbols.map ((symbol) => this.marketId (symbol))
1074
+ }
1075
+
1076
+ symbol (symbol) {
1077
+ return this.market (symbol).symbol || symbol
1078
+ }
1079
+
1080
+ implodeHostname (url) {
1081
+ return this.implodeParams (url, { 'hostname': this.hostname })
1082
+ }
1083
+
1084
+ resolvePath (path, params) {
1085
+ return [
1086
+ this.implodeParams (path, params),
1087
+ this.omit (params, this.extractParams (path))
1088
+ ];
1089
+ }
1090
+
1091
+ parseBidAsk (bidask, priceKey = 0, amountKey = 1) {
1092
+ const price = this.safeNumber (bidask, priceKey)
1093
+ const amount = this.safeNumber (bidask, amountKey)
1094
+ return [ price, amount ]
1095
+ }
1096
+
1097
+ parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) {
1098
+ return Object.values (bidasks || []).map ((bidask) => this.parseBidAsk (bidask, priceKey, amountKey))
1099
+ }
1100
+
1101
+ async fetchL2OrderBook (symbol, limit = undefined, params = {}) {
1102
+ const orderbook = await this.fetchOrderBook (symbol, limit, params)
1103
+ return extend (orderbook, {
1104
+ 'bids': sortBy (aggregate (orderbook.bids), 0, true),
1105
+ 'asks': sortBy (aggregate (orderbook.asks), 0),
1106
+ })
1107
+ }
1108
+
1109
+ parseOrderBook (orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1) {
1110
+ return {
1111
+ 'symbol': symbol,
1112
+ 'bids': sortBy ((bidsKey in orderbook) ? this.parseBidsAsks (orderbook[bidsKey], priceKey, amountKey) : [], 0, true),
1113
+ 'asks': sortBy ((asksKey in orderbook) ? this.parseBidsAsks (orderbook[asksKey], priceKey, amountKey) : [], 0),
1114
+ 'timestamp': timestamp,
1115
+ 'datetime': this.iso8601 (timestamp),
1116
+ 'nonce': undefined,
1117
+ }
1118
+ }
1119
+
1120
+ safeBalance (balance) {
1121
+
1122
+ const codes = Object.keys (this.omit (balance, [ 'info', 'timestamp', 'datetime', 'free', 'used', 'total' ]));
1123
+
1124
+ balance['free'] = {}
1125
+ balance['used'] = {}
1126
+ balance['total'] = {}
1127
+
1128
+ for (let i = 0; i < codes.length; i++) {
1129
+ const code = codes[i]
1130
+ if (balance[code].total === undefined) {
1131
+ if (balance[code].free !== undefined && balance[code].used !== undefined) {
1132
+ balance[code].total = Precise.stringAdd (balance[code].free, balance[code].used)
1133
+ }
1134
+ }
1135
+ if (balance[code].free === undefined) {
1136
+ if (balance[code].total !== undefined && balance[code].used !== undefined) {
1137
+ balance[code].free = Precise.stringSub (balance[code].total, balance[code].used)
1138
+ }
1139
+ }
1140
+ if (balance[code].used === undefined) {
1141
+ if (balance[code].total !== undefined && balance[code].free !== undefined) {
1142
+ balance[code].used = Precise.stringSub (balance[code].total, balance[code].free)
1143
+ }
1144
+ }
1145
+ balance[code].free = this.parseNumber (balance[code].free)
1146
+ balance[code].used = this.parseNumber (balance[code].used)
1147
+ balance[code].total = this.parseNumber (balance[code].total)
1148
+ balance.free[code] = balance[code].free
1149
+ balance.used[code] = balance[code].used
1150
+ balance.total[code] = balance[code].total
1151
+ }
1152
+ return balance
1153
+ }
1154
+
1155
+ async fetchBalance (params = {}) {
1156
+ throw new NotSupported (this.id + ' fetchBalance() not supported yet')
1157
+ }
1158
+
1159
+ async fetchPartialBalance (part, params = {}) {
1160
+ const balance = await this.fetchBalance (params)
1161
+ return balance[part]
1162
+ }
1163
+
1164
+ fetchFreeBalance (params = {}) {
1165
+ return this.fetchPartialBalance ('free', params)
1166
+ }
1167
+
1168
+ fetchUsedBalance (params = {}) {
1169
+ return this.fetchPartialBalance ('used', params)
1170
+ }
1171
+
1172
+ fetchTotalBalance (params = {}) {
1173
+ return this.fetchPartialBalance ('total', params)
1174
+ }
1175
+
1176
+ async fetchStatus (params = {}) {
1177
+ if (this.has['fetchTime']) {
1178
+ const time = await this.fetchTime (params)
1179
+ this.status = this.extend (this.status, {
1180
+ 'updated': time,
1181
+ })
1182
+ }
1183
+ return this.status
1184
+ }
1185
+
1186
+ async fetchTradingFees (params = {}) {
1187
+ throw new NotSupported (this.id + ' fetchTradingFees() not supported yet')
1188
+ }
1189
+
1190
+ async fetchTradingFee (symbol, params = {}) {
1191
+ if (!this.has['fetchTradingFees']) {
1192
+ throw new NotSupported (this.id + ' fetchTradingFee() not supported yet')
1193
+ }
1194
+ return await this.fetchTradingFees (params)
1195
+ }
1196
+
1197
+ async loadTradingLimits (symbols = undefined, reload = false, params = {}) {
1198
+ if (this.has['fetchTradingLimits']) {
1199
+ if (reload || !('limitsLoaded' in this.options)) {
1200
+ const response = await this.fetchTradingLimits (symbols);
1201
+ for (let i = 0; i < symbols.length; i++) {
1202
+ const symbol = symbols[i];
1203
+ this.markets[symbol] = this.deepExtend (this.markets[symbol], response[symbol]);
1204
+ }
1205
+ this.options['limitsLoaded'] = this.milliseconds ();
1206
+ }
1207
+ }
1208
+ return this.markets;
1209
+ }
1210
+
1211
+ filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp', tail = false) {
1212
+ const sinceIsDefined = (since !== undefined && since !== null)
1213
+ if (sinceIsDefined) {
1214
+ array = array.filter ((entry) => entry[key] >= since)
1215
+ }
1216
+ if (limit !== undefined && limit !== null) {
1217
+ array = tail ? array.slice (-limit) : array.slice (0, limit)
1218
+ }
1219
+ return array
1220
+ }
1221
+
1222
+ filterByValueSinceLimit (array, field, value = undefined, since = undefined, limit = undefined, key = 'timestamp', tail = false) {
1223
+
1224
+ const valueIsDefined = value !== undefined && value !== null
1225
+ const sinceIsDefined = since !== undefined && since !== null
1226
+
1227
+ // single-pass filter for both symbol and since
1228
+ if (valueIsDefined || sinceIsDefined) {
1229
+ array = array.filter ((entry) =>
1230
+ ((valueIsDefined ? (entry[field] === value) : true) &&
1231
+ (sinceIsDefined ? (entry[key] >= since) : true)))
1232
+ }
1233
+
1234
+ if (limit !== undefined && limit !== null) {
1235
+ array = tail ? array.slice (-limit) : array.slice (0, limit)
1236
+ }
1237
+
1238
+ return array
1239
+ }
1240
+
1241
+ filterBySymbolSinceLimit (array, symbol = undefined, since = undefined, limit = undefined, tail = false) {
1242
+ return this.filterByValueSinceLimit (array, 'symbol', symbol, since, limit, 'timestamp', tail)
1243
+ }
1244
+
1245
+ filterByCurrencySinceLimit (array, code = undefined, since = undefined, limit = undefined, tail = false) {
1246
+ return this.filterByValueSinceLimit (array, 'currency', code, since, limit, 'timestamp', tail)
1247
+ }
1248
+
1249
+ filterByArray (objects, key, values = undefined, indexed = true) {
1250
+
1251
+ objects = Object.values (objects)
1252
+
1253
+ // return all of them if no values were passed
1254
+ if (values === undefined || values === null) {
1255
+ return indexed ? indexBy (objects, key) : objects
1256
+ }
1257
+
1258
+ const result = []
1259
+ for (let i = 0; i < objects.length; i++) {
1260
+ if (values.includes (objects[i][key])) {
1261
+ result.push (objects[i])
1262
+ }
1263
+ }
1264
+
1265
+ return indexed ? indexBy (result, key) : result
1266
+ }
1267
+
1268
+ safeTicker (ticker, market = undefined, legacy = true) {
1269
+ if (legacy) {
1270
+ let symbol = this.safeValue (ticker, 'symbol');
1271
+ if (symbol === undefined) {
1272
+ symbol = this.safeSymbol (undefined, market);
1273
+ }
1274
+ const timestamp = this.safeInteger (ticker, 'timestamp');
1275
+ let baseVolume = this.safeValue (ticker, 'baseVolume');
1276
+ let quoteVolume = this.safeValue (ticker, 'quoteVolume');
1277
+ let vwap = this.safeValue (ticker, 'vwap');
1278
+ if (vwap === undefined) {
1279
+ vwap = this.vwap (baseVolume, quoteVolume);
1280
+ }
1281
+ let open = this.safeValue (ticker, 'open');
1282
+ let close = this.safeValue (ticker, 'close');
1283
+ let last = this.safeValue (ticker, 'last');
1284
+ let change = this.safeValue (ticker, 'change');
1285
+ let percentage = this.safeValue (ticker, 'percentage');
1286
+ let average = this.safeValue (ticker, 'average');
1287
+ if ((last !== undefined) && (close === undefined)) {
1288
+ close = last;
1289
+ } else if ((last === undefined) && (close !== undefined)) {
1290
+ last = close;
1291
+ }
1292
+ if ((last !== undefined) && (open !== undefined)) {
1293
+ if (change === undefined) {
1294
+ change = last - open;
1295
+ }
1296
+ if (average === undefined) {
1297
+ average = this.sum (last, open) / 2;
1298
+ }
1299
+ }
1300
+ if ((percentage === undefined) && (change !== undefined) && (open !== undefined) && (open > 0)) {
1301
+ percentage = change / open * 100;
1302
+ }
1303
+ if ((change === undefined) && (percentage !== undefined) && (last !== undefined)) {
1304
+ change = percentage / 100 * last;
1305
+ }
1306
+ if ((open === undefined) && (last !== undefined) && (change !== undefined)) {
1307
+ open = last - change;
1308
+ }
1309
+ if ((vwap !== undefined) && (baseVolume !== undefined) && (quoteVolume === undefined)) {
1310
+ quoteVolume = vwap / baseVolume;
1311
+ }
1312
+ if ((vwap !== undefined) && (quoteVolume !== undefined) && (baseVolume === undefined)) {
1313
+ baseVolume = quoteVolume / vwap;
1314
+ }
1315
+ ticker['symbol'] = symbol;
1316
+ ticker['timestamp'] = timestamp;
1317
+ ticker['datetime'] = this.iso8601 (timestamp);
1318
+ ticker['open'] = open;
1319
+ ticker['close'] = close;
1320
+ ticker['last'] = last;
1321
+ ticker['vwap'] = vwap;
1322
+ ticker['change'] = change;
1323
+ ticker['percentage'] = percentage;
1324
+ ticker['average'] = average;
1325
+ return ticker;
1326
+ } else {
1327
+ let open = this.safeValue (ticker, 'open');
1328
+ let close = this.safeValue (ticker, 'close');
1329
+ let last = this.safeValue (ticker, 'last');
1330
+ let change = this.safeValue (ticker, 'change');
1331
+ let percentage = this.safeValue (ticker, 'percentage');
1332
+ let average = this.safeValue (ticker, 'average');
1333
+ let vwap = this.safeValue (ticker, 'vwap');
1334
+ const baseVolume = this.safeValue (ticker, 'baseVolume');
1335
+ const quoteVolume = this.safeValue (ticker, 'quoteVolume');
1336
+ if (vwap === undefined) {
1337
+ vwap = Precise.stringDiv (quoteVolume, baseVolume);
1338
+ }
1339
+ if ((last !== undefined) && (close === undefined)) {
1340
+ close = last;
1341
+ } else if ((last === undefined) && (close !== undefined)) {
1342
+ last = close;
1343
+ }
1344
+ if ((last !== undefined) && (open !== undefined)) {
1345
+ if (change === undefined) {
1346
+ change = Precise.stringSub (last, open);
1347
+ }
1348
+ if (average === undefined) {
1349
+ average = Precise.stringDiv (Precise.stringAdd (last, open), '2');
1350
+ }
1351
+ }
1352
+ if ((percentage === undefined) && (change !== undefined) && (open !== undefined) && (Precise.stringGt (open, '0'))) {
1353
+ percentage = Precise.stringMul (Precise.stringDiv (change, open), '100');
1354
+ }
1355
+ if ((change === undefined) && (percentage !== undefined) && (last !== undefined)) {
1356
+ change = Precise.stringDiv (Precise.stringMul (percentage, last), '100');
1357
+ }
1358
+ if ((open === undefined) && (last !== undefined) && (change !== undefined)) {
1359
+ open = Precise.stringSub (last, change);
1360
+ }
1361
+ // timestamp and symbol operations don't belong in safeTicker
1362
+ // they should be done in the derived classes
1363
+ return this.extend (ticker, {
1364
+ 'bid': this.safeNumber (ticker, 'bid'),
1365
+ 'bidVolume': this.safeNumber (ticker, 'bidVolume'),
1366
+ 'ask': this.safeNumber (ticker, 'ask'),
1367
+ 'askVolume': this.safeNumber (ticker, 'askVolume'),
1368
+ 'high': this.safeNumber (ticker, 'high'),
1369
+ 'low': this.safeNumber (ticker, 'low'),
1370
+ 'open': this.parseNumber (open),
1371
+ 'close': this.parseNumber (close),
1372
+ 'last': this.parseNumber (last),
1373
+ 'change': this.parseNumber (change),
1374
+ 'percentage': this.parseNumber (percentage),
1375
+ 'average': this.parseNumber (average),
1376
+ 'vwap': this.parseNumber (vwap),
1377
+ 'baseVolume': this.parseNumber (baseVolume),
1378
+ 'quoteVolume': this.parseNumber (quoteVolume),
1379
+ });
1380
+ }
1381
+ }
1382
+
1383
+ parseTickers (tickers, symbols = undefined, params = {}) {
1384
+ const result = [];
1385
+ const values = Object.values (tickers || []);
1386
+ for (let i = 0; i < values.length; i++) {
1387
+ result.push (this.extend (this.parseTicker (values[i]), params));
1388
+ }
1389
+ return this.filterByArray (result, 'symbol', symbols);
1390
+ }
1391
+
1392
+ parseDepositAddresses (addresses, codes = undefined, indexed = true, params = {}) {
1393
+ let result = [];
1394
+ for (let i = 0; i < addresses.length; i++) {
1395
+ const address = this.extend (this.parseDepositAddress (addresses[i]), params);
1396
+ result.push (address);
1397
+ }
1398
+ if (codes) {
1399
+ result = this.filterByArray (result, 'currency', codes, false);
1400
+ }
1401
+ return indexed ? this.indexBy (result, 'currency') : result;
1402
+ }
1403
+
1404
+ parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) {
1405
+ let result = Object.values (trades || []).map ((trade) => this.merge (this.parseTrade (trade, market), params))
1406
+ result = sortBy2 (result, 'timestamp', 'id')
1407
+ const symbol = (market !== undefined) ? market['symbol'] : undefined
1408
+ const tail = since === undefined
1409
+ return this.filterBySymbolSinceLimit (result, symbol, since, limit, tail)
1410
+ }
1411
+
1412
+ parseTransactions (transactions, currency = undefined, since = undefined, limit = undefined, params = {}) {
1413
+ let result = Object.values (transactions || []).map ((transaction) => this.extend (this.parseTransaction (transaction, currency), params))
1414
+ result = this.sortBy (result, 'timestamp');
1415
+ const code = (currency !== undefined) ? currency['code'] : undefined;
1416
+ const tail = since === undefined;
1417
+ return this.filterByCurrencySinceLimit (result, code, since, limit, tail);
1418
+ }
1419
+
1420
+ parseTransfers (transfers, currency = undefined, since = undefined, limit = undefined, params = {}) {
1421
+ let result = Object.values (transfers || []).map ((transfer) => this.extend (this.parseTransfer (transfer, currency), params))
1422
+ result = this.sortBy (result, 'timestamp');
1423
+ const code = (currency !== undefined) ? currency['code'] : undefined;
1424
+ const tail = since === undefined;
1425
+ return this.filterByCurrencySinceLimit (result, code, since, limit, tail);
1426
+ }
1427
+
1428
+ parseLedger (data, currency = undefined, since = undefined, limit = undefined, params = {}) {
1429
+ let result = [];
1430
+ const array = Object.values (data || []);
1431
+ for (let i = 0; i < array.length; i++) {
1432
+ const itemOrItems = this.parseLedgerEntry (array[i], currency);
1433
+ if (Array.isArray (itemOrItems)) {
1434
+ for (let j = 0; j < itemOrItems.length; j++) {
1435
+ result.push (this.extend (itemOrItems[j], params));
1436
+ }
1437
+ } else {
1438
+ result.push (this.extend (itemOrItems, params));
1439
+ }
1440
+ }
1441
+ result = this.sortBy (result, 'timestamp');
1442
+ const code = (currency !== undefined) ? currency['code'] : undefined;
1443
+ const tail = since === undefined;
1444
+ return this.filterByCurrencySinceLimit (result, code, since, limit, tail);
1445
+ }
1446
+
1447
+ safeLedgerEntry (entry, currency = undefined) {
1448
+ currency = this.safeCurrency (undefined, currency);
1449
+ let direction = this.safeString (entry, 'direction');
1450
+ let before = this.safeString (entry, 'before');
1451
+ let after = this.safeString (entry, 'after');
1452
+ let amount = this.safeString (entry, 'amount');
1453
+ let fee = this.safeString (entry, 'fee');
1454
+ if (amount !== undefined && fee !== undefined) {
1455
+ if (before === undefined && after !== undefined) {
1456
+ const amountAndFee = Precise.stringAdd (amount, fee);
1457
+ before = Precise.stringSub (after, amountAndFee);
1458
+ } else if (before !== undefined && after === undefined) {
1459
+ const amountAndFee = Precise.stringAdd (amount, fee);
1460
+ after = Precise.stringAdd (before, amountAndFee);
1461
+ }
1462
+ }
1463
+ if (before !== undefined && after !== undefined) {
1464
+ if (direction === undefined) {
1465
+ if (Precise.stringGt (before, after)) {
1466
+ direction = 'out';
1467
+ }
1468
+ if (Precise.stringGt (after, before)) {
1469
+ direction = 'in';
1470
+ }
1471
+ }
1472
+ if (amount === undefined && fee !== undefined) {
1473
+ const betweenAfterBefore = Precise.stringSub (after, before);
1474
+ amount = Precise.stringSub (betweenAfterBefore, fee);
1475
+ }
1476
+ if (amount !== undefined && fee === undefined) {
1477
+ const betweenAfterBefore = Precise.stringSub (after, before);
1478
+ fee = Precise.stringSub (betweenAfterBefore, amount);
1479
+ }
1480
+ }
1481
+ return this.extend ({
1482
+ 'id': undefined,
1483
+ 'timestamp': undefined,
1484
+ 'datetime': undefined,
1485
+ 'direction': undefined,
1486
+ 'account': undefined,
1487
+ 'referenceId': undefined,
1488
+ 'referenceAccount': undefined,
1489
+ 'type': undefined,
1490
+ 'currency': currency['code'],
1491
+ 'amount': amount,
1492
+ 'before': before,
1493
+ 'after': after,
1494
+ 'status': undefined,
1495
+ 'fee': fee,
1496
+ 'info': undefined,
1497
+ }, entry);
1498
+ }
1499
+
1500
+ parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) {
1501
+ //
1502
+ // the value of orders is either a dict or a list
1503
+ //
1504
+ // dict
1505
+ //
1506
+ // {
1507
+ // 'id1': { ... },
1508
+ // 'id2': { ... },
1509
+ // 'id3': { ... },
1510
+ // ...
1511
+ // }
1512
+ //
1513
+ // list
1514
+ //
1515
+ // [
1516
+ // { 'id': 'id1', ... },
1517
+ // { 'id': 'id2', ... },
1518
+ // { 'id': 'id3', ... },
1519
+ // ...
1520
+ // ]
1521
+ //
1522
+ let result = Array.isArray (orders) ?
1523
+ Object.values (orders).map ((order) => this.extend (this.parseOrder (order, market), params)) :
1524
+ Object.entries (orders).map (([ id, order ]) => this.extend (this.parseOrder (this.extend ({ 'id': id }, order), market), params))
1525
+ result = sortBy (result, 'timestamp')
1526
+ const symbol = (market !== undefined) ? market['symbol'] : undefined
1527
+ const tail = since === undefined
1528
+ return this.filterBySymbolSinceLimit (result, symbol, since, limit, tail)
1529
+ }
1530
+
1531
+ safeCurrency (currencyId, currency = undefined) {
1532
+ if ((currencyId === undefined) && (currency !== undefined)) {
1533
+ return currency
1534
+ }
1535
+ if ((this.currencies_by_id !== undefined) && (currencyId in this.currencies_by_id)) {
1536
+ return this.currencies_by_id[currencyId]
1537
+ }
1538
+ return {
1539
+ 'id': currencyId,
1540
+ 'code': (currencyId === undefined) ? currencyId : this.commonCurrencyCode (currencyId.toUpperCase ()),
1541
+ }
1542
+ }
1543
+
1544
+ safeCurrencyCode (currencyId, currency = undefined) {
1545
+ currency = this.safeCurrency (currencyId, currency)
1546
+ return currency['code']
1547
+ }
1548
+
1549
+ safeMarket (marketId, market = undefined, delimiter = undefined) {
1550
+ if (marketId !== undefined) {
1551
+ if (this.markets_by_id !== undefined && marketId in this.markets_by_id) {
1552
+ market = this.markets_by_id[marketId]
1553
+ } else if (delimiter !== undefined) {
1554
+ const parts = marketId.split (delimiter)
1555
+ if (parts.length === 2) {
1556
+ const baseId = this.safeString (parts, 0);
1557
+ const quoteId = this.safeString (parts, 1);
1558
+ const base = this.safeCurrencyCode (baseId)
1559
+ const quote = this.safeCurrencyCode (quoteId)
1560
+ const symbol = base + '/' + quote
1561
+ return {
1562
+ 'id': marketId,
1563
+ 'symbol': symbol,
1564
+ 'base': base,
1565
+ 'quote': quote,
1566
+ 'baseId': baseId,
1567
+ 'quoteId': quoteId,
1568
+ }
1569
+ } else {
1570
+ return {
1571
+ 'id': marketId,
1572
+ 'symbol': marketId,
1573
+ 'base': undefined,
1574
+ 'quote': undefined,
1575
+ 'baseId': undefined,
1576
+ 'quoteId': undefined,
1577
+ }
1578
+ }
1579
+ }
1580
+ }
1581
+ if (market !== undefined) {
1582
+ return market
1583
+ }
1584
+ return {
1585
+ 'id': marketId,
1586
+ 'symbol': marketId,
1587
+ 'base': undefined,
1588
+ 'quote': undefined,
1589
+ 'baseId': undefined,
1590
+ 'quoteId': undefined,
1591
+ }
1592
+ }
1593
+
1594
+ safeSymbol (marketId, market = undefined, delimiter = undefined) {
1595
+ market = this.safeMarket (marketId, market, delimiter)
1596
+ return market['symbol'];
1597
+ }
1598
+
1599
+ filterBySymbol (array, symbol = undefined) {
1600
+ return ((symbol !== undefined) ? array.filter ((entry) => entry.symbol === symbol) : array)
1601
+ }
1602
+
1603
+ parseFundingRate (contract, market = undefined) {
1604
+ throw new NotSupported (this.id + ' parseFundingRate() not supported yet')
1605
+ }
1606
+
1607
+ parseFundingRates (response, market = undefined) {
1608
+ const result = {};
1609
+ for (let i = 0; i < response.length; i++) {
1610
+ const parsed = this.parseFundingRate (response[i], market);
1611
+ result[parsed['symbol']] = parsed;
1612
+ }
1613
+ return result;
1614
+ }
1615
+
1616
+ parseOHLCV (ohlcv, market = undefined) {
1617
+ return Array.isArray (ohlcv) ? ohlcv.slice (0, 6) : ohlcv
1618
+ }
1619
+
1620
+ parseOHLCVs (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
1621
+ // this code is commented out temporarily to catch for exchange-specific errors
1622
+ // if (!this.isArray (ohlcvs)) {
1623
+ // throw new ExchangeError (this.id + ' parseOHLCVs() expected an array in the ohlcvs argument, but got ' + typeof ohlcvs);
1624
+ // }
1625
+ const parsed = ohlcvs.map ((ohlcv) => this.parseOHLCV (ohlcv, market))
1626
+ const sorted = this.sortBy (parsed, 0)
1627
+ const tail = since === undefined
1628
+ return this.filterBySinceLimit (sorted, since, limit, 0, tail)
1629
+ }
1630
+
1631
+ editLimitBuyOrder (id, symbol, ...args) {
1632
+ return this.editLimitOrder (id, symbol, 'buy', ...args)
1633
+ }
1634
+
1635
+ editLimitSellOrder (id, symbol, ...args) {
1636
+ return this.editLimitOrder (id, symbol, 'sell', ...args)
1637
+ }
1638
+
1639
+ editLimitOrder (id, symbol, ...args) {
1640
+ return this.editOrder (id, symbol, 'limit', ...args)
1641
+ }
1642
+
1643
+ async editOrder (id, symbol, ...args) {
1644
+ if (!this.enableRateLimit) {
1645
+ throw new ExchangeError (this.id + ' editOrder() requires enableRateLimit = true')
1646
+ }
1647
+ await this.cancelOrder (id, symbol);
1648
+ return this.createOrder (symbol, ...args)
1649
+ }
1650
+
1651
+ createLimitOrder (symbol, side, amount, price, params = {}) {
1652
+ return this.createOrder (symbol, 'limit', side, amount, price, params)
1653
+ }
1654
+
1655
+ createMarketOrder (symbol, side, amount, price, params = {}) {
1656
+ return this.createOrder (symbol, 'market', side, amount, price, params)
1657
+ }
1658
+
1659
+ createLimitBuyOrder (symbol, amount, price, params = {}) {
1660
+ return this.createOrder (symbol, 'limit', 'buy', amount, price, params)
1661
+ }
1662
+
1663
+ createLimitSellOrder (symbol, amount, price, params = {}) {
1664
+ return this.createOrder (symbol, 'limit', 'sell', amount, price, params)
1665
+ }
1666
+
1667
+ createMarketBuyOrder (symbol, amount, params = {}) {
1668
+ return this.createOrder (symbol, 'market', 'buy', amount, undefined, params)
1669
+ }
1670
+
1671
+ createMarketSellOrder (symbol, amount, params = {}) {
1672
+ return this.createOrder (symbol, 'market', 'sell', amount, undefined, params)
1673
+ }
1674
+
1675
+ costToPrecision (symbol, cost) {
1676
+ const market = this.market (symbol)
1677
+ return decimalToPrecision (cost, TRUNCATE, market.precision.price, this.precisionMode, this.paddingMode)
1678
+ }
1679
+
1680
+ priceToPrecision (symbol, price) {
1681
+ const market = this.market (symbol)
1682
+ return decimalToPrecision (price, ROUND, market.precision.price, this.precisionMode, this.paddingMode)
1683
+ }
1684
+
1685
+ amountToPrecision (symbol, amount) {
1686
+ const market = this.market (symbol)
1687
+ return decimalToPrecision (amount, TRUNCATE, market.precision.amount, this.precisionMode, this.paddingMode)
1688
+ }
1689
+
1690
+ feeToPrecision (symbol, fee) {
1691
+ const market = this.market (symbol)
1692
+ return decimalToPrecision (fee, ROUND, market.precision.price, this.precisionMode, this.paddingMode)
1693
+ }
1694
+
1695
+ currencyToPrecision (code, fee) {
1696
+ return decimalToPrecision (fee, ROUND, this.currencies[code]['precision'], this.precisionMode, this.paddingMode);
1697
+ }
1698
+
1699
+ calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) {
1700
+ const market = this.markets[symbol];
1701
+ const feeSide = this.safeString (market, 'feeSide', 'quote');
1702
+ let key = 'quote';
1703
+ let cost = undefined;
1704
+ if (feeSide === 'quote') {
1705
+ // the fee is always in quote currency
1706
+ cost = amount * price;
1707
+ } else if (feeSide === 'base') {
1708
+ // the fee is always in base currency
1709
+ cost = amount;
1710
+ } else if (feeSide === 'get') {
1711
+ // the fee is always in the currency you get
1712
+ cost = amount;
1713
+ if (side === 'sell') {
1714
+ cost *= price;
1715
+ } else {
1716
+ key = 'base';
1717
+ }
1718
+ } else if (feeSide === 'give') {
1719
+ // the fee is always in the currency you give
1720
+ cost = amount;
1721
+ if (side === 'buy') {
1722
+ cost *= price;
1723
+ } else {
1724
+ key = 'base';
1725
+ }
1726
+ }
1727
+ const rate = market[takerOrMaker];
1728
+ if (cost !== undefined) {
1729
+ cost *= rate;
1730
+ }
1731
+ return {
1732
+ 'type': takerOrMaker,
1733
+ 'currency': market[key],
1734
+ 'rate': rate,
1735
+ 'cost': cost,
1736
+ };
1737
+ }
1738
+
1739
+ checkRequiredDependencies () {
1740
+ return
1741
+ }
1742
+
1743
+ remove0xPrefix (hexData) {
1744
+ if (hexData.slice (0, 2) === '0x') {
1745
+ return hexData.slice (2)
1746
+ } else {
1747
+ return hexData
1748
+ }
1749
+ }
1750
+
1751
+ hashMessage (message) {
1752
+ // takes a hex encoded message
1753
+ const binaryMessage = this.base16ToBinary (this.remove0xPrefix (message))
1754
+ const prefix = this.stringToBinary ('\x19Ethereum Signed Message:\n' + binaryMessage.sigBytes)
1755
+ return '0x' + this.hash (this.binaryConcat (prefix, binaryMessage), 'keccak', 'hex')
1756
+ }
1757
+
1758
+ signHash (hash, privateKey) {
1759
+ const signature = this.ecdsa (hash.slice (-64), privateKey.slice (-64), 'secp256k1', undefined)
1760
+ return {
1761
+ 'r': '0x' + signature['r'],
1762
+ 's': '0x' + signature['s'],
1763
+ 'v': 27 + signature['v'],
1764
+ }
1765
+ }
1766
+
1767
+ signMessage (message, privateKey) {
1768
+ return this.signHash (this.hashMessage (message), privateKey.slice (-64))
1769
+ }
1770
+
1771
+ signMessageString (message, privateKey) {
1772
+ // still takes the input as a hex string
1773
+ // same as above but returns a string instead of an object
1774
+ const signature = this.signMessage (message, privateKey)
1775
+ return signature['r'] + this.remove0xPrefix (signature['s']) + this.binaryToBase16 (this.numberToBE (signature['v']))
1776
+ }
1777
+
1778
+ oath () {
1779
+ if (typeof this.twofa !== 'undefined') {
1780
+ return this.totp (this.twofa)
1781
+ } else {
1782
+ throw new ExchangeError (this.id + ' this.twofa has not been set')
1783
+ }
1784
+ }
1785
+
1786
+ getNetwork (network, code) {
1787
+ network = network.toUpperCase ();
1788
+ const aliases = {
1789
+ 'ETHEREUM': 'ETH',
1790
+ 'ETHER': 'ETH',
1791
+ 'ERC20': 'ETH',
1792
+ 'ETH': 'ETH',
1793
+ 'TRC20': 'TRX',
1794
+ 'TRON': 'TRX',
1795
+ 'TRX': 'TRX',
1796
+ 'BEP20': 'BSC',
1797
+ 'BSC': 'BSC',
1798
+ 'HRC20': 'HT',
1799
+ 'HECO': 'HT',
1800
+ 'SPL': 'SOL',
1801
+ 'SOL': 'SOL',
1802
+ 'TERRA': 'LUNA',
1803
+ 'LUNA': 'LUNA',
1804
+ 'POLYGON': 'MATIC',
1805
+ 'MATIC': 'MATIC',
1806
+ 'EOS': 'EOS',
1807
+ 'WAVES': 'WAVES',
1808
+ 'AVALANCHE': 'AVAX',
1809
+ 'AVAX': 'AVAX',
1810
+ 'QTUM': 'QTUM',
1811
+ 'CHZ': 'CHZ',
1812
+ 'NEO': 'NEO',
1813
+ 'ONT': 'ONT',
1814
+ 'RON': 'RON',
1815
+ };
1816
+ if (network === code) {
1817
+ return network;
1818
+ } else if (network in aliases) {
1819
+ return aliases[network];
1820
+ } else {
1821
+ throw new NotSupported (this.id + ' network ' + network + ' is not yet supported');
1822
+ }
1823
+ }
1824
+
1825
+ reduceFeesByCurrency (fees, string = false) {
1826
+ //
1827
+ // this function takes a list of fee structures having the following format
1828
+ //
1829
+ // string = true
1830
+ //
1831
+ // [
1832
+ // { 'currency': 'BTC', 'cost': '0.1' },
1833
+ // { 'currency': 'BTC', 'cost': '0.2' },
1834
+ // { 'currency': 'BTC', 'cost': '0.2', 'rate': '0.00123' },
1835
+ // { 'currency': 'BTC', 'cost': '0.4', 'rate': '0.00123' },
1836
+ // { 'currency': 'BTC', 'cost': '0.5', 'rate': '0.00456' },
1837
+ // { 'currency': 'USDT', 'cost': '12.3456' },
1838
+ // ]
1839
+ //
1840
+ // string = false
1841
+ //
1842
+ // [
1843
+ // { 'currency': 'BTC', 'cost': 0.1 },
1844
+ // { 'currency': 'BTC', 'cost': 0.2 },
1845
+ // { 'currency': 'BTC', 'cost': 0.2, 'rate': 0.00123 },
1846
+ // { 'currency': 'BTC', 'cost': 0.4, 'rate': 0.00123 },
1847
+ // { 'currency': 'BTC', 'cost': 0.5, 'rate': 0.00456 },
1848
+ // { 'currency': 'USDT', 'cost': 12.3456 },
1849
+ // ]
1850
+ //
1851
+ // and returns a reduced fee list, where fees are summed per currency and rate (if any)
1852
+ //
1853
+ // string = true
1854
+ //
1855
+ // [
1856
+ // { 'currency': 'BTC', 'cost': '0.3' },
1857
+ // { 'currency': 'BTC', 'cost': '0.6', 'rate': '0.00123' },
1858
+ // { 'currency': 'BTC', 'cost': '0.5', 'rate': '0.00456' },
1859
+ // { 'currency': 'USDT', 'cost': '12.3456' },
1860
+ // ]
1861
+ //
1862
+ // string = false
1863
+ //
1864
+ // [
1865
+ // { 'currency': 'BTC', 'cost': 0.3 },
1866
+ // { 'currency': 'BTC', 'cost': 0.6, 'rate': 0.00123 },
1867
+ // { 'currency': 'BTC', 'cost': 0.5, 'rate': 0.00456 },
1868
+ // { 'currency': 'USDT', 'cost': 12.3456 },
1869
+ // ]
1870
+ //
1871
+ const reduced = {};
1872
+ for (let i = 0; i < fees.length; i++) {
1873
+ const fee = fees[i];
1874
+ const feeCurrencyCode = this.safeString (fee, 'currency');
1875
+ if (feeCurrencyCode !== undefined) {
1876
+ const rate = this.safeString (fee, 'rate');
1877
+ const cost = this.safeValue (fee, 'cost');
1878
+ if (!(feeCurrencyCode in reduced)) {
1879
+ reduced[feeCurrencyCode] = {};
1880
+ }
1881
+ const rateKey = (rate === undefined) ? '' : rate;
1882
+ if (rateKey in reduced[feeCurrencyCode]) {
1883
+ if (string) {
1884
+ reduced[feeCurrencyCode][rateKey]['cost'] = Precise.stringAdd (reduced[feeCurrencyCode][rateKey]['cost'], cost);
1885
+ } else {
1886
+ reduced[feeCurrencyCode][rateKey]['cost'] = this.sum (reduced[feeCurrencyCode][rateKey]['cost'], cost);
1887
+ }
1888
+ } else {
1889
+ reduced[feeCurrencyCode][rateKey] = {
1890
+ 'currency': feeCurrencyCode,
1891
+ 'cost': string ? cost : this.parseNumber (cost),
1892
+ };
1893
+ if (rate !== undefined) {
1894
+ reduced[feeCurrencyCode][rateKey]['rate'] = string ? rate : this.parseNumber (rate);
1895
+ }
1896
+ }
1897
+ }
1898
+ }
1899
+ let result = [];
1900
+ const feeValues = Object.values (reduced);
1901
+ for (let i = 0; i < feeValues.length; i++) {
1902
+ const reducedFeeValues = Object.values (feeValues[i]);
1903
+ result = this.arrayConcat (result, reducedFeeValues);
1904
+ }
1905
+ return result;
1906
+ }
1907
+
1908
+ safeTrade (trade, market = undefined) {
1909
+ const amount = this.safeString (trade, 'amount');
1910
+ const price = this.safeString (trade, 'price');
1911
+ let cost = this.safeString (trade, 'cost');
1912
+ if (cost === undefined) {
1913
+ // contract trading
1914
+ const contractSize = this.safeString (market, 'contractSize');
1915
+ let multiplyPrice = price;
1916
+ if (contractSize !== undefined) {
1917
+ const inverse = this.safeValue (market, 'inverse', false);
1918
+ if (inverse) {
1919
+ multiplyPrice = Precise.stringDiv ('1', price);
1920
+ }
1921
+ multiplyPrice = Precise.stringMul (multiplyPrice, contractSize);
1922
+ }
1923
+ cost = Precise.stringMul (multiplyPrice, amount);
1924
+ }
1925
+ const parseFee = this.safeValue (trade, 'fee') === undefined;
1926
+ const parseFees = this.safeValue (trade, 'fees') === undefined;
1927
+ const shouldParseFees = parseFee || parseFees;
1928
+ const fees = this.safeValue (trade, 'fees', []);
1929
+ if (shouldParseFees) {
1930
+ const tradeFees = this.safeValue (trade, 'fees');
1931
+ if (tradeFees !== undefined) {
1932
+ for (let j = 0; j < tradeFees.length; j++) {
1933
+ const tradeFee = tradeFees[j];
1934
+ fees.push (this.extend ({}, tradeFee));
1935
+ }
1936
+ } else {
1937
+ const tradeFee = this.safeValue (trade, 'fee');
1938
+ if (tradeFee !== undefined) {
1939
+ fees.push (this.extend ({}, tradeFee));
1940
+ }
1941
+ }
1942
+ }
1943
+ const fee = this.safeValue (trade, 'fee');
1944
+ if (shouldParseFees) {
1945
+ const reducedFees = this.reduceFees ? this.reduceFeesByCurrency (fees, true) : fees;
1946
+ const reducedLength = reducedFees.length;
1947
+ for (let i = 0; i < reducedLength; i++) {
1948
+ reducedFees[i]['cost'] = this.safeNumber (reducedFees[i], 'cost');
1949
+ if ('rate' in reducedFees[i]) {
1950
+ reducedFees[i]['rate'] = this.safeNumber (reducedFees[i], 'rate');
1951
+ }
1952
+ }
1953
+ if (!parseFee && (reducedLength === 0)) {
1954
+ fee['cost'] = this.safeNumber (fee, 'cost');
1955
+ if ('rate' in fee) {
1956
+ fee['rate'] = this.safeNumber (fee, 'rate');
1957
+ }
1958
+ reducedFees.push (fee);
1959
+ }
1960
+ if (parseFees) {
1961
+ trade['fees'] = reducedFees;
1962
+ }
1963
+ if (parseFee && (reducedLength === 1)) {
1964
+ trade['fee'] = reducedFees[0];
1965
+ }
1966
+ const tradeFee = this.safeValue (trade, 'fee');
1967
+ if (tradeFee !== undefined) {
1968
+ tradeFee['cost'] = this.safeNumber (tradeFee, 'cost');
1969
+ if ('rate' in tradeFee) {
1970
+ tradeFee['rate'] = this.safeNumber (tradeFee, 'rate');
1971
+ }
1972
+ trade['fee'] = tradeFee;
1973
+ }
1974
+ }
1975
+ trade['amount'] = this.parseNumber (amount);
1976
+ trade['price'] = this.parseNumber (price);
1977
+ trade['cost'] = this.parseNumber (cost);
1978
+ return trade;
1979
+ }
1980
+
1981
+ safeOrder (order, market = undefined) {
1982
+ // parses numbers as strings
1983
+ // it is important pass the trades as unparsed rawTrades
1984
+ let amount = this.omitZero (this.safeString (order, 'amount'));
1985
+ let remaining = this.safeString (order, 'remaining');
1986
+ let filled = this.safeString (order, 'filled');
1987
+ let cost = this.safeString (order, 'cost');
1988
+ let average = this.omitZero (this.safeString (order, 'average'));
1989
+ let price = this.omitZero (this.safeString (order, 'price'));
1990
+ let lastTradeTimeTimestamp = this.safeInteger (order, 'lastTradeTimestamp');
1991
+ const parseFilled = (filled === undefined);
1992
+ const parseCost = (cost === undefined);
1993
+ const parseLastTradeTimeTimestamp = (lastTradeTimeTimestamp === undefined);
1994
+ const fee = this.safeValue (order, 'fee');
1995
+ const parseFee = (fee === undefined);
1996
+ const parseFees = this.safeValue (order, 'fees') === undefined;
1997
+ const shouldParseFees = parseFee || parseFees;
1998
+ const fees = this.safeValue (order, 'fees', []);
1999
+ let trades = [];
2000
+ if (parseFilled || parseCost || shouldParseFees) {
2001
+ const rawTrades = this.safeValue (order, 'trades', trades);
2002
+ const oldNumber = this.number;
2003
+ // we parse trades as strings here!
2004
+ this.number = String;
2005
+ trades = this.parseTrades (rawTrades, market, undefined, undefined, {
2006
+ 'symbol': order['symbol'],
2007
+ 'side': order['side'],
2008
+ 'type': order['type'],
2009
+ 'order': order['id'],
2010
+ });
2011
+ this.number = oldNumber;
2012
+ if (Array.isArray (trades) && trades.length) {
2013
+ // move properties that are defined in trades up into the order
2014
+ if (order['symbol'] === undefined) {
2015
+ order['symbol'] = trades[0]['symbol'];
2016
+ }
2017
+ if (order['side'] === undefined) {
2018
+ order['side'] = trades[0]['side'];
2019
+ }
2020
+ if (order['type'] === undefined) {
2021
+ order['type'] = trades[0]['type'];
2022
+ }
2023
+ if (order['id'] === undefined) {
2024
+ order['id'] = trades[0]['order'];
2025
+ }
2026
+ if (parseFilled) {
2027
+ filled = '0';
2028
+ }
2029
+ if (parseCost) {
2030
+ cost = '0';
2031
+ }
2032
+ for (let i = 0; i < trades.length; i++) {
2033
+ const trade = trades[i];
2034
+ const tradeAmount = this.safeString (trade, 'amount');
2035
+ if (parseFilled && (tradeAmount !== undefined)) {
2036
+ filled = Precise.stringAdd (filled, tradeAmount);
2037
+ }
2038
+ const tradeCost = this.safeString (trade, 'cost');
2039
+ if (parseCost && (tradeCost !== undefined)) {
2040
+ cost = Precise.stringAdd (cost, tradeCost);
2041
+ }
2042
+ const tradeTimestamp = this.safeValue (trade, 'timestamp');
2043
+ if (parseLastTradeTimeTimestamp && (tradeTimestamp !== undefined)) {
2044
+ if (lastTradeTimeTimestamp === undefined) {
2045
+ lastTradeTimeTimestamp = tradeTimestamp;
2046
+ } else {
2047
+ lastTradeTimeTimestamp = Math.max (lastTradeTimeTimestamp, tradeTimestamp);
2048
+ }
2049
+ }
2050
+ if (shouldParseFees) {
2051
+ const tradeFees = this.safeValue (trade, 'fees');
2052
+ if (tradeFees !== undefined) {
2053
+ for (let j = 0; j < tradeFees.length; j++) {
2054
+ const tradeFee = tradeFees[j];
2055
+ fees.push (this.extend ({}, tradeFee));
2056
+ }
2057
+ } else {
2058
+ const tradeFee = this.safeValue (trade, 'fee');
2059
+ if (tradeFee !== undefined) {
2060
+ fees.push (this.extend ({}, tradeFee));
2061
+ }
2062
+ }
2063
+ }
2064
+ }
2065
+ }
2066
+ }
2067
+ if (shouldParseFees) {
2068
+ const reducedFees = this.reduceFees ? this.reduceFeesByCurrency (fees, true) : fees;
2069
+ const reducedLength = reducedFees.length;
2070
+ for (let i = 0; i < reducedLength; i++) {
2071
+ reducedFees[i]['cost'] = this.parseNumber (reducedFees[i]['cost']);
2072
+ if ('rate' in reducedFees[i]) {
2073
+ reducedFees[i]['rate'] = this.parseNumber (reducedFees['i']['rate'])
2074
+ }
2075
+ }
2076
+ if (!parseFee && (reducedLength === 0)) {
2077
+ fee['cost'] = this.safeNumber (fee, 'cost');
2078
+ if ('rate' in fee) {
2079
+ fee['rate'] = this.parseNumber (fee['rate'])
2080
+ }
2081
+ reducedFees.push (fee);
2082
+ }
2083
+ if (parseFees) {
2084
+ order['fees'] = reducedFees;
2085
+ }
2086
+ if (parseFee && (reducedLength === 1)) {
2087
+ order['fee'] = reducedFees[0];
2088
+ }
2089
+ }
2090
+ if (amount === undefined) {
2091
+ // ensure amount = filled + remaining
2092
+ if (filled !== undefined && remaining !== undefined) {
2093
+ amount = Precise.stringAdd (filled, remaining);
2094
+ } else if (this.safeString (order, 'status') === 'closed') {
2095
+ amount = filled;
2096
+ }
2097
+ }
2098
+ if (filled === undefined) {
2099
+ if (amount !== undefined && remaining !== undefined) {
2100
+ filled = Precise.stringSub (amount, remaining);
2101
+ }
2102
+ }
2103
+ if (remaining === undefined) {
2104
+ if (amount !== undefined && filled !== undefined) {
2105
+ remaining = Precise.stringSub (amount, filled);
2106
+ }
2107
+ }
2108
+ // ensure that the average field is calculated correctly
2109
+ if (average === undefined) {
2110
+ if ((filled !== undefined) && (cost !== undefined) && Precise.stringGt (filled, '0')) {
2111
+ average = Precise.stringDiv (cost, filled);
2112
+ }
2113
+ }
2114
+ // also ensure the cost field is calculated correctly
2115
+ const costPriceExists = (average !== undefined) || (price !== undefined);
2116
+ if (parseCost && (filled !== undefined) && costPriceExists) {
2117
+ let multiplyPrice = undefined;
2118
+ if (average === undefined) {
2119
+ multiplyPrice = price;
2120
+ } else {
2121
+ multiplyPrice = average;
2122
+ }
2123
+ // contract trading
2124
+ const contractSize = this.safeString (market, 'contractSize');
2125
+ if (contractSize !== undefined) {
2126
+ const inverse = this.safeValue (market, 'inverse', false);
2127
+ if (inverse) {
2128
+ multiplyPrice = Precise.stringDiv ('1', multiplyPrice);
2129
+ }
2130
+ multiplyPrice = Precise.stringMul (multiplyPrice, contractSize);
2131
+ }
2132
+ cost = Precise.stringMul (multiplyPrice, filled);
2133
+ }
2134
+ // support for market orders
2135
+ const orderType = this.safeValue (order, 'type');
2136
+ const emptyPrice = (price === "market_price") || (price === undefined) || Precise.stringEquals (price, '0');
2137
+ if (emptyPrice && (orderType === 'market')) {
2138
+ price = average;
2139
+ }
2140
+ // we have trades with string values at this point so we will mutate them
2141
+ for (let i = 0; i < trades.length; i++) {
2142
+ const entry = trades[i];
2143
+ entry['amount'] = this.safeNumber (entry, 'amount');
2144
+ entry['price'] = this.safeNumber (entry, 'price');
2145
+ entry['cost'] = this.safeNumber (entry, 'cost');
2146
+ const fee = this.safeValue (entry, 'fee', {});
2147
+ fee['cost'] = this.safeNumber (fee, 'cost');
2148
+ if ('rate' in fee) {
2149
+ fee['rate'] = this.safeNumber (fee, 'rate');
2150
+ }
2151
+ entry['fee'] = fee;
2152
+ }
2153
+ // timeInForceHandling
2154
+ let timeInForce = this.safeString (order, 'timeInForce');
2155
+ if (this.safeValue (order, 'postOnly', false)) {
2156
+ timeInForce = 'PO';
2157
+ } else if (this.safeString (order, 'type') === 'market') {
2158
+ timeInForce = 'IOC';
2159
+ }
2160
+ return this.extend (order, {
2161
+ 'lastTradeTimestamp': lastTradeTimeTimestamp,
2162
+ 'price': this.parseNumber (price),
2163
+ 'amount': this.parseNumber (amount),
2164
+ 'cost': this.parseNumber (cost),
2165
+ 'average': this.parseNumber (average),
2166
+ 'filled': this.parseNumber (filled),
2167
+ 'remaining': this.parseNumber (remaining),
2168
+ 'timeInForce': timeInForce,
2169
+ 'trades': trades,
2170
+ });
2171
+ }
2172
+
2173
+ parseNumber (value, d = undefined) {
2174
+ if (value === undefined) {
2175
+ return d
2176
+ } else {
2177
+ try {
2178
+ return this.number (value)
2179
+ } catch (e) {
2180
+ return d
2181
+ }
2182
+ }
2183
+ }
2184
+
2185
+ safeNumber (object, key, d = undefined) {
2186
+ const value = this.safeString (object, key)
2187
+ return this.parseNumber (value, d)
2188
+ }
2189
+
2190
+ safeNumber2 (object, key1, key2, d = undefined) {
2191
+ const value = this.safeString2 (object, key1, key2)
2192
+ return this.parseNumber (value, d)
2193
+ }
2194
+
2195
+ parsePrecision (precision) {
2196
+ if (precision === undefined) {
2197
+ return undefined
2198
+ }
2199
+ return '1e' + Precise.stringNeg (precision)
2200
+ }
2201
+
2202
+ handleWithdrawTagAndParams (tag, params) {
2203
+ if (typeof tag === 'object') {
2204
+ params = this.extend (tag, params)
2205
+ tag = undefined
2206
+ }
2207
+ if (tag === undefined) {
2208
+ tag = this.safeString (params, 'tag')
2209
+ if (tag !== undefined) {
2210
+ params = this.omit (params, 'tag');
2211
+ }
2212
+ }
2213
+ return [ tag, params ]
2214
+ }
2215
+
2216
+ getSupportedMapping (key, mapping = {}) {
2217
+ // Takes a key and a dictionary, and returns the dictionary's value for that key
2218
+ // :throws:
2219
+ // NotSupported if the dictionary does not contain the key
2220
+ if (key in mapping) {
2221
+ return mapping[key]
2222
+ } else {
2223
+ throw new NotSupported (this.id + ' ' + key + ' does not have a value in mapping')
2224
+ }
2225
+ }
2226
+
2227
+ async fetchBorrowRate (code, params = {}) {
2228
+ await this.loadMarkets ();
2229
+ if (!this.has['fetchBorrowRates']) {
2230
+ throw new NotSupported (this.id + ' fetchBorrowRate() is not supported yet')
2231
+ }
2232
+ const borrowRates = await this.fetchBorrowRates (params);
2233
+ const rate = this.safeValue (borrowRates, code);
2234
+ if (rate === undefined) {
2235
+ throw new ExchangeError (this.id + ' fetchBorrowRate() could not find the borrow rate for currency code ' + code);
2236
+ }
2237
+ return rate;
2238
+ }
2239
+
2240
+ handleMarketTypeAndParams (methodName, market = undefined, params = {}) {
2241
+ const defaultType = this.safeString2 (this.options, 'defaultType', 'type', 'spot');
2242
+ const methodOptions = this.safeValue (this.options, methodName);
2243
+ let methodType = defaultType;
2244
+ if (methodOptions !== undefined) {
2245
+ if (typeof methodOptions === 'string') {
2246
+ methodType = methodOptions;
2247
+ } else {
2248
+ methodType = this.safeString2 (methodOptions, 'defaultType', 'type', methodType);
2249
+ }
2250
+ }
2251
+ const marketType = (market === undefined) ? methodType : market['type'];
2252
+ const type = this.safeString2 (params, 'defaultType', 'type', marketType);
2253
+ params = this.omit (params, [ 'defaultType', 'type' ]);
2254
+ return [ type, params ];
2255
+ }
2256
+
2257
+ async loadTimeDifference (params = {}) {
2258
+ const serverTime = await this.fetchTime (params);
2259
+ const after = this.milliseconds ();
2260
+ this.options['timeDifference'] = after - serverTime;
2261
+ return this.options['timeDifference'];
2262
+ }
2263
+
2264
+ parseLeverageTiers (response, symbols, marketIdKey) {
2265
+ const tiers = {};
2266
+ for (let i = 0; i < response.length; i++) {
2267
+ const item = response[i];
2268
+ const id = this.safeString (item, marketIdKey);
2269
+ const market = this.safeMarket (id);
2270
+ const symbol = market['symbol'];
2271
+ let symbolsLength = 0;
2272
+ if (symbols !== undefined) {
2273
+ symbolsLength = symbols.length;
2274
+ }
2275
+ const contract = this.safeValue (market, 'contract', false);
2276
+ if (contract && (symbolsLength === 0 || symbols.includes (symbol))) {
2277
+ tiers[symbol] = this.parseMarketLeverageTiers (item, market);
2278
+ }
2279
+ }
2280
+ return tiers;
2281
+ }
2282
+
2283
+ async fetchMarketLeverageTiers (symbol, params = {}) {
2284
+ if (this.has['fetchLeverageTiers']) {
2285
+ const market = await this.market (symbol);
2286
+ if (!market['contract']) {
2287
+ throw new BadSymbol (this.id + ' fetchLeverageTiers() supports contract markets only');
2288
+ }
2289
+ const tiers = await this.fetchLeverageTiers ([ symbol ]);
2290
+ return this.safeValue (tiers, symbol);
2291
+ } else {
2292
+ throw new NotSupported (this.id + ' fetchMarketLeverageTiers() is not supported yet');
2293
+ }
2294
+
2295
+ }
2296
+
2297
+ isPostOnly (type, timeInForce, exchangeSpecificOption, params = {}) {
2298
+ /**
2299
+ * @param {string} type: Order type
2300
+ * @param {string} timeInForce
2301
+ * @param {boolean} exchangeSpecificOption: True if the exchange specific post only setting is set
2302
+ * @param {dict} params: Exchange specific params
2303
+ * @returns {boolean}: true if a post only order, false otherwise
2304
+ */
2305
+ let postOnly = this.safeValue2 (params, 'postOnly', 'post_only', false);
2306
+ params = this.omit (params, [ 'post_only', 'postOnly' ]);
2307
+ const timeInForceUpper = timeInForce.toUpperCase ();
2308
+ const typeLower = type.toLowerCase ();
2309
+ const ioc = timeInForceUpper === 'IOC';
2310
+ const fok = timeInForceUpper === 'FOK';
2311
+ const timeInForcePostOnly = timeInForceUpper === 'PO';
2312
+ const isMarket = typeLower === 'market';
2313
+ postOnly = postOnly || (typeLower === 'postonly') || timeInForcePostOnly || exchangeSpecificOption;
2314
+ if (postOnly) {
2315
+ if (ioc || fok) {
2316
+ throw new InvalidOrder (this.id + ' postOnly orders cannot have timeInForce equal to ' + timeInForce);
2317
+ } else if (isMarket) {
2318
+ throw new InvalidOrder (this.id + ' postOnly orders cannot have type ' + type);
2319
+ } else {
2320
+ timeInForce = timeInForcePostOnly ? undefined : timeInForce;
2321
+ return [ 'limit', true, timeInForce, params ];
2322
+ }
2323
+ } else {
2324
+ return [ type, false, timeInForce, params ];
2325
+ }
2326
+ }
2327
+
2328
+ async createPostOnlyOrder (symbol, type, side, amount, price, params = {}) {
2329
+ if (!this.has['createPostOnlyOrder']) {
2330
+ throw new NotSupported (this.id + 'createPostOnlyOrder() is not supported yet');
2331
+ }
2332
+ const query = this.extend (params, { 'postOnly': true });
2333
+ return await this.createOrder (symbol, type, side, amount, price, query);
2334
+ }
2335
+
2336
+ async createStopOrder (symbol, type, side, amount, price = undefined, stopPrice = undefined, params = {}) {
2337
+ if (!this.has['createStopOrder']) {
2338
+ throw new NotSupported (this.id + ' createStopOrder() is not supported yet');
2339
+ }
2340
+ if (stopPrice === undefined) {
2341
+ throw new ArgumentsRequired(this.id + ' create_stop_order() requires a stopPrice argument');
2342
+ }
2343
+ const query = this.extend (params, { 'stopPrice': stopPrice });
2344
+ return await this.createOrder (symbol, type, side, amount, price, query);
2345
+ }
2346
+
2347
+ async createStopLimitOrder(symbol, side, amount, price, stopPrice, params = {}) {
2348
+ if (!this.has['createStopLimitOrder']) {
2349
+ throw new NotSupported(this.id + ' createStopLimitOrder() is not supported yet');
2350
+ }
2351
+ const query = this.extend(params, {'stopPrice': stopPrice});
2352
+ return this.createOrder(symbol, 'limit', side, amount, price, query);
2353
+ }
2354
+
2355
+ async createStopMarketOrder(symbol, side, amount, stopPrice, params = {}) {
2356
+ if (!this.has['createStopMarketOrder']) {
2357
+ throw new NotSupported(this.id + ' createStopMarketOrder() is not supported yet');
2358
+ }
2359
+ const query = this.extend(params, {'stopPrice': stopPrice});
2360
+ return this.createOrder(symbol, 'market', side, amount, undefined, query);
2361
+ }
2362
+
2363
+ parseBorrowInterests (response, market = undefined) {
2364
+ const interest = [];
2365
+ for (let i = 0; i < response.length; i++) {
2366
+ const row = response[i];
2367
+ interest.push (this.parseBorrowInterest (row, market));
2368
+ }
2369
+ return interest;
2370
+ }
2371
+ }