UNPKG

consequunturatque

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

1,197 lines (1,176 loc) 77.7 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const { ExchangeError, InvalidOrder, BadRequest, InsufficientFunds, OrderNotFound, AuthenticationError, RateLimitExceeded, ExchangeNotAvailable, CancelPending, ArgumentsRequired, PermissionDenied } = require ('./base/errors'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class ftx extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'ftx', 'name': 'FTX', 'countries': [ 'HK' ], 'rateLimit': 50, 'certified': true, 'pro': true, 'hostname': 'ftx.com', // or ftx.us 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg', 'www': 'https://ftx.com', 'api': { 'public': 'https://{hostname}', 'private': 'https://{hostname}', }, 'doc': 'https://github.com/ftexchange/ftx', 'fees': 'https://ftexchange.zendesk.com/hc/en-us/articles/360024479432-Fees', 'referral': 'https://ftx.com/#a=1623029', }, 'has': { 'cancelAllOrders': true, 'cancelOrder': true, 'createOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchClosedOrders': false, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': false, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFees': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'timeframes': { '15s': '15', '1m': '60', '5m': '300', '15m': '900', '1h': '3600', '4h': '14400', '1d': '86400', }, 'api': { 'public': { 'get': [ 'coins', 'markets', 'markets/{market_name}', 'markets/{market_name}/orderbook', // ?depth={depth} 'markets/{market_name}/trades', // ?limit={limit}&start_time={start_time}&end_time={end_time} 'markets/{market_name}/candles', // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time} // futures 'futures', 'futures/{future_name}', 'futures/{future_name}/stats', 'funding_rates', 'indexes/{index_name}/weights', 'expired_futures', 'indexes/{market_name}/candles', // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time} // leverage tokens 'lt/tokens', 'lt/{token_name}', // options 'options/requests', 'options/trades', 'stats/24h_options_volume', 'options/historical_volumes/BTC', 'options/open_interest/BTC', 'options/historical_open_interest/BTC', ], }, 'private': { 'get': [ 'account', 'positions', 'wallet/coins', 'wallet/balances', 'wallet/all_balances', 'wallet/deposit_address/{coin}', // ?method={method} 'wallet/deposits', 'wallet/withdrawals', 'wallet/withdrawal_fee', 'wallet/airdrops', 'wallet/saved_addresses', 'orders', // ?market={market} 'orders/history', // ?market={market} 'orders/{order_id}', 'orders/by_client_id/{client_order_id}', 'conditional_orders', // ?market={market} 'conditional_orders/{conditional_order_id}/triggers', 'conditional_orders/history', // ?market={market} 'spot_margin/borrow_rates', 'spot_margin/lending_rates', 'spot_margin/borrow_summary', 'spot_margin/market_info', // ?market={market} 'spot_margin/borrow_history', 'spot_margin/lending_history', 'spot_margin/offers', 'spot_margin/lending_info', 'fills', // ?market={market} 'funding_payments', // leverage tokens 'lt/balances', 'lt/creations', 'lt/redemptions', // subaccounts 'subaccounts', 'subaccounts/{nickname}/balances', // otc 'otc/quotes/{quoteId}', // options 'options/my_requests', 'options/requests/{request_id}/quotes', 'options/my_quotes', 'options/account_info', 'options/positions', 'options/fills', // staking 'staking/stakes', 'staking/unstake_requests', 'staking/balances', 'staking/staking_rewards', ], 'post': [ 'account/leverage', 'wallet/withdrawals', 'wallet/saved_addresses', 'orders', 'conditional_orders', 'orders/{order_id}/modify', 'orders/by_client_id/{client_order_id}/modify', 'conditional_orders/{order_id}/modify', // spot margin 'spot_margin/offers', // leverage tokens 'lt/{token_name}/create', 'lt/{token_name}/redeem', // subaccounts 'subaccounts', 'subaccounts/update_name', 'subaccounts/transfer', // otc 'otc/quotes/{quote_id}/accept', 'otc/quotes', // options 'options/requests', 'options/requests/{request_id}/quotes', 'options/quotes/{quote_id}/accept', // staking 'staking/unstake_requests', 'srm_stakes/stakes', ], 'delete': [ 'wallet/saved_addresses/{saved_address_id}', 'orders/{order_id}', 'orders/by_client_id/{client_order_id}', 'orders', 'conditional_orders/{order_id}', // subaccounts 'subaccounts', // options 'options/requests/{request_id}', 'options/quotes/{quote_id}', // staking 'staking/unstake_requests/{request_id}', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': 0.02 / 100, 'taker': 0.07 / 100, 'tiers': { 'taker': [ [0, 0.07 / 100], [1000000, 0.06 / 100], [5000000, 0.055 / 100], [10000000, 0.05 / 100], [15000000, 0.045 / 100], [35000000, 0.04 / 100], ], 'maker': [ [0, 0.02 / 100], [1000000, 0.02 / 100], [5000000, 0.015 / 100], [10000000, 0.015 / 100], [15000000, 0.01 / 100], [35000000, 0.01 / 100], ], }, }, 'funding': { 'withdraw': {}, }, }, 'exceptions': { 'exact': { 'Please slow down': RateLimitExceeded, // {"error":"Please slow down","success":false} 'Size too small for provide': InvalidOrder, // {"error":"Size too small for provide","success":false} 'Not logged in': AuthenticationError, // {"error":"Not logged in","success":false} 'Not enough balances': InsufficientFunds, // {"error":"Not enough balances","success":false} 'InvalidPrice': InvalidOrder, // {"error":"Invalid price","success":false} 'Size too small': InvalidOrder, // {"error":"Size too small","success":false} 'Size too large': InvalidOrder, // {"error":"Size too large","success":false} 'Missing parameter price': InvalidOrder, // {"error":"Missing parameter price","success":false} 'Order not found': OrderNotFound, // {"error":"Order not found","success":false} 'Order already closed': InvalidOrder, // {"error":"Order already closed","success":false} 'Trigger price too high': InvalidOrder, // {"error":"Trigger price too high","success":false} 'Trigger price too low': InvalidOrder, // {"error":"Trigger price too low","success":false} 'Order already queued for cancellation': CancelPending, // {"error":"Order already queued for cancellation","success":false} }, 'broad': { 'Account does not have enough margin for order': InsufficientFunds, 'Invalid parameter': BadRequest, // {"error":"Invalid parameter start_time","success":false} 'The requested URL was not found on the server': BadRequest, 'No such coin': BadRequest, 'No such market': BadRequest, 'Do not send more than': RateLimitExceeded, 'An unexpected error occurred': ExchangeNotAvailable, // {"error":"An unexpected error occurred, please try again later (58BC21C795).","success":false} 'Please retry request': ExchangeNotAvailable, // {"error":"Please retry request","success":false} 'Please try again': ExchangeNotAvailable, // {"error":"Please try again","success":false} 'Only have permissions for subaccount': PermissionDenied, // {"success":false,"error":"Only have permissions for subaccount *sub_name*"} }, }, 'precisionMode': TICK_SIZE, 'options': { // support for canceling conditional orders // https://github.com/ccxt/ccxt/issues/6669 'cancelOrder': { 'method': 'privateDeleteOrdersOrderId', // privateDeleteConditionalOrdersOrderId }, 'fetchOpenOrders': { 'method': 'privateGetOrders', // privateGetConditionalOrders }, 'fetchOrders': { 'method': 'privateGetOrdersHistory', // privateGetConditionalOrdersHistory }, 'sign': { 'ftx.com': 'FTX', 'ftx.us': 'FTXUS', }, }, }); } async fetchCurrencies (params = {}) { const response = await this.publicGetCoins (params); const currencies = this.safeValue (response, 'result', []); // // { // "success":true, // "result": [ // {"id":"BTC","name":"Bitcoin"}, // {"id":"ETH","name":"Ethereum"}, // {"id":"ETHMOON","name":"10X Long Ethereum Token","underlying":"ETH"}, // {"id":"EOSBULL","name":"3X Long EOS Token","underlying":"EOS"}, // ], // } // const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'id'); const code = this.safeCurrencyCode (id); const name = this.safeString (currency, 'name'); result[code] = { 'id': id, 'code': code, 'info': currency, 'type': undefined, 'name': name, 'active': undefined, 'fee': undefined, 'precision': undefined, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined }, 'amount': { 'min': undefined, 'max': undefined }, }, }; } return result; } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); // // { // 'success': true, // "result": [ // { // "ask":170.37, // "baseCurrency":null, // "bid":170.31, // "change1h":-0.019001554672655036, // "change24h":-0.024841165359738997, // "changeBod":-0.03816406029469881, // "enabled":true, // "last":170.37, // "name":"ETH-PERP", // "price":170.37, // "priceIncrement":0.01, // "quoteCurrency":null, // "quoteVolume24h":7742164.59889, // "sizeIncrement":0.001, // "type":"future", // "underlying":"ETH", // "volumeUsd24h":7742164.59889 // }, // { // "ask":170.44, // "baseCurrency":"ETH", // "bid":170.41, // "change1h":-0.018485459257126403, // "change24h":-0.023825887743413515, // "changeBod":-0.037605872388481086, // "enabled":true, // "last":172.72, // "name":"ETH/USD", // "price":170.44, // "priceIncrement":0.01, // "quoteCurrency":"USD", // "quoteVolume24h":382802.0252, // "sizeIncrement":0.001, // "type":"spot", // "underlying":null, // "volumeUsd24h":382802.0252 // }, // ], // } // const result = []; const markets = this.safeValue (response, 'result', []); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString2 (market, 'baseCurrency', 'underlying'); const quoteId = this.safeString (market, 'quoteCurrency', 'USD'); const type = this.safeString (market, 'type'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); // check if a market is a spot or future market const symbol = (type === 'future') ? this.safeString (market, 'name') : (base + '/' + quote); const active = this.safeValue (market, 'enabled'); const sizeIncrement = this.safeNumber (market, 'sizeIncrement'); const priceIncrement = this.safeNumber (market, 'priceIncrement'); const precision = { 'amount': sizeIncrement, 'price': priceIncrement, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'type': type, 'future': (type === 'future'), 'spot': (type === 'spot'), 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': sizeIncrement, 'max': undefined, }, 'price': { 'min': priceIncrement, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { // // { // "ask":171.29, // "baseCurrency":null, // base currency for spot markets // "bid":171.24, // "change1h":-0.0012244897959183673, // "change24h":-0.031603346901854366, // "changeBod":-0.03297013492914808, // "enabled":true, // "last":171.44, // "name":"ETH-PERP", // "price":171.29, // "priceIncrement":0.01, // "quoteCurrency":null, // quote currency for spot markets // "quoteVolume24h":8570651.12113, // "sizeIncrement":0.001, // "type":"future", // "underlying":"ETH", // null for spot markets // "volumeUsd24h":8570651.12113, // } // let symbol = undefined; const marketId = this.safeString (ticker, 'name'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const type = this.safeString (ticker, 'type'); if (type === 'future') { symbol = marketId; } else { const base = this.safeCurrencyCode (this.safeString (ticker, 'baseCurrency')); const quote = this.safeCurrencyCode (this.safeString (ticker, 'quoteCurrency')); if ((base !== undefined) && (quote !== undefined)) { symbol = base + '/' + quote; } } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeNumber (ticker, 'last'); const timestamp = this.safeTimestamp (ticker, 'time', this.milliseconds ()); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'high'), 'low': this.safeNumber (ticker, 'low'), 'bid': this.safeNumber (ticker, 'bid'), 'bidVolume': this.safeNumber (ticker, 'bidSize'), 'ask': this.safeNumber (ticker, 'ask'), 'askVolume': this.safeNumber (ticker, 'askSize'), 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': this.safeNumber (ticker, 'change24h'), 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeNumber (ticker, 'quoteVolume24h'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_name': market['id'], }; const response = await this.publicGetMarketsMarketName (this.extend (request, params)); // // { // "success":true, // "result":{ // "ask":171.29, // "baseCurrency":null, // base currency for spot markets // "bid":171.24, // "change1h":-0.0012244897959183673, // "change24h":-0.031603346901854366, // "changeBod":-0.03297013492914808, // "enabled":true, // "last":171.44, // "name":"ETH-PERP", // "price":171.29, // "priceIncrement":0.01, // "quoteCurrency":null, // quote currency for spot markets // "quoteVolume24h":8570651.12113, // "sizeIncrement":0.001, // "type":"future", // "underlying":"ETH", // null for spot markets // "volumeUsd24h":8570651.12113, // } // } // const result = this.safeValue (response, 'result', {}); return this.parseTicker (result, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarkets (params); // // { // 'success': true, // "result": [ // { // "ask":170.44, // "baseCurrency":"ETH", // "bid":170.41, // "change1h":-0.018485459257126403, // "change24h":-0.023825887743413515, // "changeBod":-0.037605872388481086, // "enabled":true, // "last":172.72, // "name":"ETH/USD", // "price":170.44, // "priceIncrement":0.01, // "quoteCurrency":"USD", // "quoteVolume24h":382802.0252, // "sizeIncrement":0.001, // "type":"spot", // "underlying":null, // "volumeUsd24h":382802.0252 // }, // ], // } // const tickers = this.safeValue (response, 'result', []); return this.parseTickers (tickers, symbols); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_name': market['id'], }; if (limit !== undefined) { request['depth'] = limit; // max 100, default 20 } const response = await this.publicGetMarketsMarketNameOrderbook (this.extend (request, params)); // // { // "success":true, // "result":{ // "asks":[ // [171.95,279.865], // [171.98,102.42], // [171.99,124.11], // ], // "bids":[ // [171.93,69.749], // [171.9,288.325], // [171.88,87.47], // ], // } // } // const result = this.safeValue (response, 'result', {}); return this.parseOrderBook (result, symbol); } parseOHLCV (ohlcv, market = undefined) { // // { // "close":177.23, // "high":177.45, // "low":177.2, // "open":177.43, // "startTime":"2019-10-17T13:27:00+00:00", // "time":1571318820000.0, // "volume":0.0 // } // return [ this.safeInteger (ohlcv, 'time'), this.safeNumber (ohlcv, 'open'), this.safeNumber (ohlcv, 'high'), this.safeNumber (ohlcv, 'low'), this.safeNumber (ohlcv, 'close'), this.safeNumber (ohlcv, 'volume'), ]; } getMarketId (symbol, key, params = {}) { const parts = this.getMarketParams (symbol, key, params); return this.safeString (parts, 1, symbol); } getMarketParams (symbol, key, params = {}) { let market = undefined; let marketId = undefined; if (symbol in this.markets) { market = this.market (symbol); marketId = market['id']; } else { marketId = this.safeString (params, key, symbol); } return [ market, marketId ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const [ market, marketId ] = this.getMarketParams (symbol, 'market_name', params); const request = { 'resolution': this.timeframes[timeframe], 'market_name': marketId, }; // max 1501 candles, including the current candle when since is not specified limit = (limit === undefined) ? 1501 : limit; if (since === undefined) { request['end_time'] = this.seconds (); request['limit'] = limit; request['start_time'] = request['end_time'] - limit * this.parseTimeframe (timeframe); } else { request['start_time'] = parseInt (since / 1000); request['limit'] = limit; request['end_time'] = this.sum (request['start_time'], limit * this.parseTimeframe (timeframe)); } const response = await this.publicGetMarketsMarketNameCandles (this.extend (request, params)); // // { // "success": true, // "result":[ // { // "close":177.23, // "high":177.45, // "low":177.2, // "open":177.43, // "startTime":"2019-10-17T13:27:00+00:00", // "time":1571318820000.0, // "volume":0.0 // }, // { // "close":177.26, // "high":177.33, // "low":177.23, // "open":177.23, // "startTime":"2019-10-17T13:28:00+00:00", // "time":1571318880000.0, // "volume":0.0 // }, // ], // } // const result = this.safeValue (response, 'result', []); return this.parseOHLCVs (result, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "id":1715826, // "liquidation":false, // "price":171.62, // "side":"buy", // "size":2.095, // "time":"2019-10-18T12:59:54.288166+00:00" // } // // fetchMyTrades (private) // // { // "fee": 20.1374935, // "feeRate": 0.0005, // "feeCurrency": "USD", // "future": "EOS-0329", // "id": 11215, // "liquidity": "taker", // "market": "EOS-0329", // "baseCurrency": null, // "quoteCurrency": null, // "orderId": 8436981, // "price": 4.201, // "side": "buy", // "size": 9587, // "time": "2019-03-27T19:15:10.204619+00:00", // "type": "order" // } // // { // "baseCurrency": "BTC", // "fee": 0, // "feeCurrency": "USD", // "feeRate": 0, // "future": null, // "id": 664079556, // "liquidity": "taker", // "market": null, // "orderId": null, // "price": 34830.61359, // "quoteCurrency": "USD", // "side": "sell", // "size": 0.0005996, // "time": "2021-01-15T16:05:29.246135+00:00", // "tradeId": null, // "type": "otc" // } // // with -ve fee // { // "id": 1171258927, // "fee": -0.0000713875, // "side": "sell", // "size": 1, // "time": "2021-03-11T13:34:35.523627+00:00", // "type": "order", // "price": 14.2775, // "future": null, // "market": "SOL/USD", // "feeRate": -0.000005, // "orderId": 33182929044, // "tradeId": 582936801, // "liquidity": "maker", // "feeCurrency": "USD", // "baseCurrency": "SOL", // "quoteCurrency": "USD" // } // // // from OTC order // { // "id": 1172129651, // "fee": 0, // "side": "sell", // "size": 1.47568846, // "time": "2021-03-11T15:04:46.893383+00:00", // "type": "otc", // "price": 14.60932598, // "future": null, // "market": null, // "feeRate": 0, // "orderId": null, // "tradeId": null, // "liquidity": "taker", // "feeCurrency": "USD", // "baseCurrency": "BCHA", // "quoteCurrency": "USD" // } const id = this.safeString (trade, 'id'); const takerOrMaker = this.safeString (trade, 'liquidity'); const marketId = this.safeString (trade, 'market'); let symbol = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const base = this.safeCurrencyCode (this.safeString (trade, 'baseCurrency')); const quote = this.safeCurrencyCode (this.safeString (trade, 'quoteCurrency')); if ((base !== undefined) && (quote !== undefined)) { symbol = base + '/' + quote; } else { symbol = marketId; } } const timestamp = this.parse8601 (this.safeString (trade, 'time')); const priceString = this.safeString (trade, 'price'); const amountString = this.safeString (trade, 'size'); const price = this.parseNumber (priceString); const amount = this.parseNumber (amountString); const cost = this.parseNumber (Precise.stringMul (priceString, amountString)); if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const side = this.safeString (trade, 'side'); let fee = undefined; const feeCost = this.safeNumber (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'feeCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, 'rate': this.safeNumber (trade, 'feeRate'), }; } const orderId = this.safeString (trade, 'orderId'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const [ market, marketId ] = this.getMarketParams (symbol, 'market_name', params); const request = { 'market_name': marketId, }; if (since !== undefined) { request['start_time'] = parseInt (since / 1000); // start_time doesn't work without end_time request['end_time'] = this.seconds (); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetMarketsMarketNameTrades (this.extend (request, params)); // // { // "success":true, // "result":[ // { // "id":1715826, // "liquidation":false, // "price":171.62, // "side":"buy", // "size":2.095, // "time":"2019-10-18T12:59:54.288166+00:00" // }, // { // "id":1715763, // "liquidation":false, // "price":171.89, // "side":"sell", // "size":1.477, // "time":"2019-10-18T12:58:38.443734+00:00" // }, // ], // } // const result = this.safeValue (response, 'result', []); return this.parseTrades (result, market, since, limit); } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccount (params); // // { // "success": true, // "result": { // "backstopProvider": true, // "collateral": 3568181.02691129, // "freeCollateral": 1786071.456884368, // "initialMarginRequirement": 0.12222384240257728, // "liquidating": false, // "maintenanceMarginRequirement": 0.07177992558058484, // "makerFee": 0.0002, // "marginFraction": 0.5588433331419503, // "openMarginFraction": 0.2447194090423075, // "takerFee": 0.0005, // "totalAccountValue": 3568180.98341129, // "totalPositionSize": 6384939.6992, // "username": "user@domain.com", // "positions": [ // { // "cost": -31.7906, // "entryPrice": 138.22, // "future": "ETH-PERP", // "initialMarginRequirement": 0.1, // "longOrderSize": 1744.55, // "maintenanceMarginRequirement": 0.04, // "netSize": -0.23, // "openSize": 1744.32, // "realizedPnl": 3.39441714, // "shortOrderSize": 1732.09, // "side": "sell", // "size": 0.23, // "unrealizedPnl": 0, // }, // ], // }, // } // const result = this.safeValue (response, 'result', {}); return { 'info': response, 'maker': this.safeNumber (result, 'makerFee'), 'taker': this.safeNumber (result, 'takerFee'), }; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetWalletBalances (params); // // { // "success": true, // "result": [ // { // "coin": "USDTBEAR", // "free": 2320.2, // "total": 2340.2 // }, // ], // } // const result = { 'info': response, }; const balances = this.safeValue (response, 'result', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode (this.safeString (balance, 'coin')); const account = this.account (); account['free'] = this.safeString (balance, 'free'); account['total'] = this.safeString (balance, 'total'); result[code] = account; } return this.parseBalance (result, false); } parseOrderStatus (status) { const statuses = { 'new': 'open', 'open': 'open', 'closed': 'closed', // filled or canceled 'triggered': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // limit orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder, editOrder // // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 0, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "remainingSize": 31431, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null, // } // // market orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder // // { // "avgFillPrice": 2666.0, // "clientId": None, // "createdAt": "2020-02-12T00: 53: 49.009726+00: 00", // "filledSize": 0.0007, // "future": None, // "id": 3109208514, // "ioc": True, // "market": "BNBBULL/USD", // "postOnly": False, // "price": None, // "reduceOnly": False, // "remainingSize": 0.0, // "side": "buy", // "size": 0.0007, // "status": "closed", // "type": "market" // } // // createOrder (conditional, "stop", "trailingStop", or "takeProfit") // // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "triggerPrice": 0.306525, // "orderId": null, // "side": "sell", // "size": 31431, // "status": "open", // "type": "stop", // "orderPrice": null, // "error": null, // "triggeredAt": null, // "reduceOnly": false // } // // editOrder (conditional, stop, trailing stop, take profit) // // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "triggerPrice": 0.306225, // "orderId": null, // "side": "sell", // "size": 31431, // "status": "open", // "type": "stop", // "orderPrice": null, // "error": null, // "triggeredAt": null, // "reduceOnly": false, // "orderType": "market", // "filledSize": 0, // "avgFillPrice": null, // "retryUntilFilled": false // } // // canceled order with a closed status // // { // "avgFillPrice":null, // "clientId":null, // "createdAt":"2020-09-01T13:45:57.119695+00:00", // "filledSize":0.0, // "future":null, // "id":8553541288, // "ioc":false, // "liquidation":false, // "market":"XRP/USDT", // "postOnly":false, // "price":0.5, // "reduceOnly":false, // "remainingSize":0.0, // "side":"sell", // "size":46.0, // "status":"closed", // "type":"limit" // } // const id = this.safeString (order, 'id'); const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); let status = this.parseOrderStatus (this.safeString (order, 'status')); const amount = this.safeNumber (order, 'size'); const filled = this.safeNumber (order, 'filledSize'); let remaining = this.safeNumber (order, 'remainingSize'); if ((remaining === 0.0) && (amount !== undefined) && (filled !== undefined)) { remaining = Math.max (amount - filled, 0); if (remaining > 0) { status = 'canceled'; } } let symbol = undefined; const marketId = this.safeString (order, 'market'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { // support for delisted market ids // https://github.com/ccxt/ccxt/issues/7113 symbol = marketId; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const side = this.safeString (order, 'side'); const type = this.safeString (order, 'type'); const average = this.safeNumber (order, 'avgFillPrice'); const price = this.safeNumber2 (order, 'price', 'triggerPrice', average); let cost = undefined; if (filled !== undefined && price !== undefined) { cost = filled * price; } const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'triggeredAt')); const clientOrderId = this.safeString (order, 'clientId'); const stopPrice = this.safeNumber (order, 'triggerPrice'); const postOnly = this.safeValue (order, 'postOnly'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'timeInForce': undefined, 'postOnly': postOnly, 'side': side, 'price': price, 'stopPrice': stopPrice, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'side': side, // "buy" or "sell" // 'price': 0.306525, // send null for market orders 'type': type, // "limit", "market", "stop", "trailingStop", or "takeProfit" 'size': parseFloat (this.amountToPrecision (symbol, amount)), // 'reduceOnly': false, // optional, default is false // 'ioc': false, // optional, default is false, limit or market orders only // 'postOnly': false, // optional, default is false, limit or market orders only // 'clientId': 'abcdef0123456789', // string, optional, client order id, limit or market orders only }; const clientOrderId = this.safeString2 (params, 'clientId', 'clientOrderId'); if (clientOrderId !== undefined) { request['clientId'] = clientOrderId; params = this.omit (params, [ 'clientId', 'clientOrderId' ]); } let method = undefined; if (type === 'limit') { method = 'privatePostOrders'; request['price'] = parseFloat (this.priceToPrecision (symbol, price)); } else if (type === 'market') { method = 'privatePostOrders'; request['price'] = null; } else if ((type === 'stop') || (type === 'takeProfit')) { method = 'privatePostConditionalOrders'; const stopPrice = this.safeNumber2 (params, 'stopPrice', 'triggerPrice'); if (stopPrice === undefined) { throw new ArgumentsRequired (this.id + ' createOrder () requires a stopPrice parameter or a triggerPrice parameter for ' + type + ' orders'); } else { params = this.omit (params, [ 'stopPrice', 'triggerPrice' ]); request['triggerPrice'] = parseFloat (this.priceToPrecision (symbol, stopPrice)); } if (price !== undefined) { request['orderPrice'] = parseFloat (this.priceToPrecision (symbol, price)); // optional, order type is limit if this is specified, otherwise market } } else if (type === 'trailingStop') { method = 'privatePostConditionalOrders'; request['trailValue'] = parseFloat (this.priceToPrecision (symbol, price)); // negative for "sell", positive for "buy" } else { throw new InvalidOrder (this.id + ' createOrder () does not support order type ' + type + ', only limit, market, stop, trailingStop, or takeProfit orders are supported'); } const response = await this[method] (this.extend (request, params)); // // orders // // { // "success": true, // "result": [ // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 0, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "remainingSize": 31431, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null, // } // ] // } // // conditional orders // // { // "success": true, // "result": [ // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "triggerPrice": 0.306525, // "orderId": null, // "side": "sell", // "size": 31431, // "status": "open", // "type": "stop", // "orderPrice": null, // "error": null, // "triggeredAt