ccxt
Version:
1,131 lines (1,129 loc) • 145 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/bitrue.js';
import { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, BadRequest, BadSymbol, AccountSuspended, OrderImmediatelyFillable, OnMaintenance, NotSupported } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TRUNCATE, TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class bitrue
* @augments Exchange
*/
export default class bitrue extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'bitrue',
'name': 'Bitrue',
'countries': ['SG'],
'rateLimit': 10,
'certified': false,
'version': 'v1',
'pro': true,
// new metainfo interface
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': true,
'future': false,
'option': false,
'addMargin': false,
'borrowCrossMargin': false,
'borrowIsolatedMargin': false,
'borrowMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'closeAllPositions': false,
'closePosition': false,
'createMarketBuyOrderWithCost': true,
'createMarketOrderWithCost': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'createOrderWithTakeProfitAndStopLoss': false,
'createOrderWithTakeProfitAndStopLossWs': false,
'createReduceOnlyOrder': true,
'createStopLimitOrder': true,
'createStopMarketOrder': true,
'createStopOrder': true,
'fetchBalance': true,
'fetchBidsAsks': true,
'fetchBorrowInterest': false,
'fetchBorrowRate': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': false,
'fetchBorrowRatesPerSymbol': false,
'fetchClosedOrders': true,
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': true,
'fetchDepositAddress': false,
'fetchDeposits': true,
'fetchDepositsWithdrawals': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingHistory': false,
'fetchFundingInterval': false,
'fetchFundingIntervals': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchGreeks': false,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchIsolatedPositions': false,
'fetchLeverage': false,
'fetchLeverages': false,
'fetchLeverageTiers': false,
'fetchLiquidations': false,
'fetchLongShortRatio': false,
'fetchLongShortRatioHistory': false,
'fetchMarginAdjustmentHistory': false,
'fetchMarginMode': false,
'fetchMarginModes': false,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMarkPrices': false,
'fetchMyLiquidations': false,
'fetchMySettlementHistory': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterest': false,
'fetchOpenInterestHistory': false,
'fetchOpenInterests': false,
'fetchOpenOrders': true,
'fetchOption': false,
'fetchOptionChain': false,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': false,
'fetchPosition': false,
'fetchPositionHistory': false,
'fetchPositionMode': false,
'fetchPositions': false,
'fetchPositionsHistory': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': false,
'fetchStatus': true,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfers': true,
'fetchVolatilityHistory': false,
'fetchWithdrawals': true,
'reduceMargin': false,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'setLeverage': true,
'setMargin': true,
'setMarginMode': false,
'setPositionMode': false,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1H',
'2h': '2H',
'4h': '4H',
'1d': '1D',
'1w': '1W',
},
'urls': {
'logo': 'https://github.com/user-attachments/assets/67abe346-1273-461a-bd7c-42fa32907c8e',
'api': {
'spot': 'https://www.bitrue.com/api',
'fapi': 'https://fapi.bitrue.com/fapi',
'dapi': 'https://fapi.bitrue.com/dapi',
'kline': 'https://www.bitrue.com/kline-api',
},
'www': 'https://www.bitrue.com',
'referral': 'https://www.bitrue.com/affiliate/landing?cn=600000&inviteCode=EZWETQE',
'doc': [
'https://github.com/Bitrue-exchange/bitrue-official-api-docs',
'https://www.bitrue.com/api-docs',
],
'fees': 'https://bitrue.zendesk.com/hc/en-001/articles/4405479952537',
},
// from spotV1PublicGetExchangeInfo:
// general 25000 weight in 1 minute per IP. = 416.66 per second a weight of 0.24 for 1
// orders 750 weight in 6 seconds per IP. = 125 per second a weight of 0.8 for 1
// orders 200 weight in 10 seconds per User. = 20 per second a weight of 5 for 1
// withdraw 3000 weight in 1 hour per User. = 0.833 per second a weight of 120 for 1
// withdraw 1000 weight in 1 day per User. = 0.011574 per second a weight of 8640 for 1
'api': {
'spot': {
'kline': {
'public': {
'get': {
'public.json': 0.24,
'public{currency}.json': 0.24,
},
},
},
'v1': {
'public': {
'get': {
'ping': 0.24,
'time': 0.24,
'exchangeInfo': 0.24,
'depth': { 'cost': 1, 'byLimit': [[100, 0.24], [500, 1.2], [1000, 2.4]] },
'trades': 0.24,
'historicalTrades': 1.2,
'aggTrades': 0.24,
'ticker/24hr': { 'cost': 0.24, 'noSymbol': 9.6 },
'ticker/price': 0.24,
'ticker/bookTicker': 0.24,
'market/kline': 0.24,
},
},
'private': {
'get': {
'order': 5,
'openOrders': 5,
'allOrders': 25,
'account': 25,
'myTrades': 25,
'etf/net-value/{symbol}': 0.24,
'withdraw/history': 120,
'deposit/history': 120,
},
'post': {
'order': 5,
'withdraw/commit': 120,
},
'delete': {
'order': 5,
},
},
},
'v2': {
'private': {
'get': {
'myTrades': 1.2,
},
},
},
},
'fapi': {
'v1': {
'public': {
'get': {
'ping': 0.24,
'time': 0.24,
'contracts': 0.24,
'depth': 0.24,
'ticker': 0.24,
'klines': 0.24,
},
},
},
'v2': {
'private': {
'get': {
'myTrades': 5,
'openOrders': 5,
'order': 5,
'account': 5,
'leverageBracket': 5,
'commissionRate': 5,
'futures_transfer_history': 5,
'forceOrdersHistory': 5,
},
'post': {
'positionMargin': 5,
'level_edit': 5,
'cancel': 5,
'order': 25,
'allOpenOrders': 5,
'futures_transfer': 5,
},
},
},
},
'dapi': {
'v1': {
'public': {
'get': {
'ping': 0.24,
'time': 0.24,
'contracts': 0.24,
'depth': 0.24,
'ticker': 0.24,
'klines': 0.24,
},
},
},
'v2': {
'private': {
'get': {
'myTrades': 5,
'openOrders': 5,
'order': 5,
'account': 5,
'leverageBracket': 5,
'commissionRate': 5,
'futures_transfer_history': 5,
'forceOrdersHistory': 5,
},
'post': {
'positionMargin': 5,
'level_edit': 5,
'cancel': 5,
'order': 5,
'allOpenOrders': 5,
'futures_transfer': 5,
},
},
},
},
},
'fees': {
'trading': {
'feeSide': 'get',
'tierBased': false,
'percentage': true,
'taker': this.parseNumber('0.00098'),
'maker': this.parseNumber('0.00098'),
},
'future': {
'trading': {
'feeSide': 'quote',
'tierBased': true,
'percentage': true,
'taker': this.parseNumber('0.000400'),
'maker': this.parseNumber('0.000200'),
'tiers': {
'taker': [
[this.parseNumber('0'), this.parseNumber('0.000400')],
[this.parseNumber('250'), this.parseNumber('0.000400')],
[this.parseNumber('2500'), this.parseNumber('0.000350')],
[this.parseNumber('7500'), this.parseNumber('0.000320')],
[this.parseNumber('22500'), this.parseNumber('0.000300')],
[this.parseNumber('50000'), this.parseNumber('0.000270')],
[this.parseNumber('100000'), this.parseNumber('0.000250')],
[this.parseNumber('200000'), this.parseNumber('0.000220')],
[this.parseNumber('400000'), this.parseNumber('0.000200')],
[this.parseNumber('750000'), this.parseNumber('0.000170')],
],
'maker': [
[this.parseNumber('0'), this.parseNumber('0.000200')],
[this.parseNumber('250'), this.parseNumber('0.000160')],
[this.parseNumber('2500'), this.parseNumber('0.000140')],
[this.parseNumber('7500'), this.parseNumber('0.000120')],
[this.parseNumber('22500'), this.parseNumber('0.000100')],
[this.parseNumber('50000'), this.parseNumber('0.000080')],
[this.parseNumber('100000'), this.parseNumber('0.000060')],
[this.parseNumber('200000'), this.parseNumber('0.000040')],
[this.parseNumber('400000'), this.parseNumber('0.000020')],
[this.parseNumber('750000'), this.parseNumber('0')],
],
},
},
},
'delivery': {
'trading': {
'feeSide': 'base',
'tierBased': true,
'percentage': true,
'taker': this.parseNumber('0.000500'),
'maker': this.parseNumber('0.000100'),
'tiers': {
'taker': [
[this.parseNumber('0'), this.parseNumber('0.000500')],
[this.parseNumber('250'), this.parseNumber('0.000450')],
[this.parseNumber('2500'), this.parseNumber('0.000400')],
[this.parseNumber('7500'), this.parseNumber('0.000300')],
[this.parseNumber('22500'), this.parseNumber('0.000250')],
[this.parseNumber('50000'), this.parseNumber('0.000240')],
[this.parseNumber('100000'), this.parseNumber('0.000240')],
[this.parseNumber('200000'), this.parseNumber('0.000240')],
[this.parseNumber('400000'), this.parseNumber('0.000240')],
[this.parseNumber('750000'), this.parseNumber('0.000240')],
],
'maker': [
[this.parseNumber('0'), this.parseNumber('0.000100')],
[this.parseNumber('250'), this.parseNumber('0.000080')],
[this.parseNumber('2500'), this.parseNumber('0.000050')],
[this.parseNumber('7500'), this.parseNumber('0.0000030')],
[this.parseNumber('22500'), this.parseNumber('0')],
[this.parseNumber('50000'), this.parseNumber('-0.000050')],
[this.parseNumber('100000'), this.parseNumber('-0.000060')],
[this.parseNumber('200000'), this.parseNumber('-0.000070')],
[this.parseNumber('400000'), this.parseNumber('-0.000080')],
[this.parseNumber('750000'), this.parseNumber('-0.000090')],
],
},
},
},
},
// exchange-specific options
'options': {
'createMarketBuyOrderRequiresPrice': true,
'fetchMarkets': [
'spot',
'linear',
'inverse',
],
// 'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades
'fetchMyTradesMethod': 'v2PrivateGetMyTrades',
'hasAlreadyAuthenticatedSuccessfully': false,
'currencyToPrecisionRoundingMode': TRUNCATE,
'recvWindow': 5 * 1000,
'timeDifference': 0,
'adjustForTimeDifference': false,
'parseOrderToPrecision': false,
'newOrderRespType': {
'market': 'FULL',
'limit': 'FULL', // we change it from 'ACK' by default to 'FULL' (returns immediately if limit is not hit)
},
'networks': {
'ERC20': 'ETH',
'TRC20': 'TRX',
'AETERNITY': 'Aeternity',
'AION': 'AION',
'ALGO': 'Algorand',
'ASK': 'ASK',
'ATOM': 'ATOM',
'AVAXC': 'AVAX C-Chain',
'BCH': 'BCH',
'BEP2': 'BEP2',
'BEP20': 'BEP20',
'Bitcoin': 'Bitcoin',
'BRP20': 'BRP20',
'ADA': 'Cardano',
'CASINOCOIN': 'CasinoCoin',
'CASINOCOIN-XRPL': 'CasinoCoin XRPL',
'CONTENTOS': 'Contentos',
'DASH': 'Dash',
'DECOIN': 'Decoin',
'DFI': 'DeFiChain',
'DGB': 'DGB',
'DIVI': 'Divi',
'DOGE': 'dogecoin',
'EOS': 'EOS',
'ETC': 'ETC',
'FILECOIN': 'Filecoin',
'FREETON': 'FREETON',
'HBAR': 'HBAR',
'HEDERA': 'Hedera Hashgraph',
'HRC20': 'HRC20',
'ICON': 'ICON',
'ICP': 'ICP',
'IGNIS': 'Ignis',
'INTERNETCOMPUTER': 'Internet Computer',
'IOTA': 'IOTA',
'KAVA': 'KAVA',
'KSM': 'KSM',
'LTC': 'LiteCoin',
'LUNA': 'Luna',
'MATIC': 'MATIC',
'MOBILECOIN': 'Mobile Coin',
'MONACOIN': 'MonaCoin',
'XMR': 'Monero',
'NEM': 'NEM',
'NEP5': 'NEP5',
'OMNI': 'OMNI',
'PAC': 'PAC',
'DOT': 'Polkadot',
'RAVEN': 'Ravencoin',
'SAFEX': 'Safex',
'SOL': 'SOLANA',
'SGB': 'Songbird',
'XML': 'Stellar Lumens',
'XYM': 'Symbol',
'XTZ': 'Tezos',
'theta': 'theta',
'THETA': 'THETA',
'VECHAIN': 'VeChain',
'WANCHAIN': 'Wanchain',
'XINFIN': 'XinFin Network',
'XRP': 'XRP',
'XRPL': 'XRPL',
'ZIL': 'ZIL',
},
'defaultType': 'spot',
'timeframes': {
'spot': {
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1H',
'2h': '2H',
'4h': '4H',
'12h': '12H',
'1d': '1D',
'1w': '1W',
},
'future': {
'1m': '1min',
'5m': '5min',
'15m': '15min',
'30m': '30min',
'1h': '1h',
'1d': '1day',
'1w': '1week',
'1M': '1month',
},
},
'accountsByType': {
'spot': 'wallet',
'future': 'contract',
'swap': 'contract',
'funding': 'wallet',
'fund': 'wallet',
'contract': 'contract',
},
},
'commonCurrencies': {
'MIM': 'MIM Swarm',
},
'precisionMode': TICK_SIZE,
'features': {
'default': {
'sandbox': false,
'createOrder': {
'marginMode': false,
'triggerPrice': true,
'triggerPriceType': undefined,
'triggerDirection': undefined,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': false,
'trailing': false,
'leverage': false,
'marketBuyRequiresPrice': true,
'marketBuyByCost': true,
'selfTradePrevention': false,
'iceberg': true, // todo implement
},
'createOrders': undefined,
'fetchMyTrades': {
'marginMode': false,
'limit': 1000,
'daysBack': 100000,
'untilDays': 100000,
'symbolRequired': true,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': true,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': undefined,
'trigger': false,
'trailing': false,
'symbolRequired': true,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'marginMode': false,
'limit': 1000,
'daysBack': 90,
'daysBackCanceled': 1,
'untilDays': 90,
'trigger': false,
'trailing': false,
'symbolRequired': true,
},
'fetchOHLCV': {
'limit': 1440,
},
},
'spot': {
'extends': 'default',
},
'forDerivatives': {
'extends': 'default',
'createOrder': {
'marginMode': true,
'leverage': true,
'marketBuyRequiresPrice': false,
'marketBuyByCost': false,
},
'fetchOHLCV': {
'limit': 300,
},
'fetchClosedOrders': undefined,
},
'swap': {
'linear': {
'extends': 'forDerivatives',
},
'inverse': {
'extends': 'forDerivatives',
},
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
'exceptions': {
'exact': {
'System is under maintenance.': OnMaintenance,
'System abnormality': ExchangeError,
'You are not authorized to execute this request.': PermissionDenied,
'API key does not exist': AuthenticationError,
'Order would trigger immediately.': OrderImmediatelyFillable,
'Stop price would trigger immediately.': OrderImmediatelyFillable,
'Order would immediately match and take.': OrderImmediatelyFillable,
'Account has insufficient balance for requested action.': InsufficientFunds,
'Rest API trading is not enabled.': ExchangeNotAvailable,
"You don't have permission.": PermissionDenied,
'Market is closed.': ExchangeNotAvailable,
'Too many requests. Please try again later.': DDoSProtection,
'-1000': ExchangeNotAvailable,
'-1001': ExchangeNotAvailable,
'-1002': AuthenticationError,
'-1003': RateLimitExceeded,
'-1013': InvalidOrder,
'-1015': RateLimitExceeded,
'-1016': ExchangeNotAvailable,
'-1020': BadRequest,
'-1021': InvalidNonce,
'-1022': AuthenticationError,
'-1100': BadRequest,
'-1101': BadRequest,
'-1102': BadRequest,
'-1103': BadRequest,
'-1104': BadRequest,
'-1105': BadRequest,
'-1106': BadRequest,
'-1111': BadRequest,
'-1112': InvalidOrder,
'-1114': BadRequest,
'-1115': BadRequest,
'-1116': BadRequest,
'-1117': BadRequest,
'-1166': InvalidOrder,
'-1118': BadRequest,
'-1119': BadRequest,
'-1120': BadRequest,
'-1121': BadSymbol,
'-1125': AuthenticationError,
'-1127': BadRequest,
'-1128': BadRequest,
'-1130': BadRequest,
'-1131': BadRequest,
'-1160': InvalidOrder,
'-1156': InvalidOrder,
'-2008': AuthenticationError,
'-2010': ExchangeError,
'-2011': OrderNotFound,
'-2013': OrderNotFound,
'-2014': AuthenticationError,
'-2015': AuthenticationError,
'-2017': InsufficientFunds,
'-2019': InsufficientFunds,
'-3005': InsufficientFunds,
'-3006': InsufficientFunds,
'-3008': InsufficientFunds,
'-3010': ExchangeError,
'-3015': ExchangeError,
'-3022': AccountSuspended,
'-4028': BadRequest,
'-3020': InsufficientFunds,
'-3041': InsufficientFunds,
'-5013': InsufficientFunds,
'-11008': InsufficientFunds,
'-4051': InsufficientFunds, // {"code":-4051,"msg":"Isolated balance insufficient."}
},
'broad': {
'Insufficient account balance': InsufficientFunds,
'has no operation privilege': PermissionDenied,
'MAX_POSITION': InvalidOrder, // {"code":-2010,"msg":"Filter failure: MAX_POSITION"}
},
},
});
}
nonce() {
return this.milliseconds() - this.options['timeDifference'];
}
/**
* @method
* @name bitrue#fetchStatus
* @description the latest known information on the availability of the exchange API
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#test-connectivity
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure}
*/
async fetchStatus(params = {}) {
const response = await this.spotV1PublicGetPing(params);
//
// empty means working status.
//
// {}
//
const keys = Object.keys(response);
const keysLength = keys.length;
const formattedStatus = keysLength ? 'maintenance' : 'ok';
return {
'status': formattedStatus,
'updated': undefined,
'eta': undefined,
'url': undefined,
'info': response,
};
}
/**
* @method
* @name bitrue#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#check-server-time
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
async fetchTime(params = {}) {
const response = await this.spotV1PublicGetTime(params);
//
// {
// "serverTime":1635467280514
// }
//
return this.safeInteger(response, 'serverTime');
}
/**
* @method
* @name bitrue#fetchCurrencies
* @description fetches all available currencies on an exchange
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
const response = await this.spotV1PublicGetExchangeInfo(params);
//
// {
// "timezone":"CTT",
// "serverTime":1635464889117,
// "rateLimits":[
// {"rateLimitType":"REQUESTS_WEIGHT","interval":"MINUTES","limit":6000},
// {"rateLimitType":"ORDERS","interval":"SECONDS","limit":150},
// {"rateLimitType":"ORDERS","interval":"DAYS","limit":288000},
// ],
// "exchangeFilters":[],
// "symbols":[
// {
// "symbol":"SHABTC",
// "status":"TRADING",
// "baseAsset":"sha",
// "baseAssetPrecision":0,
// "quoteAsset":"btc",
// "quotePrecision":10,
// "orderTypes":["MARKET","LIMIT"],
// "icebergAllowed":false,
// "filters":[
// {"filterType":"PRICE_FILTER","minPrice":"0.00000001349","maxPrice":"0.00000017537","priceScale":10},
// {"filterType":"LOT_SIZE","minQty":"1.0","minVal":"0.00020","maxQty":"1000000000","volumeScale":0},
// ],
// "defaultPrice":"0.0000006100",
// },
// ],
// "coins":[
// {
// "coin": "near",
// "coinFulName": "NEAR Protocol",
// "chains": [ "BEP20", ],
// "chainDetail": [
// {
// "chain": "BEP20",
// "enableWithdraw": true,
// "enableDeposit": true,
// "withdrawFee": "0.2000",
// "minWithdraw": "5.0000",
// "maxWithdraw": "1000000000000000.0000",
// },
// ],
// },
// ],
// }
//
const result = {};
const coins = this.safeList(response, 'coins', []);
for (let i = 0; i < coins.length; i++) {
const currency = coins[i];
const id = this.safeString(currency, 'coin');
const name = this.safeString(currency, 'coinFulName');
const code = this.safeCurrencyCode(id);
const networkDetails = this.safeList(currency, 'chainDetail', []);
const networks = {};
for (let j = 0; j < networkDetails.length; j++) {
const entry = networkDetails[j];
const networkId = this.safeString(entry, 'chain');
const network = this.networkIdToCode(networkId, code);
networks[network] = {
'info': entry,
'id': networkId,
'network': network,
'deposit': this.safeBool(entry, 'enableDeposit'),
'withdraw': this.safeBool(entry, 'enableWithdraw'),
'active': undefined,
'fee': this.safeNumber(entry, 'withdrawFee'),
'precision': undefined,
'limits': {
'withdraw': {
'min': this.safeNumber(entry, 'minWithdraw'),
'max': this.safeNumber(entry, 'maxWithdraw'),
},
},
};
}
result[code] = this.safeCurrencyStructure({
'id': id,
'name': name,
'code': code,
'precision': undefined,
'info': currency,
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'networks': networks,
'fee': undefined,
'fees': undefined,
'type': 'crypto',
'limits': {
'withdraw': {
'min': undefined,
'max': undefined,
},
},
});
}
return result;
}
/**
* @method
* @name bitrue#fetchMarkets
* @description retrieves data on all markets for bitrue
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#exchangeInfo_endpoint
* @see https://www.bitrue.com/api-docs#current-open-contract
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#current-open-contract
* @param {object} [params] extra parameters specific to the exchange api endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const promisesRaw = [];
const fetchMarkets = this.safeValue(this.options, 'fetchMarkets', ['spot', 'linear', 'inverse']);
for (let i = 0; i < fetchMarkets.length; i++) {
const marketType = fetchMarkets[i];
if (marketType === 'spot') {
promisesRaw.push(this.spotV1PublicGetExchangeInfo(params));
}
else if (marketType === 'linear') {
promisesRaw.push(this.fapiV1PublicGetContracts(params));
}
else if (marketType === 'inverse') {
promisesRaw.push(this.dapiV1PublicGetContracts(params));
}
else {
throw new ExchangeError(this.id + ' fetchMarkets() this.options fetchMarkets "' + marketType + '" is not a supported market type');
}
}
const promises = await Promise.all(promisesRaw);
const spotMarkets = this.safeValue(this.safeValue(promises, 0), 'symbols', []);
const futureMarkets = this.safeValue(promises, 1);
const deliveryMarkets = this.safeValue(promises, 2);
let markets = spotMarkets;
markets = this.arrayConcat(markets, futureMarkets);
markets = this.arrayConcat(markets, deliveryMarkets);
//
// spot
//
// {
// "timezone":"CTT",
// "serverTime":1635464889117,
// "rateLimits":[
// {"rateLimitType":"REQUESTS_WEIGHT","interval":"MINUTES","limit":6000},
// {"rateLimitType":"ORDERS","interval":"SECONDS","limit":150},
// {"rateLimitType":"ORDERS","interval":"DAYS","limit":288000},
// ],
// "exchangeFilters":[],
// "symbols":[
// {
// "symbol":"SHABTC",
// "status":"TRADING",
// "baseAsset":"sha",
// "baseAssetPrecision":0,
// "quoteAsset":"btc",
// "quotePrecision":10,
// "orderTypes":["MARKET","LIMIT"],
// "icebergAllowed":false,
// "filters":[
// {"filterType":"PRICE_FILTER","minPrice":"0.00000001349","maxPrice":"0.00000017537","priceScale":10},
// {"filterType":"LOT_SIZE","minQty":"1.0","minVal":"0.00020","maxQty":"1000000000","volumeScale":0},
// ],
// "defaultPrice":"0.0000006100",
// },
// ],
// "coins":[
// {
// "coin":"sbr",
// "coinFulName":"Saber",
// "enableWithdraw":true,
// "enableDeposit":true,
// "chains":["SOLANA"],
// "withdrawFee":"2.0",
// "minWithdraw":"5.0",
// "maxWithdraw":"1000000000000000",
// },
// ],
// }
//
// swap / delivery
//
// [
// {
// "symbol": "H-HT-USDT",
// "pricePrecision": 8,
// "side": 1,
// "maxMarketVolume": 100000,
// "multiplier": 6,
// "minOrderVolume": 1,
// "maxMarketMoney": 10000000,
// "type": "H", // E: perpetual contract, S: test contract, others are mixed contract
// "maxLimitVolume": 1000000,
// "maxValidOrder": 20,
// "multiplierCoin": "HT",
// "minOrderMoney": 0.001,
// "maxLimitMoney": 1000000,
// "status": 1
// }
// ]
//
if (this.options['adjustForTimeDifference']) {
await this.loadTimeDifference();
}
return this.parseMarkets(markets);
}
parseMarket(market) {
const id = this.safeString(market, 'symbol');
const lowercaseId = this.safeStringLower(market, 'symbol');
const side = this.safeInteger(market, 'side'); // 1 linear, 0 inverse, undefined spot
let type = undefined;
let isLinear = undefined;
let isInverse = undefined;
if (side === undefined) {
type = 'spot';
}
else {
type = 'swap';
isLinear = (side === 1);
isInverse = (side === 0);
}
const isContract = (type !== 'spot');
let baseId = this.safeString(market, 'baseAsset');
let quoteId = this.safeString(market, 'quoteAsset');
let settleId = undefined;
let settle = undefined;
if (isContract) {
const symbolSplit = id.split('-');
baseId = this.safeString(symbolSplit, 1);
quoteId = this.safeString(symbolSplit, 2);
if (isLinear) {
settleId = quoteId;
}
else {
settleId = baseId;
}
settle = this.safeCurrencyCode(settleId);
}
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
let symbol = base + '/' + quote;
if (settle !== undefined) {
symbol += ':' + settle;
}
const filters = this.safeList(market, 'filters', []);
const filtersByType = this.indexBy(filters, 'filterType');
const status = this.safeString(market, 'status');
const priceFilter = this.safeDict(filtersByType, 'PRICE_FILTER', {});
const amountFilter = this.safeDict(filtersByType, 'LOT_SIZE', {});
const defaultPricePrecision = this.safeString(market, 'pricePrecision');
const defaultAmountPrecision = this.safeString(market, 'quantityPrecision');
const pricePrecision = this.safeString(priceFilter, 'priceScale', defaultPricePrecision);
const amountPrecision = this.safeString(amountFilter, 'volumeScale', defaultAmountPrecision);
const multiplier = this.safeString(market, 'multiplier');
let maxQuantity = this.safeNumber(amountFilter, 'maxQty');
if (maxQuantity === undefined) {
maxQuantity = this.safeNumber(market, 'maxValidOrder');
}
let minCost = this.safeNumber(amountFilter, 'minVal');
if (minCost === undefined) {
minCost = this.safeNumber(market, 'minOrderMoney');
}
return {
'id': id,
'lowercaseId': lowercaseId,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': type,
'spot': (type === 'spot'),
'margin': false,
'swap': isContract,
'future': false,
'option': false,
'active': (status === 'TRADING'),
'contract': isContract,
'linear': isLinear,
'inverse': isInverse,
'contractSize': this.parseNumber(Precise.stringAbs(multiplier)),
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber(this.parsePrecision(amountPrecision)),
'price': this.parseNumber(this.parsePrecision(pricePrecision)),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber(amountFilter, 'minQty'),
'max': maxQuantity,
},
'price': {
'min': this.safeNumber(priceFilter, 'minPrice'),
'max': this.safeNumber(priceFilter, 'maxPrice'),
},
'cost': {
'min': minCost,
'max': undefined,
},
},
'created': undefined,
'info': market,
};
}
parseBalance(response) {
//
// spot
//
// {
// "makerCommission":0,
// "takerCommission":0,
// "buyerCommission":0,
// "sellerCommission":0,
// "updateTime":null,
// "balances":[
// {"asset":"sbr","free":"0","locked":"0"},
// {"asset":"ksm","free":"0","locked":"0"},
// {"asset":"neo3s","free":"0","locked":"0"},
// ],
// "canTrade":false,
// "canWithdraw":false,
// "canDeposit":false
// }
//
// swap
//
// {
// "account":[
// {
// "marginCoin":"USDT",
// "coinPrecious":4,
// "accountNormal":1010.4043400372839856,
// "accountLock":2.9827889600000006,
// "partPositionNormal":0,
// "totalPositionNormal":0,
// "achievedAmount":0,
// "unrealizedAmount":0,
// "totalMarginRate":0,
// "totalEquity":1010.4043400372839856,
// "partEquity":0,
// "totalCost":0,
// "sumMarginRate":0,
// "sumOpenRealizedAmount":0,
// "canUseTrialFund":0,
// "sumMaintenanceMargin":null,
// "futureModel":null,
// "positionVos":[]
// }
// ]
// }
//
const result = {
'info': response,
};
const timestamp = this.safeInteger(response, 'updateTime');
const balances = this.safeValue2(response, 'balances', 'account', []);
for (let i = 0; i < balances.length; i++) {
const balance = balances[i];
const currencyId = this.safeString2(balance, 'asset', 'marginCoin');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
account['free'] = this.safeString2(balance, 'free', 'accountNormal');
account['used'] = this.safeString2(balance, 'locked', 'accountLock');
result[code] = account;
}
result['timestamp'] = timestamp;
result['datetime'] = this.iso8601(timestamp);
return this.safeBalance(result);
}
/**
* @method
* @name bitrue#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#account-information-user_data
* @see https://www.bitrue.com/api-docs#account-information-v2-user_data-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#account-information-v2-user_data-hmac-sha256
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.type] 'future', 'delivery', 'spot', 'swap'
* @param {string} [params.subType] 'linear', 'inverse'
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
async fetchBalance(params = {}) {
await this.loadMarkets();
let type = undefined;
[type, params] = this.handleMarketTypeAndParams('fetchBalance', undefined, params);
let subType = undefined;
[subType, params] = this.handleSubTypeAndParams('fetchBalance', undefined, params);
let response = undefined;
let result = undefined;
if (type === 'swap') {
if (subType !== undefined && subType === 'inverse') {
response = await this.dapiV2PrivateGetAccount(params);
result = this.safeDict(response, 'data', {});
//
// {
// "code":"0",
// "msg":"Success",
// "data":{
// "account":[
// {
// "marginCoin":"USD",
// "coinPrecious":4,
//