UNPKG

consequunturatque

Version:

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

1,133 lines (1,112 loc) 102 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, InsufficientFunds, BadRequest, RateLimitExceeded, InvalidNonce } = require ('./base/errors'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class bybit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bybit', 'name': 'Bybit', 'countries': [ 'VG' ], // British Virgin Islands 'version': 'v2', 'userAgent': undefined, 'rateLimit': 100, 'hostname': 'bybit.com', // bybit.com, bytick.com 'has': { 'cancelOrder': true, 'CORS': true, 'cancelAllOrders': true, 'createOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchClosedOrders': true, 'fetchDeposits': true, 'fetchLedger': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchOrderTrades': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTransactions': false, 'fetchWithdrawals': true, 'fetchPositions': true, }, 'timeframes': { '1m': '1', '3m': '3', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '6h': '360', '12h': '720', '1d': 'D', '1w': 'W', '1M': 'M', '1y': 'Y', }, 'urls': { 'test': 'https://api-testnet.{hostname}', 'logo': 'https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg', 'api': 'https://api.{hostname}', 'www': 'https://www.bybit.com', 'doc': [ 'https://bybit-exchange.github.io/docs/inverse/', 'https://bybit-exchange.github.io/docs/linear/', 'https://github.com/bybit-exchange', ], 'fees': 'https://help.bybit.com/hc/en-us/articles/360039261154', 'referral': 'https://www.bybit.com/app/register?ref=X7Prm', }, 'api': { 'futures': { 'private': { 'get': [ 'position/list', 'order/list', 'order', 'stop-order/list', 'stop-order', 'execution/list', 'trade/closed-pnl/list', ], 'post': [ 'order/create', 'order/cancel', 'order/cancelAll', 'order/replace', 'stop-order/create', 'stop-order/cancel', 'stop-order/cancelAll', 'stop-order/replace', 'position/change-position-margin', 'position/trading-stop', 'position/leverage/save', 'position/switch-mode', 'position/switch-isolated', ], }, }, 'v2': { 'public': { 'get': [ 'orderBook/L2', 'kline/list', 'tickers', 'trading-records', 'symbols', 'liq-records', 'mark-price-kline', 'index-price-kline', 'premium-index-kline', 'open-interest', 'big-deal', 'account-ratio', 'time', 'announcement', ], }, 'private': { 'get': [ 'order/list', 'order', 'stop-order/list', 'stop-order', 'position/list', 'execution/list', 'trade/closed-pnl/list', 'funding/prev-funding-rate', 'funding/prev-funding', 'funding/predicted-funding', 'account/api-key', 'account/lcp', 'wallet/balance', 'wallet/fund/records', 'wallet/withdraw/list', 'exchange-order/list', ], 'post': [ 'order/create', 'order/cancel', 'order/cancelAll', 'order/replace', 'stop-order/create', 'stop-order/cancel', 'stop-order/cancelAll', 'stop-order/replace', 'position/change-position-margin', 'position/trading-stop', 'position/leverage/save', ], }, }, 'public': { 'linear': { 'get': [ 'kline', 'recent-trading-records', 'funding/prev-funding-rate', 'mark-price-kline', 'index-price-kline', 'premium-index-kline', 'risk-limit', ], }, }, 'private': { 'linear': { 'get': [ 'order/list', 'order/search', 'stop-order/list', 'stop-order/search', 'position/list', 'trade/execution/list', 'trade/closed-pnl/list', 'funding/predicted-funding', 'funding/prev-funding', ], 'post': [ 'order/create', 'order/cancel', 'order/cancel-all', 'order/replace', 'stop-order/create', 'stop-order/cancel', 'stop-order/cancel-all', 'stop-order/replace', 'position/set-auto-add-margin', 'position/switch-isolated', 'tpsl/switch-mode', 'position/add-margin', 'position/set-leverage', 'position/trading-stop', ], }, }, 'openapi': { 'wallet': { 'get': [ 'risk-limit/list', ], 'post': [ 'risk-limit', ], }, }, }, 'httpExceptions': { '403': RateLimitExceeded, // Forbidden -- You request too many times }, 'exceptions': { 'exact': { '10001': BadRequest, // parameter error '10002': InvalidNonce, // request expired, check your timestamp and recv_window '10003': AuthenticationError, // Invalid apikey '10004': AuthenticationError, // invalid sign '10005': PermissionDenied, // permission denied for current apikey '10006': RateLimitExceeded, // too many requests '10007': AuthenticationError, // api_key not found in your request parameters '10010': PermissionDenied, // request ip mismatch '10017': BadRequest, // request path not found or request method is invalid '20001': OrderNotFound, // Order not exists '20003': InvalidOrder, // missing parameter side '20004': InvalidOrder, // invalid parameter side '20005': InvalidOrder, // missing parameter symbol '20006': InvalidOrder, // invalid parameter symbol '20007': InvalidOrder, // missing parameter order_type '20008': InvalidOrder, // invalid parameter order_type '20009': InvalidOrder, // missing parameter qty '20010': InvalidOrder, // qty must be greater than 0 '20011': InvalidOrder, // qty must be an integer '20012': InvalidOrder, // qty must be greater than zero and less than 1 million '20013': InvalidOrder, // missing parameter price '20014': InvalidOrder, // price must be greater than 0 '20015': InvalidOrder, // missing parameter time_in_force '20016': InvalidOrder, // invalid value for parameter time_in_force '20017': InvalidOrder, // missing parameter order_id '20018': InvalidOrder, // invalid date format '20019': InvalidOrder, // missing parameter stop_px '20020': InvalidOrder, // missing parameter base_price '20021': InvalidOrder, // missing parameter stop_order_id '20022': BadRequest, // missing parameter leverage '20023': BadRequest, // leverage must be a number '20031': BadRequest, // leverage must be greater than zero '20070': BadRequest, // missing parameter margin '20071': BadRequest, // margin must be greater than zero '20084': BadRequest, // order_id or order_link_id is required '30001': BadRequest, // order_link_id is repeated '30003': InvalidOrder, // qty must be more than the minimum allowed '30004': InvalidOrder, // qty must be less than the maximum allowed '30005': InvalidOrder, // price exceeds maximum allowed '30007': InvalidOrder, // price exceeds minimum allowed '30008': InvalidOrder, // invalid order_type '30009': ExchangeError, // no position found '30010': InsufficientFunds, // insufficient wallet balance '30011': PermissionDenied, // operation not allowed as position is undergoing liquidation '30012': PermissionDenied, // operation not allowed as position is undergoing ADL '30013': PermissionDenied, // position is in liq or adl status '30014': InvalidOrder, // invalid closing order, qty should not greater than size '30015': InvalidOrder, // invalid closing order, side should be opposite '30016': ExchangeError, // TS and SL must be cancelled first while closing position '30017': InvalidOrder, // estimated fill price cannot be lower than current Buy liq_price '30018': InvalidOrder, // estimated fill price cannot be higher than current Sell liq_price '30019': InvalidOrder, // cannot attach TP/SL params for non-zero position when placing non-opening position order '30020': InvalidOrder, // position already has TP/SL params '30021': InvalidOrder, // cannot afford estimated position_margin '30022': InvalidOrder, // estimated buy liq_price cannot be higher than current mark_price '30023': InvalidOrder, // estimated sell liq_price cannot be lower than current mark_price '30024': InvalidOrder, // cannot set TP/SL/TS for zero-position '30025': InvalidOrder, // trigger price should bigger than 10% of last price '30026': InvalidOrder, // price too high '30027': InvalidOrder, // price set for Take profit should be higher than Last Traded Price '30028': InvalidOrder, // price set for Stop loss should be between Liquidation price and Last Traded Price '30029': InvalidOrder, // price set for Stop loss should be between Last Traded Price and Liquidation price '30030': InvalidOrder, // price set for Take profit should be lower than Last Traded Price '30031': InsufficientFunds, // insufficient available balance for order cost '30032': InvalidOrder, // order has been filled or cancelled '30033': RateLimitExceeded, // The number of stop orders exceeds maximum limit allowed '30034': OrderNotFound, // no order found '30035': RateLimitExceeded, // too fast to cancel '30036': ExchangeError, // the expected position value after order execution exceeds the current risk limit '30037': InvalidOrder, // order already cancelled '30041': ExchangeError, // no position found '30042': InsufficientFunds, // insufficient wallet balance '30043': PermissionDenied, // operation not allowed as position is undergoing liquidation '30044': PermissionDenied, // operation not allowed as position is undergoing AD '30045': PermissionDenied, // operation not allowed as position is not normal status '30049': InsufficientFunds, // insufficient available balance '30050': ExchangeError, // any adjustments made will trigger immediate liquidation '30051': ExchangeError, // due to risk limit, cannot adjust leverage '30052': ExchangeError, // leverage can not less than 1 '30054': ExchangeError, // position margin is invalid '30057': ExchangeError, // requested quantity of contracts exceeds risk limit '30063': ExchangeError, // reduce-only rule not satisfied '30067': InsufficientFunds, // insufficient available balance '30068': ExchangeError, // exit value must be positive '34026': ExchangeError, // the limit is no change }, 'broad': { 'unknown orderInfo': OrderNotFound, // {"ret_code":-1,"ret_msg":"unknown orderInfo","ext_code":"","ext_info":"","result":null,"time_now":"1584030414.005545","rate_limit_status":99,"rate_limit_reset_ms":1584030414003,"rate_limit":100} 'invalid api_key': AuthenticationError, // {"ret_code":10003,"ret_msg":"invalid api_key","ext_code":"","ext_info":"","result":null,"time_now":"1599547085.415797"} }, }, 'precisionMode': TICK_SIZE, 'options': { 'marketTypes': { 'BTC/USDT': 'linear', 'BCH/USDT': 'linear', 'ETH/USDT': 'linear', 'LTC/USDT': 'linear', 'XTZ/USDT': 'linear', 'LINK/USDT': 'linear', 'ADA/USDT': 'linear', 'DOT/USDT': 'linear', 'UNI/USDT': 'linear', }, 'defaultType': 'linear', // may also be inverse 'code': 'BTC', 'cancelAllOrders': { // 'method': 'v2PrivatePostOrderCancelAll', // v2PrivatePostStopOrderCancelAll }, 'recvWindow': 5 * 1000, // 5 sec default 'timeDifference': 0, // the difference between system clock and exchange server clock 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.00075, 'maker': -0.00025, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, }); } nonce () { return this.milliseconds () - this.options['timeDifference']; } async loadTimeDifference (params = {}) { const serverTime = await this.fetchTime (params); const after = this.milliseconds (); this.options['timeDifference'] = after - serverTime; return this.options['timeDifference']; } async fetchTime (params = {}) { const response = await this.v2PublicGetTime (params); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: {}, // time_now: '1583933682.448826' // } // return this.safeTimestamp (response, 'time_now'); } async fetchMarkets (params = {}) { if (this.options['adjustForTimeDifference']) { await this.loadTimeDifference (); } const response = await this.v2PublicGetSymbols (params); // // { // "ret_code":0, // "ret_msg":"OK", // "ext_code":"", // "ext_info":"", // "result":[ // { // "name":"BTCUSD", // "alias":"BTCUSD", // "status":"Trading", // "base_currency":"BTC", // "quote_currency":"USD", // "price_scale":2, // "taker_fee":"0.00075", // "maker_fee":"-0.00025", // "leverage_filter":{"min_leverage":1,"max_leverage":100,"leverage_step":"0.01"}, // "price_filter":{"min_price":"0.5","max_price":"999999.5","tick_size":"0.5"}, // "lot_size_filter":{"max_trading_qty":1000000,"min_trading_qty":1,"qty_step":1} // }, // { // "name":"BTCUSDT", // "alias":"BTCUSDT", // "status":"Trading", // "base_currency":"BTC", // "quote_currency":"USDT", // "price_scale":2, // "taker_fee":"0.00075", // "maker_fee":"-0.00025", // "leverage_filter":{"min_leverage":1,"max_leverage":100,"leverage_step":"0.01"}, // "price_filter":{"min_price":"0.5","max_price":"999999.5","tick_size":"0.5"}, // "lot_size_filter":{"max_trading_qty":100,"min_trading_qty":0.001,"qty_step":0.001} // }, // ], // "time_now":"1610539664.818033" // } // const markets = this.safeValue (response, 'result', []); const options = this.safeValue (this.options, 'fetchMarkets', {}); const linearQuoteCurrencies = this.safeValue (options, 'linear', { 'USDT': true }); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString2 (market, 'name', 'symbol'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const linear = (quote in linearQuoteCurrencies); const inverse = !linear; let symbol = base + '/' + quote; const baseQuote = base + quote; let type = 'swap'; if (baseQuote !== id) { symbol = id; type = 'future'; } const lotSizeFilter = this.safeValue (market, 'lot_size_filter', {}); const priceFilter = this.safeValue (market, 'price_filter', {}); const precision = { 'amount': this.safeNumber (lotSizeFilter, 'qty_step'), 'price': this.safeNumber (priceFilter, 'tick_size'), }; const status = this.safeString (market, 'status'); let active = undefined; if (status !== undefined) { active = (status === 'Trading'); } const spot = (type === 'spot'); const swap = (type === 'swap'); const future = (type === 'future'); const option = (type === 'option'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'active': active, 'precision': precision, 'taker': this.safeNumber (market, 'taker_fee'), 'maker': this.safeNumber (market, 'maker_fee'), 'type': type, 'spot': spot, 'swap': swap, 'future': future, 'option': option, 'linear': linear, 'inverse': inverse, 'limits': { 'amount': { 'min': this.safeNumber (lotSizeFilter, 'min_trading_qty'), 'max': this.safeNumber (lotSizeFilter, 'max_trading_qty'), }, 'price': { 'min': this.safeNumber (priceFilter, 'min_price'), 'max': this.safeNumber (priceFilter, 'max_price'), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // symbol: 'BTCUSD', // bid_price: '7680', // ask_price: '7680.5', // last_price: '7680.00', // last_tick_direction: 'MinusTick', // prev_price_24h: '7870.50', // price_24h_pcnt: '-0.024204', // high_price_24h: '8035.00', // low_price_24h: '7671.00', // prev_price_1h: '7780.00', // price_1h_pcnt: '-0.012853', // mark_price: '7683.27', // index_price: '7682.74', // open_interest: 188829147, // open_value: '23670.06', // total_turnover: '25744224.90', // turnover_24h: '102997.83', // total_volume: 225448878806, // volume_24h: 809919408, // funding_rate: '0.0001', // predicted_funding_rate: '0.0001', // next_funding_time: '2020-03-12T00:00:00Z', // countdown_hour: 7 // } // const timestamp = undefined; const marketId = this.safeString (ticker, 'symbol'); const symbol = this.safeSymbol (marketId, market); const last = this.safeNumber (ticker, 'last_price'); const open = this.safeNumber (ticker, 'prev_price_24h'); let percentage = this.safeNumber (ticker, 'price_24h_pcnt'); if (percentage !== undefined) { percentage *= 100; } let change = undefined; let average = undefined; if ((last !== undefined) && (open !== undefined)) { change = last - open; average = this.sum (open, last) / 2; } const baseVolume = this.safeNumber (ticker, 'turnover_24h'); const quoteVolume = this.safeNumber (ticker, 'volume_24h'); const vwap = this.vwap (baseVolume, quoteVolume); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'high_price_24h'), 'low': this.safeNumber (ticker, 'low_price_24h'), 'bid': this.safeNumber (ticker, 'bid_price'), 'bidVolume': undefined, 'ask': this.safeNumber (ticker, 'ask_price'), 'askVolume': undefined, 'vwap': vwap, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.v2PublicGetTickers (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // symbol: 'BTCUSD', // bid_price: '7680', // ask_price: '7680.5', // last_price: '7680.00', // last_tick_direction: 'MinusTick', // prev_price_24h: '7870.50', // price_24h_pcnt: '-0.024204', // high_price_24h: '8035.00', // low_price_24h: '7671.00', // prev_price_1h: '7780.00', // price_1h_pcnt: '-0.012853', // mark_price: '7683.27', // index_price: '7682.74', // open_interest: 188829147, // open_value: '23670.06', // total_turnover: '25744224.90', // turnover_24h: '102997.83', // total_volume: 225448878806, // volume_24h: 809919408, // funding_rate: '0.0001', // predicted_funding_rate: '0.0001', // next_funding_time: '2020-03-12T00:00:00Z', // countdown_hour: 7 // } // ], // time_now: '1583948195.818255' // } // const result = this.safeValue (response, 'result', []); const first = this.safeValue (result, 0); const timestamp = this.safeTimestamp (response, 'time_now'); const ticker = this.parseTicker (first, market); ticker['timestamp'] = timestamp; ticker['datetime'] = this.iso8601 (timestamp); return ticker; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.v2PublicGetTickers (params); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // symbol: 'BTCUSD', // bid_price: '7680', // ask_price: '7680.5', // last_price: '7680.00', // last_tick_direction: 'MinusTick', // prev_price_24h: '7870.50', // price_24h_pcnt: '-0.024204', // high_price_24h: '8035.00', // low_price_24h: '7671.00', // prev_price_1h: '7780.00', // price_1h_pcnt: '-0.012853', // mark_price: '7683.27', // index_price: '7682.74', // open_interest: 188829147, // open_value: '23670.06', // total_turnover: '25744224.90', // turnover_24h: '102997.83', // total_volume: 225448878806, // volume_24h: 809919408, // funding_rate: '0.0001', // predicted_funding_rate: '0.0001', // next_funding_time: '2020-03-12T00:00:00Z', // countdown_hour: 7 // } // ], // time_now: '1583948195.818255' // } // const result = this.safeValue (response, 'result', []); const tickers = {}; for (let i = 0; i < result.length; i++) { const ticker = this.parseTicker (result[i]); const symbol = ticker['symbol']; tickers[symbol] = ticker; } return this.filterByArray (tickers, 'symbol', symbols); } parseOHLCV (ohlcv, market = undefined) { // // inverse perpetual BTC/USD // // { // symbol: 'BTCUSD', // interval: '1', // open_time: 1583952540, // open: '7760.5', // high: '7764', // low: '7757', // close: '7763.5', // volume: '1259766', // turnover: '162.32773718999994' // } // // linear perpetual BTC/USDT // // { // "id":143536, // "symbol":"BTCUSDT", // "period":"15", // "start_at":1587883500, // "volume":1.035, // "open":7540.5, // "high":7541, // "low":7540.5, // "close":7541 // } // return [ this.safeTimestamp2 (ohlcv, 'open_time', 'start_at'), this.safeNumber (ohlcv, 'open'), this.safeNumber (ohlcv, 'high'), this.safeNumber (ohlcv, 'low'), this.safeNumber (ohlcv, 'close'), this.safeNumber2 (ohlcv, 'turnover', 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'interval': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe); const now = this.seconds (); if (since === undefined) { if (limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires a since argument or a limit argument'); } else { request['from'] = now - limit * duration; } } else { request['from'] = parseInt (since / 1000); } if (limit !== undefined) { request['limit'] = limit; // max 200, default 200 } const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); const method = (marketType === 'linear') ? 'publicLinearGetKline' : 'v2PublicGetKlineList'; const response = await this[method] (this.extend (request, params)); // // inverse perpetual BTC/USD // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // symbol: 'BTCUSD', // interval: '1', // open_time: 1583952540, // open: '7760.5', // high: '7764', // low: '7757', // close: '7763.5', // volume: '1259766', // turnover: '162.32773718999994' // }, // ], // time_now: '1583953082.397330' // } // // linear perpetual BTC/USDT // // { // "ret_code":0, // "ret_msg":"OK", // "ext_code":"", // "ext_info":"", // "result":[ // { // "id":143536, // "symbol":"BTCUSDT", // "period":"15", // "start_at":1587883500, // "volume":1.035, // "open":7540.5, // "high":7541, // "low":7540.5, // "close":7541 // } // ], // "time_now":"1587884120.168077" // } // const result = this.safeValue (response, 'result', {}); return this.parseOHLCVs (result, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // id: 43785688, // symbol: 'BTCUSD', // price: 7786, // qty: 67, // side: 'Sell', // time: '2020-03-11T19:18:30.123Z' // } // // fetchMyTrades, fetchOrderTrades (private) // // { // "closed_size": 0, // "cross_seq": 277136382, // "exec_fee": "0.0000001", // "exec_id": "256e5ef8-abfe-5772-971b-f944e15e0d68", // "exec_price": "8178.5", // "exec_qty": 1, // // the docs say the exec_time field is "abandoned" now // // the user should use "trade_time_ms" // "exec_time": "1571676941.70682", // "exec_type": "Trade", //Exec Type Enum // "exec_value": "0.00012227", // "fee_rate": "0.00075", // "last_liquidity_ind": "RemovedLiquidity", //Liquidity Enum // "leaves_qty": 0, // "nth_fill": 2, // "order_id": "7ad50cb1-9ad0-4f74-804b-d82a516e1029", // "order_link_id": "", // "order_price": "8178", // "order_qty": 1, // "order_type": "Market", //Order Type Enum // "side": "Buy", //Side Enum // "symbol": "BTCUSD", //Symbol Enum // "user_id": 1, // "trade_time_ms": 1577480599000 // } // const id = this.safeString2 (trade, 'id', 'exec_id'); const marketId = this.safeString (trade, 'symbol'); market = this.safeMarket (marketId, market); const symbol = market['symbol']; const amountString = this.safeString2 (trade, 'qty', 'exec_qty'); const priceString = this.safeString2 (trade, 'exec_price', 'price'); let cost = this.safeNumber (trade, 'exec_value'); const amount = this.parseNumber (amountString); const price = this.parseNumber (priceString); if (cost === undefined) { cost = this.parseNumber (Precise.stringMul (priceString, amountString)); } let timestamp = this.parse8601 (this.safeString (trade, 'time')); if (timestamp === undefined) { timestamp = this.safeInteger (trade, 'trade_time_ms'); } const side = this.safeStringLower (trade, 'side'); const lastLiquidityInd = this.safeString (trade, 'last_liquidity_ind'); const takerOrMaker = (lastLiquidityInd === 'AddedLiquidity') ? 'maker' : 'taker'; const feeCost = this.safeNumber (trade, 'exec_fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyCode = market['inverse'] ? market['base'] : market['quote']; fee = { 'cost': feeCost, 'currency': feeCurrencyCode, 'rate': this.safeNumber (trade, 'fee_rate'), }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': this.safeString (trade, 'order_id'), 'type': this.safeStringLower (trade, 'order_type'), 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], // 'from': 123, // from id }; if (limit !== undefined) { request['count'] = limit; // default 500, max 1000 } const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); const method = (marketType === 'linear') ? 'publicLinearGetRecentTradingRecords' : 'v2PublicGetTradingRecords'; const response = await this[method] (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // id: 43785688, // symbol: 'BTCUSD', // price: 7786, // qty: 67, // side: 'Sell', // time: '2020-03-11T19:18:30.123Z' // }, // ], // time_now: '1583954313.393362' // } // const result = this.safeValue (response, 'result', {}); return this.parseTrades (result, market, since, limit); } parseOrderBook (orderbook, symbol, timestamp = undefined, bidsKey = 'Buy', asksKey = 'Sell', priceKey = 'price', amountKey = 'size') { const bids = []; const asks = []; for (let i = 0; i < orderbook.length; i++) { const bidask = orderbook[i]; const side = this.safeString (bidask, 'side'); if (side === 'Buy') { bids.push (this.parseBidAsk (bidask, priceKey, amountKey)); } else if (side === 'Sell') { asks.push (this.parseBidAsk (bidask, priceKey, amountKey)); } else { throw new ExchangeError (this.id + ' parseOrderBook encountered an unrecognized bidask format: ' + this.json (bidask)); } } return { 'symbol': symbol, 'bids': this.sortBy (bids, 0, true), 'asks': this.sortBy (asks, 0), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'nonce': undefined, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.v2PublicGetOrderBookL2 (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { symbol: 'BTCUSD', price: '7767.5', size: 677956, side: 'Buy' }, // { symbol: 'BTCUSD', price: '7767', size: 580690, side: 'Buy' }, // { symbol: 'BTCUSD', price: '7766.5', size: 475252, side: 'Buy' }, // { symbol: 'BTCUSD', price: '7768', size: 330847, side: 'Sell' }, // { symbol: 'BTCUSD', price: '7768.5', size: 97159, side: 'Sell' }, // { symbol: 'BTCUSD', price: '7769', size: 6508, side: 'Sell' }, // ], // time_now: '1583954829.874823' // } // const result = this.safeValue (response, 'result', []); const timestamp = this.safeTimestamp (response, 'time_now'); return this.parseOrderBook (result, symbol, timestamp, 'Buy', 'Sell', 'price', 'size'); } async fetchBalance (params = {}) { await this.loadMarkets (); const request = {}; const coin = this.safeString (params, 'coin'); const code = this.safeString (params, 'code'); if (coin !== undefined) { request['coin'] = coin; } else if (code !== undefined) { const currency = this.currency (code); request['coin'] = currency['id']; } const response = await this.v2PrivateGetWalletBalance (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: { // BTC: { // equity: 0, // available_balance: 0, // used_margin: 0, // order_margin: 0, // position_margin: 0, // occ_closing_fee: 0, // occ_funding_fee: 0, // wallet_balance: 0, // realised_pnl: 0, // unrealised_pnl: 0, // cum_realised_pnl: 0, // given_cash: 0, // service_cash: 0 // } // }, // time_now: '1583937810.370020', // rate_limit_status: 119, // rate_limit_reset_ms: 1583937810367, // rate_limit: 120 // } // const result = { 'info': response, }; const balances = this.safeValue (response, 'result', {}); const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const balance = balances[currencyId]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeString (balance, 'available_balance'); account['used'] = this.safeString (balance, 'used_margin'); account['total'] = this.safeString (balance, 'equity'); result[code] = account; } return this.parseBalance (result, false); } parseOrderStatus (status) { const statuses = { // basic orders 'Created': 'open', 'Rejected': 'rejected', // order is triggered but failed upon being placed 'New': 'open', 'PartiallyFilled': 'open', 'Filled': 'closed', 'Cancelled': 'canceled', 'PendingCancel': 'canceling', // the engine has received the cancellation but there is no guarantee that it will be successful // conditional orders 'Active': 'open', // order is triggered and placed successfully 'Untriggered': 'open', // order waits to be triggered 'Triggered': 'closed', // order is triggered // 'Cancelled': 'canceled', // order is cancelled // 'Rejected': 'rejected', // order is triggered but fail to be placed 'Deactivated': 'canceled', // conditional order was cancelled before triggering }; return this.safeString (statuses, status, status); } parseTimeInForce (timeInForce) { const timeInForces = { 'GoodTillCancel': 'GTC', 'ImmediateOrCancel': 'IOC', 'FillOrKill': 'FOK', 'PostOnly': 'PO', }; return this.safeString (timeInForces, timeInForce, timeInForce); } parseOrder (order, market = undefined) { // // createOrder // // { // "user_id": 1, // "order_id": "335fd977-e5a5-4781-b6d0-c772d5bfb95b", // "symbol": "BTCUSD", // "side": "Buy", // "order_type": "Limit", // "price": 8800, // "qty": 1, // "time_in_force": "GoodTillCancel", // "order_status": "Created", // "last_exec_time": 0, // "last_exec_price": 0, // "leaves_qty": 1, // "cum_exec_qty": 0, // in contracts, where 1 contract = 1 quote currency unit (USD for inverse contracts) // "cum_exec_value": 0, // in contract's underlying currency (BTC for inverse contracts) // "cum_exec_fee": 0, // "reject_reason": "", // "order_link_id": "", // "created_at": "2019-11-30T11:03:43.452Z", // "updated_at": "2019-11-30T11:03:43.455Z" // } // // fetchOrder // // { // "user_id" : 599946, // "symbol" : "BTCUSD", // "side" : "Buy", // "order_type" : "Limit", // "price" : "7948", // "qty" : 10, // "time_in_force" : "GoodTillCancel", // "order_status" : "Filled", // "ext_fields" : { // "o_req_num" : -1600687220498, // "xreq_type" : "x_create" // }, // "last_exec_time" : "1588150113.968422", // "last_exec_price" : "7948", // "leaves_qty" : 0, // "leaves_value" : "0", // "cum_exec_qty" : 10, // "cum_exec_value" : "0.00125817", // "cum_exec_fee" : "-0.00000031", // "reject_reason" : "", // "cancel_type" : "", // "order_link_id" : "", // "created_at" : "2020-04-29T08:45:24.399146Z", // "updated_at" : "2020-04-29T08:48:33.968422Z", // "order_id" : "dd2504b9-0157-406a-99e1-efa522373944" // } // // conditional order // // { // "user_id":##, // "symbol":"BTCUSD", // "side":"Buy", // "order_type":"Market", // "price":0, // "qty":10, // "time_in_force":"GoodTillCancel", // "stop_order_type":"Stop", // "trigger_by":"LastPrice", // "base_price":11833, // "order_status":"Untriggered", // "ext_fields":{ // "stop_order_type":"Stop", // "trigger_by":"LastPrice", // "base_price":11833, // "expected_direction":"Rising", // "trigger_price":12400, // "close_on_trigger":true, // "op_from":"api", // "remark":"145.53.159.48", // "o_req_num":0 // }, // "leaves_qty":10, // "leaves_value":0.00080645, // "reject_reason":null, // "cross_seq":-1, // "created_at":"2020-08-21T09:18:48.000Z", // "updated_at":"2020-08-21T09:18:48.000Z", // "stop_px":12400, // "stop_order_id":"3f3b54b1-3379-42c