ccxt-bybit
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,297 lines (1,267 loc) • 58.2 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const hitbtc = require ('./hitbtc');
const { BadSymbol, PermissionDenied, ExchangeError, ExchangeNotAvailable, OrderNotFound, InsufficientFunds, InvalidOrder, RequestTimeout, AuthenticationError } = require ('./base/errors');
const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
module.exports = class hitbtc2 extends hitbtc {
describe () {
return this.deepExtend (super.describe (), {
'id': 'hitbtc2',
'name': 'HitBTC',
'countries': [ 'HK' ],
'rateLimit': 1500,
'version': '2',
'has': {
'createDepositAddress': true,
'fetchDepositAddress': true,
'CORS': false,
'editOrder': true,
'fetchCurrencies': true,
'fetchOHLCV': true,
'fetchTickers': true,
'fetchOrder': true,
'fetchOrders': false,
'fetchOpenOrders': true,
'fetchClosedOrders': true,
'fetchMyTrades': true,
'withdraw': true,
'fetchOrderTrades': false, // not implemented yet
'fetchDeposits': false,
'fetchWithdrawals': false,
'fetchTransactions': true,
'fetchTradingFee': true,
},
'timeframes': {
'1m': 'M1',
'3m': 'M3',
'5m': 'M5',
'15m': 'M15',
'30m': 'M30', // default
'1h': 'H1',
'4h': 'H4',
'1d': 'D1',
'1w': 'D7',
'1M': '1M',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg',
'api': 'https://api.hitbtc.com',
'www': 'https://hitbtc.com',
'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466',
'doc': [
'https://api.hitbtc.com',
'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv2.md',
],
'fees': [
'https://hitbtc.com/fees-and-limits',
'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits',
],
},
'api': {
'public': {
'get': [
'symbol', // Available Currency Symbols
'symbol/{symbol}', // Get symbol info
'currency', // Available Currencies
'currency/{currency}', // Get currency info
'ticker', // Ticker list for all symbols
'ticker/{symbol}', // Ticker for symbol
'trades/{symbol}', // Trades
'orderbook/{symbol}', // Orderbook
'candles/{symbol}', // Candles
],
},
'private': {
'get': [
'order', // List your current open orders
'order/{clientOrderId}', // Get a single order by clientOrderId
'trading/balance', // Get trading balance
'trading/fee/all', // Get trading fee rate
'trading/fee/{symbol}', // Get trading fee rate
'history/trades', // Get historical trades
'history/order', // Get historical orders
'history/order/{id}/trades', // Get historical trades by specified order
'account/balance', // Get main acccount balance
'account/transactions', // Get account transactions
'account/transactions/{id}', // Get account transaction by id
'account/crypto/address/{currency}', // Get deposit crypro address
],
'post': [
'order', // Create new order
'account/crypto/withdraw', // Withdraw crypro
'account/crypto/address/{currency}', // Create new deposit crypro address
'account/transfer', // Transfer amount to trading
],
'put': [
'order/{clientOrderId}', // Create new order
'account/crypto/withdraw/{id}', // Commit withdraw crypro
],
'delete': [
'order', // Cancel all open orders
'order/{clientOrderId}', // Cancel order
'account/crypto/withdraw/{id}', // Rollback withdraw crypro
],
'patch': [
'order/{clientOrderId}', // Cancel Replace order
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.1 / 100,
'taker': 0.2 / 100,
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {
'BTC': 0.001,
'BCC': 0.0018,
'ETH': 0.00958,
'BCH': 0.0018,
'USDT': 100,
'DASH': 0.03,
'BTG': 0.0005,
'XRP': 0.509,
'LTC': 0.003,
'ZEC': 0.0001,
'XMR': 0.09,
'1ST': 0.84,
'ADX': 5.7,
'AE': 6.7,
'AEON': 0.01006,
'AIR': 565,
'AMM': 14,
'AMP': 342,
'ANT': 6.7,
'ARDR': 1,
'ARN': 18.5,
'ART': 26,
'ATB': 0.0004,
'ATL': 27,
'ATM': 504,
'ATS': 860,
'AVT': 1.9,
'BAS': 113,
'BCN': 0.1,
'BET': 124,
'BKB': 46,
'BMC': 32,
'BMT': 100,
'BNT': 2.57,
'BQX': 4.7,
'BTCA': 351.21,
'BTM': 40,
'BTX': 0.04,
'BUS': 0.004,
'CAPP': 97,
'CCT': 6,
'CDT': 100,
'CDX': 30,
'CFI': 61,
'CL': 13.85,
'CLD': 0.88,
'CND': 574,
'CNX': 0.04,
'COSS': 65,
'CPAY': 5.487,
'CSNO': 16,
'CTR': 15,
'CTX': 146,
'CVC': 8.46,
'DATA': 12.949,
'DBIX': 0.0168,
'DCN': 1280,
'DCT': 0.02,
'DDF': 342,
'DENT': 1000,
'DGB': 0.4,
'DGD': 0.01,
'DICE': 0.32,
'DLT': 0.26,
'DNT': 0.21,
'DOGE': 2,
'DOV': 34,
'DRPU': 24,
'DRT': 240,
'DSH': 0.017,
'EBET': 84,
'EBTC': 20,
'EBTCOLD': 6.6,
'ECAT': 14,
'EDG': 2,
'EDO': 2.9,
'EKO': 1136.36,
'ELE': 0.00172,
'ELM': 0.004,
'EMC': 0.03,
'MGO': 14,
'ENJ': 163,
'EOS': 1.5,
'ERO': 34,
'ETBS': 15,
'ETC': 0.002,
'ETP': 0.004,
'EVX': 5.4,
'EXN': 456,
'FCN': 0.000005,
'FRD': 65,
'FUEL': 123.00105,
'FUN': 202.9598309,
'FYN': 1.849,
'FYP': 66.13,
'GAME': 0.004,
'GNO': 0.0034,
'GUP': 4,
'GVT': 1.2,
'HSR': 0.04,
'HAC': 144,
'HDG': 7,
'HGT': 1082,
'HPC': 0.4,
'HVN': 120,
'ICN': 0.55,
'ICO': 34,
'ICOS': 0.35,
'IND': 76,
'INDI': 790,
'ITS': 15.0012,
'IXT': 11,
'KBR': 143,
'KICK': 112,
'KMD': 4,
'LA': 41,
'LEND': 388,
'LAT': 1.44,
'LIFE': 13000,
'LRC': 27,
'LSK': 0.3,
'LOC': 11.076,
'LUN': 0.34,
'MAID': 5,
'MANA': 143,
'MCAP': 5.44,
'MIPS': 43,
'MNE': 1.33,
'MSP': 121,
'MCO': 0.357,
'MTH': 92,
'MYB': 3.9,
'NDC': 165,
'NEBL': 0.04,
'NET': 3.96,
'NTO': 998,
'NGC': 2.368,
'NXC': 13.39,
'NXT': 3,
'OAX': 15,
'ODN': 0.004,
'OMG': 2,
'OPT': 335,
'ORME': 2.8,
'OTN': 0.57,
'PAY': 3.1,
'PIX': 96,
'PLBT': 0.33,
'PLR': 114,
'PLU': 0.87,
'POE': 784,
'POLL': 3.5,
'PPT': 2,
'PRE': 32,
'PRG': 39,
'PRO': 41,
'PRS': 60,
'PTOY': 0.5,
'QAU': 63,
'QCN': 0.03,
'QTUM': 0.04,
'QVT': 64,
'REP': 0.02,
'RKC': 15,
'RLC': 1.21,
'RVT': 14,
'SC': 30,
'SAN': 2.24,
'SBD': 0.03,
'SCL': 2.6,
'SISA': 1640,
'SKIN': 407,
'SWFTC': 352.94,
'SMART': 0.4,
'SMS': 0.0375,
'SNC': 36,
'SNGLS': 4,
'SNM': 48,
'SNT': 233,
'STAR': 0.144,
'STORM': 153.19,
'STEEM': 0.01,
'STRAT': 0.01,
'SPF': 14.4,
'STU': 14,
'STX': 11,
'SUB': 17,
'SUR': 3,
'SWT': 0.51,
'TAAS': 0.91,
'TBT': 2.37,
'TFL': 15,
'TIME': 0.03,
'TIX': 7.1,
'TKN': 1,
'TGT': 173,
'TKR': 84,
'TNT': 90,
'TRST': 1.6,
'TRX': 270,
'UET': 480,
'UGT': 15,
'UTT': 3,
'VEN': 14,
'VERI': 0.037,
'VIB': 50,
'VIBE': 145,
'VOISE': 618,
'WEALTH': 0.0168,
'WINGS': 2.4,
'WTC': 0.75,
'WRC': 48,
'XAUR': 3.23,
'XDN': 0.01,
'XEM': 15,
'XUC': 0.9,
'YOYOW': 140,
'ZAP': 24,
'ZRX': 23,
'ZSC': 191,
},
'deposit': {
'BTC': 0,
'ETH': 0,
'BCH': 0,
'USDT': 0,
'BTG': 0,
'LTC': 0,
'ZEC': 0,
'XMR': 0,
'1ST': 0,
'ADX': 0,
'AE': 0,
'AEON': 0,
'AIR': 0,
'AMP': 0,
'ANT': 0,
'ARDR': 0,
'ARN': 0,
'ART': 0,
'ATB': 0,
'ATL': 0,
'ATM': 0,
'ATS': 0,
'AVT': 0,
'BAS': 0,
'BCN': 0,
'BET': 0,
'BKB': 0,
'BMC': 0,
'BMT': 0,
'BNT': 0,
'BQX': 0,
'BTM': 0,
'BTX': 0,
'BUS': 0,
'CCT': 0,
'CDT': 0,
'CDX': 0,
'CFI': 0,
'CLD': 0,
'CND': 0,
'CNX': 0,
'COSS': 0,
'CSNO': 0,
'CTR': 0,
'CTX': 0,
'CVC': 0,
'DBIX': 0,
'DCN': 0,
'DCT': 0,
'DDF': 0,
'DENT': 0,
'DGB': 0,
'DGD': 0,
'DICE': 0,
'DLT': 0,
'DNT': 0,
'DOGE': 0,
'DOV': 0,
'DRPU': 0,
'DRT': 0,
'DSH': 0,
'EBET': 0,
'EBTC': 0,
'EBTCOLD': 0,
'ECAT': 0,
'EDG': 0,
'EDO': 0,
'ELE': 0,
'ELM': 0,
'EMC': 0,
'EMGO': 0,
'ENJ': 0,
'EOS': 0,
'ERO': 0,
'ETBS': 0,
'ETC': 0,
'ETP': 0,
'EVX': 0,
'EXN': 0,
'FRD': 0,
'FUEL': 0,
'FUN': 0,
'FYN': 0,
'FYP': 0,
'GNO': 0,
'GUP': 0,
'GVT': 0,
'HAC': 0,
'HDG': 0,
'HGT': 0,
'HPC': 0,
'HVN': 0,
'ICN': 0,
'ICO': 0,
'ICOS': 0,
'IND': 0,
'INDI': 0,
'ITS': 0,
'IXT': 0,
'KBR': 0,
'KICK': 0,
'LA': 0,
'LAT': 0,
'LIFE': 0,
'LRC': 0,
'LSK': 0,
'LUN': 0,
'MAID': 0,
'MANA': 0,
'MCAP': 0,
'MIPS': 0,
'MNE': 0,
'MSP': 0,
'MTH': 0,
'MYB': 0,
'NDC': 0,
'NEBL': 0,
'NET': 0,
'NTO': 0,
'NXC': 0,
'NXT': 0,
'OAX': 0,
'ODN': 0,
'OMG': 0,
'OPT': 0,
'ORME': 0,
'OTN': 0,
'PAY': 0,
'PIX': 0,
'PLBT': 0,
'PLR': 0,
'PLU': 0,
'POE': 0,
'POLL': 0,
'PPT': 0,
'PRE': 0,
'PRG': 0,
'PRO': 0,
'PRS': 0,
'PTOY': 0,
'QAU': 0,
'QCN': 0,
'QTUM': 0,
'QVT': 0,
'REP': 0,
'RKC': 0,
'RVT': 0,
'SAN': 0,
'SBD': 0,
'SCL': 0,
'SISA': 0,
'SKIN': 0,
'SMART': 0,
'SMS': 0,
'SNC': 0,
'SNGLS': 0,
'SNM': 0,
'SNT': 0,
'STEEM': 0,
'STRAT': 0,
'STU': 0,
'STX': 0,
'SUB': 0,
'SUR': 0,
'SWT': 0,
'TAAS': 0,
'TBT': 0,
'TFL': 0,
'TIME': 0,
'TIX': 0,
'TKN': 0,
'TKR': 0,
'TNT': 0,
'TRST': 0,
'TRX': 0,
'UET': 0,
'UGT': 0,
'VEN': 0,
'VERI': 0,
'VIB': 0,
'VIBE': 0,
'VOISE': 0,
'WEALTH': 0,
'WINGS': 0,
'WTC': 0,
'XAUR': 0,
'XDN': 0,
'XEM': 0,
'XUC': 0,
'YOYOW': 0,
'ZAP': 0,
'ZRX': 0,
'ZSC': 0,
},
},
},
'options': {
'defaultTimeInForce': 'FOK',
},
'exceptions': {
'504': RequestTimeout, // {"error":{"code":504,"message":"Gateway Timeout"}}
'1002': AuthenticationError, // {"error":{"code":1002,"message":"Authorization failed","description":""}}
'1003': PermissionDenied, // "Action is forbidden for this API key"
'2010': InvalidOrder, // "Quantity not a valid number"
'2001': BadSymbol, // "Symbol not found"
'2011': InvalidOrder, // "Quantity too low"
'2020': InvalidOrder, // "Price not a valid number"
'20002': OrderNotFound, // canceling non-existent order
'20001': InsufficientFunds,
},
});
}
feeToPrecision (symbol, fee) {
return this.decimalToPrecision (fee, TRUNCATE, 8, DECIMAL_PLACES);
}
async fetchMarkets (params = {}) {
const response = await this.publicGetSymbol (params);
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const id = this.safeString (market, 'id');
const baseId = this.safeString (market, 'baseCurrency');
const quoteId = this.safeString (market, 'quoteCurrency');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const lot = this.safeFloat (market, 'quantityIncrement');
const step = this.safeFloat (market, 'tickSize');
const precision = {
'price': this.precisionFromString (market['tickSize']),
// FIXME: for lots > 1 the following line returns 0
// 'amount': this.precisionFromString (market['quantityIncrement']),
'amount': -1 * parseInt (Math.log10 (lot)),
};
const taker = this.safeFloat (market, 'takeLiquidityRate');
const maker = this.safeFloat (market, 'provideLiquidityRate');
result.push (this.extend (this.fees['trading'], {
'info': market,
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': true,
'taker': taker,
'maker': maker,
'precision': precision,
'limits': {
'amount': {
'min': lot,
'max': undefined,
},
'price': {
'min': step,
'max': undefined,
},
'cost': {
'min': lot * step,
'max': undefined,
},
},
}));
}
return result;
}
async fetchCurrencies (params = {}) {
const response = await this.publicGetCurrency (params);
const result = {};
for (let i = 0; i < response.length; i++) {
const currency = response[i];
const id = this.safeString (currency, 'id');
// todo: will need to rethink the fees
// to add support for multiple withdrawal/deposit methods and
// differentiated fees for each particular method
const precision = 8; // default precision, todo: fix "magic constants"
const code = this.safeCurrencyCode (id);
const payin = this.safeValue (currency, 'payinEnabled');
const payout = this.safeValue (currency, 'payoutEnabled');
const transfer = this.safeValue (currency, 'transferEnabled');
let active = payin && payout && transfer;
if ('disabled' in currency) {
if (currency['disabled']) {
active = false;
}
}
let type = 'fiat';
if (('crypto' in currency) && currency['crypto']) {
type = 'crypto';
}
const name = this.safeString (currency, 'fullName');
result[code] = {
'id': id,
'code': code,
'type': type,
'payin': payin,
'payout': payout,
'transfer': transfer,
'info': currency,
'name': name,
'active': active,
'fee': this.safeFloat (currency, 'payoutFee'), // todo: redesign
'precision': precision,
'limits': {
'amount': {
'min': Math.pow (10, -precision),
'max': Math.pow (10, precision),
},
'price': {
'min': Math.pow (10, -precision),
'max': Math.pow (10, precision),
},
'cost': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': Math.pow (10, precision),
},
},
};
}
return result;
}
async fetchTradingFee (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = this.extend ({
'symbol': market['id'],
}, this.omit (params, 'symbol'));
const response = await this.privateGetTradingFeeSymbol (request);
//
// {
// takeLiquidityRate: '0.001',
// provideLiquidityRate: '-0.0001'
// }
//
return {
'info': response,
'maker': this.safeFloat (response, 'provideLiquidityRate'),
'taker': this.safeFloat (response, 'takeLiquidityRate'),
};
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const type = this.safeString (params, 'type', 'trading');
const method = 'privateGet' + this.capitalize (type) + 'Balance';
const query = this.omit (params, 'type');
const response = await this[method] (query);
const result = { 'info': response };
for (let i = 0; i < response.length; i++) {
const balance = response[i];
const currencyId = this.safeString (balance, 'currency');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (balance, 'available');
account['used'] = this.safeFloat (balance, 'reserved');
result[code] = account;
}
return this.parseBalance (result);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) {
const timestamp = this.parse8601 (ohlcv['timestamp']);
return [
timestamp,
parseFloat (ohlcv['open']),
parseFloat (ohlcv['max']),
parseFloat (ohlcv['min']),
parseFloat (ohlcv['close']),
parseFloat (ohlcv['volume']),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'period': this.timeframes[timeframe],
};
if (since !== undefined) {
request['from'] = this.iso8601 (since);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetCandlesSymbol (this.extend (request, params));
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'symbol': this.marketId (symbol),
};
if (limit !== undefined) {
request['limit'] = limit; // default = 100, 0 = unlimited
}
const response = await this.publicGetOrderbookSymbol (this.extend (request, params));
return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'size');
}
parseTicker (ticker, market = undefined) {
const timestamp = this.parse8601 (ticker['timestamp']);
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
const baseVolume = this.safeFloat (ticker, 'volume');
const quoteVolume = this.safeFloat (ticker, 'volumeQuote');
const open = this.safeFloat (ticker, 'open');
const last = this.safeFloat (ticker, 'last');
let change = undefined;
let percentage = undefined;
let average = undefined;
if (last !== undefined && open !== undefined) {
change = last - open;
average = this.sum (last, open) / 2;
if (open > 0) {
percentage = change / open * 100;
}
}
let vwap = undefined;
if (quoteVolume !== undefined) {
if (baseVolume !== undefined) {
if (baseVolume > 0) {
vwap = quoteVolume / baseVolume;
}
}
}
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'high'),
'low': this.safeFloat (ticker, 'low'),
'bid': this.safeFloat (ticker, 'bid'),
'bidVolume': undefined,
'ask': this.safeFloat (ticker, 'ask'),
'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 fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.publicGetTicker (params);
const result = {};
for (let i = 0; i < response.length; i++) {
const ticker = response[i];
const marketId = this.safeString (ticker, 'symbol');
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
const market = this.markets_by_id[marketId];
const symbol = market['symbol'];
result[symbol] = this.parseTicker (ticker, market);
} else {
result[marketId] = this.parseTicker (ticker);
}
}
}
return result;
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.publicGetTickerSymbol (this.extend (request, params));
if ('message' in response) {
throw new ExchangeError (this.id + ' ' + response['message']);
}
return this.parseTicker (response, market);
}
parseTrade (trade, market = undefined) {
//
// createMarketOrder
//
// { fee: "0.0004644",
// id: 386394956,
// price: "0.4644",
// quantity: "1",
// timestamp: "2018-10-25T16:41:44.780Z" }
//
// fetchTrades ...
//
// fetchMyTrades ...
//
const timestamp = this.parse8601 (trade['timestamp']);
let symbol = undefined;
const marketId = this.safeString (trade, 'symbol');
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
} else {
symbol = marketId;
}
}
if (symbol === undefined) {
if (market !== undefined) {
symbol = market['symbol'];
}
}
let fee = undefined;
const feeCost = this.safeFloat (trade, 'fee');
if (feeCost !== undefined) {
const feeCurrency = market ? market['quote'] : undefined;
fee = {
'cost': feeCost,
'currency': feeCurrency,
};
}
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
const orderId = this.safeString (trade, 'clientOrderId');
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'quantity');
const cost = price * amount;
const side = this.safeString (trade, 'side');
const id = this.safeString (trade, 'id');
return {
'info': trade,
'id': id,
'order': orderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
};
}
async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let currency = undefined;
const request = {};
if (code !== undefined) {
currency = this.currency (code);
request['asset'] = currency['id'];
}
if (since !== undefined) {
request['startTime'] = since;
}
const response = await this.privateGetAccountTransactions (this.extend (request, params));
return this.parseTransactions (response, currency, since, limit);
}
parseTransaction (transaction, currency = undefined) {
//
// {
// id: 'd53ee9df-89bf-4d09-886e-849f8be64647',
// index: 1044718371,
// type: 'payout',
// status: 'success',
// currency: 'ETH',
// amount: '4.522683200000000000000000',
// createdAt: '2018-06-07T00:43:32.426Z',
// updatedAt: '2018-06-07T00:45:36.447Z',
// hash: '0x973e5683dfdf80a1fb1e0b96e19085b6489221d2ddf864daa46903c5ec283a0f',
// address: '0xC5a59b21948C1d230c8C54f05590000Eb3e1252c',
// fee: '0.00958',
// },
// {
// id: 'e6c63331-467e-4922-9edc-019e75d20ba3',
// index: 1044714672,
// type: 'exchangeToBank',
// status: 'success',
// currency: 'ETH',
// amount: '4.532263200000000000',
// createdAt: '2018-06-07T00:42:39.543Z',
// updatedAt: '2018-06-07T00:42:39.683Z',
// },
// {
// id: '3b052faa-bf97-4636-a95c-3b5260015a10',
// index: 1009280164,
// type: 'bankToExchange',
// status: 'success',
// currency: 'CAS',
// amount: '104797.875800000000000000',
// createdAt: '2018-05-19T02:34:36.750Z',
// updatedAt: '2018-05-19T02:34:36.857Z',
// },
// {
// id: 'd525249f-7498-4c81-ba7b-b6ae2037dc08',
// index: 1009279948,
// type: 'payin',
// status: 'success',
// currency: 'CAS',
// amount: '104797.875800000000000000',
// createdAt: '2018-05-19T02:30:16.698Z',
// updatedAt: '2018-05-19T02:34:28.159Z',
// hash: '0xa6530e1231de409cf1f282196ed66533b103eac1df2aa4a7739d56b02c5f0388',
// address: '0xd53ed559a6d963af7cb3f3fcd0e7ca499054db8b',
// }
//
// {
// "id": "4f351f4f-a8ee-4984-a468-189ed590ddbd",
// "index": 3112719565,
// "type": "withdraw",
// "status": "success",
// "currency": "BCHOLD",
// "amount": "0.02423133",
// "createdAt": "2019-07-16T16:52:04.494Z",
// "updatedAt": "2019-07-16T16:54:07.753Z"
// }
const id = this.safeString (transaction, 'id');
const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt'));
const updated = this.parse8601 (this.safeString (transaction, 'updatedAt'));
const currencyId = this.safeString (transaction, 'currency');
const code = this.safeCurrencyCode (currencyId, currency);
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
const amount = this.safeFloat (transaction, 'amount');
const address = this.safeString (transaction, 'address');
const txid = this.safeString (transaction, 'hash');
let fee = undefined;
const feeCost = this.safeFloat (transaction, 'fee');
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': code,
};
}
const type = this.parseTransactionType (this.safeString (transaction, 'type'));
return {
'info': transaction,
'id': id,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'address': address,
'tag': undefined,
'type': type,
'amount': amount,
'currency': code,
'status': status,
'updated': updated,
'fee': fee,
};
}
parseTransactionStatus (status) {
const statuses = {
'pending': 'pending',
'failed': 'failed',
'success': 'ok',
};
return this.safeString (statuses, status, status);
}
parseTransactionType (type) {
const types = {
'payin': 'deposit',
'payout': 'withdrawal',
'withdraw': 'withdrawal',
};
return this.safeString (types, type, type);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
if (since !== undefined) {
request['sort'] = 'ASC';
request['from'] = this.iso8601 (since);
}
const response = await this.publicGetTradesSymbol (this.extend (request, params));
return this.parseTrades (response, market, since, limit);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
// their max accepted length is 32 characters
const uuid = this.uuid ();
const parts = uuid.split ('-');
let clientOrderId = parts.join ('');
clientOrderId = clientOrderId.slice (0, 32);
amount = parseFloat (amount);
const request = {
'clientOrderId': clientOrderId,
'symbol': market['id'],
'side': side,
'quantity': this.amountToPrecision (symbol, amount),
'type': type,
};
if (type === 'limit') {
request['price'] = this.priceToPrecision (symbol, price);
} else {
request['timeInForce'] = this.options['defaultTimeInForce'];
}
const response = await this.privatePostOrder (this.extend (request, params));
const order = this.parseOrder (response);
if (order['status'] === 'rejected') {
throw new InvalidOrder (this.id + ' order was rejected by the exchange ' + this.json (order));
}
const id = order['id'];
this.orders[id] = order;
return order;
}
async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
await this.loadMarkets ();
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
// their max accepted length is 32 characters
const uuid = this.uuid ();
const parts = uuid.split ('-');
let requestClientId = parts.join ('');
requestClientId = requestClientId.slice (0, 32);
const request = {
'clientOrderId': id,
'requestClientId': requestClientId,
};
if (amount !== undefined) {
request['quantity'] = this.amountToPrecision (symbol, amount);
}
if (price !== undefined) {
request['price'] = this.priceToPrecision (symbol, price);
}
const response = await this.privatePatchOrderClientOrderId (this.extend (request, params));
const order = this.parseOrder (response);
this.orders[order['id']] = order;
return order;
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
const request = {
'clientOrderId': id,
};
const response = await this.privateDeleteOrderClientOrderId (this.extend (request, params));
return this.parseOrder (response);
}
parseOrderStatus (status) {
const statuses = {
'new': 'open',
'suspended': 'open',
'partiallyFilled': 'open',
'filled': 'closed',
'canceled': 'canceled',
'expired': 'failed',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// createMarketOrder
//
// { clientOrderId: "fe36aa5e190149bf9985fb673bbb2ea0",
// createdAt: "2018-10-25T16:41:44.780Z",
// cumQuantity: "1",
// id: "66799540063",
// quantity: "1",
// side: "sell",
// status: "filled",
// symbol: "XRPUSDT",
// timeInForce: "FOK",
// tradesReport: [ { fee: "0.0004644",
// id: 386394956,
// price: "0.4644",
// quantity: "1",
// timestamp: "2018-10-25T16:41:44.780Z" } ],
// type: "market",
// updatedAt: "2018-10-25T16:41:44.780Z" }
//
const created = this.parse8601 (this.safeString (order, 'createdAt'));
const updated = this.parse8601 (this.safeString (order, 'updatedAt'));
const marketId = this.safeString (order, 'symbol');
let symbol = undefined;
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
} else {
symbol = marketId;
}
}
if (symbol === undefined) {
if (market !== undefined) {
symbol = market['id'];
}
}
const amount = this.safeFloat (order, 'quantity');
const filled = this.safeFloat (order, 'cumQuantity');
const status = this.parseOrderStatus (this.safeString (order, 'status'));
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
const id = this.safeString (order, 'clientOrderId');
let price = this.safeFloat (order, 'price');
if (price === undefined) {
if (id in this.orders) {
price = this.orders[id]['price'];
}
}
let remaining = undefined;
let cost = undefined;
if (amount !== undefined) {
if (filled !== undefined) {
remaining = amount - filled;
if (price !== undefined) {
cost = filled * price;
}
}
}
const type = this.safeString (order, 'type');
const side = this.safeString (order, 'side');
let trades = this.safeValue (order, 'tradesReport');
let fee = undefined;
let average = undefined;
if (trades !== undefined) {
trades = this.parseTrades (trades, market);
let feeCost = undefined;
const numTrades = trades.length;
let tradesCost = 0;
for (let i = 0; i < numTrades; i++) {
if (feeCost === undefined) {
feeCost = 0;
}
tradesCost = this.sum (tradesCost, trades[i]['cost']);
const tradeFee = this.safeValue (trades[i], 'fee', {});
const tradeFeeCost = this.safeFloat (tradeFee, 'cost');
if (tradeFeeCost !== undefined) {
feeCost = this.sum (feeCost, tradeFeeCost);
}
}
cost = tradesCost;
if ((filled !== undefined) && (filled > 0)) {
average = cost / filled;
if (type === 'market') {
if (price === undefined) {
price = average;
}
}
}
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': market['quote'],
};
}
}
return {
'id': id,
'timestamp': created,
'datetime': this.iso8601 (created),
'lastTradeTimestamp': updated,
'status': status,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'average': average,
'amount': amount,
'cost': cost,
'filled': filled,
'remaining': remaining,
'fee': fee,
'trades': trades,
'info': order,
};
}
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
const request = {
'clientOrderId': id,
};
const response = await this.privateGetHistoryOrder (this.extend (request, params));
const numOrders = response.length;
if (numOrders > 0) {
return this.parseOrder (response[0]);
}
throw new OrderNotFound (this.id + ' order ' + id + ' not found');
}
async fetchOpenOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
// we use clientOrderId as the order id with HitBTC intentionally
// because most of their endpoints will require clientOrderId
// explained here: https://github.com/ccxt/ccxt/issues/5674
const request = {
'clientOrderId': id,
};
const response = await this.privateGetOrderClientOrderId (this.extend (request, params));
return this.parseOrder (response);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market (symbol);
request['symbol'] = market['id'];
}
const response = await this.privateGetOrder (this.extend (request, params));
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 (symbol !== undefined) {
market = this.market (symbol);