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.
- package/.cache/eslintcache +1 -0
- package/.dockerignore +6 -0
- package/.eslintignore +1 -0
- package/.gitattributes +5 -0
- package/.readthedocs.yaml +16 -0
- package/CONTRIBUTING.md +1049 -0
- package/LICENSE.txt +21 -0
- package/README.md +537 -0
- package/SECURITY.md +5 -0
- package/build/cleanup-old-tags.js +94 -0
- package/build/countries.js +256 -0
- package/build/export-exchanges.js +520 -0
- package/build/fs.js +51 -0
- package/build/transpile.js +1772 -0
- package/build/vss.js +78 -0
- package/ccxt.browser.js +7 -0
- package/ccxt.d.ts +692 -0
- package/ccxt.js +171 -0
- package/cleanup.sh +2 -0
- package/composer-install.sh +20 -0
- package/dist/ccxt.browser.js +208383 -0
- package/gource.sh +3 -0
- package/index.html +7 -0
- package/js/.eslintrc +87 -0
- package/js/aax.js +2686 -0
- package/js/ascendex.js +2584 -0
- package/js/base/.eslintrc.js +43 -0
- package/js/base/Exchange.js +2371 -0
- package/js/base/Precise.js +283 -0
- package/js/base/errorHierarchy.js +47 -0
- package/js/base/errors.js +55 -0
- package/js/base/functions/crypto.js +158 -0
- package/js/base/functions/encode.js +118 -0
- package/js/base/functions/generic.js +270 -0
- package/js/base/functions/misc.js +138 -0
- package/js/base/functions/number.js +329 -0
- package/js/base/functions/platform.js +38 -0
- package/js/base/functions/string.js +21 -0
- package/js/base/functions/throttle.js +79 -0
- package/js/base/functions/time.js +210 -0
- package/js/base/functions/type.js +66 -0
- package/js/base/functions.js +28 -0
- package/js/bequant.js +32 -0
- package/js/bibox.js +1407 -0
- package/js/bigone.js +1366 -0
- package/js/binance.js +5652 -0
- package/js/binancecoinm.js +46 -0
- package/js/binanceus.js +46 -0
- package/js/binanceusdm.js +49 -0
- package/js/bit2c.js +535 -0
- package/js/bitbank.js +842 -0
- package/js/bitbay.js +16 -0
- package/js/bitbns.js +1073 -0
- package/js/bitcoincom.js +15 -0
- package/js/bitfinex.js +1433 -0
- package/js/bitfinex2.js +2025 -0
- package/js/bitflyer.js +840 -0
- package/js/bitforex.js +614 -0
- package/js/bitget.js +2397 -0
- package/js/bithumb.js +980 -0
- package/js/bitmart.js +2516 -0
- package/js/bitmex.js +1809 -0
- package/js/bitopro.js +1443 -0
- package/js/bitpanda.js +1782 -0
- package/js/bitrue.js +1747 -0
- package/js/bitso.js +1062 -0
- package/js/bitstamp.js +1757 -0
- package/js/bitstamp1.js +343 -0
- package/js/bittrex.js +1876 -0
- package/js/bitvavo.js +1579 -0
- package/js/bkex.js +1233 -0
- package/js/bl3p.js +346 -0
- package/js/blockchaincom.js +969 -0
- package/js/btcalpha.js +680 -0
- package/js/btcbox.js +477 -0
- package/js/btcmarkets.js +1022 -0
- package/js/btctradeua.js +466 -0
- package/js/btcturk.js +734 -0
- package/js/buda.js +946 -0
- package/js/bw.js +1265 -0
- package/js/bybit.js +3372 -0
- package/js/bytetrade.js +1336 -0
- package/js/cdax.js +1646 -0
- package/js/cex.js +1410 -0
- package/js/coinbase.js +1342 -0
- package/js/coinbaseprime.js +31 -0
- package/js/coinbasepro.js +1466 -0
- package/js/coincheck.js +755 -0
- package/js/coinex.js +3400 -0
- package/js/coinfalcon.js +880 -0
- package/js/coinmate.js +794 -0
- package/js/coinone.js +816 -0
- package/js/coinspot.js +345 -0
- package/js/crex24.js +1636 -0
- package/js/cryptocom.js +1832 -0
- package/js/currencycom.js +1748 -0
- package/js/delta.js +1547 -0
- package/js/deribit.js +2148 -0
- package/js/digifinex.js +1585 -0
- package/js/eqonex.js +1660 -0
- package/js/exmo.js +1670 -0
- package/js/fairdesk.js +1231 -0
- package/js/flowbtc.js +35 -0
- package/js/fmfwio.js +34 -0
- package/js/ftx.js +2751 -0
- package/js/ftxus.js +38 -0
- package/js/gateio.js +4174 -0
- package/js/gemini.js +1397 -0
- package/js/hitbtc.js +1343 -0
- package/js/hitbtc3.js +2329 -0
- package/js/hollaex.js +1486 -0
- package/js/huobi.js +5706 -0
- package/js/huobijp.js +1710 -0
- package/js/huobipro.js +18 -0
- package/js/idex.js +1439 -0
- package/js/independentreserve.js +649 -0
- package/js/indodax.js +742 -0
- package/js/itbit.js +722 -0
- package/js/kraken.js +2179 -0
- package/js/kucoin.js +2571 -0
- package/js/kucoinfutures.js +1771 -0
- package/js/kuna.js +809 -0
- package/js/latoken.js +1445 -0
- package/js/lbank.js +760 -0
- package/js/liquid.js +1432 -0
- package/js/luno.js +873 -0
- package/js/lykke.js +1147 -0
- package/js/mercado.js +771 -0
- package/js/mexc.js +3151 -0
- package/js/ndax.js +2233 -0
- package/js/novadax.js +1318 -0
- package/js/oceanex.js +816 -0
- package/js/okcoin.js +3841 -0
- package/js/okex.js +16 -0
- package/js/okex5.js +16 -0
- package/js/okx.js +4795 -0
- package/js/paymium.js +498 -0
- package/js/phemex.js +2957 -0
- package/js/poloniex.js +1674 -0
- package/js/probit.js +1346 -0
- package/js/qtrade.js +1588 -0
- package/js/ripio.js +1061 -0
- package/js/static_dependencies/BN/bn.js +3526 -0
- package/js/static_dependencies/README.md +1 -0
- package/js/static_dependencies/crypto-js/crypto-js.js +5988 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/base.js +375 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js +433 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/index.js +8 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js +180 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/short.js +938 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curves.js +204 -0
- package/js/static_dependencies/elliptic/lib/elliptic/ec/index.js +240 -0
- package/js/static_dependencies/elliptic/lib/elliptic/ec/key.js +119 -0
- package/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js +24 -0
- package/js/static_dependencies/elliptic/lib/elliptic/eddsa/index.js +145 -0
- package/js/static_dependencies/elliptic/lib/elliptic/eddsa/key.js +100 -0
- package/js/static_dependencies/elliptic/lib/elliptic/eddsa/signature.js +65 -0
- package/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js +780 -0
- package/js/static_dependencies/elliptic/lib/elliptic/utils.js +214 -0
- package/js/static_dependencies/elliptic/lib/elliptic.js +22 -0
- package/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js +114 -0
- package/js/static_dependencies/fetch-ponyfill/fetch-node.js +39 -0
- package/js/static_dependencies/node-fetch/index.js +1564 -0
- package/js/static_dependencies/node-rsa/NodeRSA.js +223 -0
- package/js/static_dependencies/node-rsa/asn1/ber/errors.js +13 -0
- package/js/static_dependencies/node-rsa/asn1/ber/index.js +21 -0
- package/js/static_dependencies/node-rsa/asn1/ber/reader.js +262 -0
- package/js/static_dependencies/node-rsa/asn1/ber/types.js +36 -0
- package/js/static_dependencies/node-rsa/asn1/index.js +17 -0
- package/js/static_dependencies/node-rsa/encryptEngines/js.js +34 -0
- package/js/static_dependencies/node-rsa/formats/components.js +71 -0
- package/js/static_dependencies/node-rsa/formats/formats.js +31 -0
- package/js/static_dependencies/node-rsa/formats/pkcs1.js +148 -0
- package/js/static_dependencies/node-rsa/formats/pkcs8.js +187 -0
- package/js/static_dependencies/node-rsa/libs/jsbn.js +1252 -0
- package/js/static_dependencies/node-rsa/libs/rsa.js +147 -0
- package/js/static_dependencies/node-rsa/schemes/pkcs1.js +176 -0
- package/js/static_dependencies/node-rsa/schemes/schemes.js +21 -0
- package/js/static_dependencies/node-rsa/utils.js +98 -0
- package/js/static_dependencies/qs/formats.js +18 -0
- package/js/static_dependencies/qs/index.js +11 -0
- package/js/static_dependencies/qs/parse.js +242 -0
- package/js/static_dependencies/qs/stringify.js +269 -0
- package/js/static_dependencies/qs/utils.js +230 -0
- package/js/stex.js +1925 -0
- package/js/test/.eslintrc.js +42 -0
- package/js/test/Exchange/test.balance.js +61 -0
- package/js/test/Exchange/test.borrowRate.js +32 -0
- package/js/test/Exchange/test.currency.js +52 -0
- package/js/test/Exchange/test.fetchBalance.js +23 -0
- package/js/test/Exchange/test.fetchBorrowInterest.js +59 -0
- package/js/test/Exchange/test.fetchBorrowRate.js +32 -0
- package/js/test/Exchange/test.fetchBorrowRates.js +28 -0
- package/js/test/Exchange/test.fetchClosedOrders.js +32 -0
- package/js/test/Exchange/test.fetchCurrencies.js +35 -0
- package/js/test/Exchange/test.fetchDeposits.js +31 -0
- package/js/test/Exchange/test.fetchFundingFees.js +19 -0
- package/js/test/Exchange/test.fetchFundingRateHistory.js +40 -0
- package/js/test/Exchange/test.fetchL2OrderBook.js +23 -0
- package/js/test/Exchange/test.fetchLedger.js +42 -0
- package/js/test/Exchange/test.fetchLeverageTiers.js +33 -0
- package/js/test/Exchange/test.fetchMarketLeverageTiers.js +22 -0
- package/js/test/Exchange/test.fetchMarkets.js +33 -0
- package/js/test/Exchange/test.fetchMyTrades.js +42 -0
- package/js/test/Exchange/test.fetchOHLCV.js +46 -0
- package/js/test/Exchange/test.fetchOpenOrders.js +36 -0
- package/js/test/Exchange/test.fetchOrderBook.js +25 -0
- package/js/test/Exchange/test.fetchOrderBooks.js +35 -0
- package/js/test/Exchange/test.fetchOrders.js +41 -0
- package/js/test/Exchange/test.fetchPositions.js +47 -0
- package/js/test/Exchange/test.fetchStatus.js +35 -0
- package/js/test/Exchange/test.fetchTicker.js +38 -0
- package/js/test/Exchange/test.fetchTickers.js +49 -0
- package/js/test/Exchange/test.fetchTrades.js +39 -0
- package/js/test/Exchange/test.fetchTradingFee.js +18 -0
- package/js/test/Exchange/test.fetchTradingFees.js +22 -0
- package/js/test/Exchange/test.fetchTransactions.js +31 -0
- package/js/test/Exchange/test.fetchWithdrawals.js +31 -0
- package/js/test/Exchange/test.ledgerItem.js +46 -0
- package/js/test/Exchange/test.leverageTier.js +33 -0
- package/js/test/Exchange/test.loadMarkets.js +35 -0
- package/js/test/Exchange/test.market.js +129 -0
- package/js/test/Exchange/test.ohlcv.js +33 -0
- package/js/test/Exchange/test.order.js +62 -0
- package/js/test/Exchange/test.orderbook.js +61 -0
- package/js/test/Exchange/test.position.js +21 -0
- package/js/test/Exchange/test.throttle.js +94 -0
- package/js/test/Exchange/test.ticker.js +95 -0
- package/js/test/Exchange/test.trade.js +68 -0
- package/js/test/Exchange/test.tradingFee.js +34 -0
- package/js/test/Exchange/test.transaction.js +35 -0
- package/js/test/base/.eslintrc +38 -0
- package/js/test/base/functions/test.crypto.js +110 -0
- package/js/test/base/functions/test.datetime.js +62 -0
- package/js/test/base/functions/test.generic.js +152 -0
- package/js/test/base/functions/test.number.js +362 -0
- package/js/test/base/functions/test.time.js +56 -0
- package/js/test/base/functions/test.type.js +53 -0
- package/js/test/base/test.base.js +193 -0
- package/js/test/errors/test.InsufficientFunds.js +86 -0
- package/js/test/errors/test.InvalidNonce.js +64 -0
- package/js/test/errors/test.InvalidOrder.js +35 -0
- package/js/test/errors/test.OrderNotFound.js +39 -0
- package/js/test/test.js +426 -0
- package/js/test/test.timeout_hang.js +12 -0
- package/js/therock.js +1431 -0
- package/js/tidebit.js +632 -0
- package/js/tidex.js +939 -0
- package/js/timex.js +1283 -0
- package/js/upbit.js +1622 -0
- package/js/vcc.js +1353 -0
- package/js/wavesexchange.js +2185 -0
- package/js/wazirx.js +732 -0
- package/js/whitebit.js +1352 -0
- package/js/woo.js +1577 -0
- package/js/xena.js +1948 -0
- package/js/yobit.js +1129 -0
- package/js/zaif.js +647 -0
- package/js/zb.js +4088 -0
- package/js/zipmex.js +40 -0
- package/js/zonda.js +1497 -0
- package/multilang.sh +159 -0
- package/package.json +591 -0
- 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
|
+
}
|