astro-perp-ccxt-dev
Version:
3,375 lines • 148 kB
JavaScript
'use strict';
var bitrue$1 = require('./abstract/bitrue.js');
var errors = require('./base/errors.js');
var Precise = require('./base/Precise.js');
var number = require('./base/functions/number.js');
var sha256 = require('./static_dependencies/noble-hashes/sha256.js');
// ----------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/**
* @class bitrue
* @augments Exchange
*/
class bitrue extends bitrue$1 {
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': number.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': number.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.': errors.OnMaintenance,
'System abnormality': errors.ExchangeError,
'You are not authorized to execute this request.': errors.PermissionDenied,
'API key does not exist': errors.AuthenticationError,
'Order would trigger immediately.': errors.OrderImmediatelyFillable,
'Stop price would trigger immediately.': errors.OrderImmediatelyFillable,
'Order would immediately match and take.': errors.OrderImmediatelyFillable,
'Account has insufficient balance for requested action.': errors.InsufficientFunds,
'Rest API trading is not enabled.': errors.ExchangeNotAvailable,
"You don't have permission.": errors.PermissionDenied,
'Market is closed.': errors.ExchangeNotAvailable,
'Too many requests. Please try again later.': errors.DDoSProtection,
'-1000': errors.ExchangeNotAvailable,
'-1001': errors.ExchangeNotAvailable,
'-1002': errors.AuthenticationError,
'-1003': errors.RateLimitExceeded,
'-1013': errors.InvalidOrder,
'-1015': errors.RateLimitExceeded,
'-1016': errors.ExchangeNotAvailable,
'-1020': errors.BadRequest,
'-1021': errors.InvalidNonce,
'-1022': errors.AuthenticationError,
'-1100': errors.BadRequest,
'-1101': errors.BadRequest,
'-1102': errors.BadRequest,
'-1103': errors.BadRequest,
'-1104': errors.BadRequest,
'-1105': errors.BadRequest,
'-1106': errors.BadRequest,
'-1111': errors.BadRequest,
'-1112': errors.InvalidOrder,
'-1114': errors.BadRequest,
'-1115': errors.BadRequest,
'-1116': errors.BadRequest,
'-1117': errors.BadRequest,
'-1166': errors.InvalidOrder,
'-1118': errors.BadRequest,
'-1119': errors.BadRequest,
'-1120': errors.BadRequest,
'-1121': errors.BadSymbol,
'-1125': errors.AuthenticationError,
'-1127': errors.BadRequest,
'-1128': errors.BadRequest,
'-1130': errors.BadRequest,
'-1131': errors.BadRequest,
'-1160': errors.InvalidOrder,
'-1156': errors.InvalidOrder,
'-2008': errors.AuthenticationError,
'-2010': errors.ExchangeError,
'-2011': errors.OrderNotFound,
'-2013': errors.OrderNotFound,
'-2014': errors.AuthenticationError,
'-2015': errors.AuthenticationError,
'-2017': errors.InsufficientFunds,
'-2019': errors.InsufficientFunds,
'-3005': errors.InsufficientFunds,
'-3006': errors.InsufficientFunds,
'-3008': errors.InsufficientFunds,
'-3010': errors.ExchangeError,
'-3015': errors.ExchangeError,
'-3022': errors.AccountSuspended,
'-4028': errors.BadRequest,
'-3020': errors.InsufficientFunds,
'-3041': errors.InsufficientFunds,
'-5013': errors.InsufficientFunds,
'-11008': errors.InsufficientFunds,
'-4051': errors.InsufficientFunds, // {"code":-4051,"msg":"Isolated balance insufficient."}
},
'broad': {
'Insufficient account balance': errors.InsufficientFunds,
'has no operation privilege': errors.PermissionDenied,
'MAX_POSITION': errors.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 errors.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["default"].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,
// "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":[]
// }
// ]
// }
// }
//
}
else {
response = await this.fapiV2PrivateGetAccount(params);
result = this.safeDict(response, 'data', {});
//
// {
// "code":"0",
// "msg":"Success",
// "data":{
// "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":[]
// }
// ]
// }
// }
//
}
}
else {
response = await this.spotV1PrivateGetAccount(params);
result = response;
//
// {
// "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
// }
//
}
return this.parseBalance(result);
}
/**
* @method
* @name bitrue#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#order-book
* @see https://www.bitrue.com/api-docs#order-book
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#order-book
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
if (market['swap']) {
const request = {
'contractName': market['id'],
};
if (limit !== undefined) {
if (limit > 100) {
limit = 100;
}
request['limit'] = limit; // default 100, max 100, see https://www.bitrue.com/api-docs#order-book
}
if (market['linear']) {
response = await this.fapiV1PublicGetDepth(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV1PublicGetDepth(this.extend(request, params));
}
}
else if (market['spot']) {
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
if (limit > 1000) {
limit = 1000;
}
request['limit'] = limit; // default 100, max 1000, see https://github.com/Bitrue-exchange/bitrue-official-api-docs#order-book
}
response = await this.spotV1PublicGetDepth(this.extend(request, params));
}
else {
throw new errors.NotSupported(this.id + ' fetchOrderBook only support spot & swap markets');
}
//
// spot
//
// {
// "lastUpdateId":1635474910177,
// "bids":[
// ["61436.84","0.05",[]],
// ["61435.77","0.0124",[]],
// ["61434.88","0.012",[]],
// ],
// "asks":[
// ["61452.46","0.0001",[]],
// ["61452.47","0.0597",[]],
// ["61452.76","0.0713",[]],
// ]
// }
//
// swap
//
// {
// "asks": [[34916.5, 2582], [34916.6, 2193], [34916.7, 2629], [34916.8, 3478], [34916.9, 2718]],
// "bids": [[34916.4, 92065], [34916.3, 25703], [34916.2, 37259], [34916.1, 26446], [34916, 44456]],
// "time": 1699338305000
// }
//
const timestamp = this.safeInteger2(response, 'time', 'lastUpdateId');
const orderbook = this.parseOrderBook(response, symbol, timestamp);
orderbook['nonce'] = this.safeInteger(response, 'lastUpdateId');
return orderbook;
}
parseTicker(ticker, market = undefined) {
//
// fetchBidsAsks
//
// {
// "symbol": "LTCBTC",
// "bidPrice": "4.00000000",
// "bidQty": "431.00000000",
// "askPrice": "4.00000200",
// "askQty": "9.00000000"
// }
//
// fetchTicker
//
// {
// "symbol": "BNBBTC",
// "priceChange": "0.000248",
// "priceChangePercent": "3.5500",
// "weightedAvgPrice": null,
// "prevClosePrice": null,
// "lastPrice": "0.007226",
// "lastQty": null,
// "bidPrice": "0.007208",
// "askPrice": "0.007240",
// "openPrice": "0.006978",
// "highPrice": "0.007295",
// "lowPrice": "0.006935",
// "volume": "11749.86",
// "quoteVolume": "84.1066211",
// "openTime": 0,
// "closeTime": 0,
// "firstId": 0,
// "lastId": 0,
// "count": 0
// }
//
const symbol = this.safeSymbol(undefined, market);
const last = this.safeString2(ticker, 'lastPrice', 'last');
const timestamp = this.safeInteger(ticker, 'time');
let percentage = undefined;
if (market['swap']) {
percentage = Precise["default"].stringMul(this.safeString(ticker, 'rose'), '100');
}
else {
percentage = this.safeString(ticker, 'priceChangePercent');
}
return this.safeTicker({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': this.safeString2(ticker, 'highPrice', 'high'),
'low': this.safeString2(ticker, 'lowPrice', 'low'),
'bid': this.safeString2(ticker, 'bidPrice', 'buy'),
'bidVolume': this.safeString(ticker, 'bidQty'),
'ask': this.safeString2(ticker, 'askPrice', 'sell'),
'askVolume': this.safeString(ticker, 'askQty'),
'vwap': this.safeString(ticker, 'weightedAvgPrice'),
'open': this.safeString(ticker, 'openPrice'),
'close': last,
'last': last,
'previousClose': undefined,
'change': this.safeString(ticker, 'priceChange'),
'percentage': percentage,
'average': undefined,
'baseVolume': this.safeString2(ticker, 'volume', 'vol'),
'quoteVolume': this.safeString(ticker, 'quoteVolume'),
'info': ticker,
}, market);
}
/**
* @method
* @name bitrue#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#24hr-ticker-price-change-statistics
* @see https://www.bitrue.com/api-docs#ticker
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#ticker
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
let data = undefined;
if (market['swap']) {
const request = {
'contractName': market['id'],
};
if (market['linear']) {
response = await this.fapiV1PublicGetTicker(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV1PublicGetTicker(this.extend(request, params));
}
data = response;
}
else if (market['spot']) {
const request = {
'symbol': market['id'],
};
response = await this.spotV1PublicGetTicker24hr(this.extend(request, params));
data = this.safeDict(response, 0, {});
}
else {
throw new errors.NotSupported(this.id + ' fetchTicker only support spot & swap markets');
}
//
// spot
//
// [{
// symbol: 'BTCUSDT',
// priceChange: '105.20',
// priceChangePercent: '0.3000',
// weightedAvgPrice: null,
// prevClosePrice: null,
// lastPrice: '34905.21',
// lastQty: null,
// bidPrice: '34905.21',
// askPrice: '34905.22',
// openPrice: '34800.01',
// highPrice: '35276.33',
// lowPrice: '34787.51',
// volume: '12549.6481',
// quoteVolume: '439390492.917',
// openTime: '0',
// closeTime: '0',
// firstId: '0',
// lastId: '0',
// count: '0'
// }]
//
// swap
//
// {
// "high": "35296",
// "vol": "779308354",
// "last": "34884.1",
// "low": "34806.7",
// "buy": 34883.9,
// "sell": 34884,
// "rose": "-0.0027957315",
// "time": 1699348013000
// }
//
return this.parseTicker(data, market);
}
/**
* @method
* @name bitrue#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://www.bitrue.com/api_docs_includes_file/spot/index.html#kline-data
* @see https://www.bitrue.com/api_docs_includes_file/futures/index.html#kline-candlestick-data
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] the latest time in ms to fetch transfers for
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const timeframes = this.safeDict(this.options, 'timeframes', {});
let response = undefined;
let data = undefined;
if (market['swap']) {
const timeframesFuture = this.safeDict(timeframes, 'future', {});
const request = {
'contractName': market['id'],
// 1min / 5min / 15min / 30min / 1h / 1day / 1week / 1month
'interval': this.safeString(timeframesFuture, timeframe, '1min'),
};
if (limit !== undefined) {
request['limit'] = limit;
}
if (market['linear']) {
response = await this.fapiV1PublicGetKlines(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV1PublicGetKlines(this.extend(request, params));
}
data = response;
}
else if (market['spot']) {
const timeframesSpot = this.safeDict(timeframes, 'spot', {});
const request = {
'symbol': market['id'],
// 1m / 5m / 15m / 30m / 1H / 2H / 4H / 12H / 1D / 1W
'scale': this.safeString(timeframesSpot, timeframe, '1m'),
};
if (limit !== undefined) {
request['limit'] = limit;
}
const until = this.safeInteger(params, 'until');
if (until !== undefined) {
params = this.omit(params, 'until');
request['fromIdx'] = until;
}
response = await this.spotV1PublicGetMarketKline(this.extend(request, params));
data = this.safeList(response, 'data', []);
}
else {
throw new errors.NotSupported(this.id + ' fetchOHLCV only support spot & swap markets');
}
//
// spot
//
// {
// "symbol":"BTCUSDT",
// "scale":"KLINE_1MIN",
// "data":[
// {
// "i":"1660825020",
// "a":"93458.778",
// "v":"3.9774",
// "c":"23494.99",
// "h":"23509.63",
// "l":"23491.93",
// "o":"23508.34"
// }
// ]
// }
//
// swap
//
// [
// {
// "high": "35360.7",
// "vol": "110288",
// "low": "35347.9",
// "idx": 1699411680000,
// "close": "35347.9",
// "open": "35349.4"
// }
// ]
//
return this.parseOHLCVs(data, market, timeframe, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// spot
//
// {
// "i":"1660825020",
// "a":"93458.778",
// "v":"3.9774",
// "c":"23494.99",
// "h":"23509.63",
// "l":"23491.93",
// "o":"23508.34"
// }
//
// swap
//
// {
// "high": "35360.7",
// "vol": "110288",
// "low": "35347.9",
// "idx": 1699411680000,
// "close": "35347.9",
// "open": "35349.4"
// }
//
let timestamp = this.safeTimestamp(ohlcv, 'i');
if (timestamp === undefined) {
timestamp = this.safeInteger(ohlcv, 'idx');
}
return [
timestamp,
this.safeNumber2(ohlcv, 'o', 'open'),
this.safeNumber2(ohlcv, 'h', 'high'),
this.safeNumber2(ohlcv, 'l', 'low'),
this.safeNumber2(ohlcv, 'c', 'close'),
this.safeNumber2(ohlcv, 'v', 'vol'),
];
}
/**
* @method
* @name bitrue#fetchBidsAsks
* @description fetches the bid and ask price and volume for multiple markets
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#symbol-order-book-ticker
* @see https://www.bitrue.com/api-docs#ticker
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#ticker
* @param {string[]|undefined} symbols unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchBidsAsks(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols, undefined, false);
const first = this.safeString(symbols, 0);
const market = this.market(first);
let response = undefined;
if (market['swap']) {
const request = {
'contractName': market['id'],
};
if (market['linear']) {
response = await this.fapiV1PublicGetTicker(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV1PublicGetTicker(this.extend(request, params));
}
}
else if (market['spot']) {
const request = {
'symbol': market['id'],
};
response = await this.spotV1PublicGetTickerBookTicker(this.extend(request, params));
}
else {
throw new errors.NotSupported(this.id + ' fetchBidsAsks only support spot & swap markets');
}
//
// spot
//
// {
// "symbol": "LTCBTC",
// "bidPrice": "4.00000000",
// "bidQty": "431.00000000",
// "askPrice": "4.00000200",
// "askQty": "9.00000000"
// }
//
// swap
//
// {
// "high": "35296",
// "vol": "779308354",
// "last": "34884.1",
// "low": "34806.7",
// "buy": 34883.9,
// "sell": 34884,
// "rose": "-0.0027957315",
// "time": 1699348013000
// }
//
const data = {};
data[market['id']] = response;
return this.parseTickers(data, symbols);
}
/**
* @method
* @name bitrue#fetchTickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#24hr-ticker-price-change-statistics
* @see https://www.bitrue.com/api-docs#ticker
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#ticker
* @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols);
let response = undefined;
let data = undefined;
const request = {};
let type = undefined;
if (symbols !== undefined) {
const first = this.safeString(symbols, 0);
const market = this.market(first);
if (market['swap']) {
throw new errors.NotSupported(this.id + ' fetchTickers does not support swap markets, please use fetchTicker instead');
}
else if (market['spot']) {
response = await this.spotV1PublicGetTicker24hr(this.extend(request, params));
data = response;
}
else {
throw new errors.NotSupported(this.id + ' fetchTickers only support spot & swap markets');
}
}
else {
[type, params] = this.handleMarketTypeAndParams('fetchTickers', undefined, params);
if (type !== 'spot') {
throw new errors.NotSupported(this.id + ' fetchTickers only support spot when symbols are not proved');
}
response = await this.spotV1PublicGetTicker24hr(this.extend(request, params));
data = response;
}
//
// spot
//
// [{
// symbol: 'BTCUSDT',
// priceChange: '105.20',
// priceChangePercent: '0.3000',
// weightedAvgPrice: null,
// prevClosePrice: null,
// lastPrice: '34905.21',
// lastQty: null,
// bidPrice: '34905.21',
// askPrice: '34905.22',
// openPrice: '34800.01',
// highPrice: '35276.33',
// lowPrice: '34787.51',
// volume: '12549.6481',
// quoteVolume: '439390492.917',
// openTime: '0',
// closeTime: '0',
// firstId: '0',
// lastId: '0',
// count: '0'
// }]
//
// swap
//
// {
// "high": "35296",
// "vol": "779308354",
// "last": "34884.1",
// "low": "34806.7",
// "buy": 34883.9,
// "sell": 34884,
// "rose": "-0.0027957315",
// "time": 1699348013000
// }
//
// the exchange returns market ids with an underscore from the tickers endpoint
// the market ids do not have an underscore, so it has to be removed
// https://github.com/ccxt/ccxt/issues/13856
const tickers = {};
for (let i = 0; i < data.length; i++) {
const ticker = this.safeDict(data, i, {});
const market = this.safeMarket(this.safeString(ticker, 'symbol'));
tickers[market['id']] = ticker;
}
return this.parseTickers(tickers, symbols);
}
parseTrade(trade, market = undefined) {
//
// fetchTrades
//
// {
// "id": 28457,
// "price": "4.00000100",
// "qty": "12.00000000",
// "time": 1499865549590, // Actual timestamp of trade
// "isBuyerMaker": true,
// "isBestMatch": true
// }
//
// fetchTrades - spot
//
// {
// "symbol":"USDCUSDT",
// "id":20725156,
// "orderId":2880918576,
// "origClientOrderId":null,
// "price":"0.9996000000000000",
// "qty":"100.0000000000000000",
// "commission":null,
// "commissionAssert":null,
// "time":1635558511000,
// "isBuyer":false,
// "isMaker":false,
// "isBestMatch":true
// }
//
// fetchTrades - future
//
// {
// "tradeId":12,
// "price":0.9,
// "qty":1,
// "amount":9,
// "contractName":"E-SAND-USDT",
// "side":"BUY",
// "fee":"0.0018",
// "bidId":1558124009467904992,
// "askId":1558124043827644908,
// "bidUserId":10294,
// "askUserId":10467,
// "isBuyer":true,
// "isMaker":true,
// "ctime":1678426306000
// }
//
const timestamp = this.safeInteger2(trade, 'ctime', 'time');
const priceString = this.safeString(trade, 'price');
const amountString = this.safeString(trade, 'qty');
const marketId = this.safeString2(trade, 'symbol', 'contractName');
const symbol = this.safeSymbol(marketId, market);
const orderId = this.safeString(trade, 'orderId');
const id = this.safeString2(trade, 'id', 'tradeId');
let side = undefined;
const buyerMaker = this.safeBool(trade, 'isBuyerMaker'); // ignore "m" until Bitrue fixes api
const isBuyer = this.safeBool(trade, 'isBuyer');
if (buyerMaker !== undefined) {
side = buyerMaker ? 'sell' : 'buy';
}
if (isBuyer !== undefined) {
side = isBuyer ? 'buy' : 'sell'; // this is a true side
}
let fee = undefined;
if ('commission' in trade) {
fee = {
'cost': this.safeString2(trade, 'commission', 'fee'),
'currency': this.safeCurrencyCode(this.safeString(trade, 'commissionAssert')),
};
}
let takerOrMaker = undefined;
const isMaker = this.safeBool(trade, 'isMaker');
if (isMaker !== undefined) {
takerOrMaker = isMaker ? 'maker' : 'taker';
}
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'id': id,
'order': orderId,
'type': undefined,
'side': side,
'takerOrMaker': takerOrMaker,
'price': priceString,
'amount': amountString,
'cost': undefined,
'fee': fee,
}, market);
}
/**
* @method
* @name bitrue#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#recent-trades-list
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
if (market['spot']) {
const request = {
'symbol': market['id'],
// 'limit': 100, // default 100, max = 1000
};
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 1000
}
response = await this.spotV1PublicGetTrades(this.extend(request, params));
}
else {
throw new errors.NotSupported(this.id + ' fetchTrades only support spot markets');
}
//
// spot
//
// [
// {
// "id": 28457,
// "price": "4.00000100",
// "qty": "12.00000000",
// "time": 1499865549590,
// "isBuyerMaker": true,
// "isBestMatch": true
// }
// ]
//
return this.parseTrades(response, market, since, limit);
}
parseOrderStatus(status) {
const statuses = {
'INIT': 'open',
'PENDING_CREATE': 'open',
'NEW': 'open',
'PARTIALLY_FILLED': 'open',
'FILLED': 'closed',
'CANCELED': 'canceled',
'PENDING_CANCEL': 'canceling',
'REJECTED': 'rejected',
'EXPIRED': 'expired',
};
return this.safeString(statuses, status, status);
}
parseOrder(order, market = undefined) {
//
// createOrder - spot
//
// {
// "symbol":"USDCUSDT",
// "orderId":2878854881,
// "clientOrderId":"",
// "transactTime":1635551031276
// }
//
// createOrder - future
//
// {
// "orderId":1690615676032452985,
// }
//
// fetchOrders - spot
//
// {
// "symbol":"USDCUSDT",
// "orderId":"2878854881",
// "clientOrderId":"",
// "price":"1.1000000000000000",
// "origQty":"100.0000000000000000",
// "executedQty":"0.0000000000000000",
// "cummulativeQuoteQty":"0.0000000000000000",
// "status":"NEW",
// "timeInForce":"",
// "type":"LIMIT",
// "side":"SELL",
// "stopPrice":"",
// "icebergQty":"",
// "time":1635551031000,
// "updateTime":1635551031000,
// "isWorking":false
// }
//
// fetchOrders - future
//
// {
// "orderId":1917641,
// "price":100,
// "origQty":10,
// "origAmount":10,
// "executedQty":1,
// "avgPrice":10000,
// "status":"INIT",
// "type":"LIMIT",
// "side":"BUY",
// "action":"OPEN",
// "transactTime":1686716571425
// "clientOrderId":4949299210
// }
//
const status = this.parseOrderStatus(this.safeString2(order, 'status', 'orderStatus'));
const marketId = this.safeString(order, 'symbol');
const symbol = this.safeSymbol(marketId, market);
const filled = this.safeString(order, 'executedQty');
let timestamp = undefined;
let lastTradeTimestamp = undefined;
if ('time' in order) {
timestamp = this.safeInteger(order, 'time');
}
else if ('transactTime' in order) {
timestamp = this.safeInteger(order, 'transactTime');
}
else if ('updateTime' in order) {
if (status === 'open') {
if (Precise["default"].stringGt(filled, '0')) {
lastTradeTimestamp = this.safeInteger(order, 'updateTime');
}
else {
timestamp = this.safeInteger(order, 'updateTime');
}
}
}
const average = this.safeString(order, 'avgPrice');
const price = this.safeString(order, 'price');
const amount = this.safeString(order, 'origQty');
// - Spot/Margin market: cummulativeQuoteQty
// - Futures market: cumQuote.
// Note this is not the actual cost, since Binance futures uses leverage to calculate margins.
const cost = this.safeString2(order, 'cummulativeQuoteQty', 'cumQuote');
const id = this.safeString(order, 'orderId');
let type = this.safeStringLower(order, 'type');
const side = this.safeStringLower(order, 'side');
const fills = this.safeList(order, 'fills', []);
const clientOrderId = this.safeString(order, 'clientOrderId');
const timeInForce = this.safeString(order, 'timeInForce');
const postOnly = (type === 'limit_maker') || (timeInForce === 'GTX') || (type === 'post_only');
if (type === 'limit_maker') {
type = 'limit';
}
const triggerPrice = this.parseNumber(this.omitZero(this.safeString(order, 'stopPrice')));
return this.safeOrder({
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'triggerPrice': triggerPrice,
'amount': amount,
'cost': cost,
'average': average,
'filled': filled,
'remaining': undefined,
'status': status,
'fee': undefined,
'trades': fills,
}, market);
}
/**
* @method
* @name bitrue#createMarketBuyOrderWithCost
* @description create a market buy order by providing the symbol and cost
* @see https://www.bitrue.com/api-docs#new-order-trade-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#new-order-trade-hmac-sha256
* @param {string} symbol unified symbol of the market to create an order in
* @param {float} cost how much you want to trade in units of the quote currency
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createMarketBuyOrderWithCost(symbol, cost, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['swap']) {
throw new errors.NotSupported(this.id + ' createMarketBuyOrderWithCost() supports swap orders only');
}
params['createMarketBuyOrderRequiresPrice'] = false;
return await this.createOrder(symbol, 'market', 'buy', cost, undefined, params);
}
/**
* @method
* @name bitrue#createOrder
* @description create a trade order
* @see https://www.bitrue.com/api_docs_includes_file/spot/index.html#new-order-trade
* @see https://www.bitrue.com/api_docs_includes_file/futures/index.html#new-order-trade-hmac-sha256
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {float} [params.triggerPrice] *spot only* the price at which a trigger order is triggered at
* @param {string} [params.clientOrderId] a unique id for the order, automatically generated if not sent
* @param {decimal} [params.leverage] in future order, the leverage value of the order should consistent with the user contract configuration, default is 1
* @param {string} [params.timeInForce] 'fok', 'ioc' or 'po'
* @param {bool} [params.postOnly] default false
* @param {bool} [params.reduceOnly] default false
* EXCHANGE SPECIFIC PARAMETERS
* @param {decimal} [params.icebergQty]
* @param {long} [params.recvWindow]
* @param {float} [params.cost] *swap market buy only* the quote quantity that can be used as an alternative for the amount
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
let data = undefined;
const uppercaseType = type.toUpperCase();
const request = {
'side': side.toUpperCase(),
'type': uppercaseType,
// 'timeInForce': '',
// 'price': this.priceToPrecision (symbol, price),
// 'newClientOrderId': clientOrderId, // automatically generated if not sent
// 'stopPrice': this.priceToPrecision (symbol, 'stopPrice'),
// 'icebergQty': this.amountToPrecision (symbol, icebergQty),
};
if (uppercaseType === 'LIMIT') {
if (price === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a price argument');
}
request['price'] = this.priceToPrecision(symbol, price);
}
if (market['swap']) {
const isMarket = uppercaseType === 'MARKET';
const timeInForce = this.safeStringLower(params, 'timeInForce');
const postOnly = this.isPostOnly(isMarket, undefined, params);
if (postOnly) {
request['type'] = 'POST_ONLY';
}
else if (timeInForce === 'fok') {
request['type'] = 'FOK';
}
else if (timeInForce === 'ioc') {
request['type'] = 'IOC';
}
request['contractName'] = market['id'];
let createMarketBuyOrderRequiresPrice = true;
[createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
if (isMarket && (side === 'buy') && createMarketBuyOrderRequiresPrice) {
const cost = this.safeString(params, 'cost');
params = this.omit(params, 'cost');
if (price === undefined && cost === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires the price argument with swap 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 to supply the cost in the amount argument (the exchange-specific behaviour)');
}
else {
const amountString = this.numberToString(amount);
const priceString = this.numberToString(price);
const quoteAmount = Precise["default"].stringMul(amountString, priceString);
const requestAmount = (cost !== undefined) ? cost : quoteAmount;
request['amount'] = this.costToPrecision(symbol, requestAmount);
request['volume'] = this.costToPrecision(symbol, requestAmount);
}
}
else {
request['amount'] = this.parseToNumeric(amount);
request['volume'] = this.parseToNumeric(amount);
}
request['positionType'] = 1;
const reduceOnly = this.safeValue2(params, 'reduceOnly', 'reduce_only');
request['open'] = reduceOnly ? 'CLOSE' : 'OPEN';
const leverage = this.safeString(params, 'leverage', '1');
request['leverage'] = this.parseToNumeric(leverage);
params = this.omit(params, ['leverage', 'reduceOnly', 'reduce_only', 'timeInForce']);
if (market['linear']) {
response = await this.fapiV2PrivatePostOrder(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivatePostOrder(this.extend(request, params));
}
data = this.safeDict(response, 'data', {});
}
else if (market['spot']) {
request['symbol'] = market['id'];
request['quantity'] = this.amountToPrecision(symbol, amount);
const validOrderTypes = this.safeValue(market['info'], 'orderTypes');
if (!this.inArray(uppercaseType, validOrderTypes)) {
throw new errors.InvalidOrder(this.id + ' ' + type + ' is not a valid order type in market ' + symbol);
}
const clientOrderId = this.safeString2(params, 'newClientOrderId', 'clientOrderId');
if (clientOrderId !== undefined) {
params = this.omit(params, ['newClientOrderId', 'clientOrderId']);
request['newClientOrderId'] = clientOrderId;
}
const triggerPrice = this.safeValue2(params, 'triggerPrice', 'stopPrice');
if (triggerPrice !== undefined) {
params = this.omit(params, ['triggerPrice', 'stopPrice']);
request['stopPrice'] = this.priceToPrecision(symbol, triggerPrice);
}
response = await this.spotV1PrivatePostOrder(this.extend(request, params));
data = response;
}
else {
throw new errors.NotSupported(this.id + ' createOrder only support spot & swap markets');
}
//
// spot
//
// {
// "symbol": "BTCUSDT",
// "orderId": 307650651173648896,
// "orderIdStr": "307650651173648896",
// "clientOrderId": "6gCrw2kRUAF9CvJDGP16IP",
// "transactTime": 1507725176595
// }
//
// swap
//
// {
// "code": "0",
// "msg": "Success",
// "data": {
// "orderId": 1690615676032452985
// }
// }
//
return this.parseOrder(data, market);
}
/**
* @method
* @name bitrue#fetchOrder
* @description fetches information on an order made by the user
* @see https://www.bitrue.com/api_docs_includes_file/spot/index.html#query-order-user_data
* @see https://www.bitrue.com/api_docs_includes_file/futures/index.html#query-order-user_data-hmac-sha256
* @param {string} id the order id
* @param {string} symbol unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrder(id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
const origClientOrderId = this.safeValue2(params, 'origClientOrderId', 'clientOrderId');
params = this.omit(params, ['origClientOrderId', 'clientOrderId']);
let response = undefined;
let data = undefined;
const request = {};
if (origClientOrderId === undefined) {
request['orderId'] = id;
}
else {
if (market['swap']) {
request['clientOrderId'] = origClientOrderId;
}
else {
request['origClientOrderId'] = origClientOrderId;
}
}
if (market['swap']) {
request['contractName'] = market['id'];
if (market['linear']) {
response = await this.fapiV2PrivateGetOrder(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivateGetOrder(this.extend(request, params));
}
data = this.safeDict(response, 'data', {});
}
else if (market['spot']) {
request['orderId'] = id; // spot market id is mandatory
request['symbol'] = market['id'];
response = await this.spotV1PrivateGetOrder(this.extend(request, params));
data = response;
}
else {
throw new errors.NotSupported(this.id + ' fetchOrder only support spot & swap markets');
}
//
// spot
//
// {
// "symbol": "LTCBTC",
// "orderId": 1,
// "clientOrderId": "myOrder1",
// "price": "0.1",
// "origQty": "1.0",
// "executedQty": "0.0",
// "cummulativeQuoteQty": "0.0",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "stopPrice": "0.0",
// "icebergQty": "0.0",
// "time": 1499827319559,
// "updateTime": 1499827319559,
// "isWorking": true
// }
//
// swap
//
// {
// "code":0,
// "msg":"success",
// "data":{
// "orderId":1917641,
// "price":100,
// "origQty":10,
// "origAmount":10,
// "executedQty":1,
// "avgPrice":10000,
// "status":"INIT",
// "type":"LIMIT",
// "side":"BUY",
// "action":"OPEN",
// "transactTime":1686716571425
// "clientOrderId":4949299210
// }
// }
//
return this.parseOrder(data, market);
}
/**
* @method
* @name bitrue#fetchClosedOrders
* @description fetches information on multiple closed orders made by the user
* @see https://www.bitrue.com/api_docs_includes_file/spot/index.html#all-orders-user_data
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchClosedOrders() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
if (!market['spot']) {
throw new errors.NotSupported(this.id + ' fetchClosedOrders only support spot markets');
}
const request = {
'symbol': market['id'],
// 'orderId': 123445, // long
// 'startTime': since,
// 'endTime': this.milliseconds (),
// 'limit': limit, // default 100, max 1000
};
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 1000
}
const response = await this.spotV1PrivateGetAllOrders(this.extend(request, params));
//
// [
// {
// "symbol": "LTCBTC",
// "orderId": 1,
// "clientOrderId": "myOrder1",
// "price": "0.1",
// "origQty": "1.0",
// "executedQty": "0.0",
// "cummulativeQuoteQty": "0.0",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "stopPrice": "0.0",
// "icebergQty": "0.0",
// "time": 1499827319559,
// "updateTime": 1499827319559,
// "isWorking": true
// }
// ]
//
return this.parseOrders(response, market, since, limit);
}
/**
* @method
* @name bitrue#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @see https://www.bitrue.com/api_docs_includes_file/spot/index.html#current-open-orders-user_data
* @see https://www.bitrue.com/api_docs_includes_file/futures/index.html#cancel-all-open-orders-trade-hmac-sha256
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch open orders for
* @param {int} [limit] the maximum number of open order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchOpenOrders() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
let data = undefined;
const request = {};
if (market['swap']) {
request['contractName'] = market['id'];
if (market['linear']) {
response = await this.fapiV2PrivateGetOpenOrders(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivateGetOpenOrders(this.extend(request, params));
}
data = this.safeList(response, 'data', []);
}
else if (market['spot']) {
request['symbol'] = market['id'];
response = await this.spotV1PrivateGetOpenOrders(this.extend(request, params));
data = response;
}
else {
throw new errors.NotSupported(this.id + ' fetchOpenOrders only support spot & swap markets');
}
//
// spot
//
// [
// {
// "symbol":"USDCUSDT",
// "orderId":"2878854881",
// "clientOrderId":"",
// "price":"1.1000000000000000",
// "origQty":"100.0000000000000000",
// "executedQty":"0.0000000000000000",
// "cummulativeQuoteQty":"0.0000000000000000",
// "status":"NEW",
// "timeInForce":"",
// "type":"LIMIT",
// "side":"SELL",
// "stopPrice":"",
// "icebergQty":"",
// "time":1635551031000,
// "updateTime":1635551031000,
// "isWorking":false
// }
// ]
//
// swap
//
// {
// "code": "0",
// "msg": "Success",
// "data": [{
// "orderId": 1917641,
// "clientOrderId": "2488514315",
// "price": 100,
// "origQty": 10,
// "origAmount": 10,
// "executedQty": 1,
// "avgPrice": 12451,
// "status": "INIT",
// "type": "LIMIT",
// "side": "BUY",
// "action": "OPEN",
// "transactTime": 1686717303975
// }
// ]
// }
//
return this.parseOrders(data, market, since, limit);
}
/**
* @method
* @name bitrue#cancelOrder
* @description cancels an open order
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#cancel-order-trade
* @see https://www.bitrue.com/api-docs#cancel-order-trade-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#cancel-order-trade-hmac-sha256
* @param {string} id order id
* @param {string} symbol unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrder(id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
const origClientOrderId = this.safeValue2(params, 'origClientOrderId', 'clientOrderId');
params = this.omit(params, ['origClientOrderId', 'clientOrderId']);
let response = undefined;
let data = undefined;
const request = {};
if (origClientOrderId === undefined) {
request['orderId'] = id;
}
else {
if (market['swap']) {
request['clientOrderId'] = origClientOrderId;
}
else {
request['origClientOrderId'] = origClientOrderId;
}
}
if (market['swap']) {
request['contractName'] = market['id'];
if (market['linear']) {
response = await this.fapiV2PrivatePostCancel(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivatePostCancel(this.extend(request, params));
}
data = this.safeDict(response, 'data', {});
}
else if (market['spot']) {
request['symbol'] = market['id'];
response = await this.spotV1PrivateDeleteOrder(this.extend(request, params));
data = response;
}
else {
throw new errors.NotSupported(this.id + ' cancelOrder only support spot & swap markets');
}
//
// spot
//
// {
// "symbol": "LTCBTC",
// "origClientOrderId": "myOrder1",
// "orderId": 1,
// "clientOrderId": "cancelMyOrder1"
// }
//
// swap
//
// {
// "code": "0",
// "msg": "Success",
// "data": {
// "orderId": 1690615847831143159
// }
// }
//
return this.parseOrder(data, market);
}
/**
* @method
* @name bitrue#cancelAllOrders
* @description cancel all open orders in a market
* @see https://www.bitrue.com/api-docs#cancel-all-open-orders-trade-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#cancel-all-open-orders-trade-hmac-sha256
* @param {string} symbol unified market symbol of the market to cancel orders in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
* @returns {object[]} a list of [order structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#order-structure}
*/
async cancelAllOrders(symbol = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
let data = undefined;
if (market['swap']) {
const request = {
'contractName': market['id'],
};
if (market['linear']) {
response = await this.fapiV2PrivatePostAllOpenOrders(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivatePostAllOpenOrders(this.extend(request, params));
}
data = this.safeList(response, 'data', []);
}
else {
throw new errors.NotSupported(this.id + ' cancelAllOrders only support future markets');
}
//
// swap
//
// {
// 'code': '0',
// 'msg': 'Success',
// 'data': null
// }
//
return this.parseOrders(data, market);
}
/**
* @method
* @name bitrue#fetchMyTrades
* @description fetch all trades made by the user
* @see https://www.bitrue.com/api_docs_includes_file/spot/index.html#account-trade-list-user_data
* @see https://www.bitrue.com/api_docs_includes_file/futures/index.html#account-trade-list-user_data-hmac-sha256
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchMyTrades() requires a symbol argument');
}
const market = this.market(symbol);
let response = undefined;
let data = undefined;
const request = {};
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
if (limit > 1000) {
limit = 1000;
}
request['limit'] = limit;
}
if (market['swap']) {
request['contractName'] = market['id'];
if (market['linear']) {
response = await this.fapiV2PrivateGetMyTrades(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivateGetMyTrades(this.extend(request, params));
}
data = this.safeList(response, 'data', []);
}
else if (market['spot']) {
request['symbol'] = market['id'];
response = await this.spotV2PrivateGetMyTrades(this.extend(request, params));
data = response;
}
else {
throw new errors.NotSupported(this.id + ' fetchMyTrades only support spot & swap markets');
}
//
// spot
//
// [
// {
// "symbol":"USDCUSDT",
// "id":20725156,
// "orderId":2880918576,
// "origClientOrderId":null,
// "price":"0.9996000000000000",
// "qty":"100.0000000000000000",
// "commission":null,
// "commissionAssert":null,
// "time":1635558511000,
// "isBuyer":false,
// "isMaker":false,
// "isBestMatch":true
// }
// ]
//
// swap
//
// {
// "code":"0",
// "msg":"Success",
// "data":[
// {
// "tradeId":12,
// "price":0.9,
// "qty":1,
// "amount":9,
// "contractName":"E-SAND-USDT",
// "side":"BUY",
// "fee":"0.0018",
// "bidId":1558124009467904992,
// "askId":1558124043827644908,
// "bidUserId":10294,
// "askUserId":10467,
// "isBuyer":true,
// "isMaker":true,
// "ctime":1678426306000
// }
// ]
// }
//
return this.parseTrades(data, market, since, limit);
}
/**
* @method
* @name bitrue#fetchDeposits
* @description fetch all deposits made to an account
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#deposit-history--withdraw_data
* @param {string} code unified currency code
* @param {int} [since] the earliest time in ms to fetch deposits for
* @param {int} [limit] the maximum number of deposits structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchDeposits() requires a code argument');
}
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'coin': currency['id'],
'status': 1, // 0 init, 1 finished, default 0
// 'offset': 0,
// 'limit': limit, // default 10, max 1000
// 'startTime': since,
// 'endTime': this.milliseconds (),
};
if (since !== undefined) {
request['startTime'] = since;
// request['endTime'] = this.sum (since, 7776000000);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.spotV1PrivateGetDepositHistory(this.extend(request, params));
//
// {
// "code":200,
// "msg":"succ",
// "data":[
// {
// "id":2659137,
// "symbol":"USDC",
// "amount":"200.0000000000000000",
// "fee":"0.0E-15",
// "createdAt":1635503169000,
// "updatedAt":1635503202000,
// "addressFrom":"0x2faf487a4414fe77e2327f0bf4ae2a264a776ad2",
// "addressTo":"0x190ceccb1f8bfbec1749180f0ba8922b488d865b",
// "txid":"0x9970aec41099ac385568859517308707bc7d716df8dabae7b52f5b17351c3ed0",
// "confirmations":5,
// "status":0,
// "tagType":null,
// },
// {
// "id":2659137,
// "symbol": "XRP",
// "amount": "20.0000000000000000",
// "fee": "0.0E-15",
// "createdAt": 1544669393000,
// "updatedAt": 1544669413000,
// "addressFrom": "",
// "addressTo": "raLPjTYeGezfdb6crXZzcC8RkLBEwbBHJ5_18113641",
// "txid": "515B23E1F9864D3AF7F5B4C4FCBED784BAE861854FAB95F4031922B6AAEFC7AC",
// "confirmations": 7,
// "status": 1,
// "tagType": "Tag"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransactions(data, currency, since, limit);
}
/**
* @method
* @name bitrue#fetchWithdrawals
* @description fetch all withdrawals made from an account
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#withdraw-history--withdraw_data
* @param {string} code unified currency code
* @param {int} [since] the earliest time in ms to fetch withdrawals for
* @param {int} [limit] the maximum number of withdrawals structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchWithdrawals() requires a code argument');
}
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'coin': currency['id'],
'status': 5, // 0 init, 5 finished, 6 canceled, default 0
// 'offset': 0,
// 'limit': limit, // default 10, max 1000
// 'startTime': since,
// 'endTime': this.milliseconds (),
};
if (since !== undefined) {
request['startTime'] = since;
// request['endTime'] = this.sum (since, 7776000000);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.spotV1PrivateGetWithdrawHistory(this.extend(request, params));
//
// {
// "code": 200,
// "msg": "succ",
// "data": [
// {
// "id": 183745,
// "symbol": "usdt_erc20",
// "amount": "8.4000000000000000",
// "fee": "1.6000000000000000",
// "payAmount": "0.0000000000000000",
// "createdAt": 1595336441000,
// "updatedAt": 1595336576000,
// "addressFrom": "",
// "addressTo": "0x2edfae3878d7b6db70ce4abed177ab2636f60c83",
// "txid": "",
// "confirmations": 0,
// "status": 6,
// "tagType": null
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransactions(data, currency);
}
parseTransactionStatusByType(status, type = undefined) {
const statusesByType = {
'deposit': {
'0': 'pending',
'1': 'ok',
},
'withdrawal': {
'0': 'pending',
'5': 'ok',
'6': 'canceled',
},
};
const statuses = this.safeDict(statusesByType, type, {});
return this.safeString(statuses, status, status);
}
parseTransaction(transaction, currency = undefined) {
//
// fetchDeposits
//
// {
// "symbol": "XRP",
// "amount": "261.3361000000000000",
// "fee": "0.0E-15",
// "createdAt": 1548816979000,
// "updatedAt": 1548816999000,
// "addressFrom": "",
// "addressTo": "raLPjTYeGezfdb6crXZzcC8RkLBEwbBHJ5_18113641",
// "txid": "86D6EB68A7A28938BCE06BD348F8C07DEF500C5F7FE92069EF8C0551CE0F2C7D",
// "confirmations": 8,
// "status": 1,
// "tagType": "Tag"
// },
// {
// "symbol": "XRP",
// "amount": "20.0000000000000000",
// "fee": "0.0E-15",
// "createdAt": 1544669393000,
// "updatedAt": 1544669413000,
// "addressFrom": "",
// "addressTo": "raLPjTYeGezfdb6crXZzcC8RkLBEwbBHJ5_18113641",
// "txid": "515B23E1F9864D3AF7F5B4C4FCBED784BAE861854FAB95F4031922B6AAEFC7AC",
// "confirmations": 7,
// "status": 1,
// "tagType": "Tag"
// }
//
// fetchWithdrawals
//
// {
// "id": 183745,
// "symbol": "usdt_erc20",
// "amount": "8.4000000000000000",
// "fee": "1.6000000000000000",
// "payAmount": "0.0000000000000000",
// "createdAt": 1595336441000,
// "updatedAt": 1595336576000,
// "addressFrom": "",
// "addressTo": "0x2edfae3878d7b6db70ce4abed177ab2636f60c83",
// "txid": "",
// "confirmations": 0,
// "status": 6,
// "tagType": null
// }
//
// withdraw
//
// {
// "msg": null,
// "amount": 1000,
// "fee": 1,
// "ctime": null,
// "coin": "usdt_erc20",
// "withdrawId": 1156423,
// "addressTo": "0x2edfae3878d7b6db70ce4abed177ab2636f60c83"
// }
//
const id = this.safeString2(transaction, 'id', 'withdrawId');
const tagType = this.safeString(transaction, 'tagType');
let addressTo = this.safeString(transaction, 'addressTo');
let addressFrom = this.safeString(transaction, 'addressFrom');
let tagTo = undefined;
let tagFrom = undefined;
if (tagType !== undefined) {
if (addressTo !== undefined) {
const parts = addressTo.split('_');
addressTo = this.safeString(parts, 0);
tagTo = this.safeString(parts, 1);
}
if (addressFrom !== undefined) {
const parts = addressFrom.split('_');
addressFrom = this.safeString(parts, 0);
tagFrom = this.safeString(parts, 1);
}
}
const txid = this.safeString(transaction, 'txid');
const timestamp = this.safeInteger(transaction, 'createdAt');
const updated = this.safeInteger(transaction, 'updatedAt');
const payAmount = ('payAmount' in transaction);
const ctime = ('ctime' in transaction);
const type = (payAmount || ctime) ? 'withdrawal' : 'deposit';
const status = this.parseTransactionStatusByType(this.safeString(transaction, 'status'), type);
const amount = this.safeNumber(transaction, 'amount');
let network = undefined;
let currencyId = this.safeString2(transaction, 'symbol', 'coin');
if (currencyId !== undefined) {
const parts = currencyId.split('_');
currencyId = this.safeString(parts, 0);
const networkId = this.safeString(parts, 1);
if (networkId !== undefined) {
network = networkId.toUpperCase();
}
}
const code = this.safeCurrencyCode(currencyId, currency);
const feeCost = this.safeNumber(transaction, 'fee');
let fee = undefined;
if (feeCost !== undefined) {
fee = { 'currency': code, 'cost': feeCost };
}
return {
'info': transaction,
'id': id,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'network': network,
'address': addressTo,
'addressTo': addressTo,
'addressFrom': addressFrom,
'tag': tagTo,
'tagTo': tagTo,
'tagFrom': tagFrom,
'type': type,
'amount': amount,
'currency': code,
'status': status,
'updated': updated,
'internal': false,
'comment': undefined,
'fee': fee,
};
}
/**
* @method
* @name bitrue#withdraw
* @description make a withdrawal
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#withdraw-commit--withdraw_data
* @param {string} code unified currency code
* @param {float} amount the amount to withdraw
* @param {string} address the address to withdraw to
* @param {string} tag
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async withdraw(code, amount, address, tag = undefined, params = {}) {
[tag, params] = this.handleWithdrawTagAndParams(tag, params);
this.checkAddress(address);
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'coin': currency['id'],
'amount': amount,
'addressTo': address,
// 'chainName': chainName, // 'ERC20', 'TRC20', 'SOL'
// 'addressMark': '', // mark of address
// 'addrType': '', // type of address
// 'tag': tag,
};
let networkCode = undefined;
[networkCode, params] = this.handleNetworkCodeAndParams(params);
if (networkCode !== undefined) {
request['chainName'] = this.networkCodeToId(networkCode);
}
if (tag !== undefined) {
request['tag'] = tag;
}
const response = await this.spotV1PrivatePostWithdrawCommit(this.extend(request, params));
//
// {
// "code": 200,
// "msg": "succ",
// "data": {
// "msg": null,
// "amount": 1000,
// "fee": 1,
// "ctime": null,
// "coin": "usdt_erc20",
// "withdrawId": 1156423,
// "addressTo": "0x2edfae3878d7b6db70ce4abed177ab2636f60c83"
// }
// }
//
const data = this.safeDict(response, 'data', {});
return this.parseTransaction(data, currency);
}
parseDepositWithdrawFee(fee, currency = undefined) {
//
// {
// "coin": "adx",
// "coinFulName": "Ambire AdEx",
// "chains": [ "BSC" ],
// "chainDetail": [ [Object] ]
// }
//
const chainDetails = this.safeList(fee, 'chainDetail', []);
const chainDetailLength = chainDetails.length;
const result = {
'info': fee,
'withdraw': {
'fee': undefined,
'percentage': undefined,
},
'deposit': {
'fee': undefined,
'percentage': undefined,
},
'networks': {},
};
if (chainDetailLength !== 0) {
for (let i = 0; i < chainDetailLength; i++) {
const chainDetail = chainDetails[i];
const networkId = this.safeString(chainDetail, 'chain');
const currencyCode = this.safeString(currency, 'code');
const networkCode = this.networkIdToCode(networkId, currencyCode);
result['networks'][networkCode] = {
'deposit': { 'fee': undefined, 'percentage': undefined },
'withdraw': { 'fee': this.safeNumber(chainDetail, 'withdrawFee'), 'percentage': false },
};
if (chainDetailLength === 1) {
result['withdraw']['fee'] = this.safeNumber(chainDetail, 'withdrawFee');
result['withdraw']['percentage'] = false;
}
}
}
return result;
}
/**
* @method
* @name bitrue#fetchDepositWithdrawFees
* @description fetch deposit and withdraw fees
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#exchangeInfo_endpoint
* @param {string[]|undefined} codes list of unified currency codes
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a list of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure}
*/
async fetchDepositWithdrawFees(codes = undefined, params = {}) {
await this.loadMarkets();
const response = await this.spotV1PublicGetExchangeInfo(params);
const coins = this.safeList(response, 'coins');
return this.parseDepositWithdrawFees(coins, codes, 'coin');
}
parseTransfer(transfer, currency = undefined) {
//
// fetchTransfers
//
// {
// 'transferType': 'wallet_to_contract',
// 'symbol': 'USDT',
// 'amount': 1.0,
// 'status': 1,
// 'ctime': 1685404575000
// }
//
// transfer
//
// {}
//
const transferType = this.safeString(transfer, 'transferType');
let fromAccount = undefined;
let toAccount = undefined;
if (transferType !== undefined) {
const accountSplit = transferType.split('_to_');
fromAccount = this.safeString(accountSplit, 0);
toAccount = this.safeString(accountSplit, 1);
}
const timestamp = this.safeInteger(transfer, 'ctime');
return {
'info': transfer,
'id': undefined,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'currency': this.safeString(currency, 'code'),
'amount': this.safeNumber(transfer, 'amount'),
'fromAccount': fromAccount,
'toAccount': toAccount,
'status': 'ok',
};
}
/**
* @method
* @name bitrue#fetchTransfers
* @description fetch a history of internal transfers made on an account
* @see https://www.bitrue.com/api-docs#get-future-account-transfer-history-list-user_data-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#get-future-account-transfer-history-list-user_data-hmac-sha256
* @param {string} code unified currency code of the currency transferred
* @param {int} [since] the earliest time in ms to fetch transfers for
* @param {int} [limit] the maximum number of transfers structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] the latest time in ms to fetch transfers for
* @param {string} [params.type] transfer type wallet_to_contract or contract_to_wallet
* @returns {object[]} a list of [transfer structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#transfer-structure}
*/
async fetchTransfers(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const type = this.safeString2(params, 'type', 'transferType');
const request = {
'transferType': type,
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['coinSymbol'] = currency['id'];
}
if (since !== undefined) {
request['beginTime'] = since;
}
if (limit !== undefined) {
if (limit > 200) {
limit = 200;
}
request['limit'] = limit;
}
const until = this.safeInteger(params, 'until');
if (until !== undefined) {
params = this.omit(params, 'until');
request['endTime'] = until;
}
const response = await this.fapiV2PrivateGetFuturesTransferHistory(this.extend(request, params));
//
// {
// 'code': '0',
// 'msg': 'Success',
// 'data': [{
// 'transferType': 'wallet_to_contract',
// 'symbol': 'USDT',
// 'amount': 1.0,
// 'status': 1,
// 'ctime': 1685404575000
// }]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransfers(data, currency, since, limit);
}
/**
* @method
* @name bitrue#transfer
* @description transfer currency internally between wallets on the same account
* @see https://www.bitrue.com/api-docs#new-future-account-transfer-user_data-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#user-commission-rate-user_data-hmac-sha256
* @param {string} code unified currency code
* @param {float} amount amount to transfer
* @param {string} fromAccount account to transfer from
* @param {string} toAccount account to transfer to
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transfer structure]{@link https://github.com/ccxt/ccxt/wiki/Manual#transfer-structure}
*/
async transfer(code, amount, fromAccount, toAccount, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const accountTypes = this.safeDict(this.options, 'accountsByType', {});
const fromId = this.safeString(accountTypes, fromAccount, fromAccount);
const toId = this.safeString(accountTypes, toAccount, toAccount);
const request = {
'coinSymbol': currency['id'],
'amount': this.currencyToPrecision(code, amount),
'transferType': fromId + '_to_' + toId,
};
const response = await this.fapiV2PrivatePostFuturesTransfer(this.extend(request, params));
//
// {
// 'code': '0',
// 'msg': 'Success',
// 'data': null
// }
//
const data = this.safeDict(response, 'data', {});
return this.parseTransfer(data, currency);
}
/**
* @method
* @name bitrue#setLeverage
* @description set the level of leverage for a market
* @see https://www.bitrue.com/api-docs#change-initial-leverage-trade-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#change-initial-leverage-trade-hmac-sha256
* @param {float} leverage the rate of leverage
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} response from the exchange
*/
async setLeverage(leverage, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' setLeverage() requires a symbol argument');
}
if ((leverage < 1) || (leverage > 125)) {
throw new errors.BadRequest(this.id + ' leverage should be between 1 and 125');
}
await this.loadMarkets();
const market = this.market(symbol);
let response = undefined;
const request = {
'contractName': market['id'],
'leverage': leverage,
};
if (!market['swap']) {
throw new errors.NotSupported(this.id + ' setLeverage only support swap markets');
}
if (market['linear']) {
response = await this.fapiV2PrivatePostLevelEdit(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivatePostLevelEdit(this.extend(request, params));
}
return response;
}
parseMarginModification(data, market = undefined) {
//
// setMargin
//
// {
// "code": 0,
// "msg": "success"
// "data": null
// }
//
return {
'info': data,
'symbol': market['symbol'],
'type': undefined,
'marginMode': 'isolated',
'amount': undefined,
'total': undefined,
'code': undefined,
'status': undefined,
'timestamp': undefined,
'datetime': undefined,
};
}
/**
* @method
* @name bitrue#setMargin
* @description Either adds or reduces margin in an isolated position in order to set the margin to a specific value
* @see https://www.bitrue.com/api-docs#modify-isolated-position-margin-trade-hmac-sha256
* @see https://www.bitrue.com/api_docs_includes_file/delivery.html#modify-isolated-position-margin-trade-hmac-sha256
* @param {string} symbol unified market symbol of the market to set margin in
* @param {float} amount the amount to set the margin to
* @param {object} [params] parameters specific to the exchange API endpoint
* @returns {object} A [margin structure]{@link https://github.com/ccxt/ccxt/wiki/Manual#add-margin-structure}
*/
async setMargin(symbol, amount, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['swap']) {
throw new errors.NotSupported(this.id + ' setMargin only support swap markets');
}
let response = undefined;
const request = {
'contractName': market['id'],
'amount': this.parseToNumeric(amount),
};
if (market['linear']) {
response = await this.fapiV2PrivatePostPositionMargin(this.extend(request, params));
}
else if (market['inverse']) {
response = await this.dapiV2PrivatePostPositionMargin(this.extend(request, params));
}
//
// {
// "code": 0,
// "msg": "success"
// "data": null
// }
//
return this.parseMarginModification(response, market);
}
sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const type = this.safeString(api, 0);
const version = this.safeString(api, 1);
const access = this.safeString(api, 2);
let url = undefined;
if ((type === 'api' && version === 'kline') || (type === 'open' && path.indexOf('listenKey') >= 0)) {
url = this.urls['api'][type];
}
else {
url = this.urls['api'][type] + '/' + version;
}
url = url + '/' + this.implodeParams(path, params);
params = this.omit(params, this.extractParams(path));
if (access === 'private') {
this.checkRequiredCredentials();
const recvWindow = this.safeInteger(this.options, 'recvWindow', 5000);
if (type === 'spot' || type === 'open') {
let query = this.urlencode(this.extend({
'timestamp': this.nonce(),
'recvWindow': recvWindow,
}, params));
const signature = this.hmac(this.encode(query), this.encode(this.secret), sha256.sha256);
query += '&' + 'signature=' + signature;
headers = {
'X-MBX-APIKEY': this.apiKey,
};
if ((method === 'GET') || (method === 'DELETE')) {
url += '?' + query;
}
else {
body = query;
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
}
else {
const timestamp = this.nonce().toString();
let signPath = undefined;
if (type === 'fapi') {
signPath = '/fapi';
}
else if (type === 'dapi') {
signPath = '/dapi';
}
signPath = signPath + '/' + version + '/' + path;
let signMessage = timestamp + method + signPath;
if (method === 'GET') {
const keys = Object.keys(params);
const keysLength = keys.length;
if (keysLength > 0) {
signMessage += '?' + this.urlencode(params);
}
const signature = this.hmac(this.encode(signMessage), this.encode(this.secret), sha256.sha256);
headers = {
'X-CH-APIKEY': this.apiKey,
'X-CH-SIGN': signature,
'X-CH-TS': timestamp,
};
url += '?' + this.urlencode(params);
}
else {
const query = this.extend({
'recvWindow': recvWindow,
}, params);
body = this.json(query);
signMessage += body;
const signature = this.hmac(this.encode(signMessage), this.encode(this.secret), sha256.sha256);
headers = {
'Content-Type': 'application/json',
'X-CH-APIKEY': this.apiKey,
'X-CH-SIGN': signature,
'X-CH-TS': timestamp,
};
}
}
}
else {
if (Object.keys(params).length) {
url += '?' + this.urlencode(params);
}
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if ((code === 418) || (code === 429)) {
throw new errors.DDoSProtection(this.id + ' ' + code.toString() + ' ' + reason + ' ' + body);
}
// error response in a form: { "code": -1013, "msg": "Invalid quantity." }
// following block cointains legacy checks against message patterns in "msg" property
// will switch "code" checks eventually, when we know all of them
if (code >= 400) {
if (body.indexOf('Price * QTY is zero or less') >= 0) {
throw new errors.InvalidOrder(this.id + ' order cost = amount * price is zero or less ' + body);
}
if (body.indexOf('LOT_SIZE') >= 0) {
throw new errors.InvalidOrder(this.id + ' order amount should be evenly divisible by lot size ' + body);
}
if (body.indexOf('PRICE_FILTER') >= 0) {
throw new errors.InvalidOrder(this.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use this.priceToPrecision (symbol, amount) ' + body);
}
}
if (response === undefined) {
return undefined; // fallback to default error handler
}
// check success value for wapi endpoints
// response in format {'msg': 'The coin does not exist.', 'success': true/false}
const success = this.safeBool(response, 'success', true);
if (!success) {
const messageInner = this.safeString(response, 'msg');
let parsedMessage = undefined;
if (messageInner !== undefined) {
try {
parsedMessage = JSON.parse(messageInner);
}
catch (e) {
// do nothing
parsedMessage = undefined;
}
if (parsedMessage !== undefined) {
response = parsedMessage;
}
}
}
const message = this.safeString(response, 'msg');
if (message !== undefined) {
this.throwExactlyMatchedException(this.exceptions['exact'], message, this.id + ' ' + message);
this.throwBroadlyMatchedException(this.exceptions['broad'], message, this.id + ' ' + message);
}
// checks against error codes
const error = this.safeString(response, 'code');
if (error !== undefined) {
// https://github.com/ccxt/ccxt/issues/6501
// https://github.com/ccxt/ccxt/issues/7742
if ((error === '200') || Precise["default"].stringEquals(error, '0')) {
return undefined;
}
// a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
// despite that their message is very confusing, it is raised by Binance
// on a temporary ban, the API key is valid, but disabled for a while
if ((error === '-2015') && this.options['hasAlreadyAuthenticatedSuccessfully']) {
throw new errors.DDoSProtection(this.id + ' temporary banned: ' + body);
}
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException(this.exceptions['exact'], error, feedback);
throw new errors.ExchangeError(feedback);
}
if (!success) {
throw new errors.ExchangeError(this.id + ' ' + body);
}
return undefined;
}
calculateRateLimiterCost(api, method, path, params, config = {}) {
if (('noSymbol' in config) && !('symbol' in params)) {
return config['noSymbol'];
}
else if (('byLimit' in config) && ('limit' in params)) {
const limit = params['limit'];
const byLimit = config['byLimit'];
for (let i = 0; i < byLimit.length; i++) {
const entry = byLimit[i];
if (limit <= entry[0]) {
return entry[1];
}
}
}
return this.safeValue(config, 'cost', 1);
}
}
module.exports = bitrue;