Files
Superalgos/TaskServer/node_modules/ccxt/js/hbtc.js
T
2020-05-10 14:17:33 +02:00

2020 lines
82 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { AuthenticationError, ExchangeError, OrderNotFound, ArgumentsRequired, BadSymbol, BadRequest, NullResponse, InvalidOrder, BadResponse, NotSupported, ExchangeNotAvailable, RequestTimeout, RateLimitExceeded, PermissionDenied, InsufficientFunds, InvalidAddress } = require ('./base/errors');
const { TICK_SIZE, TRUNCATE } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
module.exports = class hbtc extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'hbtc',
'name': 'HBTC',
'countries': [ 'CN' ],
'rateLimit': 2000,
'version': 'v1',
'has': {
'CORS': false,
'fetchTime': true,
'fetchBidAsk': true,
'fetchBidsAsks': true,
'fetchTickers': true,
'fetchTicker': true,
'fetchDepositAddress': false,
'fetchOHLCV': true,
'fetchOrder': true,
'fetchOrders': false,
'fetchOpenOrders': true,
'fetchClosedOrders': true,
'fetchTradingLimits': true,
'fetchMarkets': true,
'fetchMyTrades': true,
'withdraw': true,
'fetchCurrencies': false,
'fetchDeposits': true,
'fetchWithdrawals': true,
'fetchAccounts': true,
'fetchLedger': true,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/80134449-70663300-85a7-11ea-8942-e204cdeaab5d.jpg', // 交易所LOGO
'api': {
'quote': 'https://api.hbtc.com/openapi/quote', // 市场API数据端点
'contract': 'https://api.hbtc.com/openapi/contract', // 合约API数据端点
'option': 'https://api.hbtc.com/openapi/option', // 合约API数据端点
'public': 'https://api.hbtc.com/openapi', // 公共API数据端点
'private': 'https://api.hbtc.com/openapi', // 私有API数据端点
'zendesk': 'https://hbtc.zendesk.com/hc/en-us',
},
'www': 'https://www.hbtc.com', // 公司主页
'referral': 'https://www.hbtc.com/register/O2S8NS', // 邀请链接
'doc': 'https://github.com/bhexopen/BHEX-OpenApi/tree/master/doc', // openapi文档地址
'fees': 'https://hbtc.zendesk.com/hc/zh-cn/articles/360009274694', // 费率介绍
},
'api': {
'public': {
'get': [
'ping',
'time',
'brokerInfo', // 查询当前broker交易规则和symbol信息
'getOptions',
],
},
'quote': {
'get': [
'depth', // 获取深度
'depth/merged',
'trades', // 获取当前最新成交
'klines', // 获取K线数据
'ticker/24hr', // 获取24小时价格变化数据
'ticker/price',
'ticker/bookTicker',
'contract/index', // 获取合约标的指数价格
'contract/depth', // 获取合约深度
'contract/depth/merged',
'contract/trades', // 获取合约最近成交,
'contract/klines', // 获取合约的K线数据
'contract/ticker/24hr',
'option/index',
'option/depth',
'option/depth/merged',
'option/trades',
'option/klines',
'option/ticker/24hr',
],
},
'contract': {
'get': [
// public
'insurance',
'fundingRate', // 获取资金费率信息
// private
'openOrders', // 查询合约当前委托
'historyOrders', // 查询合约历史委托
'getOrder', // 查询合约订单详情
'myTrades', // 查询合约历史成交
'positions', // 查询合约当前持仓
'account', // 查询合约账户信息
],
'post': [
'order', // 创建合约订单
'modifyMargin', // 修改保证金
],
'delete': [
'order/cancel', // 取消合约订单
'order/batchCancel',
],
},
'option': {
'get': [
'openOrders',
'positions',
'historyOrders',
// 'getOrder',
'myTrades',
'settlements',
'account',
],
'post': [
'order',
],
'delete': [
'order/cancel',
],
},
'private': {
'get': [
'order', // 查询订单
'openOrders', // 查询当前委托
'historyOrders', // 查询历史委托
'account', // 获取当前账户信息
'myTrades', // 查询历史成交
'depositOrders',
'withdrawalOrders',
'withdraw/detail',
'balance_flow',
],
'post': [
'order', // 创建新订单
'order/test',
'userDataStream',
'subAccount/query',
'transfer',
'user/transfer',
'withdraw',
],
'put': [
'userDataStream',
],
'delete': [
'order', // 取消订单
'userDataStream',
],
},
},
'precisionMode': TICK_SIZE,
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.002, // MAKER费率
'taker': 0.002, // TAKER费率
},
},
'exceptions': {
'exact': {
// general server or network errors
'-1000': ExchangeError, // An unknown error occured while processing the request
'-1001': ExchangeError, // Internal error, unable to process your request. Please try again
'-1002': AuthenticationError, // You are not authorized to execute this request. Request need API Key included in. We suggest that API Key be included in any request
'-1003': RateLimitExceeded, // Too many requests, please use the websocket for live updates
'-1004': BadRequest,
'-1005': PermissionDenied,
'-1006': BadResponse, // An unexpected response was received from the message bus. Execution status unknown. OPEN API server find some exception in execute request.Please report to Customer service
'-1007': RequestTimeout, // Timeout waiting for response from backend server. Send status unknown, execution status unknown
'-1014': InvalidOrder, // Unsupported order combination
'-1015': RateLimitExceeded, // Reach the rate limit.Please slow down your request speed
'-1016': ExchangeNotAvailable, // This service is no longer available
'-1020': NotSupported, // This operation is not supported
'-1021': BadRequest, // Timestamp for this request is outside of the recvWindow
'-1022': AuthenticationError, // Signature for this request is not valid
// request issues
'-1100': BadRequest, // Illegal characters found in a parameter
'-1101': BadRequest, // Too many parameters sent for this endpoint
'-1102': BadRequest, // A mandatory parameter was not sent, was empty/null, or malformed
'-1103': BadRequest, // An unknown parameter was sent
'-1104': BadRequest, // Not all sent parameters were read
'-1105': BadRequest, // A parameter was empty
'-1106': BadRequest, // A parameter was sent when not required
'-1111': BadRequest, // Precision is over the maximum defined for this asset
'-1112': NullResponse, // No orders on book for symbol
'-1114': InvalidOrder, // TimeInForce parameter sent when not required
'-1115': InvalidOrder, // Invalid timeInForce
'-1116': InvalidOrder, // Invalid orderType
'-1117': InvalidOrder, // Invalid side
'-1118': InvalidOrder, // New client order ID was empty
'-1119': InvalidOrder, // Original client order ID was empty
'-1120': BadRequest, // Invalid interval
'-1121': BadSymbol, // Invalid symbol
'-1125': AuthenticationError, // This listenKey does not exist
'-1127': BadRequest, // Lookup interval is too big
'-1128': BadRequest, // Combination of optional parameters invalid
'-1130': BadRequest, // Invalid data sent for a parameter
'-1131': InsufficientFunds,
'-1132': InvalidOrder, // Order price too high
'-1133': InvalidOrder, // Order price lower than the minimum,please check general broker info
'-1134': InvalidOrder, // Order price decimal too long,please check general broker info
'-1135': InvalidOrder, // Order quantity too large
'-1136': InvalidOrder, // Order quantity lower than the minimum
'-1137': InvalidOrder, // Order quantity decimal too long
'-1138': InvalidOrder, // Order price exceeds permissible range
'-1139': InvalidOrder, // Order has been filled
'-1140': InvalidOrder, // Transaction amount lower than the minimum
'-1141': InvalidOrder, // Duplicate clientOrderId
'-1142': InvalidOrder, // Order has been canceled
'-1143': OrderNotFound, // Cannot be found on order book
'-1144': InvalidOrder, // Order has been locked
'-1145': InvalidOrder, // This order type does not support cancellation
'-1146': RequestTimeout, // Order creation timeout
'-1147': RequestTimeout, // Order cancellation timeout
'-1149': InvalidOrder, // Create order failed
'-1187': InvalidAddress, // Withdrawal address not in whitelist
'-2010': InvalidOrder, // NEW_ORDER_REJECTED
'-2011': InvalidOrder, // CANCEL_REJECTED
'-2013': OrderNotFound, // Order does not exist
'-2014': AuthenticationError, // API-key format invalid
'-2015': AuthenticationError, // Invalid API-key, IP, or permissions for action
'-2016': ExchangeError, // No trading window could be found for the symbol. Try ticker/24hrs instead
},
},
// exchange-specific options
'options': {
'fetchTickers': {
'method': 'quoteGetTicker24hr',
},
},
});
}
async fetchTime (params = {}) {
const response = await this.publicGetTime (params);
//
// {
// "serverTime": 1527777538000
// }
//
return this.safeInteger (response, 'serverTime');
}
parseMarket (market, type = 'spot') {
const filters = this.safeValue (market, 'filters', []);
const id = this.safeString (market, 'symbol');
let baseId = this.safeString (market, 'baseAsset');
const quoteId = this.safeString (market, 'quoteAsset');
let base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
let symbol = base + '/' + quote;
let spot = true;
let future = false;
let option = false;
let inverse = false;
if (type === 'future') {
symbol = id;
spot = false;
future = true;
inverse = this.safeValue (market, 'inverse', false);
baseId = this.safeString (market, 'underlying');
base = this.safeCurrencyCode (baseId);
} else if (type === 'option') {
symbol = id;
spot = false;
option = true;
}
let amountMin = undefined;
let amountMax = undefined;
let priceMin = undefined;
let priceMax = undefined;
let costMin = undefined;
for (let j = 0; j < filters.length; j++) {
const filter = filters[j];
const filterType = this.safeString (filter, 'filterType');
if (filterType === 'LOT_SIZE') {
amountMin = this.safeFloat (filter, 'minQty');
amountMax = this.safeFloat (filter, 'maxQty');
}
if (filterType === 'PRICE_FILTER') {
priceMin = this.safeFloat (filter, 'minPrice');
priceMax = this.safeFloat (filter, 'maxPrice');
}
if (filterType === 'MIN_NOTIONAL') {
costMin = this.safeFloat (filter, 'minNotional');
}
}
if ((costMin === undefined) && (amountMin !== undefined) && (priceMin !== undefined)) {
costMin = amountMin * priceMin;
}
const precision = {
'price': this.safeFloat2 (market, 'quotePrecision', 'quoteAssetPrecision'),
'amount': this.safeFloat (market, 'baseAssetPrecision'),
};
const limits = {
'amount': {
'min': amountMin,
'max': amountMax,
},
'price': {
'min': priceMin,
'max': priceMax,
},
'cost': {
'min': costMin,
'max': undefined,
},
};
return {
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': true,
'type': type,
'spot': spot,
'future': future,
'option': option,
'inverse': inverse,
'precision': precision,
'limits': limits,
'info': market,
};
}
async fetchMarkets (params = {}) {
const response = await this.publicGetBrokerInfo (params);
//
// {
// "timezone":"UTC",
// "serverTime":"1588015885118",
// "brokerFilters":[],
// "symbols":[
// {
// "filters":[
// {"minPrice":"0.01","maxPrice":"100000.00000000","tickSize":"0.01","filterType":"PRICE_FILTER"},
// {"minQty":"0.0005","maxQty":"100000.00000000","stepSize":"0.000001","filterType":"LOT_SIZE"},
// {"minNotional":"5","filterType":"MIN_NOTIONAL"}
// ],
// "exchangeId":"301",
// "symbol":"BTCUSDT",
// "symbolName":"BTCUSDT",
// "status":"TRADING",
// "baseAsset":"BTC",
// "baseAssetPrecision":"0.000001",
// "quoteAsset":"USDT",
// "quotePrecision":"0.01",
// "icebergAllowed":false
// },
// ],
// "options":[
// {
// "filters":[
// {"minPrice":"0.01","maxPrice":"100000.00000000","tickSize":"0.01","filterType":"PRICE_FILTER"},
// {"minQty":"0.01","maxQty":"100000.00000000","stepSize":"0.001","filterType":"LOT_SIZE"},
// {"minNotional":"1","filterType":"MIN_NOTIONAL"}
// ],
// "exchangeId":"301",
// "symbol":"BTC0501CS8500",
// "symbolName":"BTC0501CS8500",
// "status":"TRADING",
// "baseAsset":"BTC0501CS8500",
// "baseAssetPrecision":"0.001",
// "quoteAsset":"BUSDT",
// "quotePrecision":"0.01",
// "icebergAllowed":false
// },
// ],
// "contracts":[
// {
// "filters":[
// {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"},
// {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"},
// {"minNotional":"0.000001","filterType":"MIN_NOTIONAL"}
// ],
// "exchangeId":"301",
// "symbol":"BTC-PERP-REV",
// "symbolName":"BTC-PERP-REV",
// "status":"TRADING",
// "baseAsset":"BTC-PERP-REV",
// "baseAssetPrecision":"1",
// "quoteAsset":"USDT",
// "quoteAssetPrecision":"0.1",
// "icebergAllowed":false,
// "inverse":true,
// "index":"BTCUSDT",
// "marginToken":"TBTC",
// "marginPrecision":"0.00000001",
// "contractMultiplier":"1.0",
// "underlying":"TBTC",
// "riskLimits":[
// {"riskLimitId":"200000001","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"},
// {"riskLimitId":"200000002","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"},
// {"riskLimitId":"200000003","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"},
// {"riskLimitId":"200000004","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"}
// ]
// },
// {
// "filters":[
// {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"},
// {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"},
// {"minNotional":"0.000001","filterType":"MIN_NOTIONAL"}
// ],
// "exchangeId":"301",
// "symbol":"BTC-SWAP",
// "symbolName":"BTC-SWAP",
// "status":"TRADING",
// "baseAsset":"BTC-SWAP",
// "baseAssetPrecision":"1",
// "quoteAsset":"USDT",
// "quoteAssetPrecision":"0.1",
// "icebergAllowed":false,
// "inverse":true,
// "index":"BTCUSDT",
// "marginToken":"BTC",
// "marginPrecision":"0.00000001",
// "contractMultiplier":"1.0",
// "underlying":"BTC",
// "riskLimits":[
// {"riskLimitId":"500000001","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"},
// {"riskLimitId":"500000002","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"},
// {"riskLimitId":"500000003","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"},
// {"riskLimitId":"500000004","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"}
// ]
// },
// {
// "filters":[
// {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"},
// {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"},
// {"minNotional":"0.000000001","filterType":"MIN_NOTIONAL"}
// ],
// "exchangeId":"301",
// "symbol":"BTC-PERP-BUSDT",
// "symbolName":"BTC-PERP-BUSDT",
// "status":"TRADING",
// "baseAsset":"BTC-PERP-BUSDT",
// "baseAssetPrecision":"1",
// "quoteAsset":"BUSDT",
// "quoteAssetPrecision":"0.1",
// "icebergAllowed":false,
// "inverse":false,
// "index":"BTCUSDT",
// "marginToken":"BUSDT",
// "marginPrecision":"0.0001",
// "contractMultiplier":"0.0001",
// "underlying":"TBTC",
// "riskLimits":[
// {"riskLimitId":"600000132","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"},
// {"riskLimitId":"600000133","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"},
// {"riskLimitId":"600000134","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"},
// {"riskLimitId":"600000135","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"}
// ]
// },
// ]
// }
//
const result = [];
const symbols = this.safeValue (response, 'symbols', []);
for (let i = 0; i < symbols.length; i++) {
const market = this.parseMarket (symbols[i], 'spot');
result.push (market);
}
const options = this.safeValue (response, 'options', []);
for (let i = 0; i < options.length; i++) {
const market = this.parseMarket (options[i], 'option');
result.push (market);
}
const contracts = this.safeValue (response, 'contracts', []);
for (let i = 0; i < contracts.length; i++) {
const market = this.parseMarket (contracts[i], 'future');
result.push (market);
}
return result;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit; // default 40, max 40
}
const response = await this.quoteGetDepth (this.extend (request, params));
//
// {
// "time":1588068913453,
// "bids":[
// ["0.025278","0.0202"],
// ["0.025277","16.1132"],
// ["0.025276","7.9056"],
// ]
// "asks":[
// ["0.025302","5.9999"],
// ["0.025303","34.9151"],
// ["0.025304","92.391"],
// ]
// }
//
const timestamp = this.safeInteger (response, 'time');
return this.parseOrderBook (response, timestamp);
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.quoteGetTicker24hr (this.extend (request, params));
//
// {
// "time":1588069860794,
// "symbol":"BNB0501PS16",
// "bestBidPrice":"0.2129",
// "bestAskPrice":"0.3163",
// "volume":"33547",
// "quoteVolume":"10801.987",
// "lastPrice":"0.2625",
// "highPrice":"0.3918",
// "lowPrice":"0.2625",
// "openPrice":"0.362",
// }
//
return this.parseTicker (response, market);
}
parseTickers (rawTickers, symbols = undefined) {
const tickers = [];
for (let i = 0; i < rawTickers.length; i++) {
tickers.push (this.parseTicker (rawTickers[i]));
}
return this.filterByArray (tickers, 'symbol', symbols);
}
async fetchBidAsk (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.quoteGetTickerBookTicker (this.extend (request, params));
//
// {
// "symbol": "LTCBTC",
// "bidPrice": "4.00000000",
// "bidQty": "431.00000000",
// "askPrice": "4.00000200",
// "askQty": "9.00000000"
// }
//
return this.parseTicker (response, market);
}
async fetchBidsAsks (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.quoteGetTickerBookTicker (params);
//
// [
// {
// "symbol": "LTCBTC",
// "bidPrice": "4.00000000",
// "bidQty": "431.00000000",
// "askPrice": "4.00000200",
// "askQty": "9.00000000"
// },
// {
// "symbol": "ETHBTC",
// "bidPrice": "0.07946700",
// "bidQty": "9.00000000",
// "askPrice": "100000.00000000",
// "askQty": "1000.00000000"
// },
// ]
//
return this.parseTickers (response, symbols);
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const options = this.safeValue (this.options, 'fetchTickers', {});
const defaultMethod = this.safeString (options, 'method', 'quoteGetTicker24hr');
const defaultType = this.safeString (options, 'type', 'spot');
const type = this.safeString (params, 'type', defaultType);
const query = this.omit (params, 'type');
let method = defaultMethod;
if (type === 'future') {
method = 'quoteGetContractTicker24hr';
} else if (type === 'option') {
method = 'quoteGetOptionTicker24hr';
}
const response = await this[method] (query);
//
// [
// {
// "time": 1538725500422,
// "symbol": "ETHBTC",
// "lastPrice": "4.00000200",
// "openPrice": "99.00000000",
// "highPrice": "100.00000000",
// "lowPrice": "0.10000000",
// "volume": "8913.30000000"
// },
// ]
//
return this.parseTickers (response, symbols);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const options = this.safeValue (this.options, 'fetchBalance', {});
const defaultType = this.safeString (options, 'type', 'spot');
const type = this.safeString (params, 'type', defaultType);
const query = this.omit (params, 'type');
let method = 'privateGetAccount';
if (type === 'future') {
method = 'contractGetAccount';
} else if (type === 'option') {
method = 'optionGetAccount';
}
const response = await this[method] (query);
//
// spot
//
// {
// 'balances': [
// {
// 'asset': 'ALGO',
// 'free': '0',
// 'locked': '0'
// },
// {
// 'asset': 'BHT',
// 'free': '0',
// 'locked': '0'
// }
// ]
// }
//
// contract
//
// {
// "BUSDT":{
// "total":"1000",
// "availableMargin":"1000",
// "positionMargin":"0",
// "orderMargin":"0",
// "tokenId":"BUSDT"
// },
// "TBTC":{
// "total":"0.5",
// "availableMargin":"0.5",
// "positionMargin":"0",
// "orderMargin":"0",
// "tokenId":"TBTC"
// }
// }
//
// option
//
// {
// "optionAsset":"",
// "balances":[
// {
// "tokenName":"USDT",
// "free":"0.0",
// "locked":"0.0",
// "margin":"0.0"
// },
// {
// "tokenName":"BUSDT",
// "free":"0.0",
// "locked":"0.0",
// "margin":"0.0"
// }
// ]
// }
//
const balances = this.safeValue (response, 'balances');
const result = { 'info': response };
if (balances !== undefined) {
for (let i = 0; i < balances.length; i++) {
const balance = balances[i];
const currencyId = this.safeString2 (balance, 'asset', 'tokenName');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (balance, 'free');
account['used'] = this.safeFloat (balance, 'locked');
result[code] = account;
}
} else {
const currencyIds = Object.keys (response);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const balance = response[currencyId];
const account = this.account ();
account['free'] = this.safeFloat (balance, 'availableMargin');
account['total'] = this.safeFloat (balance, 'total');
result[code] = account;
}
}
return this.parseBalance (result);
}
async fetchTrades (symbol, since = undefined, limit = 50, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
const response = await this.quoteGetTrades (this.extend (request, params));
//
// [
// {"price":"0.025344","time":1588084082060,"qty":"1","isBuyerMaker":false},
// {"price":"0.02535","time":1588084086021,"qty":"0.553","isBuyerMaker":true},
// {"price":"0.025348","time":1588084097037,"qty":"1","isBuyerMaker":false},
// ]
//
return this.parseTrades (response, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
//
// [
// 1587906000000, // open time
// "0.1761", // open
// "0.1761", // high
// "0.1761", // low
// "0.1761", // close
// "0", // base volume
// 0, // close time
// "0", // quote volume
// 0, // number of trades
// "0", // taker buy base asset volume
// "0" // taker buy quote asset volume
// ]
//
return [
this.safeInteger (ohlcv, 0),
this.safeFloat (ohlcv, 1),
this.safeFloat (ohlcv, 2),
this.safeFloat (ohlcv, 3),
this.safeFloat (ohlcv, 4),
this.safeFloat (ohlcv, 5),
];
}
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],
};
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 500
}
const response = await this.quoteGetKlines (this.extend (request, params));
//
// [
// [1587906000000,"0.1761","0.1761","0.1761","0.1761","0",0,"0",0,"0","0"],
// [1587906180000,"0.1761","0.1761","0.1761","0.1761","0",0,"0",0,"0","0"],
// [1587906360000,"0.1761","0.1848","0.1761","0.1848","53",0,"9.7944",1,"0","0"],
// ]
//
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
const request = {
// if only fromId is set,it will get orders < that fromId in descending order
// if only toId is set, it will get orders > that toId in ascending order
// if fromId is set and toId is set, it will get orders < that fromId and > that toId in descending order
// if fromId is not set and toId it not set, most recent order are returned in descending order
// 'fromId': '43287482374',
// 'toId': '43287482374',
// 'endTime': this.milliseconds (), // optional, spot only
};
const defaultType = this.safeString (this.options, 'type', 'spot');
const options = this.safeValue (this.options, 'fetchMyTrades', {});
const fetchMyTradesType = this.safeString (options, 'type', defaultType);
let type = this.safeString (params, 'type', fetchMyTradesType);
if (symbol !== undefined) {
market = this.market (symbol);
request['symbol'] = market['id'];
type = market['type'];
}
const query = this.omit (params, 'type');
if (limit !== undefined) {
// spot default 500, max 1000
// futures and options default 20, max 1000
request['limit'] = limit;
}
let method = 'privateGetMyTrades';
if (type === 'future') {
method = 'contractGetMyTrades';
} else {
if (type === 'option') {
method = 'optionGetMyTrades';
} else {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument for ' + type + ' markets');
}
const market = this.market (symbol);
request['symbol'] = market['id'];
// spot only?
if (since !== undefined) {
request['startTime'] = since;
}
}
}
if (since !== undefined) {
request['startTime'] = since;
}
const response = await this[method] (this.extend (request, query));
//
// spot
//
// [
// {
// "id":"616384027512920576",
// "symbol":"TBTCBUSDT",
// "orderId":"616384027202542080",
// "matchOrderId":"605124954767266560",
// "price":"6826.06",
// "qty":"0.1",
// "commission":"0.682606",
// "commissionAsset":"BUSDT",
// "time":"1588214701982",
// "isBuyer":false,
// "isMaker":false,
// "fee":{
// "feeTokenId":"BUSDT",
// "feeTokenName":"BUSDT",
// "fee":"0.682606"
// }
// }
// ]
//
return this.parseTrades (response, market, since, limit);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const orderSide = side.toUpperCase ();
const orderType = type.toUpperCase ();
const request = {
'symbol': market['id'],
// BUY or SELL for spot and options
'side': orderSide,
// GTC, FOK, IOC for spot and options
// GTC, FOK, IOC, LIMIT_MAKER for futures
// 'timeInForce': 'GTC',
};
let query = params;
let method = 'privatePostOrder';
if (market['type'] === 'future') {
if ((orderSide !== 'BUY_OPEN') && (orderSide !== 'SELL_OPEN') && (orderSide !== 'BUY_CLOSE') && (orderSide !== 'SELL_CLOSE')) {
throw new NotSupported (this.id + ' createOrder() does not support order side ' + side + ' for ' + market['type'] + ' markets, only BUY_OPEN, SELL_OPEN, BUY_CLOSE and SELL_CLOSE are supported');
}
if ((orderType !== 'LIMIT') && (orderType !== 'STOP')) {
throw new NotSupported (this.id + ' createOrder() does not support order type ' + type + ' for ' + market['type'] + ' markets, only LIMIT and STOP are supported');
}
const clientOrderId = this.safeValue (params, 'clientOrderId');
if (clientOrderId === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder() requires a clientOrderId parameter for ' + market['type'] + ' markets, supply clientOrderId in the params argument');
}
const leverage = this.safeValue (params, 'leverage');
if (leverage === undefined && (orderSide === 'BUY_OPEN' || orderSide === 'SELL_OPEN')) {
throw new NotSupported (this.id + ' createOrder() requires a leverage parameter for ' + market['type'] + ' markets if orderSide is BUY_OPEN or SELL_OPEN');
}
method = 'contractPostOrder';
const priceType = this.safeString (params, 'priceType');
if (priceType === undefined) {
request['price'] = this.priceToPrecision (symbol, price);
} else {
request['priceType'] = priceType;
if (priceType === 'INPUT') {
request['price'] = this.priceToPrecision (symbol, price);
}
}
request['orderType'] = type.toUpperCase (); // LIMIT, STOP
request['quantity'] = this.amountToPrecision (symbol, amount);
// request['leverage'] = 1; // not required for closing orders
request['leverage'] = leverage;
request['clientOrderId'] = clientOrderId;
// optional
// request['priceType'] = 'INPUT', // INPUT, OPPONENT, QUEUE, OVER, MARKET
// request['triggerPrice'] = 123.45;
} else {
if (market['type'] === 'option') {
method = 'optionPostOrder';
}
const newClientOrderId = this.safeValue2 (params, 'clientOrderId', 'newClientOrderId');
if (newClientOrderId !== undefined) {
request['newClientOrderId'] = newClientOrderId;
}
request['type'] = orderType;
if (type === 'limit') {
request['price'] = this.priceToPrecision (symbol, price);
request['quantity'] = this.amountToPrecision (symbol, amount);
} else if (type === 'market') {
// for market buy it requires the amount of quote currency to spend
if (side === 'buy') {
const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true);
if (createMarketBuyOrderRequiresPrice) {
if (price !== undefined) {
amount = amount * price;
} else {
throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument (the exchange-specific behaviour)");
}
}
const precision = market['precision']['price'];
request['quantity'] = this.decimalToPrecision (amount, TRUNCATE, precision, this.precisionMode);
} else {
request['quantity'] = this.amountToPrecision (symbol, amount);
}
}
}
query = this.omit (query, [ 'clientOrderId', 'newClientOrderId' ]);
const response = await this[method] (this.extend (request, query));
//
// spot
//
// {
// "symbol":"TBTCBUSDT",
// "orderId":"616376654496877056",
// "clientOrderId":"158821382304516955",
// "transactTime":"1588213823080",
// "price":"0",
// "origQty":"1000",
// "executedQty":"0",
// "status":"NEW",
// "timeInForce":"GTC",
// "type":"MARKET",
// "side":"BUY"
// }
//
// contract
//
// {
// 'time': '1570759718825',
// 'updateTime': '0',
// 'orderId': '469961015902208000',
// 'clientOrderId': '6423344174',
// 'symbol': 'BTC-PERP-REV',
// 'price': '8200',
// 'leverage': '12.08',
// 'origQty': '5',
// 'executedQty': '0',
// 'avgPrice': '0',
// 'marginLocked': '0.00005047',
// 'orderType': 'LIMIT',
// 'side': 'BUY_OPEN',
// 'fees': [],
// 'timeInForce': 'GTC',
// 'status': 'NEW',
// 'priceType': 'INPUT'
// }
//
return this.parseOrder (response, market);
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const clientOrderId = this.safeValue2 (params, 'origClientOrderId', 'clientOrderId');
const request = {};
const defaultType = this.safeString (this.options, 'type', 'spot');
const options = this.safeValue (this.options, 'cancelOrder', {});
const cancelOrderType = this.safeString (options, 'type', defaultType);
let type = this.safeString (params, 'type', cancelOrderType);
let query = this.omit (params, 'type');
if (clientOrderId !== undefined) {
request['origClientOrderId'] = clientOrderId;
query = this.omit (query, [ 'origClientOrderId', 'clientOrderId' ]);
} else {
request['orderId'] = id;
}
let method = 'privateDeleteOrder';
const orderType = this.safeString (query, 'orderType');
if (orderType !== undefined) {
type = 'future';
}
if (type === 'future') {
method = 'contractDeleteOrderCancel';
if (orderType === undefined) {
throw new ArgumentsRequired (this.id + " cancelOrder() requires an orderType parameter, pass the { 'orderType': 'LIMIT' } or { 'orderType': 'STOP' } in params argument");
}
request['orderType'] = orderType;
} else {
if (type === 'option') {
method = 'optionDeleteOrderCancel';
}
}
const response = await this[method] (this.extend (request, query));
//
// spot
//
// {
// 'exchangeId': '301',
// 'symbol': 'BHTUSDT',
// 'clientOrderId': '0',
// 'orderId': '499890200602846976',
// 'status': 'CANCELED'
// }
//
// futures
//
// {
// "time":"1588353669383",
// "updateTime":"0",
// "orderId":"617549770304599296",
// "clientOrderId":"test-001",
// "symbol":"BTC-PERP-REV",
// "price":"10000",
// "leverage":"1",
// "origQty":"100",
// "executedQty":"0",
// "avgPrice":"0",
// "marginLocked":"0",
// "orderType":"LIMIT",
// "side":"SELL_OPEN",
// "fees":[],
// "timeInForce":"GTC",
// "status":"CANCELED",
// "priceType":"INPUT",
// }
//
return this.parseOrder (response);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
const request = {
// if orderId is set, it will get orders < that orderId otherwise most recent orders are returned
// 'orderId': '43287482374',
};
const defaultType = this.safeString (this.options, 'type', 'spot');
const options = this.safeValue (this.options, 'fetchOpenOrders', {});
const fetchOpenOrdersType = this.safeString (options, 'type', defaultType);
let type = this.safeString (params, 'type', fetchOpenOrdersType);
if (symbol !== undefined) {
market = this.market (symbol);
request['symbol'] = market['id'];
type = market['type'];
}
const query = this.omit (params, 'type');
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
let method = 'privateGetOpenOrders';
if (type === 'future') {
method = 'contractGetOpenOrders';
} else if (type === 'option') {
method = 'optionGetOpenOrders';
}
const response = await this[method] (this.extend (request, query));
//
// spot
//
// [
// {
// 'orderId': '499902955766523648',
// 'clientOrderId': '157432907618453',
// 'exchangeId': '301',
// 'symbol': 'BHTUSDT',
// 'price': '0.01',
// 'origQty': '50',
// 'executedQty': '0',
// 'cummulativeQuoteQty': '0',
// 'avgPrice': '0',
// 'status': 'NEW',
// 'timeInForce': 'GTC',
// 'type': 'LIMIT',
// 'side': 'BUY',
// 'stopPrice': '0.0',
// 'icebergQty': '0.0',
// 'time': '1574329076202',
// 'updateTime': '0',
// 'isWorking': true
// }
// ]
//
// futures
//
// [
// {
// "time":"1588353669383",
// "updateTime":"0",
// "orderId":"617549770304599296",
// "clientOrderId":"test-001",
// "symbol":"BTC-PERP-REV",
// "price":"10000",
// "leverage":"1",
// "origQty":"100",
// "executedQty":"0",
// "avgPrice":"0",
// "marginLocked":"0.01",
// "orderType":"LIMIT",
// "side":"SELL_OPEN",
// "fees":[],
// "timeInForce":"GTC",
// "status":"NEW",
// "priceType":"INPUT"
// }
// ]
//
return this.parseOrders (response, market, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
const request = {
// if orderId is set, it will get orders < that orderId otherwise most recent orders are returned
// 'orderId': '43287482374',
// 'endTime': this.milliseconds (), // optional
};
const defaultType = this.safeString (this.options, 'type', 'spot');
const options = this.safeValue (this.options, 'fetchClosedOrders', {});
const fetchClosedOrdersType = this.safeString (options, 'type', defaultType);
let type = this.safeString (params, 'type', fetchClosedOrdersType);
if (symbol !== undefined) {
market = this.market (symbol);
request['symbol'] = market['id'];
type = market['type'];
}
const query = this.omit (params, 'type');
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
if (since !== undefined) {
request['startTime'] = since;
}
let method = 'privateGetHistoryOrders';
if (type === 'future') {
method = 'contractGetHistoryOrders';
} else if (type === 'option') {
method = 'optionGetHistoryOrders';
}
const response = await this[method] (this.extend (request, query));
//
// spot
//
// [
// {
// "orderId":"616384027202542080",
// "clientOrderId":"158821470194414688",
// "exchangeId":"301",
// "symbol":"TBTCBUSDT",
// "price":"0",
// "origQty":"0.1",
// "executedQty":"0.1",
// "cummulativeQuoteQty":"682.606",
// "avgPrice":"6826.06",
// "status":"FILLED",
// "timeInForce":"GTC",
// "type":"MARKET",
// "side":"SELL",
// "stopPrice":"0.0",
// "icebergQty":"0.0",
// "time":"1588214701974",
// "updateTime":"0",
// "isWorking":true
// }
// ]
//
return this.parseOrders (response, market, since, limit);
}
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const clientOrderId = this.safeValue2 (params, 'origClientOrderId', 'clientOrderId');
const request = {};
const defaultType = this.safeString (this.options, 'type', 'spot');
const options = this.safeValue (this.options, 'fetchOrder', {});
const fetchOrderType = this.safeString (options, 'type', defaultType);
const type = this.safeString (params, 'type', fetchOrderType);
let query = this.omit (params, 'type');
if (clientOrderId !== undefined) {
request['origClientOrderId'] = clientOrderId;
query = this.omit (query, [ 'origClientOrderId', 'clientOrderId' ]);
} else {
request['orderId'] = id;
}
let method = 'privateGetOrder';
if (type === 'future') {
method = 'contractGetGetOrder';
} else if (type === 'option') {
method = 'optionGetGetOrder';
}
const response = await this[method] (this.extend (request, query));
return this.parseOrder (response);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let currency = undefined;
const request = {
// 'fromId': 'string', // if fromId is set, it will get deposits > that fromId, otherwise most recent deposits are returned
};
if (code !== undefined) {
currency = this.currency (code);
}
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetDepositOrders (this.extend (request, params));
//
// [
// {
// 'time': '1565769575929',
// 'orderId': '428100569859739648',
// 'token': 'USDT',
// 'address': '',
// 'addressTag': '',
// 'fromAddress': '',
// 'fromAddressTag': '',
// 'quantity': '1100',
// },
// ]
//
return this.parseTransactions (response, currency, since, limit);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let currency = undefined;
const request = {
// 'fromId': 'string', // if fromId is set, it will get deposits > that fromId, otherwise most recent deposits are returned
};
if (code !== undefined) {
currency = this.currency (code);
request['token'] = currency['id'];
}
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
const response = await this.privateGetWithdrawalOrders (this.extend (request, params));
//
// [
// {
// "time":"1536232111669",
// "orderId":"90161227158286336",
// "accountId":"517256161325920",
// "tokenId":"BHC",
// "tokenName":"BHC",
// "address":"0x815bF1c3cc0f49b8FC66B21A7e48fCb476051209",
// "addressExt":"address tag",
// "quantity":"14", // Withdrawal qty
// "arriveQuantity":"14", // Arrived qty
// "statusCode":"PROCESSING_STATUS",
// "status":3,
// "txid":"",
// "txidUrl":"",
// "walletHandleTime":"1536232111669",
// "feeTokenId":"BHC",
// "feeTokenName":"BHC",
// "fee":"0.1",
// "requiredConfirmNum":0, // Required confirmations
// "confirmNum":0, // Confirmations
// "kernelId":"", // BEAM and GRIN only
// "isInternalTransfer": false // True if this transfer is internal
// }
// ]
//
return this.parseTransactions (response, currency, since, limit);
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
const clientOrderId = this.safeString (params, 'clientOrderId', this.uuid ());
const request = {
'clientOrderId': clientOrderId,
'tokenId': currency['id'],
'address': address, // the withdrawal address must be in current tag list in your PC/APP client
'withdrawQuantity': amount,
// 'chainType': 'OMNI', // OMNI, ERC20, TRC20
};
if (tag !== undefined) {
request['addressExt'] = tag;
}
const response = await this.privatePostWithdraw (this.extend (request, params));
//
// {
// "status": 0,
// "success": true,
// "needBrokerAudit": false, // Whether this request needs broker auit
// "orderId": "423885103582776064" // Id for successful withdrawal
// }
//
return {
'info': response,
'id': this.safeString (response, 'orderId'),
};
}
async fetchAccounts (params = {}) {
const response = await this.privatePostSubAccountQuery (params);
//
// [
// {
// "accountId": "122216245228131",
// "accountName": "createSubAccountByCurl", // sub-account name
// "accountType": 1, // 1 token trading, 2 options, 3 futures
// "accountIndex": 1, // 0 main account, 1 sub-account
// },
// ]
//
const result = [];
for (let i = 0; i < response.length; i++) {
const account = response[i];
const accountId = this.safeString (account, 'accountId');
const accountType = this.safeString (account, 'accountType');
let type = accountType;
if (accountType === '1') {
type = 'spot';
} else if (accountType === '2') {
type = 'option';
} else if (accountType === '3') {
type = 'future';
}
result.push ({
'id': accountId,
'type': type,
'currency': undefined,
'info': account,
});
}
return result;
}
async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'accountType': 1, // spot 1, options 2, futures 3
'accountIndex': 0, // main 0, sub-account 1
'fromFlowId': '', // flowId to start from
'endFlowId': '', // flowId to end with
'endTime': 1588450533040,
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
request['tokenId'] = currency['id'];
}
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 500
}
const response = await this.privateGetBalanceFlow (this.extend (request, params));
//
// [
// {
// "id": "539870570957903104",
// "accountId": "122216245228131",
// "tokenId": "BTC",
// "tokenName": "BTC",
// "flowTypeValue": 51,
// "flowType": "USER_ACCOUNT_TRANSFER",
// "flowName": "Transfer",
// "change": "-12.5",
// "total": "379.624059937852365", // after change
// "created": "1579093587214"
// },
// {
// "id": "536072393645448960",
// "accountId": "122216245228131",
// "tokenId": "USDT",
// "tokenName": "USDT",
// "flowTypeValue": 7,
// "flowType": "AIRDROP",
// "flowName": "Airdrop",
// "change": "-2000",
// "total": "918662.0917630848",
// "created": "1578640809195"
// }
// ]
//
return this.parseLedger (response, currency, since, limit);
}
parseLedgerEntry (item, currency = undefined) {
//
// {
// "id": "539870570957903104",
// "accountId": "122216245228131",
// "tokenId": "BTC",
// "tokenName": "BTC",
// "flowTypeValue": 51,
// "flowType": "USER_ACCOUNT_TRANSFER",
// "flowName": "Transfer",
// "change": "-12.5",
// "total": "379.624059937852365", // after change
// "created": "1579093587214"
// }
//
// {
// "id": "536072393645448960",
// "accountId": "122216245228131",
// "tokenId": "USDT",
// "tokenName": "USDT",
// "flowTypeValue": 7,
// "flowType": "AIRDROP",
// "flowName": "Airdrop",
// "change": "-2000",
// "total": "918662.0917630848",
// "created": "1578640809195"
// }
//
const currencyId = this.safeString (item, 'tokenId');
const code = this.safeCurrencyCode (currencyId, currency);
const amount = this.safeFloat (item, 'change');
const after = this.safeFloat (item, 'total');
const direction = (amount < 0) ? 'out' : 'in';
let before = undefined;
if (after !== undefined && amount !== undefined) {
const difference = (direction === 'out') ? amount : -amount;
before = this.sum (after, difference);
}
const timestamp = this.safeInteger (item, 'created');
const type = this.parseLedgerEntryType (this.safeString (item, 'flowType'));
const id = this.safeString (item, 'id');
const account = this.safeString (item, 'accountId');
return {
'id': id,
'currency': code,
'account': account,
'referenceAccount': undefined,
'referenceId': undefined,
'status': undefined,
'amount': amount,
'before': before,
'after': after,
'fee': undefined,
'direction': direction,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'type': type,
'info': item,
};
}
parseLedgerEntryType (type) {
const types = {
'TRADE': 'trade',
'FEE': 'fee',
'TRANSFER': 'transfer',
'DEPOSIT': 'transaction',
'MAKER_REWARD': 'rebate',
'PNL': 'pnl',
'SETTLEMENT': 'settlement',
'LIQUIDATION': 'liquidation',
'FUNDING_SETTLEMENT': 'settlement',
'USER_ACCOUNT_TRANSFER': 'transfer',
'OTC_BUY_COIN': 'trade',
'OTC_SELL_COIN': 'trade',
'OTC_FEE': 'fee',
'OTC_TRADE': 'trade',
'ACTIVITY_AWARD': 'referral',
'INVITATION_REFERRAL_BONUS': 'referral',
'REGISTER_BONUS': 'referral',
'AIRDROP': 'airdrop',
'MINE_REWARD': 'reward',
};
return this.safeString (types, type, type);
}
parseTransactionStatus (status) {
const statuses = {
'BROKER_AUDITING_STATUS': 'pending',
'BROKER_REJECT_STATUS': 'failed',
'AUDITING_STATUS': 'pending',
'AUDIT_REJECT_STATUS': 'failed',
'PROCESSING_STATUS': 'pending',
'WITHDRAWAL_SUCCESS_STATUS': 'ok',
'WITHDRAWAL_FAILURE_STATUS': 'failed',
'BLOCK_MINING_STATUS': 'ok',
};
return this.safeString (statuses, status, status);
}
parseTransaction (transaction, currency = undefined) {
//
// fetchDeposits
//
// {
// 'time': '1565769575929',
// 'orderId': '428100569859739648',
// 'token': 'USDT',
// 'address': '',
// 'addressTag': '',
// 'fromAddress': '',
// 'fromAddressTag': '',
// 'quantity': '1100',
// }
//
// fetchWithdrawals
//
// {
// "time":"1536232111669",
// "orderId":"90161227158286336",
// "accountId":"517256161325920",
// "tokenId":"BHC",
// "tokenName":"BHC",
// "address":"0x815bF1c3cc0f49b8FC66B21A7e48fCb476051209",
// "addressExt":"address tag",
// "quantity":"14", // Withdrawal qty
// "arriveQuantity":"14", // Arrived qty
// "statusCode":"PROCESSING_STATUS",
// "status":3,
// "txid":"",
// "txidUrl":"",
// "walletHandleTime":"1536232111669",
// "feeTokenId":"BHC",
// "feeTokenName":"BHC",
// "fee":"0.1",
// "requiredConfirmNum":0, // Required confirmations
// "confirmNum":0, // Confirmations
// "kernelId":"", // BEAM and GRIN only
// "isInternalTransfer": false // True if this transfer is internal
// }
//
const id = this.safeString (transaction, 'orderId');
const address = this.safeString (transaction, 'address');
let tag = this.safeString2 (transaction, 'addressExt', 'addressTag');
if (tag !== undefined) {
if (tag.length < 1) {
tag = undefined;
}
}
const addressFrom = this.safeString (transaction, 'fromAddress');
let tagFrom = this.safeString (transaction, 'fromAddressTag');
if (tagFrom !== undefined) {
if (tagFrom.length < 1) {
tagFrom = undefined;
}
}
const currencyId = this.safeString (transaction, 'tokenId');
const code = this.safeCurrencyCode (currencyId, currency);
const timestamp = this.safeInteger (transaction, 'time');
let txid = this.safeString (transaction, 'txid');
if (txid === '') {
txid = undefined;
}
let type = undefined;
let status = this.parseTransactionStatus (this.safeString (transaction, 'statusCode'));
if (status === undefined) {
type = 'deposit';
status = 'ok';
} else {
type = 'withdrawal';
}
const amount = this.safeFloat (transaction, 'quantity');
const feeCost = this.safeFloat (transaction, 'fee');
let fee = undefined;
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (transaction, 'feeTokenId');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'currency': feeCurrencyCode,
'cost': feeCost,
};
}
return {
'info': transaction,
'id': id,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'addressFrom': addressFrom,
'address': address,
'addressTo': address,
'tagFrom': tagFrom,
'tag': tag,
'tagTo': tag,
'type': type,
'amount': amount,
'currency': code,
'status': status,
'updated': undefined,
'fee': fee,
};
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker, fetchTickers
//
// {
// "time":1588069860794,
// "symbol":"BNB0501PS16",
// "bestBidPrice":"0.2129",
// "bestAskPrice":"0.3163",
// "volume":"33547",
// "quoteVolume":"10801.987",
// "lastPrice":"0.2625",
// "highPrice":"0.3918",
// "lowPrice":"0.2625",
// "openPrice":"0.362",
// }
//
// fetchBidAsk, fetchBidAsks
//
// {
// "symbol": "LTCBTC",
// "bidPrice": "4.00000000",
// "bidQty": "431.00000000",
// "askPrice": "4.00000200",
// "askQty": "9.00000000"
// }
//
let symbol = undefined;
const marketId = this.safeString (ticker, 'symbol');
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
}
if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (ticker, 'time');
const open = this.safeFloat (ticker, 'openPrice');
const close = this.safeFloat (ticker, 'lastPrice');
let change = undefined;
let percentage = undefined;
let average = undefined;
if ((open !== undefined) && (close !== undefined)) {
change = close - open;
average = this.sum (open, close) / 2;
if ((close !== undefined) && (close > 0)) {
percentage = (change / open) * 100;
}
}
const quoteVolume = this.safeFloat (ticker, 'quoteVolume');
const baseVolume = this.safeFloat (ticker, 'volume');
let vwap = undefined;
if (baseVolume !== undefined && quoteVolume !== undefined && baseVolume > 0) {
vwap = quoteVolume / baseVolume;
}
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'highPrice'),
'low': this.safeFloat (ticker, 'lowPrice'),
'bid': this.safeFloat2 (ticker, 'bestBidPrice', 'bidPrice'),
'bidVolume': this.safeFloat (ticker, 'bidQty'),
'ask': this.safeFloat2 (ticker, 'bestAskPrice', 'askPrice'),
'askVolume': this.safeFloat (ticker, 'askQty'),
'vwap': vwap,
'open': open,
'close': close,
'last': close,
'previousClose': undefined,
'change': change,
'percentage': percentage,
'average': average,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'info': ticker,
};
}
parseTrade (trade, market) {
//
// fetchTrades (public)
//
// {
// "price":"0.025344",
// "time":1588084082060,
// "qty":"1",
// "isBuyerMaker":false
// }
//
// fetchMyTrades (private)
//
// spot
//
// {
// "id":"616384027512920576",
// "symbol":"TBTCBUSDT",
// "orderId":"616384027202542080",
// "matchOrderId":"605124954767266560",
// "price":"6826.06",
// "qty":"0.1",
// "commission":"0.682606",
// "commissionAsset":"BUSDT",
// "time":"1588214701982",
// "isBuyer":false,
// "isMaker":false,
// "fee":{
// "feeTokenId":"BUSDT",
// "feeTokenName":"BUSDT",
// "fee":"0.682606"
// }
// }
//
const id = this.safeString (trade, 'id');
const timestamp = this.safeFloat (trade, 'time');
const type = undefined;
const orderId = this.safeString (trade, 'orderId');
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'qty');
let cost = undefined;
if (price !== undefined) {
if (amount !== undefined) {
cost = price * amount;
}
}
let side = undefined;
let takerOrMaker = undefined;
if ('isBuyerMaker' in trade) {
side = trade['isBuyerMaker'] ? 'sell' : 'buy';
} else {
const isMaker = this.safeValue (trade, 'isMaker');
if (isMaker !== undefined) {
takerOrMaker = isMaker ? 'maker' : 'taker';
}
const isBuyer = this.safeValue (trade, 'isBuyer');
side = isBuyer ? 'buy' : 'sell';
}
let fee = undefined;
const feeCost = this.safeFloat (trade, 'commission');
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (trade, 'commissionAsset');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCost,
'currency': feeCurrencyCode,
};
}
let symbol = undefined;
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
return {
'id': id,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': type,
'order': orderId,
'side': side,
'takerOrMaker': takerOrMaker,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
};
}
parseOrder (order, market = undefined) {
//
// createOrder
//
// {
// "symbol":"TBTCBUSDT",
// "orderId":"616376654496877056",
// "clientOrderId":"158821382304516955",
// "transactTime":"1588213823080",
// "price":"0",
// "origQty":"1000",
// "executedQty":"0",
// "status":"NEW",
// "timeInForce":"GTC",
// "type":"MARKET",
// "side":"BUY"
// }
//
// fetchOrder, fetchOpenOrders, fetchClosedOrders
//
// spot
//
// {
// "orderId":"616384027202542080",
// "clientOrderId":"158821470194414688",
// "exchangeId":"301",
// "symbol":"TBTCBUSDT",
// "price":"0",
// "origQty":"0.1",
// "executedQty":"0.1",
// "cummulativeQuoteQty":"682.606",
// "avgPrice":"6826.06",
// "status":"FILLED",
// "timeInForce":"GTC",
// "type":"MARKET",
// "side":"SELL",
// "stopPrice":"0.0",
// "icebergQty":"0.0",
// "time":"1588214701974",
// "updateTime":"0",
// "isWorking":true
// }
//
// future
//
// {
// time: "1588353669383",
// updateTime: "0",
// orderId: "617549770304599296",
// clientOrderId: "test-001",
// symbol: "BTC-PERP-REV",
// price: "10000",
// leverage: "1",
// origQty: "100",
// executedQty: "0",
// avgPrice: "0",
// marginLocked: "0",
// orderType: "LIMIT",
// side: "SELL_OPEN",
// fees: [],
// timeInForce: "GTC",
// status: "CANCELED",
// priceType: "INPUT"
// }
//
//
const id = this.safeString (order, 'orderId');
const clientOrderId = this.safeString (order, 'clientOrderId');
let timestamp = this.safeInteger (order, 'time');
if (timestamp === undefined) {
timestamp = this.safeInteger (order, 'transactTime');
}
let symbol = undefined;
if (market === undefined) {
let marketId = this.safeString (order, 'symbol');
if (marketId !== undefined) {
marketId = marketId.toUpperCase ();
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
}
}
}
let type = this.safeStringLower (order, 'type');
const side = this.safeStringLower (order, 'side');
let price = this.safeFloat (order, 'price');
let average = this.safeFloat (order, 'avgPrice');
let amount = undefined;
let cost = this.safeFloat (order, 'cummulativeQuoteQty');
let filled = undefined;
let remaining = undefined;
if (type === undefined) {
type = this.safeStringLower (order, 'orderType');
if ((market !== undefined) && market['inverse']) {
cost = this.safeFloat (order, 'executedQty');
amount = undefined;
}
if (cost === 0.0) {
filled = 0;
}
} else {
amount = this.safeFloat (order, 'origQty');
if (type === 'market') {
price = undefined;
if (side === 'buy') {
amount = undefined;
}
}
filled = this.safeFloat (order, 'executedQty');
if (filled !== undefined) {
if (amount !== undefined) {
remaining = amount - filled;
}
}
}
if (average === 0.0) {
average = undefined;
}
const status = this.parseOrderStatus (this.safeString (order, 'status'));
if (market !== undefined) {
symbol = market['symbol'];
}
const result = {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'average': average,
'cost': cost,
'amount': amount,
'filled': filled,
'remaining': remaining,
'status': status,
'trades': undefined,
'fee': undefined,
'fees': undefined,
};
const fees = this.safeValue (order, 'fees', []);
const numFees = fees.length;
if (numFees > 0) {
result['fees'] = [];
for (let i = 0; i < fees.length; i++) {
const feeCost = this.safeFloat (fees[i], 'fee');
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (fees[i], 'feeToken');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
result['fees'].push ({
'cost': feeCost,
'currency': feeCurrencyCode,
});
}
}
}
return result;
}
parseOrderStatus (status) {
const statuses = {
'NEW': 'open',
'CANCELED': 'canceled',
'FILLED': 'closed',
'PENDING_CANCEL': 'canceled',
};
return this.safeString (statuses, status, status);
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params);
const query = this.omit (params, this.extractParams (path));
const isPublicContract = (api === 'contract') && ((path === 'insurance') || (path === 'fundingRate'));
if ((api === 'public') || (api === 'quote') || isPublicContract) {
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
} else {
const timestamp = this.milliseconds ();
this.checkRequiredCredentials ();
const request = this.extend ({
'timestamp': timestamp,
}, query);
// 准备待签名数据
const auth = this.urlencode (request);
const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256');
request['signature'] = signature;
headers = {
'X-BH-APIKEY': this.apiKey,
};
if (method === 'POST') {
body = this.urlencode (request);
headers = this.extend ({
'Content-Type': 'application/x-www-form-urlencoded',
}, headers);
} else {
url += '?' + this.urlencode (request);
}
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to default error handler
}
if ('code' in response) {
const code = this.safeString (response, 'code');
if (code !== '0') {
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
throw new ExchangeError (feedback);
}
}
}
};