UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

1,162 lines (1,159 loc) • 256 kB
// ---------------------------------------------------------------------------- // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code // EDIT THE CORRESPONDENT .ts FILE INSTEAD // --------------------------------------------------------------------------- import Exchange from './abstract/coincatch.js'; import { ArgumentsRequired, AuthenticationError, BadRequest, BadSymbol, DDoSProtection, ExchangeError, InvalidNonce, InsufficientFunds, InvalidOrder, NotSupported, OnMaintenance, OrderNotFound, PermissionDenied, RateLimitExceeded } 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 coincatch * @augments Exchange */ export default class coincatch extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'coincatch', 'name': 'CoinCatch', 'countries': ['VG'], 'rateLimit': 50, 'version': 'v1', 'certified': false, 'pro': true, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': true, 'future': false, 'option': false, 'addMargin': true, 'cancelAllOrders': true, 'cancelAllOrdersAfter': false, 'cancelOrder': true, 'cancelOrders': true, 'cancelWithdraw': false, 'closePosition': false, 'createConvertTrade': false, 'createDepositAddress': false, 'createLimitBuyOrder': true, 'createLimitSellOrder': true, 'createMarketBuyOrder': true, 'createMarketBuyOrderWithCost': true, 'createMarketOrder': true, 'createMarketOrderWithCost': false, 'createMarketSellOrder': true, 'createMarketSellOrderWithCost': false, 'createOrder': true, 'createOrders': true, 'createOrderWithTakeProfitAndStopLoss': true, 'createPostOnlyOrder': true, 'createReduceOnlyOrder': true, 'createStopLimitOrder': true, 'createStopLossOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'createTakeProfitOrder': true, 'createTrailingAmountOrder': false, 'createTrailingPercentOrder': false, 'createTriggerOrder': true, 'fetchAccounts': false, 'fetchBalance': true, 'fetchCanceledAndClosedOrders': true, 'fetchCanceledOrders': false, 'fetchClosedOrder': false, 'fetchClosedOrders': false, 'fetchConvertCurrencies': false, 'fetchConvertQuote': false, 'fetchConvertTrade': false, 'fetchConvertTradeHistory': false, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchDepositsWithdrawals': false, 'fetchDepositWithdrawFees': true, 'fetchFundingHistory': false, 'fetchFundingRate': true, 'fetchFundingRateHistory': true, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchLedger': true, 'fetchLeverage': true, 'fetchLeverageTiers': false, 'fetchMarginAdjustmentHistory': false, 'fetchMarginMode': true, 'fetchMarkets': true, 'fetchMarkOHLCV': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrder': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': false, 'fetchOrderTrades': true, 'fetchPosition': true, 'fetchPositionHistory': false, 'fetchPositionMode': true, 'fetchPositions': true, 'fetchPositionsForSymbol': true, 'fetchPositionsHistory': false, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransactions': false, 'fetchTransfers': false, 'fetchWithdrawals': true, 'reduceMargin': true, 'sandbox': false, 'setLeverage': true, 'setMargin': false, 'setMarginMode': true, 'setPositionMode': true, 'transfer': false, 'withdraw': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15': '15m', '30': '30m', '1h': '1H', '2h': '2H', '4h': '4H', '6h': '6H', '12h': '12H', '1d': '1D', '3d': '3D', '1w': '1W', '1M': '1M', }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/3d49065f-f05d-4573-88a2-1b5201ec6ff3', 'api': { 'public': 'https://api.coincatch.com', 'private': 'https://api.coincatch.com', }, 'www': 'https://www.coincatch.com/', 'doc': 'https://coincatch.github.io/github.io/en/', 'fees': 'https://www.coincatch.com/en/rate/', 'referral': { 'url': 'https://partner.coincatch.cc/bg/92hy70391729607848548', 'discount': 0.1, }, }, 'api': { 'public': { 'get': { 'api/spot/v1/public/time': 1, 'api/spot/v1/public/currencies': 20 / 3, 'api/spot/v1/market/ticker': 1, 'api/spot/v1/market/tickers': 1, 'api/spot/v1/market/fills': 2, 'api/spot/v1/market/fills-history': 2, 'api/spot/v1/market/candles': 1, 'api/spot/v1/market/history-candles': 1, 'api/spot/v1/market/depth': 1, 'api/spot/v1/market/merge-depth': 1, 'api/mix/v1/market/contracts': 1, 'api/mix/v1/market/merge-depth': 1, 'api/mix/v1/market/depth': 1, 'api/mix/v1/market/ticker': 1, 'api/mix/v1/market/tickers': 1, 'api/mix/v1/market/fills': 1, 'api/mix/v1/market/fills-history': 1, 'api/mix/v1/market/candles': 1, 'pi/mix/v1/market/index': 1, 'api/mix/v1/market/funding-time': 1, 'api/mix/v1/market/history-fundRate': 1, 'api/mix/v1/market/current-fundRate': 1, 'api/mix/v1/market/open-interest': 1, 'api/mix/v1/market/mark-price': 1, 'api/mix/v1/market/symbol-leverage': 1, 'api/mix/v1/market/queryPositionLever': 1, }, }, 'private': { 'get': { 'api/spot/v1/wallet/deposit-address': 4, 'pi/spot/v1/wallet/withdrawal-list': 1, 'api/spot/v1/wallet/withdrawal-list-v2': 1, 'api/spot/v1/wallet/deposit-list': 1, 'api/spot/v1/account/getInfo': 1, 'api/spot/v1/account/assets': 2, 'api/spot/v1/account/transferRecords': 1, 'api/mix/v1/account/account': 2, 'api/mix/v1/account/accounts': 2, 'api/mix/v1/position/singlePosition-v2': 2, 'api/mix/v1/position/allPosition-v2': 4, 'api/mix/v1/account/accountBill': 2, 'api/mix/v1/account/accountBusinessBill': 4, 'api/mix/v1/order/current': 1, 'api/mix/v1/order/marginCoinCurrent': 1, 'api/mix/v1/order/history': 2, 'api/mix/v1/order/historyProductType': 4, 'api/mix/v1/order/detail': 2, 'api/mix/v1/order/fills': 2, 'api/mix/v1/order/allFills': 2, 'api/mix/v1/plan/currentPlan': 1, 'api/mix/v1/plan/historyPlan': 2, // done }, 'post': { 'api/spot/v1/wallet/transfer-v2': 4, 'api/spot/v1/wallet/withdrawal-v2': 4, 'api/spot/v1/wallet/withdrawal-inner-v2': 1, 'api/spot/v1/account/bills': 2, 'api/spot/v1/trade/orders': 2, 'api/spot/v1/trade/batch-orders': { 'cost': 4, 'step': 10 }, 'api/spot/v1/trade/cancel-order': 1, 'api/spot/v1/trade/cancel-order-v2': 2, 'api/spot/v1/trade/cancel-symbol-order': 2, 'api/spot/v1/trade/cancel-batch-orders': 1, 'api/spot/v1/trade/cancel-batch-orders-v2': 1, 'api/spot/v1/trade/orderInfo': 1, 'api/spot/v1/trade/open-orders': 1, 'api/spot/v1/trade/history': 1, 'api/spot/v1/trade/fills': 1, 'api/spot/v1/plan/placePlan': 1, 'api/spot/v1/plan/modifyPlan': 1, 'api/spot/v1/plan/cancelPlan': 1, 'api/spot/v1/plan/currentPlan': 1, 'api/spot/v1/plan/historyPlan': 1, 'api/spot/v1/plan/batchCancelPlan': 2, 'api/mix/v1/account/open-count': 1, 'api/mix/v1/account/setLeverage': 4, 'api/mix/v1/account/setMargin': 4, 'api/mix/v1/account/setMarginMode': 4, 'api/mix/v1/account/setPositionMode': 4, 'api/mix/v1/order/placeOrder': 2, 'api/mix/v1/order/batch-orders': { 'cost': 4, 'step': 10 }, 'api/mix/v1/order/cancel-order': 2, 'api/mix/v1/order/cancel-batch-orders': 2, 'api/mix/v1/order/cancel-symbol-orders': 2, 'api/mix/v1/order/cancel-all-orders': 2, 'api/mix/v1/plan/placePlan': 2, 'api/mix/v1/plan/modifyPlan': 2, 'api/mix/v1/plan/modifyPlanPreset': 2, 'api/mix/v1/plan/placeTPSL': 2, 'api/mix/v1/plan/placeTrailStop': 2, 'api/mix/v1/plan/placePositionsTPSL': 2, 'api/mix/v1/plan/modifyTPSLPlan': 2, 'api/mix/v1/plan/cancelPlan': 2, 'api/mix/v1/plan/cancelSymbolPlan': 2, 'api/mix/v1/plan/cancelAllPlan': 2, // done }, }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'fees': { 'trading': { 'spot': { 'tierBased': false, 'percentage': true, 'feeSide': 'get', 'maker': this.parseNumber('0.001'), 'taker': this.parseNumber('0.001'), }, }, }, 'options': { 'brokerId': '47cfy', 'createMarketBuyOrderRequiresPrice': true, 'timeframes': { 'spot': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1day', '3d': '3day', '1w': '1week', '1M': '1M', }, 'swap': { '1m': '1m', '3m': '3m', '5m': '5m', '15': '15m', '30': '30m', '1h': '1H', '2h': '2H', '4h': '4H', '6h': '6H', '12h': '12H', '1d': '1D', '3d': '3D', '1w': '1W', '1M': '1M', }, }, 'currencyIdsListForParseMarket': undefined, 'broker': '', 'networks': { 'BTC': 'BITCOIN', 'ERC20': 'ERC20', 'TRC20': 'TRC20', 'BEP20': 'BEP20', 'ARB': 'ArbitrumOne', 'OPTIMISM': 'Optimism', 'LTC': 'LTC', 'BCH': 'BCH', 'ETC': 'ETC', 'SOL': 'SOL', 'NEO3': 'NEO3', 'STX': 'stacks', 'EGLD': 'Elrond', 'NEAR': 'NEARProtocol', 'ACA': 'AcalaToken', 'KLAY': 'Klaytn', 'FTM': 'Fantom', 'TERRA': 'Terra', 'WAVES': 'WAVES', 'TAO': 'TAO', 'SUI': 'SUI', 'SEI': 'SEI', 'RUNE': 'THORChain', 'ZIL': 'ZIL', 'SXP': 'Solar', 'FET': 'FET', 'AVAX': 'C-Chain', 'XRP': 'XRP', 'EOS': 'EOS', 'DOGE': 'DOGECOIN', 'CAP20': 'CAP20', 'MATIC': 'Polygon', 'CSPR': 'CSPR', 'GLMR': 'Moonbeam', 'MINA': 'MINA', 'CFX': 'CFX', 'STRAT': 'StratisEVM', 'TIA': 'Celestia', 'ChilizChain': 'ChilizChain', 'APT': 'Aptos', 'ONT': 'Ontology', 'ICP': 'ICP', 'ADA': 'Cardano', 'FIL': 'FIL', 'CELO': 'CELO', 'DOT': 'DOT', 'XLM': 'StellarLumens', 'ATOM': 'ATOM', 'CRO': 'CronosChain', }, 'networksById': { 'TRC20': 'TRC20', 'TRX(TRC20)': 'TRC20', 'ArbitrumOne': 'ARB', 'THORChain': 'RUNE', 'Solar': 'SXP', 'C-Chain': 'AVAX', 'CAP20': 'CAP20', 'CFXeSpace': 'CFX', 'CFX': 'CFX', 'StratisEVM': 'STRAT', 'ChilizChain': 'ChilizChain', 'StellarLumens': 'XLM', 'CronosChain': 'CRO', // todo check }, }, 'features': { 'default': { 'sandbox': false, 'createOrder': { 'marginMode': false, 'triggerPrice': true, 'triggerPriceType': { 'last': true, 'mark': true, 'index': false, }, 'triggerDirection': false, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'IOC': true, 'FOK': true, 'PO': true, 'GTD': false, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': true, 'marketBuyRequiresPrice': false, 'selfTradePrevention': false, 'iceberg': false, }, 'createOrders': { 'max': 50, }, 'fetchMyTrades': { 'marginMode': false, 'limit': 500, 'daysBack': 100000, 'untilDays': 100000, 'symbolRequired': true, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': 100, 'trigger': true, 'trailing': false, 'marketType': true, 'symbolRequired': false, }, 'fetchOrders': undefined, 'fetchClosedOrders': undefined, 'fetchOHLCV': { 'limit': 1000, }, }, 'spot': { 'extends': 'default', }, 'forDerivatives': { 'extends': 'default', 'createOrder': { // todo check 'attachedStopLossTakeProfit': { 'triggerPriceType': undefined, 'price': false, }, }, 'fetchMyTrades': { 'limit': 100, }, }, 'swap': { 'linear': { 'extends': 'forDerivatives', }, 'inverse': { 'extends': 'forDerivatives', }, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, 'commonCurrencies': {}, 'exceptions': { 'exact': { '22001': OrderNotFound, '429': DDoSProtection, '40001': AuthenticationError, '40002': AuthenticationError, '40003': AuthenticationError, '40005': InvalidNonce, '40006': AuthenticationError, '40007': BadRequest, '40008': InvalidNonce, '40009': AuthenticationError, '40011': AuthenticationError, '40012': AuthenticationError, '40013': ExchangeError, '40014': PermissionDenied, '40015': ExchangeError, '40016': PermissionDenied, '40017': ExchangeError, '40018': PermissionDenied, '40019': BadRequest, '40020': BadRequest, '40034': BadRequest, '400172': BadRequest, '40912': BadRequest, '40913': BadRequest, '40102': BadRequest, '40200': OnMaintenance, '40305': BadRequest, '40409': ExchangeError, '40704': ExchangeError, '40724': BadRequest, '40725': ExchangeError, '40762': InsufficientFunds, '40774': BadRequest, '40808': BadRequest, '43001': OrderNotFound, '43002': InvalidOrder, '43004': OrderNotFound, '43005': RateLimitExceeded, '43006': BadRequest, '43007': BadRequest, '43008': BadRequest, '43009': BadRequest, '43010': BadRequest, '43011': BadRequest, '43012': InsufficientFunds, '43117': InsufficientFunds, '43118': BadRequest, '43122': BadRequest, '45006': InsufficientFunds, '45110': BadRequest, // less than the minimum amount {0} {1} // {"code":"40913","msg":"orderId or clientOrderId must be passed one","requestTime":1726160988275,"data":null} }, 'broad': {}, }, 'precisionMode': TICK_SIZE, }); } calculateRateLimiterCost(api, method, path, params, config = {}) { const step = this.safeInteger(config, 'step'); const cost = this.safeInteger(config, 'cost', 1); const orders = this.safeList2(params, 'orderList', 'orderDataList', []); const ordersLength = orders.length; if ((step !== undefined) && (ordersLength > step)) { const numberOfSteps = Math.ceil(ordersLength / step); return cost * numberOfSteps; } else { return cost; } } /** * @method * @name coincatch#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @see https://coincatch.github.io/github.io/en/spot/#get-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.publicGetApiSpotV1PublicTime(params); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725046822028, // "data": "1725046822028" // } // return this.safeInteger(response, 'data'); } /** * @method * @name coincatch#fetchCurrencies * @description fetches all available currencies on an exchange * @see https://coincatch.github.io/github.io/en/spot/#get-coin-list * @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.publicGetApiSpotV1PublicCurrencies(params); const data = this.safeList(response, 'data', []); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725102364202, // "data": [ // { // "coinId": "1", // "coinName": "BTC", // "transfer": "true", // "chains": [ // { // "chainId": "10", // "chain": "BITCOIN", // "needTag": "false", // "withdrawable": "true", // "rechargeable": "true", // "withdrawFee": "0.0005", // "extraWithDrawFee": "0", // "depositConfirm": "1", // "withdrawConfirm": "1", // "minDepositAmount": "0.00001", // "minWithdrawAmount": "0.001", // "browserUrl": "https://blockchair.com/bitcoin/transaction/" // } // ] // }, // ... // ] // } // const result = {}; const currenciesIds = []; for (let i = 0; i < data.length; i++) { const currecy = data[i]; const currencyId = this.safeString(currecy, 'coinName'); currenciesIds.push(currencyId); const code = this.safeCurrencyCode(currencyId); const networks = this.safeList(currecy, 'chains'); const parsedNetworks = {}; for (let j = 0; j < networks.length; j++) { const network = networks[j]; const networkId = this.safeString(network, 'chain'); const networkCode = this.networkCodeToId(networkId); parsedNetworks[networkId] = { 'id': networkId, 'network': networkCode, 'limits': { 'deposit': { 'min': this.safeNumber(network, 'minDepositAmount'), 'max': undefined, }, 'withdraw': { 'min': this.safeNumber(network, 'minWithdrawAmount'), 'max': undefined, }, }, 'active': undefined, 'deposit': this.safeString(network, 'rechargeable') === 'true', 'withdraw': this.safeString(network, 'withdrawable') === 'true', 'fee': this.safeNumber(network, 'withdrawFee'), 'precision': undefined, 'info': network, }; } result[code] = this.safeCurrencyStructure({ 'id': currencyId, 'numericId': this.safeInteger(currecy, 'coinId'), 'code': code, 'precision': undefined, 'type': undefined, 'name': undefined, 'active': undefined, 'deposit': undefined, 'withdraw': undefined, 'fee': undefined, 'limits': { 'deposit': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, 'networks': parsedNetworks, 'info': currecy, }); } if (this.safeList(this.options, 'currencyIdsListForParseMarket') === undefined) { this.options['currencyIdsListForParseMarket'] = currenciesIds; } return result; } /** * @method * @name coincatch#fetchDepositWithdrawFees * @description fetch deposit and withdraw fees * @see https://coincatch.github.io/github.io/en/spot/#get-coin-list * @param {string[]} [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.publicGetApiSpotV1PublicCurrencies(params); const data = this.safeList(response, 'data', []); return this.parseDepositWithdrawFees(data, codes, 'coinName'); } parseDepositWithdrawFee(fee, currency = undefined) { // // { // "coinId":"1", // "coinName":"BTC", // "transfer":"true", // "chains":[ // { // "chain":null, // "needTag":"false", // "withdrawable":"true", // "rechargeAble":"true", // "withdrawFee":"0.005", // "depositConfirm":"1", // "withdrawConfirm":"1", // "minDepositAmount":"0.001", // "minWithdrawAmount":"0.001", // "browserUrl":"https://blockchair.com/bitcoin/testnet/transaction/" // } // ] // } // const chains = this.safeList(fee, 'chains', []); const chainsLength = chains.length; const result = { 'info': fee, 'withdraw': { 'fee': undefined, 'percentage': undefined, }, 'deposit': { 'fee': undefined, 'percentage': undefined, }, 'networks': {}, }; for (let i = 0; i < chainsLength; i++) { const chain = chains[i]; const networkId = this.safeString(chain, '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(chain, 'withdrawFee'), 'percentage': false }, }; if (chainsLength === 1) { result['withdraw']['fee'] = this.safeNumber(chain, 'withdrawFee'); result['withdraw']['percentage'] = false; } } return result; } /** * @method * @name coincatch#fetchMarkets * @description retrieves data on all markets for the exchange * @see https://coincatch.github.io/github.io/en/spot/#get-all-tickers * @see https://coincatch.github.io/github.io/en/mix/#get-all-symbols * @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 = await this.publicGetApiSpotV1MarketTickers(params); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725114040155, // "data": [ // { // "symbol": "BTCUSDT", // "high24h": "59461.34", // "low24h": "57723.23", // "close": "59056.02", // "quoteVol": "18240112.23368", // "baseVol": "309.05564", // "usdtVol": "18240112.2336744", // "ts": "1725114038951", // "buyOne": "59055.85", // "sellOne": "59057.45", // "bidSz": "0.0139", // "askSz": "0.0139", // "openUtc0": "59126.71", // "changeUtc": "-0.0012", // "change": "0.01662" // }, // ... // ] // } // if (this.safeList(this.options, 'currencyIdsListForParseMarket') === undefined) { await this.fetchCurrencies(); } const spotMarkets = this.safeList(response, 'data', []); const request = {}; let productType = undefined; [productType, params] = this.handleOptionAndParams(params, 'fetchMarkets', 'productType', productType); let swapMarkets = []; request['productType'] = 'umcbl'; response = await this.publicGetApiMixV1MarketContracts(this.extend(request, params)); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725297439225, // "data": [ // { // "symbol": "BTCUSDT_UMCBL", // "makerFeeRate": "0.0002", // "takerFeeRate": "0.0006", // "feeRateUpRatio": "0.005", // "openCostUpRatio": "0.01", // "quoteCoin": "USDT", // "baseCoin": "BTC", // "buyLimitPriceRatio": "0.01", // "sellLimitPriceRatio": "0.01", // "supportMarginCoins": [ "USDT" ], // "minTradeNum": "0.001", // "priceEndStep": "1", // "volumePlace": "3", // "pricePlace": "1", // "sizeMultiplier": "0.001", // "symbolType": "perpetual", // "symbolStatus": "normal", // "offTime": "-1", // "limitOpenTime": "-1", // "maintainTime": "", // "symbolName": "BTCUSDT", // "minTradeUSDT": null, // "maxPositionNum": null, // "maxOrderNum": null // } // ] // } // const swapUMCBL = this.safeList(response, 'data', []); request['productType'] = 'dmcbl'; response = await this.publicGetApiMixV1MarketContracts(this.extend(request, params)); // // { // "code":"00000", // "msg":"success", // "requestTime":1725297439646, // "data":[ // { // "symbol":"BTCUSD_DMCBL", // "makerFeeRate":"0.0002", // "takerFeeRate":"0.0006", // "feeRateUpRatio":"0.005", // "openCostUpRatio":"0.01", // "quoteCoin":"USD", // "baseCoin":"BTC", // "buyLimitPriceRatio":"0.01", // "sellLimitPriceRatio":"0.01", // "supportMarginCoins":[ // "BTC", // "ETH" // ], // "minTradeNum":"0.001", // "priceEndStep":"1", // "volumePlace":"3", // "pricePlace":"1", // "sizeMultiplier":"0.001", // "symbolType":"perpetual", // "symbolStatus":"normal", // "offTime":"-1", // "limitOpenTime":"-1", // "maintainTime":"", // "symbolName":"BTCUSD", // "minTradeUSDT":null, // "maxPositionNum":null, // "maxOrderNum":null // } // ] // } const swapDMCBL = this.safeList(response, 'data', []); const swapDMCBLExtended = []; for (let i = 0; i < swapDMCBL.length; i++) { const market = swapDMCBL[i]; const supportMarginCoins = this.safeList(market, 'supportMarginCoins', []); for (let j = 0; j < supportMarginCoins.length; j++) { const settle = supportMarginCoins[j]; const obj = { 'supportMarginCoins': [settle], }; swapDMCBLExtended.push(this.extend(market, obj)); } } swapMarkets = this.arrayConcat(swapUMCBL, swapDMCBLExtended); const markets = this.arrayConcat(spotMarkets, swapMarkets); return this.parseMarkets(markets); } parseMarket(market) { // // spot // { // "symbol": "BTCUSDT", // "high24h": "59461.34", // "low24h": "57723.23", // "close": "59056.02", // "quoteVol": "18240112.23368", // "baseVol": "309.05564", // "usdtVol": "18240112.2336744", // "ts": "1725114038951", // "buyOne": "59055.85", // "sellOne": "59057.45", // "bidSz": "0.0139", // "askSz": "0.0139", // "openUtc0": "59126.71", // "changeUtc": "-0.0012", // "change": "0.01662" // }, // // swap // { // "symbol": "BTCUSDT_UMCBL", // "makerFeeRate": "0.0002", // "takerFeeRate": "0.0006", // "feeRateUpRatio": "0.005", // "openCostUpRatio": "0.01", // "quoteCoin": "USDT", // "baseCoin": "BTC", // "buyLimitPriceRatio": "0.01", // "sellLimitPriceRatio": "0.01", // "supportMarginCoins": [ "USDT" ], // "minTradeNum": "0.001", // "priceEndStep": "1", // "volumePlace": "3", // "pricePlace": "1", // "sizeMultiplier": "0.001", // "symbolType": "perpetual", // "symbolStatus": "normal", // "offTime": "-1", // "limitOpenTime": "-1", // "maintainTime": "", // "symbolName": "BTCUSDT", // "minTradeUSDT": null, // "maxPositionNum": null, // "maxOrderNum": null // } // let marketId = this.safeString(market, 'symbol'); const tradingFees = this.safeDict(this.fees, 'trading'); const fees = this.safeDict(tradingFees, 'spot'); let baseId = this.safeString(market, 'baseCoin'); let quoteId = this.safeString(market, 'quoteCoin'); let settleId = undefined; let suffix = ''; let settle = undefined; let type = 'spot'; let isLinear = undefined; let isInverse = undefined; let subType = undefined; const isSpot = baseId === undefined; // for now spot markets have no properties baseCoin and quoteCoin if (isSpot) { const parsedMarketId = this.parseSpotMarketId(marketId); baseId = this.safeString(parsedMarketId, 'baseId'); quoteId = this.safeString(parsedMarketId, 'quoteId'); marketId += '_SPBL'; // spot markets should have current suffix } else { type = 'swap'; fees['taker'] = this.safeNumber(market, 'takerFeeRate'); fees['maker'] = this.safeNumber(market, 'makerFeeRate'); const supportMarginCoins = this.safeList(market, 'supportMarginCoins', []); settleId = this.safeString(supportMarginCoins, 0); settle = this.safeCurrencyCode(settleId); suffix = ':' + settle; isLinear = quoteId === settleId; // todo check isInverse = baseId === settleId; // todo check if (isLinear) { subType = 'linear'; } else if (isInverse) { subType = 'inverse'; } } const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const symbol = base + '/' + quote + suffix; const symbolStatus = this.safeString(market, 'symbolStatus'); const active = symbolStatus ? (symbolStatus === 'normal') : undefined; const volumePlace = this.safeString(market, 'volumePlace'); const amountPrecisionString = this.parsePrecision(volumePlace); const pricePlace = this.safeString(market, 'pricePlace'); const priceEndStep = this.safeString(market, 'priceEndStep'); const pricePrecisionString = Precise.stringMul(this.parsePrecision(pricePlace), priceEndStep); return this.safeMarketStructure({ 'id': marketId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'type': type, 'subType': subType, 'spot': isSpot, 'margin': isSpot ? false : undefined, 'swap': !isSpot, 'future': false, 'option': false, 'contract': !isSpot, 'settle': settle, 'settleId': settleId, 'contractSize': this.safeNumber(market, 'sizeMultiplier'), 'linear': isLinear, 'inverse': isInverse, 'taker': this.safeNumber(fees, 'taker'), 'maker': this.safeNumber(fees, 'maker'), 'percentage': this.safeBool(fees, 'percentage'), 'tierBased': this.safeBool(fees, 'tierBased'), 'feeSide': this.safeString(fees, 'feeSide'), 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber(amountPrecisionString), 'price': this.parseNumber(pricePrecisionString), }, 'limits': { 'amount': { 'min': this.safeNumber(market, 'minTradeNum'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'leverage': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'created': undefined, 'info': market, }); } parseSpotMarketId(marketId) { let baseId = undefined; let quoteId = undefined; const currencyIds = this.safeList(this.options, 'currencyIdsListForParseMarket', []); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const entryIndex = marketId.indexOf(currencyId); if (entryIndex > -1) { const restId = marketId.replace(currencyId, ''); if (entryIndex === 0) { baseId = currencyId; quoteId = restId; } else { baseId = restId; quoteId = currencyId; } break; } } const result = { 'baseId': baseId, 'quoteId': quoteId, }; return result; } /** * @method * @name coincatch#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://coincatch.github.io/github.io/en/spot/#get-single-ticker * @see https://coincatch.github.io/github.io/en/mix/#get-single-symbol-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); const request = { 'symbol': market['id'], }; let response = undefined; if (market['spot']) { response = await this.publicGetApiSpotV1MarketTicker(this.extend(request, params)); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725132487751, // "data": { // "symbol": "ETHUSDT", // "high24h": "2533.76", // "low24h": "2492.72", // "close": "2499.76", // "quoteVol": "21457850.7442", // "baseVol": "8517.1869", // "usdtVol": "21457850.744163", // "ts": "1725132487476", // "buyOne": "2499.75", // "sellOne": "2499.76", // "bidSz": "0.5311", // "askSz": "4.5806", // "openUtc0": "2525.69", // "changeUtc": "-0.01027", // "change": "-0.00772" // } // } // } else if (market['swap']) { response = await this.publicGetApiMixV1MarketTicker(this.extend(request, params)); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725316687174, // "data": { // "symbol": "ETHUSDT_UMCBL", // "last": "2540.6", // "bestAsk": "2540.71", // "bestBid": "2540.38", // "bidSz": "12.1", // "askSz": "20", // "high24h": "2563.91", // "low24h": "2398.3", // "timestamp": "1725316687177", // "priceChangePercent": "0.01134", // "baseVolume": "706928.96", // "quoteVolume": "1756401737.8766", // "usdtVolume": "1756401737.8766", // "openUtc": "2424.49", // "chgUtc": "0.04789", // "indexPrice": "2541.977142", // "fundingRate": "0.00006", // "holdingAmount": "144688.49", // "deliveryStartTime": null, // "deliveryTime": null, // "deliveryStatus": "normal" // } // } // } else { throw new NotSupported(this.id + ' ' + 'fetchTicker() is not supported for ' + market['type'] + ' type of markets'); } const data = this.safeDict(response, 'data', {}); return this.parseTicker(data, market); } /** * @method * @name coincatch#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://coincatch.github.io/github.io/en/spot/#get-all-tickers * @see https://coincatch.github.io/github.io/en/mix/#get-all-symbol-ticker * @param {string[]} [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 * @param {string} [params.type] 'spot' or 'swap' (default 'spot') * @param {string} [params.productType] 'umcbl' or 'dmcbl' (default 'umcbl') - USDT perpetual contract or Universal margin perpetual contract * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async fetchTickers(symbols = undefined, params = {}) { const methodName = 'fetchTickers'; await this.loadMarkets(); symbols = this.marketSymbols(symbols, undefined, true, true); const market = this.getMarketFromSymbols(symbols); let marketType = 'spot'; [marketType, params] = this.handleMarketTypeAndParams(methodName, market, params, marketType); let response = undefined; if (marketType === 'spot') { response = await this.publicGetApiSpotV1MarketTickers(params); // // { // "code": "00000", // "msg": "success", // "requestTime": 1725114040155, // "data": [ // {