consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,133 lines (1,112 loc) • 102 kB
JavaScript
'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