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
package/js/ftx.js
ADDED
@@ -0,0 +1,2751 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
// ---------------------------------------------------------------------------
|
4
|
+
|
5
|
+
const Exchange = require ('./base/Exchange');
|
6
|
+
const { TICK_SIZE } = require ('./base/functions/number');
|
7
|
+
const { ExchangeError, InvalidOrder, BadRequest, InsufficientFunds, OrderNotFound, AuthenticationError, RateLimitExceeded, ExchangeNotAvailable, CancelPending, ArgumentsRequired, PermissionDenied, BadSymbol, DuplicateOrderId, BadResponse } = require ('./base/errors');
|
8
|
+
const Precise = require ('./base/Precise');
|
9
|
+
|
10
|
+
// ---------------------------------------------------------------------------
|
11
|
+
|
12
|
+
module.exports = class ftx extends Exchange {
|
13
|
+
describe () {
|
14
|
+
return this.deepExtend (super.describe (), {
|
15
|
+
'id': 'ftx',
|
16
|
+
'name': 'FTX',
|
17
|
+
'countries': [ 'BS' ], // Bahamas
|
18
|
+
// hard limit of 7 requests per 200ms => 35 requests per 1000ms => 1000ms / 35 = 28.5714 ms between requests
|
19
|
+
// 10 withdrawal requests per 30 seconds = (1000ms / rateLimit) / (1/3) = 90.1
|
20
|
+
// cancels do not count towards rateLimit
|
21
|
+
// only 'order-making' requests count towards ratelimit
|
22
|
+
'rateLimit': 28.57,
|
23
|
+
'certified': true,
|
24
|
+
'pro': true,
|
25
|
+
'hostname': 'ftx.com', // or ftx.us
|
26
|
+
'urls': {
|
27
|
+
'logo': 'https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg',
|
28
|
+
'www': 'https://ftx.com',
|
29
|
+
'api': {
|
30
|
+
'public': 'https://{hostname}',
|
31
|
+
'private': 'https://{hostname}',
|
32
|
+
},
|
33
|
+
'doc': 'https://github.com/ftexchange/ftx',
|
34
|
+
'fees': 'https://ftexchange.zendesk.com/hc/en-us/articles/360024479432-Fees',
|
35
|
+
'referral': {
|
36
|
+
'url': 'https://ftx.com/#a=ccxt',
|
37
|
+
'discount': 0.05,
|
38
|
+
},
|
39
|
+
},
|
40
|
+
'has': {
|
41
|
+
'CORS': undefined,
|
42
|
+
'spot': true,
|
43
|
+
'margin': true,
|
44
|
+
'swap': true,
|
45
|
+
'future': true,
|
46
|
+
'option': false,
|
47
|
+
'cancelAllOrders': true,
|
48
|
+
'cancelOrder': true,
|
49
|
+
'createOrder': true,
|
50
|
+
'createReduceOnlyOrder': true,
|
51
|
+
'createStopLimitOrder': true,
|
52
|
+
'createStopMarketOrder': true,
|
53
|
+
'createStopOrder': true,
|
54
|
+
'editOrder': true,
|
55
|
+
'fetchBalance': true,
|
56
|
+
'fetchBorrowInterest': true,
|
57
|
+
'fetchBorrowRate': true,
|
58
|
+
'fetchBorrowRateHistories': true,
|
59
|
+
'fetchBorrowRateHistory': true,
|
60
|
+
'fetchBorrowRates': true,
|
61
|
+
'fetchClosedOrders': undefined,
|
62
|
+
'fetchCurrencies': true,
|
63
|
+
'fetchDepositAddress': true,
|
64
|
+
'fetchDeposits': true,
|
65
|
+
'fetchFundingFees': undefined,
|
66
|
+
'fetchFundingHistory': true,
|
67
|
+
'fetchFundingRate': true,
|
68
|
+
'fetchFundingRateHistory': true,
|
69
|
+
'fetchFundingRates': false,
|
70
|
+
'fetchIndexOHLCV': true,
|
71
|
+
'fetchLeverageTiers': false,
|
72
|
+
'fetchMarketLeverageTiers': false,
|
73
|
+
'fetchMarkets': true,
|
74
|
+
'fetchMarkOHLCV': false,
|
75
|
+
'fetchMyTrades': true,
|
76
|
+
'fetchOHLCV': true,
|
77
|
+
'fetchOpenOrders': true,
|
78
|
+
'fetchOrder': true,
|
79
|
+
'fetchOrderBook': true,
|
80
|
+
'fetchOrders': true,
|
81
|
+
'fetchOrderTrades': true,
|
82
|
+
'fetchPosition': false,
|
83
|
+
'fetchPositions': true,
|
84
|
+
'fetchPositionsRisk': false,
|
85
|
+
'fetchPremiumIndexOHLCV': false,
|
86
|
+
'fetchTicker': true,
|
87
|
+
'fetchTickers': true,
|
88
|
+
'fetchTime': false,
|
89
|
+
'fetchTrades': true,
|
90
|
+
'fetchTradingFee': false,
|
91
|
+
'fetchTradingFees': true,
|
92
|
+
'fetchTransfer': undefined,
|
93
|
+
'fetchTransfers': undefined,
|
94
|
+
'fetchWithdrawals': true,
|
95
|
+
'reduceMargin': false,
|
96
|
+
'setLeverage': true,
|
97
|
+
'setMarginMode': false, // FTX only supports cross margin
|
98
|
+
'setPositionMode': false,
|
99
|
+
'transfer': true,
|
100
|
+
'withdraw': true,
|
101
|
+
},
|
102
|
+
'timeframes': {
|
103
|
+
'15s': '15',
|
104
|
+
'1m': '60',
|
105
|
+
'5m': '300',
|
106
|
+
'15m': '900',
|
107
|
+
'1h': '3600',
|
108
|
+
'4h': '14400',
|
109
|
+
'1d': '86400',
|
110
|
+
'3d': '259200',
|
111
|
+
'1w': '604800',
|
112
|
+
'2w': '1209600',
|
113
|
+
// the exchange does not align candles to the start of the month
|
114
|
+
// it can only fetch candles in fixed intervals of multiples of whole days
|
115
|
+
// that works for all timeframes, except the monthly timeframe
|
116
|
+
// because months have varying numbers of days
|
117
|
+
'1M': '2592000',
|
118
|
+
},
|
119
|
+
'api': {
|
120
|
+
'public': {
|
121
|
+
'get': {
|
122
|
+
'coins': 1,
|
123
|
+
// markets
|
124
|
+
'markets': 1,
|
125
|
+
'markets/{market_name}': 1,
|
126
|
+
'markets/{market_name}/orderbook': 1, // ?depth={depth}
|
127
|
+
'markets/{market_name}/trades': 1, // ?limit={limit}&start_time={start_time}&end_time={end_time}
|
128
|
+
'markets/{market_name}/candles': 1, // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time}
|
129
|
+
// futures
|
130
|
+
'futures': 1,
|
131
|
+
'futures/{future_name}': 1,
|
132
|
+
'futures/{future_name}/stats': 1,
|
133
|
+
'funding_rates': 1,
|
134
|
+
'indexes/{index_name}/weights': 1,
|
135
|
+
'expired_futures': 1,
|
136
|
+
'indexes/{market_name}/candles': 1, // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time}
|
137
|
+
// wallet
|
138
|
+
'wallet/coins': 1,
|
139
|
+
// leverage tokens
|
140
|
+
'lt/tokens': 1,
|
141
|
+
'lt/{token_name}': 1,
|
142
|
+
// etfs
|
143
|
+
'etfs/rebalance_info': 1,
|
144
|
+
// options
|
145
|
+
'options/requests': 1,
|
146
|
+
'options/trades': 1,
|
147
|
+
'options/historical_volumes/BTC': 1,
|
148
|
+
'stats/24h_options_volume': 1,
|
149
|
+
'options/open_interest/BTC': 1,
|
150
|
+
'options/historical_open_interest/BTC': 1,
|
151
|
+
// spot margin
|
152
|
+
'spot_margin/history': 1,
|
153
|
+
'spot_margin/borrow_summary': 1,
|
154
|
+
// nfts
|
155
|
+
'nft/nfts': 1,
|
156
|
+
'nft/{nft_id}': 1,
|
157
|
+
'nft/{nft_id}/trades': 1,
|
158
|
+
'nft/all_trades': 1,
|
159
|
+
'nft/{nft_id}/account_info': 1,
|
160
|
+
'nft/collections': 1,
|
161
|
+
// ftx pay
|
162
|
+
'ftxpay/apps/{user_specific_id}/details': 1,
|
163
|
+
},
|
164
|
+
'post': {
|
165
|
+
'ftxpay/apps/{user_specific_id}/orders': 1,
|
166
|
+
},
|
167
|
+
},
|
168
|
+
'private': {
|
169
|
+
'get': {
|
170
|
+
// subaccounts
|
171
|
+
'subaccounts': 1,
|
172
|
+
'subaccounts/{nickname}/balances': 1,
|
173
|
+
// account
|
174
|
+
'account': 1,
|
175
|
+
'positions': 1,
|
176
|
+
// wallet
|
177
|
+
'wallet/balances': 1,
|
178
|
+
'wallet/all_balances': 1,
|
179
|
+
'wallet/deposit_address/{coin}': 1, // ?method={method}
|
180
|
+
'wallet/deposits': 1,
|
181
|
+
'wallet/withdrawals': 1,
|
182
|
+
'wallet/airdrops': 1,
|
183
|
+
'wallet/withdrawal_fee': 1,
|
184
|
+
'wallet/saved_addresses': 1,
|
185
|
+
// orders
|
186
|
+
'orders': 1, // ?market={market}
|
187
|
+
'orders/history': 1, // ?market={market}
|
188
|
+
'orders/{order_id}': 1,
|
189
|
+
'orders/by_client_id/{client_order_id}': 1,
|
190
|
+
// conditional orders
|
191
|
+
'conditional_orders': 1, // ?market={market}
|
192
|
+
'conditional_orders/{conditional_order_id}/triggers': 1,
|
193
|
+
'conditional_orders/history': 1, // ?market={market}
|
194
|
+
'fills': 1, // ?market={market}
|
195
|
+
'funding_payments': 1,
|
196
|
+
// leverage tokens
|
197
|
+
'lt/balances': 1,
|
198
|
+
'lt/creations': 1,
|
199
|
+
'lt/redemptions': 1,
|
200
|
+
// options
|
201
|
+
'options/my_requests': 1,
|
202
|
+
'options/requests/{request_id}/quotes': 1,
|
203
|
+
'options/my_quotes': 1,
|
204
|
+
'options/account_info': 1,
|
205
|
+
'options/positions': 1,
|
206
|
+
'options/fills': 1,
|
207
|
+
// staking
|
208
|
+
'staking/stakes': 1,
|
209
|
+
'staking/unstake_requests': 1,
|
210
|
+
'staking/balances': 1,
|
211
|
+
'staking/staking_rewards': 1,
|
212
|
+
// otc
|
213
|
+
'otc/quotes/{quoteId}': 1,
|
214
|
+
// spot margin
|
215
|
+
'spot_margin/borrow_rates': 1,
|
216
|
+
'spot_margin/lending_rates': 1,
|
217
|
+
'spot_margin/market_info': 1, // ?market={market}
|
218
|
+
'spot_margin/borrow_history': 1,
|
219
|
+
'spot_margin/lending_history': 1,
|
220
|
+
'spot_margin/offers': 1,
|
221
|
+
'spot_margin/lending_info': 1,
|
222
|
+
// nfts
|
223
|
+
'nft/balances': 1,
|
224
|
+
'nft/bids': 1,
|
225
|
+
'nft/deposits': 1,
|
226
|
+
'nft/withdrawals': 1,
|
227
|
+
'nft/fills': 1,
|
228
|
+
'nft/gallery/{gallery_id}': 1,
|
229
|
+
'nft/gallery_settings': 1,
|
230
|
+
// latency statistics
|
231
|
+
'stats/latency_stats': 1,
|
232
|
+
// pnl
|
233
|
+
'pnl/historical_changes': 1,
|
234
|
+
},
|
235
|
+
'post': {
|
236
|
+
// subaccounts
|
237
|
+
'subaccounts': 1,
|
238
|
+
'subaccounts/update_name': 1,
|
239
|
+
'subaccounts/transfer': 1,
|
240
|
+
// account
|
241
|
+
'account/leverage': 1,
|
242
|
+
// wallet
|
243
|
+
'wallet/deposit_address/list': 1,
|
244
|
+
'wallet/withdrawals': 90,
|
245
|
+
'wallet/saved_addresses': 1,
|
246
|
+
// orders
|
247
|
+
'orders': 1,
|
248
|
+
'conditional_orders': 1,
|
249
|
+
'orders/{order_id}/modify': 1,
|
250
|
+
'orders/by_client_id/{client_order_id}/modify': 1,
|
251
|
+
'conditional_orders/{order_id}/modify': 1,
|
252
|
+
// leverage tokens
|
253
|
+
'lt/{token_name}/create': 1,
|
254
|
+
'lt/{token_name}/redeem': 1,
|
255
|
+
// options
|
256
|
+
'options/requests': 1,
|
257
|
+
'options/requests/{request_id}/quotes': 1,
|
258
|
+
'options/quotes/{quote_id}/accept': 1,
|
259
|
+
// staking
|
260
|
+
'staking/unstake_requests': 1,
|
261
|
+
'srm_stakes/stakes': 1,
|
262
|
+
// otc
|
263
|
+
'otc/quotes/{quote_id}/accept': 1,
|
264
|
+
'otc/quotes': 1,
|
265
|
+
// spot margin
|
266
|
+
'spot_margin/offers': 1,
|
267
|
+
// nfts
|
268
|
+
'nft/offer': 1,
|
269
|
+
'nft/buy': 1,
|
270
|
+
'nft/auction': 1,
|
271
|
+
'nft/edit_auction': 1,
|
272
|
+
'nft/cancel_auction': 1,
|
273
|
+
'nft/bids': 1,
|
274
|
+
'nft/redeem': 1,
|
275
|
+
'nft/gallery_settings': 1,
|
276
|
+
// ftx pay
|
277
|
+
'ftxpay/apps/{user_specific_id}/orders': 1,
|
278
|
+
},
|
279
|
+
'delete': {
|
280
|
+
// subaccounts
|
281
|
+
'subaccounts': 1,
|
282
|
+
// wallet
|
283
|
+
'wallet/saved_addresses/{saved_address_id}': 1,
|
284
|
+
// orders
|
285
|
+
'orders/{order_id}': 1,
|
286
|
+
'orders/by_client_id/{client_order_id}': 1,
|
287
|
+
'orders': 1,
|
288
|
+
'conditional_orders/{order_id}': 1,
|
289
|
+
// options
|
290
|
+
'options/requests/{request_id}': 1,
|
291
|
+
'options/quotes/{quote_id}': 1,
|
292
|
+
// staking
|
293
|
+
'staking/unstake_requests/{request_id}': 1,
|
294
|
+
},
|
295
|
+
},
|
296
|
+
},
|
297
|
+
'fees': {
|
298
|
+
'trading': {
|
299
|
+
'tierBased': true,
|
300
|
+
'percentage': true,
|
301
|
+
'maker': this.parseNumber ('0.0002'),
|
302
|
+
'taker': this.parseNumber ('0.0007'),
|
303
|
+
'tiers': {
|
304
|
+
'taker': [
|
305
|
+
[ this.parseNumber ('0'), this.parseNumber ('0.0007') ],
|
306
|
+
[ this.parseNumber ('2000000'), this.parseNumber ('0.0006') ],
|
307
|
+
[ this.parseNumber ('5000000'), this.parseNumber ('0.00055') ],
|
308
|
+
[ this.parseNumber ('10000000'), this.parseNumber ('0.0005') ],
|
309
|
+
[ this.parseNumber ('25000000'), this.parseNumber ('0.0045') ],
|
310
|
+
[ this.parseNumber ('50000000'), this.parseNumber ('0.0004') ],
|
311
|
+
],
|
312
|
+
'maker': [
|
313
|
+
[ this.parseNumber ('0'), this.parseNumber ('0.0002') ],
|
314
|
+
[ this.parseNumber ('2000000'), this.parseNumber ('0.00015') ],
|
315
|
+
[ this.parseNumber ('5000000'), this.parseNumber ('0.0001') ],
|
316
|
+
[ this.parseNumber ('10000000'), this.parseNumber ('0.00005') ],
|
317
|
+
[ this.parseNumber ('25000000'), this.parseNumber ('0') ],
|
318
|
+
[ this.parseNumber ('50000000'), this.parseNumber ('0') ],
|
319
|
+
],
|
320
|
+
},
|
321
|
+
},
|
322
|
+
'funding': {
|
323
|
+
'withdraw': {},
|
324
|
+
},
|
325
|
+
},
|
326
|
+
'exceptions': {
|
327
|
+
'exact': {
|
328
|
+
'Slow down': RateLimitExceeded, // {"error":"Slow down","success":false}
|
329
|
+
'Size too small for provide': InvalidOrder, // {"error":"Size too small for provide","success":false}
|
330
|
+
'Not enough balances': InsufficientFunds, // {"error":"Not enough balances","success":false}
|
331
|
+
'InvalidPrice': InvalidOrder, // {"error":"Invalid price","success":false}
|
332
|
+
'Size too small': InvalidOrder, // {"error":"Size too small","success":false}
|
333
|
+
'Size too large': InvalidOrder, // {"error":"Size too large","success":false}
|
334
|
+
'Invalid price': InvalidOrder, // {"success":false,"error":"Invalid price"}
|
335
|
+
'Missing parameter price': InvalidOrder, // {"error":"Missing parameter price","success":false}
|
336
|
+
'Order not found': OrderNotFound, // {"error":"Order not found","success":false}
|
337
|
+
'Order already closed': InvalidOrder, // {"error":"Order already closed","success":false}
|
338
|
+
'Trigger price too high': InvalidOrder, // {"error":"Trigger price too high","success":false}
|
339
|
+
'Trigger price too low': InvalidOrder, // {"error":"Trigger price too low","success":false}
|
340
|
+
'Order already queued for cancellation': CancelPending, // {"error":"Order already queued for cancellation","success":false}
|
341
|
+
'Duplicate client order ID': DuplicateOrderId, // {"error":"Duplicate client order ID","success":false}
|
342
|
+
'Spot orders cannot be reduce-only': InvalidOrder, // {"error":"Spot orders cannot be reduce-only","success":false}
|
343
|
+
'Invalid reduce-only order': InvalidOrder, // {"error":"Invalid reduce-only order","success":false}
|
344
|
+
'Account does not have enough balances': InsufficientFunds, // {"success":false,"error":"Account does not have enough balances"}
|
345
|
+
'Not authorized for subaccount-specific access': PermissionDenied, // {"success":false,"error":"Not authorized for subaccount-specific access"}
|
346
|
+
'Not approved to trade this product': PermissionDenied, // {"success":false,"error":"Not approved to trade this product"}
|
347
|
+
},
|
348
|
+
'broad': {
|
349
|
+
// {"error":"Not logged in","success":false}
|
350
|
+
// {"error":"Not logged in: Invalid API key","success":false}
|
351
|
+
'Not logged in': AuthenticationError,
|
352
|
+
'Account does not have enough margin for order': InsufficientFunds,
|
353
|
+
'Invalid parameter': BadRequest, // {"error":"Invalid parameter start_time","success":false}
|
354
|
+
'The requested URL was not found on the server': BadRequest,
|
355
|
+
'No such coin': BadRequest,
|
356
|
+
'No such subaccount': AuthenticationError,
|
357
|
+
'No such future': BadSymbol,
|
358
|
+
'No such market': BadSymbol,
|
359
|
+
'Do not send more than': RateLimitExceeded,
|
360
|
+
'An unexpected error occurred': ExchangeNotAvailable, // {"error":"An unexpected error occurred, please try again later (58BC21C795).","success":false}
|
361
|
+
'Please retry request': ExchangeNotAvailable, // {"error":"Please retry request","success":false}
|
362
|
+
'Please try again': ExchangeNotAvailable, // {"error":"Please try again","success":false}
|
363
|
+
'Try again': ExchangeNotAvailable, // {"error":"Try again","success":false}
|
364
|
+
'Only have permissions for subaccount': PermissionDenied, // {"success":false,"error":"Only have permissions for subaccount *sub_name*"}
|
365
|
+
},
|
366
|
+
},
|
367
|
+
'precisionMode': TICK_SIZE,
|
368
|
+
'options': {
|
369
|
+
// support for canceling conditional orders
|
370
|
+
// https://github.com/ccxt/ccxt/issues/6669
|
371
|
+
'fetchMarkets': {
|
372
|
+
// the expiry datetime may be undefined for expiring futures, https://github.com/ccxt/ccxt/pull/12692
|
373
|
+
'throwOnUndefinedExpiry': false,
|
374
|
+
},
|
375
|
+
'cancelOrder': {
|
376
|
+
'method': 'privateDeleteOrdersOrderId', // privateDeleteConditionalOrdersOrderId
|
377
|
+
},
|
378
|
+
'fetchOpenOrders': {
|
379
|
+
'method': 'privateGetOrders', // privateGetConditionalOrders
|
380
|
+
},
|
381
|
+
'fetchOrders': {
|
382
|
+
'method': 'privateGetOrdersHistory', // privateGetConditionalOrdersHistory
|
383
|
+
},
|
384
|
+
'sign': {
|
385
|
+
'ftx.com': 'FTX',
|
386
|
+
'ftx.us': 'FTXUS',
|
387
|
+
},
|
388
|
+
'networks': {
|
389
|
+
'SOL': 'sol',
|
390
|
+
'SPL': 'sol',
|
391
|
+
'TRX': 'trx',
|
392
|
+
'TRC20': 'trx',
|
393
|
+
'ETH': 'erc20',
|
394
|
+
'ERC20': 'erc20',
|
395
|
+
'OMNI': 'omni',
|
396
|
+
'BEP2': 'bep2',
|
397
|
+
'BNB': 'bep2',
|
398
|
+
'BEP20': 'bsc',
|
399
|
+
'BSC': 'bsc',
|
400
|
+
},
|
401
|
+
},
|
402
|
+
'commonCurrencies': {
|
403
|
+
'AMC': 'AMC Entertainment Holdings',
|
404
|
+
'STARS': 'StarLaunch',
|
405
|
+
},
|
406
|
+
});
|
407
|
+
}
|
408
|
+
|
409
|
+
async fetchCurrencies (params = {}) {
|
410
|
+
const response = await this.publicGetCoins (params);
|
411
|
+
const currencies = this.safeValue (response, 'result', []);
|
412
|
+
//
|
413
|
+
// {
|
414
|
+
// "success":true,
|
415
|
+
// "result": [
|
416
|
+
// {"id":"BTC","name":"Bitcoin"},
|
417
|
+
// {"id":"ETH","name":"Ethereum"},
|
418
|
+
// {"id":"ETHMOON","name":"10X Long Ethereum Token","underlying":"ETH"},
|
419
|
+
// {"id":"EOSBULL","name":"3X Long EOS Token","underlying":"EOS"},
|
420
|
+
// ],
|
421
|
+
// }
|
422
|
+
//
|
423
|
+
const result = {};
|
424
|
+
for (let i = 0; i < currencies.length; i++) {
|
425
|
+
const currency = currencies[i];
|
426
|
+
const id = this.safeString (currency, 'id');
|
427
|
+
const code = this.safeCurrencyCode (id);
|
428
|
+
const name = this.safeString (currency, 'name');
|
429
|
+
result[code] = {
|
430
|
+
'id': id,
|
431
|
+
'code': code,
|
432
|
+
'info': currency,
|
433
|
+
'type': undefined,
|
434
|
+
'name': name,
|
435
|
+
'active': undefined,
|
436
|
+
'deposit': undefined,
|
437
|
+
'withdraw': undefined,
|
438
|
+
'fee': undefined,
|
439
|
+
'precision': undefined,
|
440
|
+
'limits': {
|
441
|
+
'withdraw': { 'min': undefined, 'max': undefined },
|
442
|
+
'amount': { 'min': undefined, 'max': undefined },
|
443
|
+
},
|
444
|
+
};
|
445
|
+
}
|
446
|
+
return result;
|
447
|
+
}
|
448
|
+
|
449
|
+
async fetchMarkets (params = {}) {
|
450
|
+
const response = await this.publicGetMarkets (params);
|
451
|
+
//
|
452
|
+
// {
|
453
|
+
// 'success': true,
|
454
|
+
// "result": [
|
455
|
+
// {
|
456
|
+
// "ask":170.37,
|
457
|
+
// "baseCurrency":null,
|
458
|
+
// "bid":170.31,
|
459
|
+
// "change1h":-0.019001554672655036,
|
460
|
+
// "change24h":-0.024841165359738997,
|
461
|
+
// "changeBod":-0.03816406029469881,
|
462
|
+
// "enabled":true,
|
463
|
+
// "last":170.37,
|
464
|
+
// "name":"ETH-PERP",
|
465
|
+
// "price":170.37,
|
466
|
+
// "priceIncrement":0.01,
|
467
|
+
// "quoteCurrency":null,
|
468
|
+
// "quoteVolume24h":7742164.59889,
|
469
|
+
// "sizeIncrement":0.001,
|
470
|
+
// "type":"future",
|
471
|
+
// "underlying":"ETH",
|
472
|
+
// "volumeUsd24h":7742164.59889
|
473
|
+
// },
|
474
|
+
// {
|
475
|
+
// "ask":170.44,
|
476
|
+
// "baseCurrency":"ETH",
|
477
|
+
// "bid":170.41,
|
478
|
+
// "change1h":-0.018485459257126403,
|
479
|
+
// "change24h":-0.023825887743413515,
|
480
|
+
// "changeBod":-0.037605872388481086,
|
481
|
+
// "enabled":true,
|
482
|
+
// "last":172.72,
|
483
|
+
// "name":"ETH/USD",
|
484
|
+
// "price":170.44,
|
485
|
+
// "priceIncrement":0.01,
|
486
|
+
// "quoteCurrency":"USD",
|
487
|
+
// "quoteVolume24h":382802.0252,
|
488
|
+
// "sizeIncrement":0.001,
|
489
|
+
// "type":"spot",
|
490
|
+
// "underlying":null,
|
491
|
+
// "volumeUsd24h":382802.0252
|
492
|
+
// },
|
493
|
+
// ],
|
494
|
+
// }
|
495
|
+
//
|
496
|
+
// {
|
497
|
+
// name: "BTC-PERP",
|
498
|
+
// enabled: true,
|
499
|
+
// postOnly: false,
|
500
|
+
// priceIncrement: "1.0",
|
501
|
+
// sizeIncrement: "0.0001",
|
502
|
+
// minProvideSize: "0.001",
|
503
|
+
// last: "60397.0",
|
504
|
+
// bid: "60387.0",
|
505
|
+
// ask: "60388.0",
|
506
|
+
// price: "60388.0",
|
507
|
+
// type: "future",
|
508
|
+
// baseCurrency: null,
|
509
|
+
// quoteCurrency: null,
|
510
|
+
// underlying: "BTC",
|
511
|
+
// restricted: false,
|
512
|
+
// highLeverageFeeExempt: true,
|
513
|
+
// change1h: "-0.0036463231533270636",
|
514
|
+
// change24h: "-0.01844838515677064",
|
515
|
+
// changeBod: "-0.010130151132675475",
|
516
|
+
// quoteVolume24h: "2892083192.6099",
|
517
|
+
// volumeUsd24h: "2892083192.6099"
|
518
|
+
// }
|
519
|
+
//
|
520
|
+
let allFuturesResponse = undefined;
|
521
|
+
if (this.has['future'] && (this.hostname !== 'ftx.us')) {
|
522
|
+
allFuturesResponse = await this.publicGetFutures ();
|
523
|
+
}
|
524
|
+
//
|
525
|
+
// {
|
526
|
+
// success: true,
|
527
|
+
// result: [
|
528
|
+
// {
|
529
|
+
// name: "1INCH-PERP",
|
530
|
+
// underlying: "1INCH",
|
531
|
+
// description: "1INCH Token Perpetual Futures",
|
532
|
+
// type: "perpetual",
|
533
|
+
// expiry: null,
|
534
|
+
// perpetual: true,
|
535
|
+
// expired: false,
|
536
|
+
// enabled: true,
|
537
|
+
// postOnly: false,
|
538
|
+
// priceIncrement: "0.0001",
|
539
|
+
// sizeIncrement: "1.0",
|
540
|
+
// last: "2.5556",
|
541
|
+
// bid: "2.5555",
|
542
|
+
// ask: "2.5563",
|
543
|
+
// index: "2.5612449804010833",
|
544
|
+
// mark: "2.5587",
|
545
|
+
// imfFactor: "0.0005",
|
546
|
+
// lowerBound: "2.4315",
|
547
|
+
// upperBound: "2.6893",
|
548
|
+
// underlyingDescription: "1INCH Token",
|
549
|
+
// expiryDescription: "Perpetual",
|
550
|
+
// moveStart: null,
|
551
|
+
// marginPrice: "2.5587",
|
552
|
+
// positionLimitWeight: "20.0",
|
553
|
+
// group: "perpetual",
|
554
|
+
// change1h: "0.00799716356760164",
|
555
|
+
// change24h: "0.004909276569004792",
|
556
|
+
// changeBod: "0.008394419484511705",
|
557
|
+
// volumeUsd24h: "17834492.0818",
|
558
|
+
// volume: "7224898.0",
|
559
|
+
// openInterest: "5597917.0",
|
560
|
+
// openInterestUsd: "14323390.2279",
|
561
|
+
// },
|
562
|
+
// ...
|
563
|
+
// ],
|
564
|
+
// }
|
565
|
+
//
|
566
|
+
const result = [];
|
567
|
+
const markets = this.safeValue (response, 'result', []);
|
568
|
+
const allFutures = this.safeValue (allFuturesResponse, 'result', []);
|
569
|
+
const allFuturesDict = this.indexBy (allFutures, 'name');
|
570
|
+
for (let i = 0; i < markets.length; i++) {
|
571
|
+
const market = markets[i];
|
572
|
+
const id = this.safeString (market, 'name');
|
573
|
+
const future = this.safeValue (allFuturesDict, id);
|
574
|
+
const marketType = this.safeString (market, 'type');
|
575
|
+
const contract = (marketType === 'future');
|
576
|
+
const baseId = this.safeString2 (market, 'baseCurrency', 'underlying');
|
577
|
+
const quoteId = this.safeString (market, 'quoteCurrency', 'USD');
|
578
|
+
const settleId = contract ? 'USD' : undefined;
|
579
|
+
let base = this.safeCurrencyCode (baseId);
|
580
|
+
const quote = this.safeCurrencyCode (quoteId);
|
581
|
+
const settle = this.safeCurrencyCode (settleId);
|
582
|
+
const spot = !contract;
|
583
|
+
const margin = !contract;
|
584
|
+
const perpetual = this.safeValue (future, 'perpetual', false);
|
585
|
+
const swap = perpetual;
|
586
|
+
const option = false;
|
587
|
+
const isFuture = contract && !swap;
|
588
|
+
let expiry = undefined;
|
589
|
+
const expiryDatetime = this.safeString (future, 'expiry');
|
590
|
+
let type = 'spot';
|
591
|
+
let symbol = base + '/' + quote;
|
592
|
+
if (swap) {
|
593
|
+
type = 'swap';
|
594
|
+
symbol = base + '/' + quote + ':' + settle;
|
595
|
+
} else if (isFuture) {
|
596
|
+
type = 'future';
|
597
|
+
expiry = this.parse8601 (expiryDatetime);
|
598
|
+
if (expiry === undefined) {
|
599
|
+
// it is likely a future that is expiring in this moment
|
600
|
+
const options = this.safeValue (this.options, 'fetchMarkets', {});
|
601
|
+
const throwOnUndefinedExpiry = this.safeValue (options, 'throwOnUndefinedExpiry', false);
|
602
|
+
if (throwOnUndefinedExpiry) {
|
603
|
+
throw new BadResponse (this.id + " symbol '" + id + "' is a future contract with an invalid expiry datetime.");
|
604
|
+
} else {
|
605
|
+
continue;
|
606
|
+
}
|
607
|
+
}
|
608
|
+
const parsedId = id.split ('-');
|
609
|
+
const length = parsedId.length;
|
610
|
+
if (length > 2) {
|
611
|
+
// handling for MOVE contracts
|
612
|
+
// BTC-MOVE-2022Q1
|
613
|
+
// BTC-MOVE-0106
|
614
|
+
// BTC-MOVE-WK-0121
|
615
|
+
parsedId.pop ();
|
616
|
+
// remove expiry
|
617
|
+
// [ 'BTC', 'MOVE' ]
|
618
|
+
// [ 'BTC', 'MOVE' ]
|
619
|
+
// [ 'BTC', 'MOVE', 'WK' ]
|
620
|
+
base = parsedId.join ('-');
|
621
|
+
}
|
622
|
+
symbol = base + '/' + quote + ':' + settle + '-' + this.yymmdd (expiry, '');
|
623
|
+
}
|
624
|
+
// check if a market is a spot or future market
|
625
|
+
const sizeIncrement = this.safeString (market, 'sizeIncrement');
|
626
|
+
const minProvideSize = this.safeString (market, 'minProvideSize');
|
627
|
+
let minAmountString = sizeIncrement;
|
628
|
+
if (minProvideSize !== undefined) {
|
629
|
+
minAmountString = Precise.stringGt (minProvideSize, sizeIncrement) ? sizeIncrement : minProvideSize;
|
630
|
+
}
|
631
|
+
result.push ({
|
632
|
+
'id': id,
|
633
|
+
'symbol': symbol,
|
634
|
+
'base': base,
|
635
|
+
'quote': quote,
|
636
|
+
'settle': settle,
|
637
|
+
'baseId': baseId,
|
638
|
+
'quoteId': quoteId,
|
639
|
+
'settleId': settleId,
|
640
|
+
'type': type,
|
641
|
+
'spot': spot,
|
642
|
+
'margin': margin,
|
643
|
+
'swap': swap,
|
644
|
+
'future': isFuture,
|
645
|
+
'option': option,
|
646
|
+
'active': this.safeValue (market, 'enabled'),
|
647
|
+
'contract': contract,
|
648
|
+
'linear': contract ? true : undefined,
|
649
|
+
'inverse': contract ? false : undefined,
|
650
|
+
'contractSize': this.parseNumber ('1'),
|
651
|
+
'expiry': expiry,
|
652
|
+
'expiryDatetime': this.iso8601 (expiry),
|
653
|
+
'strike': undefined,
|
654
|
+
'optionType': undefined,
|
655
|
+
'precision': {
|
656
|
+
'amount': this.parseNumber (sizeIncrement),
|
657
|
+
'price': this.safeNumber (market, 'priceIncrement'),
|
658
|
+
},
|
659
|
+
'limits': {
|
660
|
+
'leverage': {
|
661
|
+
'min': this.parseNumber ('1'),
|
662
|
+
'max': this.parseNumber ('20'),
|
663
|
+
},
|
664
|
+
'amount': {
|
665
|
+
'min': this.parseNumber (minAmountString),
|
666
|
+
'max': undefined,
|
667
|
+
},
|
668
|
+
'price': {
|
669
|
+
'min': undefined,
|
670
|
+
'max': undefined,
|
671
|
+
},
|
672
|
+
'cost': {
|
673
|
+
'min': undefined,
|
674
|
+
'max': undefined,
|
675
|
+
},
|
676
|
+
},
|
677
|
+
'info': market,
|
678
|
+
});
|
679
|
+
}
|
680
|
+
return result;
|
681
|
+
}
|
682
|
+
|
683
|
+
parseTicker (ticker, market = undefined) {
|
684
|
+
//
|
685
|
+
// {
|
686
|
+
// "ask":171.29,
|
687
|
+
// "baseCurrency":null, // base currency for spot markets
|
688
|
+
// "bid":171.24,
|
689
|
+
// "change1h":-0.0012244897959183673,
|
690
|
+
// "change24h":-0.031603346901854366,
|
691
|
+
// "changeBod":-0.03297013492914808,
|
692
|
+
// "enabled":true,
|
693
|
+
// "last":171.44,
|
694
|
+
// "name":"ETH-PERP",
|
695
|
+
// "price":171.29,
|
696
|
+
// "priceIncrement":0.01,
|
697
|
+
// "quoteCurrency":null, // quote currency for spot markets
|
698
|
+
// "quoteVolume24h":8570651.12113,
|
699
|
+
// "sizeIncrement":0.001,
|
700
|
+
// "type":"future",
|
701
|
+
// "underlying":"ETH", // null for spot markets
|
702
|
+
// "volumeUsd24h":8570651.12113,
|
703
|
+
// }
|
704
|
+
//
|
705
|
+
const marketId = this.safeString (ticker, 'name');
|
706
|
+
if (marketId in this.markets_by_id) {
|
707
|
+
market = this.markets_by_id[marketId];
|
708
|
+
}
|
709
|
+
const symbol = this.safeSymbol (marketId, market);
|
710
|
+
const last = this.safeString (ticker, 'last');
|
711
|
+
const timestamp = this.safeTimestamp (ticker, 'time', this.milliseconds ());
|
712
|
+
let percentage = this.safeString (ticker, 'change24h');
|
713
|
+
if (percentage !== undefined) {
|
714
|
+
percentage = Precise.stringMul (percentage, '100');
|
715
|
+
}
|
716
|
+
return this.safeTicker ({
|
717
|
+
'symbol': symbol,
|
718
|
+
'timestamp': timestamp,
|
719
|
+
'datetime': this.iso8601 (timestamp),
|
720
|
+
'high': this.safeString (ticker, 'high'),
|
721
|
+
'low': this.safeString (ticker, 'low'),
|
722
|
+
'bid': this.safeString (ticker, 'bid'),
|
723
|
+
'bidVolume': this.safeString (ticker, 'bidSize'),
|
724
|
+
'ask': this.safeString (ticker, 'ask'),
|
725
|
+
'askVolume': this.safeString (ticker, 'askSize'),
|
726
|
+
'vwap': undefined,
|
727
|
+
'open': undefined,
|
728
|
+
'close': last,
|
729
|
+
'last': last,
|
730
|
+
'previousClose': undefined,
|
731
|
+
'change': undefined,
|
732
|
+
'percentage': percentage,
|
733
|
+
'average': undefined,
|
734
|
+
'baseVolume': undefined,
|
735
|
+
'quoteVolume': this.safeString (ticker, 'quoteVolume24h'),
|
736
|
+
'info': ticker,
|
737
|
+
}, market, false);
|
738
|
+
}
|
739
|
+
|
740
|
+
async fetchTicker (symbol, params = {}) {
|
741
|
+
await this.loadMarkets ();
|
742
|
+
const market = this.market (symbol);
|
743
|
+
const request = {
|
744
|
+
'market_name': market['id'],
|
745
|
+
};
|
746
|
+
const response = await this.publicGetMarketsMarketName (this.extend (request, params));
|
747
|
+
//
|
748
|
+
// {
|
749
|
+
// "success":true,
|
750
|
+
// "result":{
|
751
|
+
// "ask":171.29,
|
752
|
+
// "baseCurrency":null, // base currency for spot markets
|
753
|
+
// "bid":171.24,
|
754
|
+
// "change1h":-0.0012244897959183673,
|
755
|
+
// "change24h":-0.031603346901854366,
|
756
|
+
// "changeBod":-0.03297013492914808,
|
757
|
+
// "enabled":true,
|
758
|
+
// "last":171.44,
|
759
|
+
// "name":"ETH-PERP",
|
760
|
+
// "price":171.29,
|
761
|
+
// "priceIncrement":0.01,
|
762
|
+
// "quoteCurrency":null, // quote currency for spot markets
|
763
|
+
// "quoteVolume24h":8570651.12113,
|
764
|
+
// "sizeIncrement":0.001,
|
765
|
+
// "type":"future",
|
766
|
+
// "underlying":"ETH", // null for spot markets
|
767
|
+
// "volumeUsd24h":8570651.12113,
|
768
|
+
// }
|
769
|
+
// }
|
770
|
+
//
|
771
|
+
const result = this.safeValue (response, 'result', {});
|
772
|
+
return this.parseTicker (result, market);
|
773
|
+
}
|
774
|
+
|
775
|
+
async fetchTickers (symbols = undefined, params = {}) {
|
776
|
+
await this.loadMarkets ();
|
777
|
+
const response = await this.publicGetMarkets (params);
|
778
|
+
//
|
779
|
+
// {
|
780
|
+
// 'success': true,
|
781
|
+
// "result": [
|
782
|
+
// {
|
783
|
+
// "ask":170.44,
|
784
|
+
// "baseCurrency":"ETH",
|
785
|
+
// "bid":170.41,
|
786
|
+
// "change1h":-0.018485459257126403,
|
787
|
+
// "change24h":-0.023825887743413515,
|
788
|
+
// "changeBod":-0.037605872388481086,
|
789
|
+
// "enabled":true,
|
790
|
+
// "last":172.72,
|
791
|
+
// "name":"ETH/USD",
|
792
|
+
// "price":170.44,
|
793
|
+
// "priceIncrement":0.01,
|
794
|
+
// "quoteCurrency":"USD",
|
795
|
+
// "quoteVolume24h":382802.0252,
|
796
|
+
// "sizeIncrement":0.001,
|
797
|
+
// "type":"spot",
|
798
|
+
// "underlying":null,
|
799
|
+
// "volumeUsd24h":382802.0252
|
800
|
+
// },
|
801
|
+
// ],
|
802
|
+
// }
|
803
|
+
//
|
804
|
+
const tickers = this.safeValue (response, 'result', []);
|
805
|
+
return this.parseTickers (tickers, symbols);
|
806
|
+
}
|
807
|
+
|
808
|
+
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
809
|
+
await this.loadMarkets ();
|
810
|
+
const market = this.market (symbol);
|
811
|
+
const request = {
|
812
|
+
'market_name': market['id'],
|
813
|
+
};
|
814
|
+
if (limit !== undefined) {
|
815
|
+
request['depth'] = limit; // max 100, default 20
|
816
|
+
}
|
817
|
+
const response = await this.publicGetMarketsMarketNameOrderbook (this.extend (request, params));
|
818
|
+
//
|
819
|
+
// {
|
820
|
+
// "success":true,
|
821
|
+
// "result":{
|
822
|
+
// "asks":[
|
823
|
+
// [171.95,279.865],
|
824
|
+
// [171.98,102.42],
|
825
|
+
// [171.99,124.11],
|
826
|
+
// ],
|
827
|
+
// "bids":[
|
828
|
+
// [171.93,69.749],
|
829
|
+
// [171.9,288.325],
|
830
|
+
// [171.88,87.47],
|
831
|
+
// ],
|
832
|
+
// }
|
833
|
+
// }
|
834
|
+
//
|
835
|
+
const result = this.safeValue (response, 'result', {});
|
836
|
+
return this.parseOrderBook (result, symbol);
|
837
|
+
}
|
838
|
+
|
839
|
+
parseOHLCV (ohlcv, market = undefined) {
|
840
|
+
//
|
841
|
+
// {
|
842
|
+
// "close":177.23,
|
843
|
+
// "high":177.45,
|
844
|
+
// "low":177.2,
|
845
|
+
// "open":177.43,
|
846
|
+
// "startTime":"2019-10-17T13:27:00+00:00",
|
847
|
+
// "time":1571318820000.0,
|
848
|
+
// "volume":0.0
|
849
|
+
// }
|
850
|
+
//
|
851
|
+
return [
|
852
|
+
this.safeInteger (ohlcv, 'time'),
|
853
|
+
this.safeNumber (ohlcv, 'open'),
|
854
|
+
this.safeNumber (ohlcv, 'high'),
|
855
|
+
this.safeNumber (ohlcv, 'low'),
|
856
|
+
this.safeNumber (ohlcv, 'close'),
|
857
|
+
this.safeNumber (ohlcv, 'volume'),
|
858
|
+
];
|
859
|
+
}
|
860
|
+
|
861
|
+
getMarketId (symbol, key, params = {}) {
|
862
|
+
const parts = this.getMarketParams (symbol, key, params);
|
863
|
+
return this.safeString (parts, 1, symbol);
|
864
|
+
}
|
865
|
+
|
866
|
+
getMarketParams (symbol, key, params = {}) {
|
867
|
+
let market = undefined;
|
868
|
+
let marketId = undefined;
|
869
|
+
if (symbol in this.markets) {
|
870
|
+
market = this.market (symbol);
|
871
|
+
marketId = market['id'];
|
872
|
+
} else {
|
873
|
+
marketId = this.safeString (params, key, symbol);
|
874
|
+
}
|
875
|
+
return [ market, marketId ];
|
876
|
+
}
|
877
|
+
|
878
|
+
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
879
|
+
await this.loadMarkets ();
|
880
|
+
const [ market, marketId ] = this.getMarketParams (symbol, 'market_name', params);
|
881
|
+
// max 1501 candles, including the current candle when since is not specified
|
882
|
+
const maxLimit = 5000;
|
883
|
+
const defaultLimit = 1500;
|
884
|
+
limit = (limit === undefined) ? defaultLimit : Math.min (limit, maxLimit);
|
885
|
+
const request = {
|
886
|
+
'resolution': this.timeframes[timeframe],
|
887
|
+
'market_name': marketId,
|
888
|
+
// 'start_time': parseInt (since / 1000),
|
889
|
+
// 'end_time': this.seconds (),
|
890
|
+
'limit': limit,
|
891
|
+
};
|
892
|
+
const price = this.safeString (params, 'price');
|
893
|
+
params = this.omit (params, 'price');
|
894
|
+
if (since !== undefined) {
|
895
|
+
const startTime = parseInt (since / 1000);
|
896
|
+
request['start_time'] = startTime;
|
897
|
+
const duration = this.parseTimeframe (timeframe);
|
898
|
+
const endTime = this.sum (startTime, limit * duration);
|
899
|
+
request['end_time'] = Math.min (endTime, this.seconds ());
|
900
|
+
if (duration > 86400) {
|
901
|
+
const wholeDaysInTimeframe = parseInt (duration / 86400);
|
902
|
+
request['limit'] = Math.min (limit * wholeDaysInTimeframe, maxLimit);
|
903
|
+
}
|
904
|
+
}
|
905
|
+
let method = 'publicGetMarketsMarketNameCandles';
|
906
|
+
if (price === 'index') {
|
907
|
+
if (symbol in this.markets) {
|
908
|
+
request['market_name'] = market['baseId'];
|
909
|
+
}
|
910
|
+
method = 'publicGetIndexesMarketNameCandles';
|
911
|
+
}
|
912
|
+
const response = await this[method] (this.extend (request, params));
|
913
|
+
//
|
914
|
+
// {
|
915
|
+
// "success": true,
|
916
|
+
// "result":[
|
917
|
+
// {
|
918
|
+
// "close":177.23,
|
919
|
+
// "high":177.45,
|
920
|
+
// "low":177.2,
|
921
|
+
// "open":177.43,
|
922
|
+
// "startTime":"2019-10-17T13:27:00+00:00",
|
923
|
+
// "time":1571318820000.0,
|
924
|
+
// "volume":0.0
|
925
|
+
// },
|
926
|
+
// {
|
927
|
+
// "close":177.26,
|
928
|
+
// "high":177.33,
|
929
|
+
// "low":177.23,
|
930
|
+
// "open":177.23,
|
931
|
+
// "startTime":"2019-10-17T13:28:00+00:00",
|
932
|
+
// "time":1571318880000.0,
|
933
|
+
// "volume":0.0
|
934
|
+
// },
|
935
|
+
// ],
|
936
|
+
// }
|
937
|
+
//
|
938
|
+
const result = this.safeValue (response, 'result', []);
|
939
|
+
return this.parseOHLCVs (result, market, timeframe, since, limit);
|
940
|
+
}
|
941
|
+
|
942
|
+
async fetchIndexOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
943
|
+
const request = {
|
944
|
+
'price': 'index',
|
945
|
+
};
|
946
|
+
return await this.fetchOHLCV (symbol, timeframe, since, limit, this.extend (request, params));
|
947
|
+
}
|
948
|
+
|
949
|
+
parseTrade (trade, market = undefined) {
|
950
|
+
//
|
951
|
+
// fetchTrades (public)
|
952
|
+
//
|
953
|
+
// {
|
954
|
+
// "id":1715826,
|
955
|
+
// "liquidation":false,
|
956
|
+
// "price":171.62,
|
957
|
+
// "side":"buy",
|
958
|
+
// "size":2.095,
|
959
|
+
// "time":"2019-10-18T12:59:54.288166+00:00"
|
960
|
+
// }
|
961
|
+
//
|
962
|
+
// fetchMyTrades (private)
|
963
|
+
//
|
964
|
+
// {
|
965
|
+
// "fee": 20.1374935,
|
966
|
+
// "feeRate": 0.0005,
|
967
|
+
// "feeCurrency": "USD",
|
968
|
+
// "future": "EOS-0329",
|
969
|
+
// "id": 11215,
|
970
|
+
// "liquidity": "taker",
|
971
|
+
// "market": "EOS-0329",
|
972
|
+
// "baseCurrency": null,
|
973
|
+
// "quoteCurrency": null,
|
974
|
+
// "orderId": 8436981,
|
975
|
+
// "price": 4.201,
|
976
|
+
// "side": "buy",
|
977
|
+
// "size": 9587,
|
978
|
+
// "time": "2019-03-27T19:15:10.204619+00:00",
|
979
|
+
// "type": "order"
|
980
|
+
// }
|
981
|
+
//
|
982
|
+
// {
|
983
|
+
// "baseCurrency": "BTC",
|
984
|
+
// "fee": 0,
|
985
|
+
// "feeCurrency": "USD",
|
986
|
+
// "feeRate": 0,
|
987
|
+
// "future": null,
|
988
|
+
// "id": 664079556,
|
989
|
+
// "liquidity": "taker",
|
990
|
+
// "market": null,
|
991
|
+
// "orderId": null,
|
992
|
+
// "price": 34830.61359,
|
993
|
+
// "quoteCurrency": "USD",
|
994
|
+
// "side": "sell",
|
995
|
+
// "size": 0.0005996,
|
996
|
+
// "time": "2021-01-15T16:05:29.246135+00:00",
|
997
|
+
// "tradeId": null,
|
998
|
+
// "type": "otc"
|
999
|
+
// }
|
1000
|
+
//
|
1001
|
+
// with -ve fee
|
1002
|
+
// {
|
1003
|
+
// "id": 1171258927,
|
1004
|
+
// "fee": -0.0000713875,
|
1005
|
+
// "side": "sell",
|
1006
|
+
// "size": 1,
|
1007
|
+
// "time": "2021-03-11T13:34:35.523627+00:00",
|
1008
|
+
// "type": "order",
|
1009
|
+
// "price": 14.2775,
|
1010
|
+
// "future": null,
|
1011
|
+
// "market": "SOL/USD",
|
1012
|
+
// "feeRate": -0.000005,
|
1013
|
+
// "orderId": 33182929044,
|
1014
|
+
// "tradeId": 582936801,
|
1015
|
+
// "liquidity": "maker",
|
1016
|
+
// "feeCurrency": "USD",
|
1017
|
+
// "baseCurrency": "SOL",
|
1018
|
+
// "quoteCurrency": "USD"
|
1019
|
+
// }
|
1020
|
+
//
|
1021
|
+
// // from OTC order
|
1022
|
+
// {
|
1023
|
+
// "id": 1172129651,
|
1024
|
+
// "fee": 0,
|
1025
|
+
// "side": "sell",
|
1026
|
+
// "size": 1.47568846,
|
1027
|
+
// "time": "2021-03-11T15:04:46.893383+00:00",
|
1028
|
+
// "type": "otc",
|
1029
|
+
// "price": 14.60932598,
|
1030
|
+
// "future": null,
|
1031
|
+
// "market": null,
|
1032
|
+
// "feeRate": 0,
|
1033
|
+
// "orderId": null,
|
1034
|
+
// "tradeId": null,
|
1035
|
+
// "liquidity": "taker",
|
1036
|
+
// "feeCurrency": "USD",
|
1037
|
+
// "baseCurrency": "BCHA",
|
1038
|
+
// "quoteCurrency": "USD"
|
1039
|
+
// }
|
1040
|
+
//
|
1041
|
+
const id = this.safeString (trade, 'id');
|
1042
|
+
const takerOrMaker = this.safeString (trade, 'liquidity');
|
1043
|
+
// a workaround for the OTC trades, they don't have a symbol
|
1044
|
+
const baseId = this.safeString (trade, 'baseCurrency');
|
1045
|
+
const quoteId = this.safeString (trade, 'quoteCurrency');
|
1046
|
+
let defaultMarketId = undefined;
|
1047
|
+
if ((baseId !== undefined) && (quoteId !== undefined)) {
|
1048
|
+
defaultMarketId = baseId + '/' + quoteId;
|
1049
|
+
}
|
1050
|
+
const marketId = this.safeString (trade, 'market', defaultMarketId);
|
1051
|
+
market = this.safeMarket (marketId, market, '/');
|
1052
|
+
const symbol = market['symbol'];
|
1053
|
+
const timestamp = this.parse8601 (this.safeString (trade, 'time'));
|
1054
|
+
const priceString = this.safeString (trade, 'price');
|
1055
|
+
const amountString = this.safeString (trade, 'size');
|
1056
|
+
const side = this.safeString (trade, 'side');
|
1057
|
+
let fee = undefined;
|
1058
|
+
const feeCostString = this.safeString (trade, 'fee');
|
1059
|
+
if (feeCostString !== undefined) {
|
1060
|
+
const feeCurrencyId = this.safeString (trade, 'feeCurrency');
|
1061
|
+
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
|
1062
|
+
fee = {
|
1063
|
+
'cost': feeCostString,
|
1064
|
+
'currency': feeCurrencyCode,
|
1065
|
+
'rate': this.safeString (trade, 'feeRate'),
|
1066
|
+
};
|
1067
|
+
}
|
1068
|
+
const orderId = this.safeString (trade, 'orderId');
|
1069
|
+
return this.safeTrade ({
|
1070
|
+
'info': trade,
|
1071
|
+
'timestamp': timestamp,
|
1072
|
+
'datetime': this.iso8601 (timestamp),
|
1073
|
+
'symbol': symbol,
|
1074
|
+
'id': id,
|
1075
|
+
'order': orderId,
|
1076
|
+
'type': undefined,
|
1077
|
+
'takerOrMaker': takerOrMaker,
|
1078
|
+
'side': side,
|
1079
|
+
'price': priceString,
|
1080
|
+
'amount': amountString,
|
1081
|
+
'cost': undefined,
|
1082
|
+
'fee': fee,
|
1083
|
+
}, market);
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
1087
|
+
await this.loadMarkets ();
|
1088
|
+
const [ market, marketId ] = this.getMarketParams (symbol, 'market_name', params);
|
1089
|
+
const request = {
|
1090
|
+
'market_name': marketId,
|
1091
|
+
};
|
1092
|
+
if (since !== undefined) {
|
1093
|
+
// the exchange aligns results to end_time returning 5000 trades max
|
1094
|
+
// the user must set the end_time (in seconds) close enough to start_time
|
1095
|
+
// for a proper pagination, fetch the most recent trades first
|
1096
|
+
// then set the end_time parameter to the timestamp of the last trade
|
1097
|
+
// start_time and end_time must be in seconds, divided by a thousand
|
1098
|
+
request['start_time'] = parseInt (since / 1000);
|
1099
|
+
// start_time doesn't work without end_time
|
1100
|
+
request['end_time'] = this.seconds ();
|
1101
|
+
}
|
1102
|
+
if (limit !== undefined) {
|
1103
|
+
request['limit'] = limit;
|
1104
|
+
}
|
1105
|
+
const response = await this.publicGetMarketsMarketNameTrades (this.extend (request, params));
|
1106
|
+
//
|
1107
|
+
// {
|
1108
|
+
// "success":true,
|
1109
|
+
// "result":[
|
1110
|
+
// {
|
1111
|
+
// "id":1715826,
|
1112
|
+
// "liquidation":false,
|
1113
|
+
// "price":171.62,
|
1114
|
+
// "side":"buy",
|
1115
|
+
// "size":2.095,
|
1116
|
+
// "time":"2019-10-18T12:59:54.288166+00:00"
|
1117
|
+
// },
|
1118
|
+
// {
|
1119
|
+
// "id":1715763,
|
1120
|
+
// "liquidation":false,
|
1121
|
+
// "price":171.89,
|
1122
|
+
// "side":"sell",
|
1123
|
+
// "size":1.477,
|
1124
|
+
// "time":"2019-10-18T12:58:38.443734+00:00"
|
1125
|
+
// },
|
1126
|
+
// ],
|
1127
|
+
// }
|
1128
|
+
//
|
1129
|
+
const result = this.safeValue (response, 'result', []);
|
1130
|
+
return this.parseTrades (result, market, since, limit);
|
1131
|
+
}
|
1132
|
+
|
1133
|
+
async fetchTradingFees (params = {}) {
|
1134
|
+
await this.loadMarkets ();
|
1135
|
+
const response = await this.privateGetAccount (params);
|
1136
|
+
//
|
1137
|
+
// {
|
1138
|
+
// "success": true,
|
1139
|
+
// "result": {
|
1140
|
+
// "backstopProvider": true,
|
1141
|
+
// "collateral": 3568181.02691129,
|
1142
|
+
// "freeCollateral": 1786071.456884368,
|
1143
|
+
// "initialMarginRequirement": 0.12222384240257728,
|
1144
|
+
// "liquidating": false,
|
1145
|
+
// "maintenanceMarginRequirement": 0.07177992558058484,
|
1146
|
+
// "makerFee": 0.0002,
|
1147
|
+
// "marginFraction": 0.5588433331419503,
|
1148
|
+
// "openMarginFraction": 0.2447194090423075,
|
1149
|
+
// "takerFee": 0.0005,
|
1150
|
+
// "totalAccountValue": 3568180.98341129,
|
1151
|
+
// "totalPositionSize": 6384939.6992,
|
1152
|
+
// "username": "user@domain.com",
|
1153
|
+
// "positions": [
|
1154
|
+
// {
|
1155
|
+
// "cost": -31.7906,
|
1156
|
+
// "entryPrice": 138.22,
|
1157
|
+
// "future": "ETH-PERP",
|
1158
|
+
// "initialMarginRequirement": 0.1,
|
1159
|
+
// "longOrderSize": 1744.55,
|
1160
|
+
// "maintenanceMarginRequirement": 0.04,
|
1161
|
+
// "netSize": -0.23,
|
1162
|
+
// "openSize": 1744.32,
|
1163
|
+
// "realizedPnl": 3.39441714,
|
1164
|
+
// "shortOrderSize": 1732.09,
|
1165
|
+
// "side": "sell",
|
1166
|
+
// "size": 0.23,
|
1167
|
+
// "unrealizedPnl": 0,
|
1168
|
+
// },
|
1169
|
+
// ],
|
1170
|
+
// },
|
1171
|
+
// }
|
1172
|
+
//
|
1173
|
+
const result = this.safeValue (response, 'result', {});
|
1174
|
+
const maker = this.safeNumber (result, 'makerFee');
|
1175
|
+
const taker = this.safeNumber (result, 'takerFee');
|
1176
|
+
const tradingFees = {};
|
1177
|
+
for (let i = 0; i < this.symbols.length; i++) {
|
1178
|
+
const symbol = this.symbols[i];
|
1179
|
+
tradingFees[symbol] = {
|
1180
|
+
'info': response,
|
1181
|
+
'symbol': symbol,
|
1182
|
+
'maker': maker,
|
1183
|
+
'taker': taker,
|
1184
|
+
'percentage': true,
|
1185
|
+
'tierBased': true,
|
1186
|
+
};
|
1187
|
+
}
|
1188
|
+
return tradingFees;
|
1189
|
+
}
|
1190
|
+
|
1191
|
+
async fetchFundingRateHistory (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
1192
|
+
//
|
1193
|
+
// Gets a history of funding rates with their timestamps
|
1194
|
+
// (param) symbol: Future currency pair (e.g. "BTC-PERP")
|
1195
|
+
// (param) limit: Not used by ftx
|
1196
|
+
// (param) since: Unix timestamp in miliseconds for the time of the earliest requested funding rate
|
1197
|
+
// (param) params: Object containing more params for the request
|
1198
|
+
// - until: Unix timestamp in miliseconds for the time of the earliest requested funding rate
|
1199
|
+
// return: [{symbol, fundingRate, timestamp}]
|
1200
|
+
//
|
1201
|
+
await this.loadMarkets ();
|
1202
|
+
const request = {};
|
1203
|
+
if (symbol !== undefined) {
|
1204
|
+
const market = this.market (symbol);
|
1205
|
+
symbol = market['symbol'];
|
1206
|
+
request['future'] = market['id'];
|
1207
|
+
}
|
1208
|
+
if (since !== undefined) {
|
1209
|
+
request['start_time'] = parseInt (since / 1000);
|
1210
|
+
}
|
1211
|
+
const till = this.safeInteger (params, 'till'); // unified in milliseconds
|
1212
|
+
const endTime = this.safeString (params, 'end_time'); // exchange-specific in seconds
|
1213
|
+
params = this.omit (params, [ 'end_time', 'till' ]);
|
1214
|
+
if (till !== undefined) {
|
1215
|
+
request['end_time'] = parseInt (till / 1000);
|
1216
|
+
} else if (endTime !== undefined) {
|
1217
|
+
request['end_time'] = endTime;
|
1218
|
+
}
|
1219
|
+
const response = await this.publicGetFundingRates (this.extend (request, params));
|
1220
|
+
//
|
1221
|
+
// {
|
1222
|
+
// "success": true,
|
1223
|
+
// "result": [
|
1224
|
+
// {
|
1225
|
+
// "future": "BTC-PERP",
|
1226
|
+
// "rate": 0.0025,
|
1227
|
+
// "time": "2019-06-02T08:00:00+00:00"
|
1228
|
+
// }
|
1229
|
+
// ]
|
1230
|
+
// }
|
1231
|
+
//
|
1232
|
+
const result = this.safeValue (response, 'result');
|
1233
|
+
const rates = [];
|
1234
|
+
for (let i = 0; i < result.length; i++) {
|
1235
|
+
const entry = result[i];
|
1236
|
+
const marketId = this.safeString (entry, 'future');
|
1237
|
+
const timestamp = this.parse8601 (this.safeString (result[i], 'time'));
|
1238
|
+
rates.push ({
|
1239
|
+
'info': entry,
|
1240
|
+
'symbol': this.safeSymbol (marketId),
|
1241
|
+
'fundingRate': this.safeNumber (entry, 'rate'),
|
1242
|
+
'timestamp': timestamp,
|
1243
|
+
'datetime': this.iso8601 (timestamp),
|
1244
|
+
});
|
1245
|
+
}
|
1246
|
+
const sorted = this.sortBy (rates, 'timestamp');
|
1247
|
+
return this.filterBySymbolSinceLimit (sorted, symbol, since, limit);
|
1248
|
+
}
|
1249
|
+
|
1250
|
+
parseBalance (response) {
|
1251
|
+
const result = {
|
1252
|
+
'info': response,
|
1253
|
+
};
|
1254
|
+
const balances = this.safeValue (response, 'result', []);
|
1255
|
+
for (let i = 0; i < balances.length; i++) {
|
1256
|
+
const balance = balances[i];
|
1257
|
+
const code = this.safeCurrencyCode (this.safeString (balance, 'coin'));
|
1258
|
+
const account = this.account ();
|
1259
|
+
account['free'] = this.safeString2 (balance, 'availableWithoutBorrow', 'free');
|
1260
|
+
account['total'] = this.safeString (balance, 'total');
|
1261
|
+
result[code] = account;
|
1262
|
+
}
|
1263
|
+
return this.safeBalance (result);
|
1264
|
+
}
|
1265
|
+
|
1266
|
+
async fetchBalance (params = {}) {
|
1267
|
+
await this.loadMarkets ();
|
1268
|
+
const response = await this.privateGetWalletBalances (params);
|
1269
|
+
//
|
1270
|
+
// {
|
1271
|
+
// "success": true,
|
1272
|
+
// "result": [
|
1273
|
+
// {
|
1274
|
+
// "coin": "USDTBEAR",
|
1275
|
+
// "free": 2320.2,
|
1276
|
+
// "total": 2340.2
|
1277
|
+
// },
|
1278
|
+
// ],
|
1279
|
+
// }
|
1280
|
+
//
|
1281
|
+
return this.parseBalance (response);
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
parseOrderStatus (status) {
|
1285
|
+
const statuses = {
|
1286
|
+
'new': 'open',
|
1287
|
+
'open': 'open',
|
1288
|
+
'closed': 'closed', // filled or canceled
|
1289
|
+
'triggered': 'closed',
|
1290
|
+
};
|
1291
|
+
return this.safeString (statuses, status, status);
|
1292
|
+
}
|
1293
|
+
|
1294
|
+
parseOrder (order, market = undefined) {
|
1295
|
+
//
|
1296
|
+
// limit orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder, editOrder
|
1297
|
+
//
|
1298
|
+
// {
|
1299
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1300
|
+
// "filledSize": 0,
|
1301
|
+
// "future": "XRP-PERP",
|
1302
|
+
// "id": 9596912,
|
1303
|
+
// "market": "XRP-PERP",
|
1304
|
+
// "price": 0.306525,
|
1305
|
+
// "remainingSize": 31431,
|
1306
|
+
// "side": "sell",
|
1307
|
+
// "size": 31431,
|
1308
|
+
// "status": "open",
|
1309
|
+
// "type": "limit",
|
1310
|
+
// "reduceOnly": false,
|
1311
|
+
// "ioc": false,
|
1312
|
+
// "postOnly": false,
|
1313
|
+
// "clientId": null,
|
1314
|
+
// }
|
1315
|
+
//
|
1316
|
+
// market orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder
|
1317
|
+
//
|
1318
|
+
// {
|
1319
|
+
// "avgFillPrice": 2666.0,
|
1320
|
+
// "clientId": None,
|
1321
|
+
// "createdAt": "2020-02-12T00: 53: 49.009726+00: 00",
|
1322
|
+
// "filledSize": 0.0007,
|
1323
|
+
// "future": None,
|
1324
|
+
// "id": 3109208514,
|
1325
|
+
// "ioc": True,
|
1326
|
+
// "market": "BNBBULL/USD",
|
1327
|
+
// "postOnly": False,
|
1328
|
+
// "price": None,
|
1329
|
+
// "reduceOnly": False,
|
1330
|
+
// "remainingSize": 0.0,
|
1331
|
+
// "side": "buy",
|
1332
|
+
// "size": 0.0007,
|
1333
|
+
// "status": "closed",
|
1334
|
+
// "type": "market"
|
1335
|
+
// }
|
1336
|
+
//
|
1337
|
+
// createOrder (conditional, "stop", "trailingStop", or "takeProfit")
|
1338
|
+
//
|
1339
|
+
// {
|
1340
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1341
|
+
// "future": "XRP-PERP",
|
1342
|
+
// "id": 9596912,
|
1343
|
+
// "market": "XRP-PERP",
|
1344
|
+
// "triggerPrice": 0.306525,
|
1345
|
+
// "orderId": null,
|
1346
|
+
// "side": "sell",
|
1347
|
+
// "size": 31431,
|
1348
|
+
// "status": "open",
|
1349
|
+
// "type": "stop",
|
1350
|
+
// "orderPrice": null,
|
1351
|
+
// "error": null,
|
1352
|
+
// "triggeredAt": null,
|
1353
|
+
// "reduceOnly": false
|
1354
|
+
// }
|
1355
|
+
//
|
1356
|
+
// editOrder (conditional, stop, trailing stop, take profit)
|
1357
|
+
//
|
1358
|
+
// {
|
1359
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1360
|
+
// "future": "XRP-PERP",
|
1361
|
+
// "id": 9596912,
|
1362
|
+
// "market": "XRP-PERP",
|
1363
|
+
// "triggerPrice": 0.306225,
|
1364
|
+
// "orderId": null,
|
1365
|
+
// "side": "sell",
|
1366
|
+
// "size": 31431,
|
1367
|
+
// "status": "open",
|
1368
|
+
// "type": "stop",
|
1369
|
+
// "orderPrice": null,
|
1370
|
+
// "error": null,
|
1371
|
+
// "triggeredAt": null,
|
1372
|
+
// "reduceOnly": false,
|
1373
|
+
// "orderType": "market",
|
1374
|
+
// "filledSize": 0,
|
1375
|
+
// "avgFillPrice": null,
|
1376
|
+
// "retryUntilFilled": false
|
1377
|
+
// }
|
1378
|
+
//
|
1379
|
+
// canceled order with a closed status
|
1380
|
+
//
|
1381
|
+
// {
|
1382
|
+
// "avgFillPrice":null,
|
1383
|
+
// "clientId":null,
|
1384
|
+
// "createdAt":"2020-09-01T13:45:57.119695+00:00",
|
1385
|
+
// "filledSize":0.0,
|
1386
|
+
// "future":null,
|
1387
|
+
// "id":8553541288,
|
1388
|
+
// "ioc":false,
|
1389
|
+
// "liquidation":false,
|
1390
|
+
// "market":"XRP/USDT",
|
1391
|
+
// "postOnly":false,
|
1392
|
+
// "price":0.5,
|
1393
|
+
// "reduceOnly":false,
|
1394
|
+
// "remainingSize":0.0,
|
1395
|
+
// "side":"sell",
|
1396
|
+
// "size":46.0,
|
1397
|
+
// "status":"closed",
|
1398
|
+
// "type":"limit"
|
1399
|
+
// }
|
1400
|
+
//
|
1401
|
+
const id = this.safeString (order, 'id');
|
1402
|
+
const timestamp = this.parse8601 (this.safeString (order, 'createdAt'));
|
1403
|
+
let status = this.parseOrderStatus (this.safeString (order, 'status'));
|
1404
|
+
const amount = this.safeString (order, 'size');
|
1405
|
+
const filled = this.safeString (order, 'filledSize');
|
1406
|
+
let remaining = this.safeString (order, 'remainingSize');
|
1407
|
+
if (Precise.stringEquals (remaining, '0')) {
|
1408
|
+
remaining = Precise.stringSub (amount, filled);
|
1409
|
+
if (Precise.stringGt (remaining, '0')) {
|
1410
|
+
status = 'canceled';
|
1411
|
+
}
|
1412
|
+
}
|
1413
|
+
const marketId = this.safeString (order, 'market');
|
1414
|
+
market = this.safeMarket (marketId, market);
|
1415
|
+
let symbol = market['symbol'];
|
1416
|
+
if (symbol === undefined) {
|
1417
|
+
// support for delisted market ids
|
1418
|
+
// https://github.com/ccxt/ccxt/issues/7113
|
1419
|
+
symbol = marketId;
|
1420
|
+
}
|
1421
|
+
const side = this.safeString (order, 'side');
|
1422
|
+
const type = this.safeString (order, 'type');
|
1423
|
+
const average = this.safeString (order, 'avgFillPrice');
|
1424
|
+
const price = this.safeString2 (order, 'price', 'triggerPrice', average);
|
1425
|
+
const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'triggeredAt'));
|
1426
|
+
const clientOrderId = this.safeString (order, 'clientId');
|
1427
|
+
const stopPrice = this.safeNumber (order, 'triggerPrice');
|
1428
|
+
const postOnly = this.safeValue (order, 'postOnly');
|
1429
|
+
return this.safeOrder ({
|
1430
|
+
'info': order,
|
1431
|
+
'id': id,
|
1432
|
+
'clientOrderId': clientOrderId,
|
1433
|
+
'timestamp': timestamp,
|
1434
|
+
'datetime': this.iso8601 (timestamp),
|
1435
|
+
'lastTradeTimestamp': lastTradeTimestamp,
|
1436
|
+
'symbol': symbol,
|
1437
|
+
'type': type,
|
1438
|
+
'timeInForce': undefined,
|
1439
|
+
'postOnly': postOnly,
|
1440
|
+
'side': side,
|
1441
|
+
'price': price,
|
1442
|
+
'stopPrice': stopPrice,
|
1443
|
+
'amount': amount,
|
1444
|
+
'cost': undefined,
|
1445
|
+
'average': average,
|
1446
|
+
'filled': filled,
|
1447
|
+
'remaining': remaining,
|
1448
|
+
'status': status,
|
1449
|
+
'fee': undefined,
|
1450
|
+
'trades': undefined,
|
1451
|
+
}, market);
|
1452
|
+
}
|
1453
|
+
|
1454
|
+
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
1455
|
+
await this.loadMarkets ();
|
1456
|
+
const market = this.market (symbol);
|
1457
|
+
const request = {
|
1458
|
+
'market': market['id'],
|
1459
|
+
'side': side, // "buy" or "sell"
|
1460
|
+
// 'price': 0.306525, // send null for market orders
|
1461
|
+
'type': type, // "limit", "market", "stop", "trailingStop", or "takeProfit"
|
1462
|
+
'size': parseFloat (this.amountToPrecision (symbol, amount)),
|
1463
|
+
// 'reduceOnly': false, // optional, default is false
|
1464
|
+
// 'ioc': false, // optional, default is false, limit or market orders only
|
1465
|
+
// 'postOnly': false, // optional, default is false, limit or market orders only
|
1466
|
+
// 'clientId': 'abcdef0123456789', // string, optional, client order id, limit or market orders only
|
1467
|
+
};
|
1468
|
+
const clientOrderId = this.safeString2 (params, 'clientId', 'clientOrderId');
|
1469
|
+
if (clientOrderId !== undefined) {
|
1470
|
+
request['clientId'] = clientOrderId;
|
1471
|
+
params = this.omit (params, [ 'clientId', 'clientOrderId' ]);
|
1472
|
+
}
|
1473
|
+
let method = undefined;
|
1474
|
+
if (type === 'limit') {
|
1475
|
+
method = 'privatePostOrders';
|
1476
|
+
request['price'] = parseFloat (this.priceToPrecision (symbol, price));
|
1477
|
+
} else if (type === 'market') {
|
1478
|
+
method = 'privatePostOrders';
|
1479
|
+
request['price'] = null;
|
1480
|
+
} else if ((type === 'stop') || (type === 'takeProfit')) {
|
1481
|
+
method = 'privatePostConditionalOrders';
|
1482
|
+
const stopPrice = this.safeNumber2 (params, 'stopPrice', 'triggerPrice');
|
1483
|
+
if (stopPrice === undefined) {
|
1484
|
+
throw new ArgumentsRequired (this.id + ' createOrder() requires a stopPrice parameter or a triggerPrice parameter for ' + type + ' orders');
|
1485
|
+
} else {
|
1486
|
+
params = this.omit (params, [ 'stopPrice', 'triggerPrice' ]);
|
1487
|
+
request['triggerPrice'] = parseFloat (this.priceToPrecision (symbol, stopPrice));
|
1488
|
+
}
|
1489
|
+
if (price !== undefined) {
|
1490
|
+
request['orderPrice'] = parseFloat (this.priceToPrecision (symbol, price)); // optional, order type is limit if this is specified, otherwise market
|
1491
|
+
}
|
1492
|
+
} else if (type === 'trailingStop') {
|
1493
|
+
const trailValue = this.safeNumber (params, 'trailValue', price);
|
1494
|
+
if (trailValue === undefined) {
|
1495
|
+
throw new ArgumentsRequired (this.id + ' createOrder() requires a trailValue parameter or a price argument (negative or positive) for a ' + type + ' order');
|
1496
|
+
}
|
1497
|
+
method = 'privatePostConditionalOrders';
|
1498
|
+
request['trailValue'] = parseFloat (this.priceToPrecision (symbol, trailValue)); // negative for "sell", positive for "buy"
|
1499
|
+
} else {
|
1500
|
+
throw new InvalidOrder (this.id + ' createOrder() does not support order type ' + type + ', only limit, market, stop, trailingStop, or takeProfit orders are supported');
|
1501
|
+
}
|
1502
|
+
const response = await this[method] (this.extend (request, params));
|
1503
|
+
//
|
1504
|
+
// orders
|
1505
|
+
//
|
1506
|
+
// {
|
1507
|
+
// "success": true,
|
1508
|
+
// "result": [
|
1509
|
+
// {
|
1510
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1511
|
+
// "filledSize": 0,
|
1512
|
+
// "future": "XRP-PERP",
|
1513
|
+
// "id": 9596912,
|
1514
|
+
// "market": "XRP-PERP",
|
1515
|
+
// "price": 0.306525,
|
1516
|
+
// "remainingSize": 31431,
|
1517
|
+
// "side": "sell",
|
1518
|
+
// "size": 31431,
|
1519
|
+
// "status": "open",
|
1520
|
+
// "type": "limit",
|
1521
|
+
// "reduceOnly": false,
|
1522
|
+
// "ioc": false,
|
1523
|
+
// "postOnly": false,
|
1524
|
+
// "clientId": null,
|
1525
|
+
// }
|
1526
|
+
// ]
|
1527
|
+
// }
|
1528
|
+
//
|
1529
|
+
// conditional orders
|
1530
|
+
//
|
1531
|
+
// {
|
1532
|
+
// "success": true,
|
1533
|
+
// "result": [
|
1534
|
+
// {
|
1535
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1536
|
+
// "future": "XRP-PERP",
|
1537
|
+
// "id": 9596912,
|
1538
|
+
// "market": "XRP-PERP",
|
1539
|
+
// "triggerPrice": 0.306525,
|
1540
|
+
// "orderId": null,
|
1541
|
+
// "side": "sell",
|
1542
|
+
// "size": 31431,
|
1543
|
+
// "status": "open",
|
1544
|
+
// "type": "stop",
|
1545
|
+
// "orderPrice": null,
|
1546
|
+
// "error": null,
|
1547
|
+
// "triggeredAt": null,
|
1548
|
+
// "reduceOnly": false
|
1549
|
+
// }
|
1550
|
+
// ]
|
1551
|
+
// }
|
1552
|
+
//
|
1553
|
+
//
|
1554
|
+
const result = this.safeValue (response, 'result', []);
|
1555
|
+
return this.parseOrder (result, market);
|
1556
|
+
}
|
1557
|
+
|
1558
|
+
async createReduceOnlyOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
1559
|
+
const request = {
|
1560
|
+
'reduceOnly': true,
|
1561
|
+
};
|
1562
|
+
return await this.createOrder (symbol, type, side, amount, price, this.extend (request, params));
|
1563
|
+
}
|
1564
|
+
|
1565
|
+
async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) {
|
1566
|
+
await this.loadMarkets ();
|
1567
|
+
const market = this.market (symbol);
|
1568
|
+
const request = {};
|
1569
|
+
let method = undefined;
|
1570
|
+
const clientOrderId = this.safeString2 (params, 'client_order_id', 'clientOrderId');
|
1571
|
+
const triggerPrice = this.safeNumber (params, 'triggerPrice');
|
1572
|
+
const orderPrice = this.safeNumber (params, 'orderPrice');
|
1573
|
+
const trailValue = this.safeNumber (params, 'trailValue');
|
1574
|
+
params = this.omit (params, [ 'client_order_id', 'clientOrderId', 'triggerPrice', 'orderPrice', 'trailValue' ]);
|
1575
|
+
const triggerPriceIsDefined = (triggerPrice !== undefined);
|
1576
|
+
const orderPriceIsDefined = (orderPrice !== undefined);
|
1577
|
+
const trailValueIsDefined = (trailValue !== undefined);
|
1578
|
+
if (triggerPriceIsDefined || orderPriceIsDefined || trailValueIsDefined) {
|
1579
|
+
method = 'privatePostConditionalOrdersOrderIdModify';
|
1580
|
+
request['order_id'] = id;
|
1581
|
+
if (triggerPriceIsDefined) {
|
1582
|
+
request['triggerPrice'] = parseFloat (this.priceToPrecision (symbol, triggerPrice));
|
1583
|
+
}
|
1584
|
+
if (orderPriceIsDefined) {
|
1585
|
+
// only for stop limit or take profit limit orders
|
1586
|
+
request['orderPrice'] = parseFloat (this.priceToPrecision (symbol, orderPrice));
|
1587
|
+
}
|
1588
|
+
if (trailValueIsDefined) {
|
1589
|
+
// negative for sell orders, positive for buy orders
|
1590
|
+
request['trailValue'] = parseFloat (this.priceToPrecision (symbol, trailValue));
|
1591
|
+
}
|
1592
|
+
} else {
|
1593
|
+
if (clientOrderId === undefined) {
|
1594
|
+
method = 'privatePostOrdersOrderIdModify';
|
1595
|
+
request['order_id'] = id;
|
1596
|
+
} else {
|
1597
|
+
method = 'privatePostOrdersByClientIdClientOrderIdModify';
|
1598
|
+
request['client_order_id'] = clientOrderId;
|
1599
|
+
// request['clientId'] = clientOrderId;
|
1600
|
+
}
|
1601
|
+
if (price !== undefined) {
|
1602
|
+
request['price'] = parseFloat (this.priceToPrecision (symbol, price));
|
1603
|
+
}
|
1604
|
+
}
|
1605
|
+
if (amount !== undefined) {
|
1606
|
+
request['size'] = parseFloat (this.amountToPrecision (symbol, amount));
|
1607
|
+
}
|
1608
|
+
const response = await this[method] (this.extend (request, params));
|
1609
|
+
//
|
1610
|
+
// regular order
|
1611
|
+
//
|
1612
|
+
// {
|
1613
|
+
// "success": true,
|
1614
|
+
// "result": {
|
1615
|
+
// "createdAt": "2019-03-05T11:56:55.728933+00:00",
|
1616
|
+
// "filledSize": 0,
|
1617
|
+
// "future": "XRP-PERP",
|
1618
|
+
// "id": 9596932,
|
1619
|
+
// "market": "XRP-PERP",
|
1620
|
+
// "price": 0.326525,
|
1621
|
+
// "remainingSize": 31431,
|
1622
|
+
// "side": "sell",
|
1623
|
+
// "size": 31431,
|
1624
|
+
// "status": "open",
|
1625
|
+
// "type": "limit",
|
1626
|
+
// "reduceOnly": false,
|
1627
|
+
// "ioc": false,
|
1628
|
+
// "postOnly": false,
|
1629
|
+
// "clientId": null,
|
1630
|
+
// }
|
1631
|
+
// }
|
1632
|
+
//
|
1633
|
+
// conditional trigger order
|
1634
|
+
//
|
1635
|
+
// {
|
1636
|
+
// "success": true,
|
1637
|
+
// "result": {
|
1638
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1639
|
+
// "future": "XRP-PERP",
|
1640
|
+
// "id": 9596912,
|
1641
|
+
// "market": "XRP-PERP",
|
1642
|
+
// "triggerPrice": 0.306225,
|
1643
|
+
// "orderId": null,
|
1644
|
+
// "side": "sell",
|
1645
|
+
// "size": 31431,
|
1646
|
+
// "status": "open",
|
1647
|
+
// "type": "stop",
|
1648
|
+
// "orderPrice": null,
|
1649
|
+
// "error": null,
|
1650
|
+
// "triggeredAt": null,
|
1651
|
+
// "reduceOnly": false,
|
1652
|
+
// "orderType": "market",
|
1653
|
+
// "filledSize": 0,
|
1654
|
+
// "avgFillPrice": null,
|
1655
|
+
// "retryUntilFilled": false
|
1656
|
+
// }
|
1657
|
+
// }
|
1658
|
+
//
|
1659
|
+
const result = this.safeValue (response, 'result', {});
|
1660
|
+
return this.parseOrder (result, market);
|
1661
|
+
}
|
1662
|
+
|
1663
|
+
async cancelOrder (id, symbol = undefined, params = {}) {
|
1664
|
+
await this.loadMarkets ();
|
1665
|
+
const request = {};
|
1666
|
+
// support for canceling conditional orders
|
1667
|
+
// https://github.com/ccxt/ccxt/issues/6669
|
1668
|
+
const options = this.safeValue (this.options, 'cancelOrder', {});
|
1669
|
+
const defaultMethod = this.safeString (options, 'method', 'privateDeleteOrdersOrderId');
|
1670
|
+
let method = this.safeString (params, 'method', defaultMethod);
|
1671
|
+
const type = this.safeValue (params, 'type');
|
1672
|
+
const clientOrderId = this.safeValue2 (params, 'client_order_id', 'clientOrderId');
|
1673
|
+
if (clientOrderId === undefined) {
|
1674
|
+
request['order_id'] = parseInt (id);
|
1675
|
+
if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) {
|
1676
|
+
method = 'privateDeleteConditionalOrdersOrderId';
|
1677
|
+
}
|
1678
|
+
} else {
|
1679
|
+
request['client_order_id'] = clientOrderId;
|
1680
|
+
method = 'privateDeleteOrdersByClientIdClientOrderId';
|
1681
|
+
}
|
1682
|
+
const query = this.omit (params, [ 'method', 'type', 'client_order_id', 'clientOrderId' ]);
|
1683
|
+
const response = await this[method] (this.extend (request, query));
|
1684
|
+
//
|
1685
|
+
// {
|
1686
|
+
// "success": true,
|
1687
|
+
// "result": "Order queued for cancelation"
|
1688
|
+
// }
|
1689
|
+
//
|
1690
|
+
const result = this.safeValue (response, 'result', {});
|
1691
|
+
return result;
|
1692
|
+
}
|
1693
|
+
|
1694
|
+
async cancelAllOrders (symbol = undefined, params = {}) {
|
1695
|
+
await this.loadMarkets ();
|
1696
|
+
const request = {
|
1697
|
+
// 'market': market['id'], // optional
|
1698
|
+
// 'conditionalOrdersOnly': false, // cancel conditional orders only
|
1699
|
+
// 'limitOrdersOnly': false, // cancel existing limit orders (non-conditional orders) only
|
1700
|
+
};
|
1701
|
+
const marketId = this.getMarketId (symbol, 'market', params);
|
1702
|
+
if (marketId !== undefined) {
|
1703
|
+
request['market'] = marketId;
|
1704
|
+
}
|
1705
|
+
const response = await this.privateDeleteOrders (this.extend (request, params));
|
1706
|
+
const result = this.safeValue (response, 'result', {});
|
1707
|
+
//
|
1708
|
+
// {
|
1709
|
+
// "success": true,
|
1710
|
+
// "result": "Orders queued for cancelation"
|
1711
|
+
// }
|
1712
|
+
//
|
1713
|
+
return result;
|
1714
|
+
}
|
1715
|
+
|
1716
|
+
async fetchOrder (id, symbol = undefined, params = {}) {
|
1717
|
+
await this.loadMarkets ();
|
1718
|
+
const request = {};
|
1719
|
+
const clientOrderId = this.safeValue2 (params, 'client_order_id', 'clientOrderId');
|
1720
|
+
let method = 'privateGetOrdersOrderId';
|
1721
|
+
if (clientOrderId === undefined) {
|
1722
|
+
request['order_id'] = id;
|
1723
|
+
} else {
|
1724
|
+
request['client_order_id'] = clientOrderId;
|
1725
|
+
params = this.omit (params, [ 'client_order_id', 'clientOrderId' ]);
|
1726
|
+
method = 'privateGetOrdersByClientIdClientOrderId';
|
1727
|
+
}
|
1728
|
+
const response = await this[method] (this.extend (request, params));
|
1729
|
+
//
|
1730
|
+
// {
|
1731
|
+
// "success": true,
|
1732
|
+
// "result": {
|
1733
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1734
|
+
// "filledSize": 10,
|
1735
|
+
// "future": "XRP-PERP",
|
1736
|
+
// "id": 9596912,
|
1737
|
+
// "market": "XRP-PERP",
|
1738
|
+
// "price": 0.306525,
|
1739
|
+
// "avgFillPrice": 0.306526,
|
1740
|
+
// "remainingSize": 31421,
|
1741
|
+
// "side": "sell",
|
1742
|
+
// "size": 31431,
|
1743
|
+
// "status": "open",
|
1744
|
+
// "type": "limit",
|
1745
|
+
// "reduceOnly": false,
|
1746
|
+
// "ioc": false,
|
1747
|
+
// "postOnly": false,
|
1748
|
+
// "clientId": null
|
1749
|
+
// }
|
1750
|
+
// }
|
1751
|
+
//
|
1752
|
+
const result = this.safeValue (response, 'result', {});
|
1753
|
+
return this.parseOrder (result);
|
1754
|
+
}
|
1755
|
+
|
1756
|
+
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
1757
|
+
await this.loadMarkets ();
|
1758
|
+
const request = {};
|
1759
|
+
const [ market, marketId ] = this.getMarketParams (symbol, 'market', params);
|
1760
|
+
if (marketId !== undefined) {
|
1761
|
+
request['market'] = marketId;
|
1762
|
+
}
|
1763
|
+
// support for canceling conditional orders
|
1764
|
+
// https://github.com/ccxt/ccxt/issues/6669
|
1765
|
+
const options = this.safeValue (this.options, 'fetchOpenOrders', {});
|
1766
|
+
const defaultMethod = this.safeString (options, 'method', 'privateGetOrders');
|
1767
|
+
let method = this.safeString (params, 'method', defaultMethod);
|
1768
|
+
const type = this.safeValue (params, 'type');
|
1769
|
+
if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) {
|
1770
|
+
method = 'privateGetConditionalOrders';
|
1771
|
+
}
|
1772
|
+
const query = this.omit (params, [ 'method', 'type' ]);
|
1773
|
+
const response = await this[method] (this.extend (request, query));
|
1774
|
+
//
|
1775
|
+
// {
|
1776
|
+
// "success": true,
|
1777
|
+
// "result": [
|
1778
|
+
// {
|
1779
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1780
|
+
// "filledSize": 10,
|
1781
|
+
// "future": "XRP-PERP",
|
1782
|
+
// "id": 9596912,
|
1783
|
+
// "market": "XRP-PERP",
|
1784
|
+
// "price": 0.306525,
|
1785
|
+
// "avgFillPrice": 0.306526,
|
1786
|
+
// "remainingSize": 31421,
|
1787
|
+
// "side": "sell",
|
1788
|
+
// "size": 31431,
|
1789
|
+
// "status": "open",
|
1790
|
+
// "type": "limit",
|
1791
|
+
// "reduceOnly": false,
|
1792
|
+
// "ioc": false,
|
1793
|
+
// "postOnly": false,
|
1794
|
+
// "clientId": null
|
1795
|
+
// }
|
1796
|
+
// ]
|
1797
|
+
// }
|
1798
|
+
//
|
1799
|
+
const result = this.safeValue (response, 'result', []);
|
1800
|
+
return this.parseOrders (result, market, since, limit);
|
1801
|
+
}
|
1802
|
+
|
1803
|
+
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
1804
|
+
await this.loadMarkets ();
|
1805
|
+
const request = {};
|
1806
|
+
const [ market, marketId ] = this.getMarketParams (symbol, 'market', params);
|
1807
|
+
if (marketId !== undefined) {
|
1808
|
+
request['market'] = marketId;
|
1809
|
+
}
|
1810
|
+
if (limit !== undefined) {
|
1811
|
+
request['limit'] = limit; // default 100, max 100
|
1812
|
+
}
|
1813
|
+
if (since !== undefined) {
|
1814
|
+
request['start_time'] = parseInt (since / 1000);
|
1815
|
+
}
|
1816
|
+
// support for canceling conditional orders
|
1817
|
+
// https://github.com/ccxt/ccxt/issues/6669
|
1818
|
+
const options = this.safeValue (this.options, 'fetchOrders', {});
|
1819
|
+
const defaultMethod = this.safeString (options, 'method', 'privateGetOrdersHistory');
|
1820
|
+
let method = this.safeString (params, 'method', defaultMethod);
|
1821
|
+
const type = this.safeValue (params, 'type');
|
1822
|
+
if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) {
|
1823
|
+
method = 'privateGetConditionalOrdersHistory';
|
1824
|
+
}
|
1825
|
+
const query = this.omit (params, [ 'method', 'type' ]);
|
1826
|
+
const response = await this[method] (this.extend (request, query));
|
1827
|
+
//
|
1828
|
+
// {
|
1829
|
+
// "success": true,
|
1830
|
+
// "result": [
|
1831
|
+
// {
|
1832
|
+
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
|
1833
|
+
// "filledSize": 10,
|
1834
|
+
// "future": "XRP-PERP",
|
1835
|
+
// "id": 9596912,
|
1836
|
+
// "market": "XRP-PERP",
|
1837
|
+
// "price": 0.306525,
|
1838
|
+
// "avgFillPrice": 0.306526,
|
1839
|
+
// "remainingSize": 31421,
|
1840
|
+
// "side": "sell",
|
1841
|
+
// "size": 31431,
|
1842
|
+
// "status": "open",
|
1843
|
+
// "type": "limit",
|
1844
|
+
// "reduceOnly": false,
|
1845
|
+
// "ioc": false,
|
1846
|
+
// "postOnly": false,
|
1847
|
+
// "clientId": null
|
1848
|
+
// }
|
1849
|
+
// ]
|
1850
|
+
// }
|
1851
|
+
//
|
1852
|
+
const result = this.safeValue (response, 'result', []);
|
1853
|
+
return this.parseOrders (result, market, since, limit);
|
1854
|
+
}
|
1855
|
+
|
1856
|
+
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
1857
|
+
const request = {
|
1858
|
+
'orderId': id,
|
1859
|
+
};
|
1860
|
+
return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params));
|
1861
|
+
}
|
1862
|
+
|
1863
|
+
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
1864
|
+
await this.loadMarkets ();
|
1865
|
+
const [ market, marketId ] = this.getMarketParams (symbol, 'market', params);
|
1866
|
+
const request = {};
|
1867
|
+
if (marketId !== undefined) {
|
1868
|
+
request['market'] = marketId;
|
1869
|
+
}
|
1870
|
+
if (since !== undefined) {
|
1871
|
+
request['start_time'] = parseInt (since / 1000);
|
1872
|
+
request['end_time'] = this.seconds ();
|
1873
|
+
}
|
1874
|
+
const response = await this.privateGetFills (this.extend (request, params));
|
1875
|
+
//
|
1876
|
+
// {
|
1877
|
+
// "success": true,
|
1878
|
+
// "result": [
|
1879
|
+
// {
|
1880
|
+
// "fee": 20.1374935,
|
1881
|
+
// "feeRate": 0.0005,
|
1882
|
+
// "future": "EOS-0329",
|
1883
|
+
// "id": 11215,
|
1884
|
+
// "liquidity": "taker",
|
1885
|
+
// "market": "EOS-0329",
|
1886
|
+
// "baseCurrency": null,
|
1887
|
+
// "quoteCurrency": null,
|
1888
|
+
// "orderId": 8436981,
|
1889
|
+
// "price": 4.201,
|
1890
|
+
// "side": "buy",
|
1891
|
+
// "size": 9587,
|
1892
|
+
// "time": "2019-03-27T19:15:10.204619+00:00",
|
1893
|
+
// "type": "order"
|
1894
|
+
// }
|
1895
|
+
// ]
|
1896
|
+
// }
|
1897
|
+
//
|
1898
|
+
const trades = this.safeValue (response, 'result', []);
|
1899
|
+
return this.parseTrades (trades, market, since, limit);
|
1900
|
+
}
|
1901
|
+
|
1902
|
+
async transfer (code, amount, fromAccount, toAccount, params = {}) {
|
1903
|
+
await this.loadMarkets ();
|
1904
|
+
const currency = this.currency (code);
|
1905
|
+
const request = {
|
1906
|
+
'coin': currency['id'],
|
1907
|
+
'source': fromAccount,
|
1908
|
+
'destination': toAccount,
|
1909
|
+
'size': amount,
|
1910
|
+
};
|
1911
|
+
const response = await this.privatePostSubaccountsTransfer (this.extend (request, params));
|
1912
|
+
//
|
1913
|
+
// {
|
1914
|
+
// success: true,
|
1915
|
+
// result: {
|
1916
|
+
// id: '31222278',
|
1917
|
+
// coin: 'USDT',
|
1918
|
+
// size: '1.0',
|
1919
|
+
// time: '2022-04-01T11:18:27.194188+00:00',
|
1920
|
+
// notes: 'Transfer from main account to testSubaccount',
|
1921
|
+
// status: 'complete'
|
1922
|
+
// }
|
1923
|
+
// }
|
1924
|
+
//
|
1925
|
+
const result = this.safeValue (response, 'result', {});
|
1926
|
+
return this.parseTransfer (result, currency);
|
1927
|
+
}
|
1928
|
+
|
1929
|
+
parseTransfer (transfer, currency = undefined) {
|
1930
|
+
//
|
1931
|
+
// {
|
1932
|
+
// id: '31222278',
|
1933
|
+
// coin: 'USDT',
|
1934
|
+
// size: '1.0',
|
1935
|
+
// time: '2022-04-01T11:18:27.194188+00:00',
|
1936
|
+
// notes: 'Transfer from main account to testSubaccount',
|
1937
|
+
// status: 'complete'
|
1938
|
+
// }
|
1939
|
+
//
|
1940
|
+
const currencyId = this.safeString (transfer, 'coin');
|
1941
|
+
const notes = this.safeString (transfer, 'notes', '');
|
1942
|
+
const status = this.safeString (transfer, 'status');
|
1943
|
+
const fromTo = notes.replace ('Transfer from ', '');
|
1944
|
+
const parts = fromTo.split (' to ');
|
1945
|
+
let fromAccount = this.safeString (parts, 0);
|
1946
|
+
fromAccount = fromAccount.replace (' account', '');
|
1947
|
+
let toAccount = this.safeString (parts, 1);
|
1948
|
+
toAccount = toAccount.replace (' account', '');
|
1949
|
+
return {
|
1950
|
+
'info': transfer,
|
1951
|
+
'id': this.safeString (transfer, 'id'),
|
1952
|
+
'timestamp': undefined,
|
1953
|
+
'datetime': this.safeString (transfer, 'time'),
|
1954
|
+
'currency': this.safeCurrencyCode (currencyId, currency),
|
1955
|
+
'amount': this.safeNumber (transfer, 'size'),
|
1956
|
+
'fromAccount': fromAccount,
|
1957
|
+
'toAccount': toAccount,
|
1958
|
+
'status': this.parseTransferStatus (status),
|
1959
|
+
};
|
1960
|
+
}
|
1961
|
+
|
1962
|
+
parseTransferStatus (status) {
|
1963
|
+
const statuses = {
|
1964
|
+
'complete': 'ok',
|
1965
|
+
};
|
1966
|
+
return this.safeString (statuses, status, status);
|
1967
|
+
}
|
1968
|
+
|
1969
|
+
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
1970
|
+
[ tag, params ] = this.handleWithdrawTagAndParams (tag, params);
|
1971
|
+
await this.loadMarkets ();
|
1972
|
+
this.checkAddress (address);
|
1973
|
+
const currency = this.currency (code);
|
1974
|
+
const request = {
|
1975
|
+
'coin': currency['id'],
|
1976
|
+
'size': amount,
|
1977
|
+
'address': address,
|
1978
|
+
// 'password': 'string', // optional withdrawal password if it is required for your account
|
1979
|
+
// 'code': '192837', // optional 2fa code if it is required for your account
|
1980
|
+
};
|
1981
|
+
if (this.password !== undefined) {
|
1982
|
+
request['password'] = this.password;
|
1983
|
+
}
|
1984
|
+
if (tag !== undefined) {
|
1985
|
+
request['tag'] = tag;
|
1986
|
+
}
|
1987
|
+
const networks = this.safeValue (this.options, 'networks', {});
|
1988
|
+
let network = this.safeStringUpper (params, 'network'); // this line allows the user to specify either ERC20 or ETH
|
1989
|
+
network = this.safeStringLower (networks, network, network); // handle ERC20>ETH alias
|
1990
|
+
if (network !== undefined) {
|
1991
|
+
request['method'] = network;
|
1992
|
+
params = this.omit (params, 'network');
|
1993
|
+
}
|
1994
|
+
const response = await this.privatePostWalletWithdrawals (this.extend (request, params));
|
1995
|
+
//
|
1996
|
+
// {
|
1997
|
+
// "success": true,
|
1998
|
+
// "result": {
|
1999
|
+
// "coin": "USDTBEAR",
|
2000
|
+
// "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE",
|
2001
|
+
// "tag": "null",
|
2002
|
+
// "fee": 0,
|
2003
|
+
// "id": 1,
|
2004
|
+
// "size": "20.2",
|
2005
|
+
// "status": "requested",
|
2006
|
+
// "time": "2019-03-05T09:56:55.728933+00:00",
|
2007
|
+
// "txid": "null"
|
2008
|
+
// }
|
2009
|
+
// }
|
2010
|
+
//
|
2011
|
+
const result = this.safeValue (response, 'result', {});
|
2012
|
+
return this.parseTransaction (result, currency);
|
2013
|
+
}
|
2014
|
+
|
2015
|
+
async fetchPositions (symbols = undefined, params = {}) {
|
2016
|
+
await this.loadMarkets ();
|
2017
|
+
const request = {
|
2018
|
+
'showAvgPrice': true,
|
2019
|
+
};
|
2020
|
+
const response = await this.privateGetPositions (this.extend (request, params));
|
2021
|
+
//
|
2022
|
+
// {
|
2023
|
+
// "success": true,
|
2024
|
+
// "result": [
|
2025
|
+
// {
|
2026
|
+
// "cost": -31.7906,
|
2027
|
+
// "entryPrice": 138.22,
|
2028
|
+
// "estimatedLiquidationPrice": 152.1,
|
2029
|
+
// "future": "ETH-PERP",
|
2030
|
+
// "initialMarginRequirement": 0.1,
|
2031
|
+
// "longOrderSize": 1744.55,
|
2032
|
+
// "maintenanceMarginRequirement": 0.04,
|
2033
|
+
// "netSize": -0.23,
|
2034
|
+
// "openSize": 1744.32,
|
2035
|
+
// "realizedPnl": 3.39441714,
|
2036
|
+
// "shortOrderSize": 1732.09,
|
2037
|
+
// "recentAverageOpenPrice": 278.98,
|
2038
|
+
// "recentPnl": 2.44,
|
2039
|
+
// "recentBreakEvenPrice": 278.98,
|
2040
|
+
// "side": "sell",
|
2041
|
+
// "size": 0.23,
|
2042
|
+
// "unrealizedPnl": 0,
|
2043
|
+
// "collateralUsed": 3.17906
|
2044
|
+
// }
|
2045
|
+
// ]
|
2046
|
+
// }
|
2047
|
+
//
|
2048
|
+
const result = this.safeValue (response, 'result', []);
|
2049
|
+
const results = [];
|
2050
|
+
for (let i = 0; i < result.length; i++) {
|
2051
|
+
results.push (this.parsePosition (result[i]));
|
2052
|
+
}
|
2053
|
+
return this.filterByArray (results, 'symbol', symbols, false);
|
2054
|
+
}
|
2055
|
+
|
2056
|
+
parsePosition (position, market = undefined) {
|
2057
|
+
//
|
2058
|
+
// {
|
2059
|
+
// "future": "XMR-PERP",
|
2060
|
+
// "size": "0.0",
|
2061
|
+
// "side": "buy",
|
2062
|
+
// "netSize": "0.0",
|
2063
|
+
// "longOrderSize": "0.0",
|
2064
|
+
// "shortOrderSize": "0.0",
|
2065
|
+
// "cost": "0.0",
|
2066
|
+
// "entryPrice": null,
|
2067
|
+
// "unrealizedPnl": "0.0",
|
2068
|
+
// "realizedPnl": "0.0",
|
2069
|
+
// "initialMarginRequirement": "0.02",
|
2070
|
+
// "maintenanceMarginRequirement": "0.006",
|
2071
|
+
// "openSize": "0.0",
|
2072
|
+
// "collateralUsed": "0.0",
|
2073
|
+
// "estimatedLiquidationPrice": null
|
2074
|
+
// }
|
2075
|
+
//
|
2076
|
+
const contractsString = this.safeString (position, 'size');
|
2077
|
+
const rawSide = this.safeString (position, 'side');
|
2078
|
+
const side = (rawSide === 'buy') ? 'long' : 'short';
|
2079
|
+
const marketId = this.safeString (position, 'future');
|
2080
|
+
const symbol = this.safeSymbol (marketId, market);
|
2081
|
+
const liquidationPriceString = this.safeString (position, 'estimatedLiquidationPrice');
|
2082
|
+
const initialMarginPercentage = this.safeString (position, 'initialMarginRequirement');
|
2083
|
+
const leverage = parseInt (Precise.stringDiv ('1', initialMarginPercentage, 0));
|
2084
|
+
// on ftx the entryPrice is actually the mark price
|
2085
|
+
const markPriceString = this.safeString (position, 'entryPrice');
|
2086
|
+
const notionalString = Precise.stringMul (contractsString, markPriceString);
|
2087
|
+
const initialMargin = Precise.stringMul (notionalString, initialMarginPercentage);
|
2088
|
+
const maintenanceMarginPercentageString = this.safeString (position, 'maintenanceMarginRequirement');
|
2089
|
+
const maintenanceMarginString = Precise.stringMul (notionalString, maintenanceMarginPercentageString);
|
2090
|
+
const unrealizedPnlString = this.safeString (position, 'recentPnl');
|
2091
|
+
const percentage = this.parseNumber (Precise.stringMul (Precise.stringDiv (unrealizedPnlString, initialMargin, 4), '100'));
|
2092
|
+
const entryPriceString = this.safeString (position, 'recentAverageOpenPrice');
|
2093
|
+
let difference = undefined;
|
2094
|
+
let collateral = undefined;
|
2095
|
+
let marginRatio = undefined;
|
2096
|
+
if ((entryPriceString !== undefined) && (Precise.stringGt (liquidationPriceString, '0'))) {
|
2097
|
+
// collateral = maintenanceMargin ± ((markPrice - liquidationPrice) * size)
|
2098
|
+
if (side === 'long') {
|
2099
|
+
difference = Precise.stringSub (markPriceString, liquidationPriceString);
|
2100
|
+
} else {
|
2101
|
+
difference = Precise.stringSub (liquidationPriceString, markPriceString);
|
2102
|
+
}
|
2103
|
+
const loss = Precise.stringMul (difference, contractsString);
|
2104
|
+
collateral = Precise.stringAdd (loss, maintenanceMarginString);
|
2105
|
+
marginRatio = this.parseNumber (Precise.stringDiv (maintenanceMarginString, collateral, 4));
|
2106
|
+
}
|
2107
|
+
// ftx has a weird definition of realizedPnl
|
2108
|
+
// it keeps the historical record of the realizedPnl per contract forever
|
2109
|
+
// so we cannot use this data
|
2110
|
+
return {
|
2111
|
+
'info': position,
|
2112
|
+
'symbol': symbol,
|
2113
|
+
'timestamp': undefined,
|
2114
|
+
'datetime': undefined,
|
2115
|
+
'initialMargin': this.parseNumber (initialMargin),
|
2116
|
+
'initialMarginPercentage': this.parseNumber (initialMarginPercentage),
|
2117
|
+
'maintenanceMargin': this.parseNumber (maintenanceMarginString),
|
2118
|
+
'maintenanceMarginPercentage': this.parseNumber (maintenanceMarginPercentageString),
|
2119
|
+
'entryPrice': this.parseNumber (entryPriceString),
|
2120
|
+
'notional': this.parseNumber (notionalString),
|
2121
|
+
'leverage': leverage,
|
2122
|
+
'unrealizedPnl': this.parseNumber (unrealizedPnlString),
|
2123
|
+
'contracts': this.parseNumber (contractsString),
|
2124
|
+
'contractSize': this.safeValue (market, 'contractSize'),
|
2125
|
+
'marginRatio': marginRatio,
|
2126
|
+
'liquidationPrice': this.parseNumber (liquidationPriceString),
|
2127
|
+
'markPrice': this.parseNumber (markPriceString),
|
2128
|
+
'collateral': this.parseNumber (collateral),
|
2129
|
+
'marginType': 'cross',
|
2130
|
+
'side': side,
|
2131
|
+
'percentage': percentage,
|
2132
|
+
};
|
2133
|
+
}
|
2134
|
+
|
2135
|
+
async fetchDepositAddress (code, params = {}) {
|
2136
|
+
await this.loadMarkets ();
|
2137
|
+
const currency = this.currency (code);
|
2138
|
+
const request = {
|
2139
|
+
'coin': currency['id'],
|
2140
|
+
};
|
2141
|
+
const networks = this.safeValue (this.options, 'networks', {});
|
2142
|
+
let network = this.safeStringUpper (params, 'network'); // this line allows the user to specify either ERC20 or ETH
|
2143
|
+
network = this.safeStringLower (networks, network, network); // handle ERC20>ETH alias
|
2144
|
+
if (network !== undefined) {
|
2145
|
+
request['method'] = network;
|
2146
|
+
params = this.omit (params, 'network');
|
2147
|
+
}
|
2148
|
+
const response = await this.privateGetWalletDepositAddressCoin (this.extend (request, params));
|
2149
|
+
//
|
2150
|
+
// {
|
2151
|
+
// "success": true,
|
2152
|
+
// "result": {
|
2153
|
+
// "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE",
|
2154
|
+
// "tag": null,
|
2155
|
+
// "method": "erc20",
|
2156
|
+
// "coin": null
|
2157
|
+
// }
|
2158
|
+
// }
|
2159
|
+
//
|
2160
|
+
const result = this.safeValue (response, 'result', {});
|
2161
|
+
const networkId = this.safeString (result, 'method');
|
2162
|
+
const address = this.safeString (result, 'address');
|
2163
|
+
this.checkAddress (address);
|
2164
|
+
return {
|
2165
|
+
'currency': code,
|
2166
|
+
'address': address,
|
2167
|
+
'tag': this.safeString (result, 'tag'),
|
2168
|
+
'network': this.safeNetwork (networkId),
|
2169
|
+
'info': response,
|
2170
|
+
};
|
2171
|
+
}
|
2172
|
+
|
2173
|
+
safeNetwork (networkId) {
|
2174
|
+
const networksById = {
|
2175
|
+
'trx': 'TRC20',
|
2176
|
+
'erc20': 'ERC20',
|
2177
|
+
'sol': 'SOL',
|
2178
|
+
'bsc': 'BEP20',
|
2179
|
+
'bep2': 'BEP2',
|
2180
|
+
};
|
2181
|
+
return this.safeString (networksById, networkId, networkId);
|
2182
|
+
}
|
2183
|
+
|
2184
|
+
parseTransactionStatus (status) {
|
2185
|
+
const statuses = {
|
2186
|
+
// what are other statuses here?
|
2187
|
+
'confirmed': 'ok', // deposits
|
2188
|
+
'complete': 'ok', // withdrawals
|
2189
|
+
'cancelled': 'canceled', // deposits
|
2190
|
+
};
|
2191
|
+
return this.safeString (statuses, status, status);
|
2192
|
+
}
|
2193
|
+
|
2194
|
+
parseTransaction (transaction, currency = undefined) {
|
2195
|
+
//
|
2196
|
+
// fetchDeposits
|
2197
|
+
//
|
2198
|
+
// airdrop
|
2199
|
+
//
|
2200
|
+
// {
|
2201
|
+
// "id": 9147072,
|
2202
|
+
// "coin": "SRM_LOCKED",
|
2203
|
+
// "size": 3.12,
|
2204
|
+
// "time": "2021-04-27T23:59:03.565983+00:00",
|
2205
|
+
// "notes": "SRM Airdrop for FTT holdings",
|
2206
|
+
// "status": "complete"
|
2207
|
+
// }
|
2208
|
+
//
|
2209
|
+
// regular deposits
|
2210
|
+
//
|
2211
|
+
// {
|
2212
|
+
// "coin": "TUSD",
|
2213
|
+
// "confirmations": 64,
|
2214
|
+
// "confirmedTime": "2019-03-05T09:56:55.728933+00:00",
|
2215
|
+
// "fee": 0,
|
2216
|
+
// "id": 1,
|
2217
|
+
// "sentTime": "2019-03-05T09:56:55.735929+00:00",
|
2218
|
+
// "size": "99.0",
|
2219
|
+
// "status": "confirmed",
|
2220
|
+
// "time": "2019-03-05T09:56:55.728933+00:00",
|
2221
|
+
// "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1"
|
2222
|
+
// }
|
2223
|
+
//
|
2224
|
+
// fetchWithdrawals
|
2225
|
+
//
|
2226
|
+
// {
|
2227
|
+
// "coin": "TUSD",
|
2228
|
+
// "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE",
|
2229
|
+
// "tag": "null",
|
2230
|
+
// "fee": 0,
|
2231
|
+
// "id": 1,
|
2232
|
+
// "size": "99.0",
|
2233
|
+
// "status": "complete",
|
2234
|
+
// "time": "2019-03-05T09:56:55.728933+00:00",
|
2235
|
+
// "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1"
|
2236
|
+
// }
|
2237
|
+
//
|
2238
|
+
// {
|
2239
|
+
// "coin": 'BTC',
|
2240
|
+
// "id": 1969806,
|
2241
|
+
// "notes": 'Transfer to Dd6gi7m2Eg4zzBbPAxuwfEaHs6tYvyUX5hbPpsTcNPXo',
|
2242
|
+
// "size": 0.003,
|
2243
|
+
// "status": 'complete',
|
2244
|
+
// "time": '2021-02-03T20:28:54.918146+00:00'
|
2245
|
+
// }
|
2246
|
+
//
|
2247
|
+
const code = this.safeCurrencyCode (this.safeString (transaction, 'coin'));
|
2248
|
+
const id = this.safeString (transaction, 'id');
|
2249
|
+
const amount = this.safeNumber (transaction, 'size');
|
2250
|
+
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
|
2251
|
+
const timestamp = this.parse8601 (this.safeString (transaction, 'time'));
|
2252
|
+
const txid = this.safeString (transaction, 'txid');
|
2253
|
+
let tag = undefined;
|
2254
|
+
let address = this.safeValue (transaction, 'address');
|
2255
|
+
if (typeof address !== 'string') {
|
2256
|
+
tag = this.safeString (address, 'tag');
|
2257
|
+
address = this.safeString (address, 'address');
|
2258
|
+
}
|
2259
|
+
if (address === undefined) {
|
2260
|
+
// parse address from internal transfer
|
2261
|
+
const notes = this.safeString (transaction, 'notes');
|
2262
|
+
if ((notes !== undefined) && (notes.indexOf ('Transfer to') >= 0)) {
|
2263
|
+
address = notes.slice (12);
|
2264
|
+
}
|
2265
|
+
}
|
2266
|
+
const fee = this.safeNumber (transaction, 'fee');
|
2267
|
+
return {
|
2268
|
+
'info': transaction,
|
2269
|
+
'id': id,
|
2270
|
+
'txid': txid,
|
2271
|
+
'timestamp': timestamp,
|
2272
|
+
'datetime': this.iso8601 (timestamp),
|
2273
|
+
'network': undefined,
|
2274
|
+
'addressFrom': undefined,
|
2275
|
+
'address': address,
|
2276
|
+
'addressTo': address,
|
2277
|
+
'tagFrom': undefined,
|
2278
|
+
'tag': tag,
|
2279
|
+
'tagTo': tag,
|
2280
|
+
'type': undefined,
|
2281
|
+
'amount': amount,
|
2282
|
+
'currency': code,
|
2283
|
+
'status': status,
|
2284
|
+
'updated': undefined,
|
2285
|
+
'fee': {
|
2286
|
+
'currency': code,
|
2287
|
+
'cost': fee,
|
2288
|
+
'rate': undefined,
|
2289
|
+
},
|
2290
|
+
};
|
2291
|
+
}
|
2292
|
+
|
2293
|
+
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
|
2294
|
+
await this.loadMarkets ();
|
2295
|
+
const response = await this.privateGetWalletDeposits (params);
|
2296
|
+
//
|
2297
|
+
// {
|
2298
|
+
// "success": true,
|
2299
|
+
// "result": {
|
2300
|
+
// "coin": "TUSD",
|
2301
|
+
// "confirmations": 64,
|
2302
|
+
// "confirmedTime": "2019-03-05T09:56:55.728933+00:00",
|
2303
|
+
// "fee": 0,
|
2304
|
+
// "id": 1,
|
2305
|
+
// "sentTime": "2019-03-05T09:56:55.735929+00:00",
|
2306
|
+
// "size": "99.0",
|
2307
|
+
// "status": "confirmed",
|
2308
|
+
// "time": "2019-03-05T09:56:55.728933+00:00",
|
2309
|
+
// "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1"
|
2310
|
+
// }
|
2311
|
+
// }
|
2312
|
+
//
|
2313
|
+
const result = this.safeValue (response, 'result', []);
|
2314
|
+
let currency = undefined;
|
2315
|
+
if (code !== undefined) {
|
2316
|
+
currency = this.currency (code);
|
2317
|
+
}
|
2318
|
+
return this.parseTransactions (result, currency, since, limit, { 'type': 'deposit' });
|
2319
|
+
}
|
2320
|
+
|
2321
|
+
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
|
2322
|
+
await this.loadMarkets ();
|
2323
|
+
const response = await this.privateGetWalletWithdrawals (params);
|
2324
|
+
//
|
2325
|
+
// {
|
2326
|
+
// "success": true,
|
2327
|
+
// "result": {
|
2328
|
+
// "coin": "TUSD",
|
2329
|
+
// "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE",
|
2330
|
+
// "tag": "null",
|
2331
|
+
// "fee": 0,
|
2332
|
+
// "id": 1,
|
2333
|
+
// "size": "99.0",
|
2334
|
+
// "status": "complete",
|
2335
|
+
// "time": "2019-03-05T09:56:55.728933+00:00",
|
2336
|
+
// "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1"
|
2337
|
+
// }
|
2338
|
+
// }
|
2339
|
+
//
|
2340
|
+
const result = this.safeValue (response, 'result', []);
|
2341
|
+
let currency = undefined;
|
2342
|
+
if (code !== undefined) {
|
2343
|
+
currency = this.currency (code);
|
2344
|
+
}
|
2345
|
+
return this.parseTransactions (result, currency, since, limit, { 'type': 'withdrawal' });
|
2346
|
+
}
|
2347
|
+
|
2348
|
+
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
2349
|
+
let request = '/api/' + this.implodeParams (path, params);
|
2350
|
+
const signOptions = this.safeValue (this.options, 'sign', {});
|
2351
|
+
const headerPrefix = this.safeString (signOptions, this.hostname, 'FTX');
|
2352
|
+
const subaccountField = headerPrefix + '-SUBACCOUNT';
|
2353
|
+
const chosenSubaccount = this.safeString2 (params, subaccountField, 'subaccount');
|
2354
|
+
if (chosenSubaccount !== undefined) {
|
2355
|
+
params = this.omit (params, [ subaccountField, 'subaccount' ]);
|
2356
|
+
}
|
2357
|
+
const query = this.omit (params, this.extractParams (path));
|
2358
|
+
const baseUrl = this.implodeHostname (this.urls['api'][api]);
|
2359
|
+
let url = baseUrl + request;
|
2360
|
+
if (method !== 'POST') {
|
2361
|
+
if (Object.keys (query).length) {
|
2362
|
+
const suffix = '?' + this.urlencode (query);
|
2363
|
+
url += suffix;
|
2364
|
+
request += suffix;
|
2365
|
+
}
|
2366
|
+
}
|
2367
|
+
if (api === 'private') {
|
2368
|
+
this.checkRequiredCredentials ();
|
2369
|
+
const timestamp = this.milliseconds ().toString ();
|
2370
|
+
let auth = timestamp + method + request;
|
2371
|
+
headers = {};
|
2372
|
+
if ((method === 'POST') || (method === 'DELETE')) {
|
2373
|
+
body = this.json (query);
|
2374
|
+
auth += body;
|
2375
|
+
headers['Content-Type'] = 'application/json';
|
2376
|
+
}
|
2377
|
+
const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256');
|
2378
|
+
headers[headerPrefix + '-KEY'] = this.apiKey;
|
2379
|
+
headers[headerPrefix + '-TS'] = timestamp;
|
2380
|
+
headers[headerPrefix + '-SIGN'] = signature;
|
2381
|
+
if (chosenSubaccount !== undefined) {
|
2382
|
+
headers[subaccountField] = chosenSubaccount;
|
2383
|
+
}
|
2384
|
+
}
|
2385
|
+
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
2386
|
+
}
|
2387
|
+
|
2388
|
+
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
2389
|
+
if (response === undefined) {
|
2390
|
+
return; // fallback to the default error handler
|
2391
|
+
}
|
2392
|
+
//
|
2393
|
+
// {"error":"Invalid parameter start_time","success":false}
|
2394
|
+
// {"error":"Not enough balances","success":false}
|
2395
|
+
//
|
2396
|
+
const success = this.safeValue (response, 'success');
|
2397
|
+
if (!success) {
|
2398
|
+
const feedback = this.id + ' ' + body;
|
2399
|
+
const error = this.safeString (response, 'error');
|
2400
|
+
this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback);
|
2401
|
+
this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback);
|
2402
|
+
throw new ExchangeError (feedback); // unknown message
|
2403
|
+
}
|
2404
|
+
}
|
2405
|
+
|
2406
|
+
async setLeverage (leverage, symbol = undefined, params = {}) {
|
2407
|
+
// WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
|
2408
|
+
// AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
|
2409
|
+
if ((leverage < 1) || (leverage > 20)) {
|
2410
|
+
throw new BadRequest (this.id + ' setLeverage() leverage should be between 1 and 20');
|
2411
|
+
}
|
2412
|
+
const request = {
|
2413
|
+
'leverage': leverage,
|
2414
|
+
};
|
2415
|
+
return await this.privatePostAccountLeverage (this.extend (request, params));
|
2416
|
+
}
|
2417
|
+
|
2418
|
+
parseIncome (income, market = undefined) {
|
2419
|
+
//
|
2420
|
+
// {
|
2421
|
+
// "future": "ETH-PERP",
|
2422
|
+
// "id": 33830,
|
2423
|
+
// "payment": 0.0441342,
|
2424
|
+
// "time": "2019-05-15T18:00:00+00:00",
|
2425
|
+
// "rate": 0.0001
|
2426
|
+
// }
|
2427
|
+
//
|
2428
|
+
const marketId = this.safeString (income, 'future');
|
2429
|
+
const symbol = this.safeSymbol (marketId, market);
|
2430
|
+
const amount = this.safeNumber (income, 'payment');
|
2431
|
+
const code = this.safeCurrencyCode ('USD');
|
2432
|
+
const id = this.safeString (income, 'id');
|
2433
|
+
const time = this.safeString (income, 'time');
|
2434
|
+
const timestamp = this.parse8601 (time);
|
2435
|
+
const rate = this.safe_number (income, 'rate');
|
2436
|
+
return {
|
2437
|
+
'info': income,
|
2438
|
+
'symbol': symbol,
|
2439
|
+
'code': code,
|
2440
|
+
'timestamp': timestamp,
|
2441
|
+
'datetime': this.iso8601 (timestamp),
|
2442
|
+
'id': id,
|
2443
|
+
'amount': amount,
|
2444
|
+
'rate': rate,
|
2445
|
+
};
|
2446
|
+
}
|
2447
|
+
|
2448
|
+
parseIncomes (incomes, market = undefined, since = undefined, limit = undefined) {
|
2449
|
+
const result = [];
|
2450
|
+
for (let i = 0; i < incomes.length; i++) {
|
2451
|
+
const entry = incomes[i];
|
2452
|
+
const parsed = this.parseIncome (entry, market);
|
2453
|
+
result.push (parsed);
|
2454
|
+
}
|
2455
|
+
const sorted = this.sortBy (result, 'timestamp');
|
2456
|
+
return this.filterBySinceLimit (sorted, since, limit, 'timestamp');
|
2457
|
+
}
|
2458
|
+
|
2459
|
+
async fetchFundingHistory (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
2460
|
+
await this.loadMarkets ();
|
2461
|
+
const request = {};
|
2462
|
+
let market = undefined;
|
2463
|
+
if (symbol !== undefined) {
|
2464
|
+
market = this.market (symbol);
|
2465
|
+
request['future'] = market['id'];
|
2466
|
+
}
|
2467
|
+
if (since !== undefined) {
|
2468
|
+
request['startTime'] = since;
|
2469
|
+
}
|
2470
|
+
const response = await this.privateGetFundingPayments (this.extend (request, params));
|
2471
|
+
const result = this.safeValue (response, 'result', []);
|
2472
|
+
return this.parseIncomes (result, market, since, limit);
|
2473
|
+
}
|
2474
|
+
|
2475
|
+
parseFundingRate (fundingRate, market = undefined) {
|
2476
|
+
//
|
2477
|
+
// perp
|
2478
|
+
// {
|
2479
|
+
// "volume": "71294.7636",
|
2480
|
+
// "nextFundingRate": "0.000033",
|
2481
|
+
// "nextFundingTime": "2021-10-14T20:00:00+00:00",
|
2482
|
+
// "openInterest": "47142.994"
|
2483
|
+
// }
|
2484
|
+
//
|
2485
|
+
// delivery
|
2486
|
+
// {
|
2487
|
+
// "volume": "4998.727",
|
2488
|
+
// "predictedExpirationPrice": "3798.820141757",
|
2489
|
+
// "openInterest": "48307.96"
|
2490
|
+
// }
|
2491
|
+
//
|
2492
|
+
const fundingRateDatetimeRaw = this.safeString (fundingRate, 'nextFundingTime');
|
2493
|
+
const fundingRateTimestamp = this.parse8601 (fundingRateDatetimeRaw);
|
2494
|
+
const estimatedSettlePrice = this.safeNumber (fundingRate, 'predictedExpirationPrice');
|
2495
|
+
return {
|
2496
|
+
'info': fundingRate,
|
2497
|
+
'symbol': market['symbol'],
|
2498
|
+
'markPrice': undefined,
|
2499
|
+
'indexPrice': undefined,
|
2500
|
+
'interestRate': this.parseNumber ('0'),
|
2501
|
+
'estimatedSettlePrice': estimatedSettlePrice,
|
2502
|
+
'timestamp': undefined,
|
2503
|
+
'datetime': undefined,
|
2504
|
+
'fundingRate': this.safeNumber (fundingRate, 'nextFundingRate'),
|
2505
|
+
'fundingTimestamp': fundingRateTimestamp,
|
2506
|
+
'fundingDatetime': this.iso8601 (fundingRateTimestamp),
|
2507
|
+
'nextFundingRate': undefined,
|
2508
|
+
'nextFundingTimestamp': undefined,
|
2509
|
+
'nextFundingDatetime': undefined,
|
2510
|
+
'previousFundingRate': undefined,
|
2511
|
+
'previousFundingTimestamp': undefined,
|
2512
|
+
'previousFundingDatetime': undefined,
|
2513
|
+
};
|
2514
|
+
}
|
2515
|
+
|
2516
|
+
async fetchFundingRate (symbol, params = {}) {
|
2517
|
+
await this.loadMarkets ();
|
2518
|
+
const market = this.market (symbol);
|
2519
|
+
const request = {
|
2520
|
+
'future_name': market['id'],
|
2521
|
+
};
|
2522
|
+
const response = await this.publicGetFuturesFutureNameStats (this.extend (request, params));
|
2523
|
+
//
|
2524
|
+
// {
|
2525
|
+
// "success": true,
|
2526
|
+
// "result": {
|
2527
|
+
// "volume": "71294.7636",
|
2528
|
+
// "nextFundingRate": "0.000033",
|
2529
|
+
// "nextFundingTime": "2021-10-14T20:00:00+00:00",
|
2530
|
+
// "openInterest": "47142.994"
|
2531
|
+
// }
|
2532
|
+
// }
|
2533
|
+
//
|
2534
|
+
const result = this.safeValue (response, 'result', {});
|
2535
|
+
return this.parseFundingRate (result, market);
|
2536
|
+
}
|
2537
|
+
|
2538
|
+
async fetchBorrowRates (params = {}) {
|
2539
|
+
await this.loadMarkets ();
|
2540
|
+
const response = await this.privateGetSpotMarginBorrowRates (params);
|
2541
|
+
//
|
2542
|
+
// {
|
2543
|
+
// "success":true,
|
2544
|
+
// "result":[
|
2545
|
+
// {"coin":"1INCH","previous":4.8763e-6,"estimate":4.8048e-6},
|
2546
|
+
// {"coin":"AAPL","previous":0.0000326469,"estimate":0.0000326469},
|
2547
|
+
// {"coin":"AAVE","previous":1.43e-6,"estimate":1.43e-6},
|
2548
|
+
// ]
|
2549
|
+
// }
|
2550
|
+
//
|
2551
|
+
const result = this.safeValue (response, 'result');
|
2552
|
+
return this.parseBorrowRates (result, 'coin');
|
2553
|
+
}
|
2554
|
+
|
2555
|
+
async fetchBorrowRateHistories (codes = undefined, since = undefined, limit = undefined, params = {}) {
|
2556
|
+
/**
|
2557
|
+
* @method
|
2558
|
+
* @name ftx#fetchBorrowRateHistory
|
2559
|
+
* @description Gets the history of the borrow rate for mutiple currencies
|
2560
|
+
* @param {str} code Unified currency code
|
2561
|
+
* @param {int} since Timestamp in ms of the earliest time to fetch the borrow rate
|
2562
|
+
* @param {int} limit Max number of [borrow rate structures]{@link https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure} to return per currency, max=48 for multiple currencies, max=5000 for a single currency
|
2563
|
+
* @param {dict} params Exchange specific parameters
|
2564
|
+
* @param {dict} params.till Timestamp in ms of the latest time to fetch the borrow rate
|
2565
|
+
* @returns A dictionary of [borrow rate structures]{@link https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure} with unified currency codes as keys
|
2566
|
+
*/
|
2567
|
+
await this.loadMarkets ();
|
2568
|
+
const request = {};
|
2569
|
+
let numCodes = 0;
|
2570
|
+
let endTime = this.safeNumber2 (params, 'till', 'end_time');
|
2571
|
+
if (codes !== undefined) {
|
2572
|
+
numCodes = codes.length;
|
2573
|
+
}
|
2574
|
+
if (numCodes === 1) {
|
2575
|
+
const millisecondsPer5000Hours = 18000000000;
|
2576
|
+
if ((limit !== undefined) && (limit > 5000)) {
|
2577
|
+
throw new BadRequest (this.id + ' fetchBorrowRateHistories() limit cannot exceed 5000 for a single currency');
|
2578
|
+
}
|
2579
|
+
if ((endTime !== undefined) && (since !== undefined) && ((endTime - since) > millisecondsPer5000Hours)) {
|
2580
|
+
throw new BadRequest (this.id + ' fetchBorrowRateHistories() requires the time range between the since time and the end time to be less than 5000 hours for a single currency');
|
2581
|
+
}
|
2582
|
+
const currency = this.currency (codes[0]);
|
2583
|
+
request['coin'] = currency['id'];
|
2584
|
+
} else {
|
2585
|
+
const millisecondsPer2Days = 172800000;
|
2586
|
+
if ((limit !== undefined) && (limit > 48)) {
|
2587
|
+
throw new BadRequest (this.id + ' fetchBorrowRateHistories() limit cannot exceed 48 for multiple currencies');
|
2588
|
+
}
|
2589
|
+
if ((endTime !== undefined) && (since !== undefined) && ((endTime - since) > millisecondsPer2Days)) {
|
2590
|
+
throw new BadRequest (this.id + ' fetchBorrowRateHistories() requires the time range between the since time and the end time to be less than 48 hours for multiple currencies');
|
2591
|
+
}
|
2592
|
+
}
|
2593
|
+
const millisecondsPerHour = 3600000;
|
2594
|
+
if (since !== undefined) {
|
2595
|
+
request['start_time'] = parseInt (since / 1000);
|
2596
|
+
if (endTime === undefined) {
|
2597
|
+
const now = this.milliseconds ();
|
2598
|
+
const sinceLimit = (limit === undefined) ? 2 : limit;
|
2599
|
+
endTime = this.sum (since, millisecondsPerHour * (sinceLimit - 1));
|
2600
|
+
endTime = Math.min (endTime, now);
|
2601
|
+
}
|
2602
|
+
} else {
|
2603
|
+
if (limit !== undefined) {
|
2604
|
+
if (endTime === undefined) {
|
2605
|
+
endTime = this.milliseconds ();
|
2606
|
+
}
|
2607
|
+
const startTime = this.sum ((endTime - millisecondsPerHour * limit), 1000);
|
2608
|
+
request['start_time'] = parseInt (startTime / 1000);
|
2609
|
+
}
|
2610
|
+
}
|
2611
|
+
if (endTime !== undefined) {
|
2612
|
+
request['end_time'] = parseInt (endTime / 1000);
|
2613
|
+
}
|
2614
|
+
const response = await this.publicGetSpotMarginHistory (this.extend (request, params));
|
2615
|
+
//
|
2616
|
+
// {
|
2617
|
+
// "success": true,
|
2618
|
+
// "result": [
|
2619
|
+
// {
|
2620
|
+
// "coin": "PYPL",
|
2621
|
+
// "time": "2022-01-24T13:00:00+00:00",
|
2622
|
+
// "size": 0.00500172,
|
2623
|
+
// "rate": 1e-6
|
2624
|
+
// },
|
2625
|
+
// ...
|
2626
|
+
// ]
|
2627
|
+
// }
|
2628
|
+
//
|
2629
|
+
const result = this.safeValue (response, 'result');
|
2630
|
+
return this.parseBorrowRateHistories (result, codes, since, limit);
|
2631
|
+
}
|
2632
|
+
|
2633
|
+
async fetchBorrowRateHistory (code, since = undefined, limit = undefined, params = {}) {
|
2634
|
+
/**
|
2635
|
+
* @method
|
2636
|
+
* @name ftx#fetchBorrowRateHistory
|
2637
|
+
* @description Gets the history of the borrow rate for a currency
|
2638
|
+
* @param {str} code Unified currency code
|
2639
|
+
* @param {int} since Timestamp in ms of the earliest time to fetch the borrow rate
|
2640
|
+
* @param {int} limit Max number of [borrow rate structures]{@link https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure} to return, max=5000
|
2641
|
+
* @param {dict} params Exchange specific parameters
|
2642
|
+
* @param {dict} params.till Timestamp in ms of the latest time to fetch the borrow rate
|
2643
|
+
* @returns An array of [borrow rate structures]{@link https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure}
|
2644
|
+
*/
|
2645
|
+
const histories = await this.fetchBorrowRateHistories ([ code ], since, limit, params);
|
2646
|
+
const borrowRateHistory = this.safeValue (histories, code);
|
2647
|
+
if (borrowRateHistory === undefined) {
|
2648
|
+
throw new BadRequest (this.id + ' fetchBorrowRateHistory() returned no data for ' + code);
|
2649
|
+
}
|
2650
|
+
return borrowRateHistory;
|
2651
|
+
}
|
2652
|
+
|
2653
|
+
parseBorrowRateHistories (response, codes, since, limit) {
|
2654
|
+
// How to calculate borrow rate
|
2655
|
+
// https://help.ftx.com/hc/en-us/articles/360053007671-Spot-Margin-Trading-Explainer
|
2656
|
+
const takerFee = this.fees['trading']['taker'].toString ();
|
2657
|
+
const spotMarginBorrowRate = Precise.stringMul ('500', takerFee);
|
2658
|
+
const borrowRateHistories = {};
|
2659
|
+
for (let i = 0; i < response.length; i++) {
|
2660
|
+
const item = response[i];
|
2661
|
+
const code = this.safeCurrencyCode (this.safeString (item, 'coin'));
|
2662
|
+
if (codes === undefined || this.inArray (code, codes)) {
|
2663
|
+
if (!(code in borrowRateHistories)) {
|
2664
|
+
borrowRateHistories[code] = [];
|
2665
|
+
}
|
2666
|
+
const lendingRate = this.safeString (item, 'rate');
|
2667
|
+
const borrowRate = Precise.stringMul (lendingRate, Precise.stringAdd ('1', spotMarginBorrowRate));
|
2668
|
+
const borrowRateStructure = this.extend (this.parseBorrowRate (item), { 'rate': borrowRate });
|
2669
|
+
borrowRateHistories[code].push (borrowRateStructure);
|
2670
|
+
}
|
2671
|
+
}
|
2672
|
+
const keys = Object.keys (borrowRateHistories);
|
2673
|
+
for (let i = 0; i < keys.length; i++) {
|
2674
|
+
const code = keys[i];
|
2675
|
+
borrowRateHistories[code] = this.filterByCurrencySinceLimit (borrowRateHistories[code], code, since, limit);
|
2676
|
+
}
|
2677
|
+
return borrowRateHistories;
|
2678
|
+
}
|
2679
|
+
|
2680
|
+
parseBorrowRates (response, codeKey) {
|
2681
|
+
const result = {};
|
2682
|
+
for (let i = 0; i < response.length; i++) {
|
2683
|
+
const item = response[i];
|
2684
|
+
const currency = this.safeString (item, codeKey);
|
2685
|
+
const code = this.safeCurrencyCode (currency);
|
2686
|
+
const borrowRate = this.parseBorrowRate (item);
|
2687
|
+
result[code] = borrowRate;
|
2688
|
+
}
|
2689
|
+
return result;
|
2690
|
+
}
|
2691
|
+
|
2692
|
+
parseBorrowRate (info, currency = undefined) {
|
2693
|
+
//
|
2694
|
+
// {
|
2695
|
+
// "coin": "1INCH",
|
2696
|
+
// "previous": 0.0000462375,
|
2697
|
+
// "estimate": 0.0000462375
|
2698
|
+
// }
|
2699
|
+
//
|
2700
|
+
const coin = this.safeString (info, 'coin');
|
2701
|
+
const datetime = this.safeString (info, 'time');
|
2702
|
+
const timestamp = this.parse8601 (datetime);
|
2703
|
+
return {
|
2704
|
+
'currency': this.safeCurrencyCode (coin),
|
2705
|
+
'rate': this.safeNumber (info, 'previous'),
|
2706
|
+
'period': 3600000,
|
2707
|
+
'timestamp': timestamp,
|
2708
|
+
'datetime': this.iso8601 (timestamp),
|
2709
|
+
'info': info,
|
2710
|
+
};
|
2711
|
+
}
|
2712
|
+
|
2713
|
+
async fetchBorrowInterest (code = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
2714
|
+
await this.loadMarkets ();
|
2715
|
+
const request = {};
|
2716
|
+
if (since !== undefined) {
|
2717
|
+
request['start_time'] = parseInt (since / 1000);
|
2718
|
+
}
|
2719
|
+
const response = await this.privateGetSpotMarginBorrowHistory (this.extend (request, params));
|
2720
|
+
//
|
2721
|
+
// {
|
2722
|
+
// "success":true,
|
2723
|
+
// "result":[
|
2724
|
+
// {"coin":"USDT","time":"2021-12-26T01:00:00+00:00","size":4593.74214725,"rate":3.3003e-6,"cost":0.0151607272085692,"feeUsd":0.0151683341034461},
|
2725
|
+
// {"coin":"USDT","time":"2021-12-26T00:00:00+00:00","size":4593.97110361,"rate":3.3003e-6,"cost":0.0151614828332441,"feeUsd":0.015169697173028324},
|
2726
|
+
// {"coin":"USDT","time":"2021-12-25T23:00:00+00:00","size":4594.20005922,"rate":3.3003e-6,"cost":0.0151622384554438,"feeUsd":0.015170200298479137},
|
2727
|
+
// ]
|
2728
|
+
// }
|
2729
|
+
//
|
2730
|
+
const result = this.safeValue (response, 'result');
|
2731
|
+
const interest = this.parseBorrowInterests (result);
|
2732
|
+
return this.filterByCurrencySinceLimit (interest, code, since, limit);
|
2733
|
+
}
|
2734
|
+
|
2735
|
+
parseBorrowInterest (info, market = undefined) {
|
2736
|
+
const coin = this.safeString (info, 'coin');
|
2737
|
+
const datetime = this.safeString (info, 'time');
|
2738
|
+
return {
|
2739
|
+
'account': 'cross',
|
2740
|
+
'symbol': undefined,
|
2741
|
+
'marginType': 'cross',
|
2742
|
+
'currency': this.safeCurrencyCode (coin),
|
2743
|
+
'interest': this.safeNumber (info, 'cost'),
|
2744
|
+
'interestRate': this.safeNumber (info, 'rate'),
|
2745
|
+
'amountBorrowed': this.safeNumber (info, 'size'),
|
2746
|
+
'timestamp': this.parse8601 (datetime),
|
2747
|
+
'datetime': datetime,
|
2748
|
+
'info': info,
|
2749
|
+
};
|
2750
|
+
}
|
2751
|
+
};
|