@kraken-crypto/ccxt
Version:
A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go
1,161 lines • 132 kB
JavaScript
// ---------------------------------------------------------------------------
import Exchange from './abstract/toobit.js';
import { OperationFailed, ArgumentsRequired, ExchangeError, BadRequest, OrderNotFound, BadSymbol, NotSupported, PermissionDenied, RateLimitExceeded, OperationRejected, InvalidOrder, InsufficientFunds } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class toobit
* @augments Exchange
*/
export default class toobit extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'toobit',
'name': 'Toobit',
'countries': ['KY'],
'version': 'v1',
'rateLimit': 20,
'certified': false,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': true,
'future': false,
'option': false,
'cancelAllOrders': true,
'cancelOrder': true,
'cancelOrders': true,
'createOrder': true,
'fetchBalance': true,
'fetchBidsAsks': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDeposits': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchIndexOHLCV': true,
'fetchLastPrices': true,
'fetchLedger': true,
'fetchMarkets': true,
'fetchMarkOHLCV': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchStatus': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchWithdrawals': true,
'setMarginMode': true,
'transfer': true,
'withdraw': true,
},
'urls': {
'logo': 'https://github.com/user-attachments/assets/0c7a97d5-182c-492e-b921-23540c868e0e',
'api': {
'common': 'https://api.toobit.com',
'private': 'https://api.toobit.com',
},
'www': 'https://www.toobit.com/',
'doc': [
'https://toobit-docs.github.io/apidocs/spot/v1/en/',
'https://toobit-docs.github.io/apidocs/usdt_swap/v1/en/',
],
'referral': {
'url': 'https://www.toobit.com/en-US/r?i=IFFPy0',
'discount': 0.1,
},
'fees': 'https://www.toobit.com/fee',
},
'api': {
'common': {
'get': {
'api/v1/time': 1,
'api/v1/ping': 1,
'api/v1/exchangeInfo': 1,
'quote/v1/depth': 1,
'quote/v1/depth/merged': 1,
'quote/v1/trades': 1,
'quote/v1/klines': 1,
'quote/v1/index/klines': 1,
'quote/v1/markPrice/klines': 1,
'quote/v1/markPrice': 1,
'quote/v1/index': 1,
'quote/v1/ticker/24hr': 40,
'quote/v1/contract/ticker/24hr': 40,
'quote/v1/ticker/price': 1,
'quote/v1/ticker/bookTicker': 1,
'api/v1/futures/fundingRate': 1,
'api/v1/futures/historyFundingRate': 1,
},
},
'private': {
'get': {
'api/v1/account': 5,
'api/v1/account/checkApiKey': 1,
'api/v1/spot/order': 1 * 1.67,
'api/v1/spot/openOrders': 1 * 1.67,
'api/v1/futures/openOrders': 1 * 1.67,
'api/v1/spot/tradeOrders': 5 * 1.67,
'api/v1/futures/historyOrders': 5 * 1.67,
'api/v1/account/trades': 5 * 1.67,
'api/v1/account/balanceFlow': 5,
'api/v1/account/depositOrders': 5,
'api/v1/account/withdrawOrders': 5,
'api/v1/account/deposit/address': 1,
// contracts
'api/v1/subAccount': 5,
'api/v1/futures/accountLeverage': 1,
'api/v1/futures/order': 1 * 1.67,
'api/v1/futures/positions': 5 * 1.67,
'api/v1/futures/balance': 5,
'api/v1/futures/userTrades': 5 * 1.67,
'api/v1/futures/balanceFlow': 5,
'api/v1/futures/commissionRate': 5,
'api/v1/futures/todayPnl': 5,
},
'post': {
'api/v1/spot/orderTest': 1 * 1.67,
'api/v1/spot/order': 1 * 1.67,
'api/v1/futures/order': 1 * 1.67,
'api/v1/spot/batchOrders': 2 * 1.67,
'api/v1/subAccount/transfer': 1,
'api/v1/account/withdraw': 1,
// contracts
'api/v1/futures/marginType': 1,
'api/v1/futures/leverage': 1,
'api/v1/futures/batchOrders': 2 * 1.67,
'api/v1/futures/position/trading-stop': 3 * 1.67,
'api/v1/futures/positionMargin': 1,
'api/v1/userDataStream': 1,
'api/v1/listenKey': 1,
},
'delete': {
'api/v1/spot/order': 1 * 1.67,
'api/v1/futures/order': 1 * 1.67,
'api/v1/spot/openOrders': 5 * 1.67,
'api/v1/futures/batchOrders': 5 * 1.67,
'api/v1/spot/cancelOrderByIds': 5 * 1.67,
'api/v1/futures/cancelOrderByIds': 5 * 1.67,
'api/v1/listenKey': 1,
},
'put': {
'api/v1/listenKey': 1,
},
},
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
'1w': '1w',
'1M': '1M',
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {
'-1000': OperationFailed,
'-1001': OperationFailed,
'-1002': PermissionDenied,
'-1003': RateLimitExceeded,
'-1004': BadRequest,
'-1006': OperationFailed,
'-1007': OperationFailed,
'-1014': OperationFailed,
'-1015': RateLimitExceeded,
'-1016': OperationRejected,
'-1020': OperationRejected,
'-1021': OperationRejected,
'-1022': OperationRejected,
'-1100': BadRequest,
'-1101': BadRequest,
'-1102': BadRequest,
'-1103': BadRequest,
'-1104': BadRequest,
'-1105': BadRequest,
'-1106': BadRequest,
'-1111': BadRequest,
'-1112': OperationRejected,
'-1114': BadRequest,
'-1115': BadRequest,
'-1116': BadRequest,
'-1117': BadRequest,
'-1118': InvalidOrder,
'-1119': InvalidOrder,
'-1120': BadRequest,
'-1121': BadRequest,
'-1125': OperationRejected,
'-1127': OperationRejected,
'-1128': BadRequest,
'-1130': BadRequest,
'-1132': OperationRejected,
'-1133': OperationRejected,
'-1134': OperationRejected,
'-1135': OperationRejected,
'-1136': OperationRejected,
'-1137': OperationRejected,
'-1138': OperationRejected,
'-1139': OperationRejected,
'-1140': OperationRejected,
'-1141': InvalidOrder,
'-1142': InvalidOrder,
'-1143': InvalidOrder,
'-1144': OperationRejected,
'-1145': OperationRejected,
'-1146': OperationFailed,
'-1147': OperationFailed,
'-1193': OperationRejected,
'-1194': OperationRejected,
'-1195': OperationRejected,
'-1196': OperationRejected,
'-1197': OperationRejected,
'-1198': OperationRejected,
'-1199': OperationRejected,
'-1200': OperationRejected,
'-1201': OperationRejected,
'-1202': OperationRejected,
'-1203': OperationRejected,
'-1206': OperationRejected,
'-2010': OperationFailed,
'-2011': OperationFailed,
'-2013': InvalidOrder,
'-2014': PermissionDenied,
'-2015': PermissionDenied,
'-2016': BadRequest,
// errors above 3xxx are from swap API
'-3050': ExchangeError,
'-3101': OperationRejected,
'-3102': OperationRejected,
'-3103': BadRequest,
'-3105': OperationRejected,
'-3107': OperationRejected,
'-3108': OperationRejected,
'-3109': OperationRejected,
'-3110': InsufficientFunds,
'-3116': OperationRejected,
'-3117': OperationRejected,
'-3120': OperationRejected,
'-3124': OperationRejected,
'-3125': OperationRejected,
'-3126': OperationRejected,
'-3127': OperationFailed,
'-3128': OperationRejected,
'-3129': BadRequest,
'-3130': OperationRejected,
'-3131': NotSupported, // Leverage reduction is not supported in Isolated Margin Mode with open positions.
},
'broad': {
'Unknown order sent': OrderNotFound,
'Duplicate order sent': InvalidOrder,
'Market is closed': OperationRejected,
'Account has insufficient balance for requested action': InsufficientFunds,
'Market orders are not supported for this symbol': OperationRejected,
'Iceberg orders are not supported for this symbol': OperationRejected,
'Stop loss orders are not supported for this symbol': OperationRejected,
'Stop loss limit orders are not supported for this symbol': OperationRejected,
'Take profit orders are not supported for this symbol': OperationRejected,
'Take profit limit orders are not supported for this symbol': OperationRejected,
'QTY is zero or less': BadRequest,
'IcebergQty exceeds QTY': OperationRejected,
'This action disabled is on this account': PermissionDenied,
'Unsupported order combination': BadRequest,
'Order would trigger immediately': OperationRejected,
'Cancel order is invalid. Check origClOrdId and orderId': OperationRejected,
'Order would immediately match and take': OperationRejected,
},
},
'commonCurrencies': {},
'options': {
'defaultType': 'spot',
'accountsByType': {
'spot': 'MAIN',
'swap': 'FUTURES',
},
'networks': {
'BTC': 'BTC',
'ERC20': 'ETH',
'ETH': 'ETH',
'BEP20': 'BSC',
'TRC20': 'TRX',
'SOL': 'SOL',
'MATIC': 'MATIC',
'ARBONE': 'ARBITRUM',
'BASE': 'BASE',
'TON': 'TON',
'AVAXC': 'AVAXC',
'DOGE': 'DOGE',
'XRP': 'XRP',
'DOT': 'DOT',
'ADA': 'ADA',
'LTC': 'LTC',
'APT': 'APT',
'ATOM': 'ATOM',
'ALGO': 'ALGO',
'NEAR': 'NEAR',
'XLM': 'XLM',
'SUI': 'SUI',
'ETC': 'ETC',
'EOS': 'EOS',
'WAVES': 'WAVES',
'ICP': 'ICP',
'ONE': 'ONE',
// 'CHZ2': 'CHZ2',
},
'networksById': {
'ETH': 'ERC20',
'ERC20': 'ERC20',
},
},
'features': {
'spot': {
'sandbox': false,
'createOrder': {
'marginMode': false,
'triggerPrice': true,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': false,
'trailing': false,
'leverage': false,
'marketBuyRequiresPrice': false,
'marketBuyByCost': false,
'selfTradePrevention': false,
'iceberg': false,
},
'createOrders': undefined,
'fetchOHLCV': {
'limit': 1000,
},
'fetchMyTrades': {
'marginMode': false,
'limit': 1000,
'daysBack': 100000,
'untilDays': 100000,
'symbolRequired': true,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 1000,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 500,
'daysBack': 100000,
'untilDays': 100000,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchClosedOrders': undefined,
},
'forDerivatives': {
'createOrders': undefined,
},
'swap': {
'linear': undefined,
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
});
}
/**
* @method
* @name toobit#fetchStatus
* @description the latest known information on the availability of the exchange API
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#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.commonGetApiV1Ping(params);
return {
'status': 'ok',
'updated': undefined,
'eta': undefined,
'url': undefined,
'info': response,
};
}
/**
* @method
* @name toobit#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#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.commonGetApiV1Time(params);
//
// {
// "serverTime": 1699827319559
// }
//
return this.safeInteger(response, 'serverTime');
}
/**
* @method
* @name toobit#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.commonGetApiV1ExchangeInfo(params);
this.options['exchangeInfo'] = response; // we store it in options for later use in fetchMarkets
//
// {
// "timezone": "UTC",
// "serverTime": "1755583099926",
// "brokerFilters": [],
// "symbols": [
// {
// "filters": [
// {
// "minPrice": "0.01",
// "maxPrice": "10000000.00000000",
// "tickSize": "0.01",
// "filterType": "PRICE_FILTER"
// },
// {
// "minQty": "0.0001",
// "maxQty": "4000",
// "stepSize": "0.0001",
// "filterType": "LOT_SIZE"
// },
// {
// "minNotional": "5",
// "filterType": "MIN_NOTIONAL"
// },
// {
// "minAmount": "5",
// "maxAmount": "6600000",
// "minBuyPrice": "0.01",
// "filterType": "TRADE_AMOUNT"
// },
// {
// "maxSellPrice": "99999999",
// "buyPriceUpRate": "0.1",
// "sellPriceDownRate": "0.1",
// "filterType": "LIMIT_TRADING"
// },
// {
// "buyPriceUpRate": "0.1",
// "sellPriceDownRate": "0.1",
// "filterType": "MARKET_TRADING"
// },
// {
// "noAllowMarketStartTime": "0",
// "noAllowMarketEndTime": "0",
// "limitOrderStartTime": "0",
// "limitOrderEndTime": "0",
// "limitMinPrice": "0",
// "limitMaxPrice": "0",
// "filterType": "OPEN_QUOTE"
// }
// ],
// "exchangeId": "301",
// "symbol": "ETHUSDT",
// "symbolName": "ETHUSDT",
// "status": "TRADING",
// "baseAsset": "ETH",
// "baseAssetName": "ETH",
// "baseAssetPrecision": "0.0001",
// "quoteAsset": "USDT",
// "quoteAssetName": "USDT",
// "quotePrecision": "0.01",
// "icebergAllowed": false,
// "isAggregate": false,
// "allowMargin": true,
// }
// ],
// "options": [],
// "contracts": [
// {
// "filters": [ ... ],
// "exchangeId": "301",
// "symbol": "BTC-SWAP-USDT",
// "symbolName": "BTC-SWAP-USDTUSDT",
// "status": "TRADING",
// "baseAsset": "BTC-SWAP-USDT",
// "baseAssetPrecision": "0.001",
// "quoteAsset": "USDT",
// "quoteAssetPrecision": "0.1",
// "icebergAllowed": false,
// "inverse": false,
// "index": "BTC",
// "indexToken": "BTCUSDT",
// "marginToken": "USDT",
// "marginPrecision": "0.0001",
// "contractMultiplier": "0.001",
// "underlying": "BTC",
// "riskLimits": [
// {
// "riskLimitId": "200020911",
// "quantity": "42000.0",
// "initialMargin": "0.02",
// "maintMargin": "0.01",
// "isWhite": false
// },
// {
// "riskLimitId": "200020912",
// "quantity": "84000.0",
// "initialMargin": "0.04",
// "maintMargin": "0.02",
// "isWhite": false
// },
// ...
// ]
// },
// ],
// "coins": [
// {
// "orgId": "9001",
// "coinId": "TCOM",
// "coinName": "TCOM",
// "coinFullName": "TCOM",
// "allowWithdraw": true,
// "allowDeposit": true,
// "chainTypes": [
// {
// "chainType": "BSC",
// "withdrawFee": "49.55478",
// "minWithdrawQuantity": "77",
// "maxWithdrawQuantity": "0",
// "minDepositQuantity": "48",
// "allowDeposit": true,
// "allowWithdraw": false
// }
// ],
// "isVirtual": false
// },
// ...
//
const coins = this.safeList(response, 'coins', []);
const result = {};
for (let i = 0; i < coins.length; i++) {
const coin = coins[i];
const parsed = this.parseCurrency(coin);
const code = parsed['code'];
result[code] = parsed;
}
return result;
}
parseCurrency(rawCurrency) {
const id = this.safeString(rawCurrency, 'coinId');
const code = this.safeCurrencyCode(id);
const networks = {};
const rawNetworks = this.safeList(rawCurrency, 'chainTypes');
for (let j = 0; j < rawNetworks.length; j++) {
const rawNetwork = rawNetworks[j];
const networkId = this.safeString(rawNetwork, 'chainType');
const networkCode = this.networkIdToCode(networkId);
networks[networkCode] = {
'id': networkId,
'network': networkCode,
'margin': undefined,
'deposit': this.safeBool(rawNetwork, 'allowDeposit'),
'withdraw': this.safeBool(rawNetwork, 'allowWithdraw'),
'active': undefined,
'fee': this.safeNumber(rawNetwork, 'withdrawFee'),
'precision': undefined,
'limits': {
'deposit': {
'min': this.safeNumber(rawNetwork, 'minDepositQuantity'),
'max': undefined,
},
'withdraw': {
'min': this.safeNumber(rawNetwork, 'minWithdrawQuantity'),
'max': this.safeNumber(rawNetwork, 'maxWithdrawQuantity'),
},
},
'info': rawNetwork,
};
}
return this.safeCurrencyStructure({
'id': id,
'code': code,
'name': this.safeString(rawCurrency, 'coinFullName'),
'type': undefined,
'active': undefined,
'deposit': this.safeBool(rawCurrency, 'allowDeposit'),
'withdraw': this.safeBool(rawCurrency, 'allowWithdraw'),
'fee': undefined,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': undefined,
},
},
'networks': networks,
'info': rawCurrency,
});
}
/**
* @method
* @name toobit#fetchMarkets
* @description retrieves data on all markets for toobit
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#exchange-information
* @see https://toobit-docs.github.io/apidocs/usdt_swap/v1/en/#exchange-information
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
let response = this.safeDict(this.options, 'exchangeInfo');
if (response !== undefined) {
this.options['exchangeInfo'] = undefined; // reset it to avoid using old cached data
}
else {
response = await this.commonGetApiV1ExchangeInfo(params);
}
//
// {
// "timezone": "UTC",
// "serverTime": "1755583099926",
// "brokerFilters": [],
// "symbols": [
// {
// "filters": [
// {
// "minPrice": "0.01",
// "maxPrice": "10000000.00000000",
// "tickSize": "0.01",
// "filterType": "PRICE_FILTER"
// },
// {
// "minQty": "0.0001",
// "maxQty": "4000",
// "stepSize": "0.0001",
// "filterType": "LOT_SIZE"
// },
// {
// "minNotional": "5",
// "filterType": "MIN_NOTIONAL"
// },
// {
// "minAmount": "5",
// "maxAmount": "6600000",
// "minBuyPrice": "0.01",
// "filterType": "TRADE_AMOUNT"
// },
// {
// "maxSellPrice": "99999999",
// "buyPriceUpRate": "0.1",
// "sellPriceDownRate": "0.1",
// "filterType": "LIMIT_TRADING"
// },
// {
// "buyPriceUpRate": "0.1",
// "sellPriceDownRate": "0.1",
// "filterType": "MARKET_TRADING"
// },
// {
// "noAllowMarketStartTime": "0",
// "noAllowMarketEndTime": "0",
// "limitOrderStartTime": "0",
// "limitOrderEndTime": "0",
// "limitMinPrice": "0",
// "limitMaxPrice": "0",
// "filterType": "OPEN_QUOTE"
// }
// ],
// "exchangeId": "301",
// "symbol": "ETHUSDT",
// "symbolName": "ETHUSDT",
// "status": "TRADING",
// "baseAsset": "ETH",
// "baseAssetName": "ETH",
// "baseAssetPrecision": "0.0001",
// "quoteAsset": "USDT",
// "quoteAssetName": "USDT",
// "quotePrecision": "0.01",
// "icebergAllowed": false,
// "isAggregate": false,
// "allowMargin": true,
// }
// ],
// "options": [],
// "contracts": [
// {
// "filters": [ ... ],
// "exchangeId": "301",
// "symbol": "BTC-SWAP-USDT",
// "symbolName": "BTC-SWAP-USDTUSDT",
// "status": "TRADING",
// "baseAsset": "BTC-SWAP-USDT",
// "baseAssetPrecision": "0.001",
// "quoteAsset": "USDT",
// "quoteAssetPrecision": "0.1",
// "icebergAllowed": false,
// "inverse": false,
// "index": "BTC",
// "indexToken": "BTCUSDT",
// "marginToken": "USDT",
// "marginPrecision": "0.0001",
// "contractMultiplier": "0.001",
// "underlying": "BTC",
// "riskLimits": [
// {
// "riskLimitId": "200020911",
// "quantity": "42000.0",
// "initialMargin": "0.02",
// "maintMargin": "0.01",
// "isWhite": false
// },
// {
// "riskLimitId": "200020912",
// "quantity": "84000.0",
// "initialMargin": "0.04",
// "maintMargin": "0.02",
// "isWhite": false
// },
// ...
// ]
// },
// ],
// "coins": [
// {
// "orgId": "9001",
// "coinId": "TCOM",
// "coinName": "TCOM",
// "coinFullName": "TCOM",
// "allowWithdraw": true,
// "allowDeposit": true,
// "chainTypes": [
// {
// "chainType": "BSC",
// "withdrawFee": "49.55478",
// "minWithdrawQuantity": "77",
// "maxWithdrawQuantity": "0",
// "minDepositQuantity": "48",
// "allowDeposit": true,
// "allowWithdraw": false
// }
// ],
// "isVirtual": false
// },
// ...
//
const symbols = this.safeList(response, 'symbols', []);
const contracts = this.safeList(response, 'contracts', []);
const all = this.arrayConcat(symbols, contracts);
const result = [];
for (let i = 0; i < all.length; i++) {
const market = all[i];
const parsed = this.parseMarket(market);
result.push(parsed);
}
return result;
}
parseMarket(market) {
const id = this.safeString(market, 'symbol');
const baseId = this.safeString(market, 'baseAsset');
const quoteId = this.safeString(market, 'quoteAsset');
const baseParts = baseId.split('-');
const baseIdClean = baseParts[0];
const base = this.safeCurrencyCode(baseIdClean);
const quote = this.safeCurrencyCode(quoteId);
const settleId = this.safeString(market, 'marginToken');
const settle = this.safeCurrencyCode(settleId);
const status = this.safeString(market, 'status');
const active = (status === 'TRADING');
const filters = this.safeList(market, 'filters', []);
const filtersByType = this.indexBy(filters, 'filterType');
const priceFilter = this.safeDict(filtersByType, 'PRICE_FILTER', {});
const lotSizeFilter = this.safeDict(filtersByType, 'LOT_SIZE', {});
const minNotionalFilter = this.safeDict(filtersByType, 'MIN_NOTIONAL', {});
let symbol = base + '/' + quote;
const isContract = ('contractMultiplier' in market);
const inverse = this.safeBool2(market, 'isInverse', 'inverse');
if (isContract) {
symbol += ':' + settle;
}
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': isContract ? 'swap' : 'spot',
'spot': !isContract,
'margin': false,
'swap': isContract,
'future': false,
'option': false,
'active': active,
'contract': isContract,
'linear': isContract ? !inverse : undefined,
'inverse': isContract ? inverse : undefined,
'contractSize': this.safeNumber(market, 'contractMultiplier'),
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.safeNumber(lotSizeFilter, 'stepSize'),
'price': this.safeNumber(priceFilter, 'tickSize'),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber(lotSizeFilter, 'minQty'),
'max': this.safeNumber(lotSizeFilter, 'maxQty'),
},
'price': {
'min': this.safeNumber(priceFilter, 'minPrice'),
'max': this.safeNumber(priceFilter, 'maxPrice'),
},
'cost': {
'min': this.safeNumber(minNotionalFilter, 'minNotional'),
'max': undefined,
},
},
'created': undefined,
'info': market,
});
}
/**
* @method
* @name toobit#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#order-book
* @see https://toobit-docs.github.io/apidocs/usdt_swap/v1/en/#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);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.commonGetQuoteV1Depth(this.extend(request, params));
//
// {
// "t": "1755593995237",
// "b": [
// [
// "115186.47",
// "4.184864"
// ],
// [
// "115186.46",
// "0.002756"
// ],
// ...
// ],
// "a": [
// [
// "115186.48",
// "6.137369"
// ],
// [
// "115186.49",
// "0.002914"
// ],
// ...
// ]
// }
//
const timestamp = this.safeInteger(response, 't');
return this.parseOrderBook(response, market['symbol'], timestamp, 'b', 'a');
}
/**
* @method
* @name toobit#fetchTrades
* @description get a list of the most recent trades for a particular symbol
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#recent-trades-list
* @see https://toobit-docs.github.io/apidocs/usdt_swap/v1/en/#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 number 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);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.commonGetQuoteV1Trades(this.extend(request, params));
//
// [
// {
// "t": "1755594277287",
// "p": "115276.99",
// "q": "0.001508",
// "ibm": true
// },
// ]
//
return this.parseTrades(response, market, since, limit);
}
parseTrade(trade, market = undefined) {
//
// fetchTrades
//
// {
// "t": "1755594277287",
// "p": "115276.99",
// "q": "0.001508",
// "ibm": true
// },
// // watchTrades have also an additional fields:
// "v": "4864732022868004630", // trade id
// "m": true, // is the buyer taker
//
// fetchMyTrades
//
// {
// "id": "2024934575206059008",
// "symbol": "ETHUSDT",
// "orderId": "2024934575097029888",
// "ticketId": "4864450547563401875",
// "price": "4641.21",
// "qty": "0.001",
// "time": "1756127012094",
// "isMaker": false,
// "commission": "0.00464121",
// "commissionAsset": "USDT",
// "makerRebate": "0",
// "symbolName": "ETHUSDT", // only in SPOT
// "isBuyer": false, // only in SPOT
// "feeAmount": "0.00464121", // only in SPOT
// "feeCoinId": "USDT", // only in SPOT
// "fee": { // only in SPOT
// "feeCoinId": "USDT",
// "feeCoinName": "USDT",
// "fee": "0.00464121"
// },
// "type": "LIMIT", // only in CONTRACT
// "side": "BUY_OPEN", // only in CONTRACT
// "realizedPnl": "0", // only in CONTRACT
// },
//
const timestamp = this.safeInteger2(trade, 't', 'time');
const priceString = this.safeString2(trade, 'p', 'price');
const amountString = this.safeString2(trade, 'q', 'qty');
const isBuyer = this.safeBool(trade, 'isBuyer');
let side = undefined;
let isBuyerMaker = this.safeBool(trade, 'ibm');
if (isBuyerMaker === undefined) {
const isBuyerTaker = this.safeBool(trade, 'm');
if (isBuyerTaker !== undefined) {
isBuyerMaker = !isBuyerTaker;
}
}
if (isBuyerMaker !== undefined) {
if (isBuyerMaker) {
side = 'sell';
}
else {
side = 'buy';
}
}
else {
if (isBuyer) {
side = 'buy';
}
else {
side = 'sell';
}
}
const feeCurrencyId = this.safeString(trade, 'feeCoinId');
const feeAmount = this.safeString(trade, 'feeAmount');
let fee = undefined;
if (feeAmount !== undefined) {
fee = {
'currency': this.safeCurrencyCode(feeCurrencyId),
'cost': feeAmount,
};
}
const isMaker = this.safeBool(trade, 'isMaker');
let takerOrMaker = undefined;
if (isMaker !== undefined) {
takerOrMaker = isMaker ? 'maker' : 'taker';
}
market = this.safeMarket(undefined, market);
const symbol = market['symbol'];
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'id': this.safeString2(trade, 'id', 'v'),
'order': this.safeString(trade, 'orderId'),
'type': undefined,
'side': side,
'amount': amountString,
'price': priceString,
'cost': undefined,
'takerOrMaker': takerOrMaker,
'fee': fee,
}, market);
}
/**
* @method
* @name toobit#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#kline-candlestick-data
* @see https://toobit-docs.github.io/apidocs/usdt_swap/v1/en/#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
* @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 request = {
'symbol': market['id'],
'interval': this.safeString(this.timeframes, timeframe, timeframe),
};
if (since !== undefined) {
request['startTime'] = since;
}
const until = this.safeInteger(params, 'until');
if (until !== undefined) {
params = this.omit(params, 'until');
request['endTime'] = until;
}
if (limit !== undefined) {
request['limit'] = limit;
}
let response = undefined;
let endpoint = undefined;
[endpoint, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'price');
if (endpoint === 'index') {
response = await this.commonGetQuoteV1IndexKlines(this.extend(request, params));
//
// {
// "code": 200,
// "data": [
// {
// "t": 1669155300000,//time
// "s": "ETHUSDT",// symbol
// "sn": "ETHUSDT",//symbol name
// "c": "1127.1",//Close price
// "h": "1130.81",//High price
// "l": "1126.17",//Low price
// "o": "1130.8",//Open price
// "v": "0"//Volume
// },
// {
// "t": 1669156200000,
// "s": "ETHUSDT",
// "sn": "ETHUSDT",
// "c": "1129.44",
// "h": "1129.54",
// "l": "1127.1",
// "o": "1127.1",
// "v": "0"
// }
// ]
// }
//
}
else if (endpoint === 'mark') {
response = await this.commonGetQuoteV1MarkPriceKlines(this.extend(request, params));
//
// {
// "code": 200,
// "data": [
// {
// "symbol": "BTCUSDT",// Symbol
// "time": 1670157900000,// time
// "low": "16991.14096",//Low price
// "open": "16991.78288",//Open price
// "high": "16996.30641",// High prce
// "close": "16996.30641",// Close price
// "volume": "0",// Volume
// "curId": 1670157900000
// }
// ]
// }
//
}
else {
response = await this.commonGetQuoteV1Klines(this.extend(request, params));
//
// [
// [
// 1755540660000,
// "116399.99",
// "116399.99",
// "116360.09",
// "116360.1",
// "2.236869",
// 0,
// "260303.79722607",
// 22,
// "2.221061",
// "258464.10338267"
// ],
// ...
//
}
return this.parseOHLCVs(response, market, timeframe, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
return [
this.safeIntegerN(ohlcv, [0