UNPKG

ccxt

Version:

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

1,068 lines (1,066 loc) • 97.4 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/coinsph.js'; import { ArgumentsRequired, AuthenticationError, BadRequest, BadResponse, BadSymbol, DuplicateOrderId, ExchangeError, ExchangeNotAvailable, InvalidAddress, InvalidOrder, InsufficientFunds, NotSupported, OrderImmediatelyFillable, OrderNotFound, PermissionDenied, RateLimitExceeded } from './base/errors.js'; import { TICK_SIZE } from './base/functions/number.js'; import { Precise } from './base/Precise.js'; import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; /** * @class coinsph * @augments Exchange */ export default class coinsph extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'coinsph', 'name': 'Coins.ph', 'countries': ['PH'], 'version': 'v1', 'rateLimit': 50, 'certified': false, 'pro': false, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'addMargin': false, 'cancelAllOrders': true, 'cancelOrder': true, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, 'createMarketBuyOrderWithCost': true, 'createMarketOrderWithCost': false, 'createMarketSellOrderWithCost': false, 'createOrder': true, 'createPostOnlyOrder': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'deposit': true, 'editOrder': false, 'fetchAccounts': false, 'fetchBalance': true, 'fetchBidsAsks': false, 'fetchBorrowInterest': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchCanceledOrders': false, 'fetchClosedOrder': false, 'fetchClosedOrders': true, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchCurrencies': true, 'fetchDeposit': undefined, 'fetchDepositAddress': true, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, 'fetchDeposits': true, 'fetchDepositWithdrawFee': false, 'fetchDepositWithdrawFees': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchL3OrderBook': false, 'fetchLedger': false, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrder': undefined, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrderBooks': false, 'fetchOrders': false, 'fetchOrderTrades': true, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, 'fetchPositions': false, 'fetchPositionsForSymbol': false, 'fetchPositionsHistory': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchTradingLimits': false, 'fetchTransactionFee': false, 'fetchTransactionFees': false, 'fetchTransactions': false, 'fetchTransfers': false, 'fetchWithdrawal': undefined, 'fetchWithdrawals': true, 'fetchWithdrawalWhitelist': false, 'reduceMargin': false, 'repayCrossMargin': false, 'repayIsolatedMargin': false, 'setLeverage': false, 'setMargin': false, 'setMarginMode': false, 'setPositionMode': false, 'signIn': false, 'transfer': false, 'withdraw': true, 'ws': false, }, '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', '3d': '3d', '1w': '1w', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/225719995-48ab2026-4ddb-496c-9da7-0d7566617c9b.jpg', 'api': { 'public': 'https://api.pro.coins.ph', 'private': 'https://api.pro.coins.ph', }, 'www': 'https://coins.ph/', 'doc': [ 'https://coins-docs.github.io/rest-api', ], 'fees': 'https://support.coins.ph/hc/en-us/sections/4407198694681-Limits-Fees', }, 'api': { 'public': { 'get': { 'openapi/v1/ping': 1, 'openapi/v1/time': 1, // cost 1 if 'symbol' param defined (one market symbol) or if 'symbols' param is a list of 1-20 market symbols // cost 20 if 'symbols' param is a list of 21-100 market symbols // cost 40 if 'symbols' param is a list of 101 or more market symbols or if both 'symbol' and 'symbols' params are omited 'openapi/quote/v1/ticker/24hr': { 'cost': 1, 'noSymbolAndNoSymbols': 40, 'byNumberOfSymbols': [[101, 40], [21, 20], [0, 1]] }, // cost 1 if 'symbol' param defined (one market symbol) // cost 2 if 'symbols' param is a list of 1 or more market symbols or if both 'symbol' and 'symbols' params are omited 'openapi/quote/v1/ticker/price': { 'cost': 1, 'noSymbol': 2 }, // cost 1 if 'symbol' param defined (one market symbol) // cost 2 if 'symbols' param is a list of 1 or more market symbols or if both 'symbol' and 'symbols' params are omited 'openapi/quote/v1/ticker/bookTicker': { 'cost': 1, 'noSymbol': 2 }, 'openapi/v1/exchangeInfo': 10, // cost 1 if limit <= 100; 5 if limit > 100. 'openapi/quote/v1/depth': { 'cost': 1, 'byLimit': [[101, 5], [0, 1]] }, 'openapi/quote/v1/klines': 1, 'openapi/quote/v1/trades': 1, 'openapi/v1/pairs': 1, 'openapi/quote/v1/avgPrice': 1, }, }, 'private': { 'get': { 'openapi/wallet/v1/config/getall': 10, 'openapi/wallet/v1/deposit/address': 10, 'openapi/wallet/v1/deposit/history': 1, 'openapi/wallet/v1/withdraw/history': 1, 'openapi/v1/account': 10, // cost 3 for a single symbol; 40 when the symbol parameter is omitted 'openapi/v1/openOrders': { 'cost': 3, 'noSymbol': 40 }, 'openapi/v1/asset/tradeFee': 1, 'openapi/v1/order': 2, // cost 10 with symbol, 40 when the symbol parameter is omitted; 'openapi/v1/historyOrders': { 'cost': 10, 'noSymbol': 40 }, 'openapi/v1/myTrades': 10, 'openapi/v1/capital/deposit/history': 1, 'openapi/v1/capital/withdraw/history': 1, 'openapi/v3/payment-request/get-payment-request': 1, 'merchant-api/v1/get-invoices': 1, 'openapi/account/v3/crypto-accounts': 1, 'openapi/transfer/v3/transfers/{id}': 1, }, 'post': { 'openapi/wallet/v1/withdraw/apply': 600, 'openapi/v1/order/test': 1, 'openapi/v1/order': 1, 'openapi/v1/capital/withdraw/apply': 1, 'openapi/v1/capital/deposit/apply': 1, 'openapi/v3/payment-request/payment-requests': 1, 'openapi/v3/payment-request/delete-payment-request': 1, 'openapi/v3/payment-request/payment-request-reminder': 1, 'openapi/v1/userDataStream': 1, 'merchant-api/v1/invoices': 1, 'merchant-api/v1/invoices-cancel': 1, 'openapi/convert/v1/get-supported-trading-pairs': 1, 'openapi/convert/v1/get-quote': 1, 'openapi/convert/v1/accpet-quote': 1, 'openapi/fiat/v1/support-channel': 1, 'openapi/fiat/v1/cash-out': 1, 'openapi/fiat/v1/history': 1, 'openapi/migration/v4/sellorder': 1, 'openapi/migration/v4/validate-field': 1, 'openapi/transfer/v3/transfers': 1, }, 'delete': { 'openapi/v1/order': 1, 'openapi/v1/openOrders': 1, 'openapi/v1/userDataStream': 1, }, }, }, 'fees': { // todo: zero fees for USDT, ETH and BTC markets till 2023-04-02 'trading': { 'feeSide': 'get', 'tierBased': true, 'percentage': true, 'maker': this.parseNumber('0.0025'), 'taker': this.parseNumber('0.003'), 'tiers': { 'taker': [ [this.parseNumber('0'), this.parseNumber('0.003')], [this.parseNumber('500000'), this.parseNumber('0.0027')], [this.parseNumber('1000000'), this.parseNumber('0.0024')], [this.parseNumber('2500000'), this.parseNumber('0.002')], [this.parseNumber('5000000'), this.parseNumber('0.0018')], [this.parseNumber('10000000'), this.parseNumber('0.0015')], [this.parseNumber('100000000'), this.parseNumber('0.0012')], [this.parseNumber('500000000'), this.parseNumber('0.0009')], [this.parseNumber('1000000000'), this.parseNumber('0.0007')], [this.parseNumber('2500000000'), this.parseNumber('0.0005')], ], 'maker': [ [this.parseNumber('0'), this.parseNumber('0.0025')], [this.parseNumber('500000'), this.parseNumber('0.0022')], [this.parseNumber('1000000'), this.parseNumber('0.0018')], [this.parseNumber('2500000'), this.parseNumber('0.0015')], [this.parseNumber('5000000'), this.parseNumber('0.0012')], [this.parseNumber('10000000'), this.parseNumber('0.001')], [this.parseNumber('100000000'), this.parseNumber('0.0008')], [this.parseNumber('500000000'), this.parseNumber('0.0007')], [this.parseNumber('1000000000'), this.parseNumber('0.0006')], [this.parseNumber('2500000000'), this.parseNumber('0.0005')], ], }, }, }, 'precisionMode': TICK_SIZE, // exchange-specific options 'options': { 'createMarketBuyOrderRequiresPrice': true, 'withdraw': { 'warning': false, }, 'deposit': { 'warning': false, }, 'createOrder': { 'timeInForce': 'GTC', 'newOrderRespType': { 'market': 'FULL', 'limit': 'FULL', // we change it from 'ACK' by default to 'FULL' }, }, 'fetchTicker': { 'method': 'publicGetOpenapiQuoteV1Ticker24hr', // publicGetOpenapiQuoteV1TickerPrice, publicGetOpenapiQuoteV1TickerBookTicker }, 'fetchTickers': { 'method': 'publicGetOpenapiQuoteV1Ticker24hr', // publicGetOpenapiQuoteV1TickerPrice, publicGetOpenapiQuoteV1TickerBookTicker }, 'networks': { // all networks: 'ETH', 'TRX', 'BSC', 'ARBITRUM', 'RON', 'BTC', 'XRP' // you can call api privateGetOpenapiWalletV1ConfigGetall to check which network is supported for the currency 'TRC20': 'TRX', 'ERC20': 'ETH', 'BEP20': 'BSC', 'ARB': 'ARBITRUM', }, }, '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': false, 'GTD': false, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': true, 'marketBuyRequiresPrice': false, 'selfTradePrevention': true, 'iceberg': false, }, 'createOrders': undefined, 'fetchMyTrades': { 'marginMode': false, 'limit': 1000, 'daysBack': 100000, 'untilDays': 100000, 'symbolRequired': true, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': undefined, 'fetchClosedOrders': { 'marginMode': false, 'limit': 1000, 'daysBack': 100000, 'daysBackCanceled': 1, 'untilDays': 100000, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOHLCV': { 'limit': 1000, }, }, 'swap': { 'linear': undefined, 'inverse': undefined, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, // https://coins-docs.github.io/errors/ 'exceptions': { 'exact': { '-1000': BadRequest, '-1001': BadRequest, '-1002': AuthenticationError, '-1003': RateLimitExceeded, '-1004': InvalidOrder, '-1006': BadResponse, '-1007': BadResponse, '-1014': InvalidOrder, '-1015': RateLimitExceeded, '-1016': NotSupported, '-1020': NotSupported, '-1021': BadRequest, '-1022': BadRequest, '-1023': AuthenticationError, '-1024': BadRequest, '-1025': BadRequest, '-1030': ExchangeError, '-1100': BadRequest, '-1101': BadRequest, '-1102': BadRequest, '-1103': BadRequest, '-1104': BadRequest, '-1105': BadRequest, '-1106': BadRequest, '-1111': BadRequest, '-1112': BadResponse, '-1114': BadRequest, '-1115': InvalidOrder, '-1116': InvalidOrder, '-1117': InvalidOrder, '-1118': InvalidOrder, '-1119': InvalidOrder, '-1120': BadRequest, '-1121': BadSymbol, '-1122': InvalidOrder, '-1125': BadRequest, '-1127': BadRequest, '-1128': BadRequest, '-1130': BadRequest, '-1131': InsufficientFunds, '-1132': InvalidOrder, '-1133': InvalidOrder, '-1134': InvalidOrder, '-1135': InvalidOrder, '-1136': InvalidOrder, '-1137': InvalidOrder, '-1138': InvalidOrder, '-1139': InvalidOrder, '-1140': InvalidOrder, '-1141': DuplicateOrderId, '-1142': InvalidOrder, '-1143': OrderNotFound, '-1144': InvalidOrder, '-1145': InvalidOrder, '-1146': InvalidOrder, '-1147': InvalidOrder, '-1148': InvalidOrder, '-1149': InvalidOrder, '-1150': InvalidOrder, '-1151': BadSymbol, '-1152': NotSupported, '-1153': AuthenticationError, '-1154': BadRequest, '-1155': BadRequest, '-1156': InvalidOrder, '-1157': BadSymbol, '-1158': InvalidOrder, '-1159': InvalidOrder, '-1160': BadRequest, '-1161': BadRequest, '-2010': InvalidOrder, '-2013': OrderNotFound, '-2011': BadRequest, '-2014': BadRequest, '-2015': AuthenticationError, '-2016': BadResponse, '-3126': InvalidOrder, '-3127': InvalidOrder, '-4001': BadRequest, '-100011': BadSymbol, '-100012': BadSymbol, '-30008': InsufficientFunds, '-30036': InsufficientFunds, '403': ExchangeNotAvailable, }, 'broad': { 'Unknown order sent': OrderNotFound, 'Duplicate order sent': DuplicateOrderId, 'Market is closed': BadSymbol, 'Account has insufficient balance for requested action': InsufficientFunds, 'Market orders are not supported for this symbol': BadSymbol, 'Iceberg orders are not supported for this symbol': BadSymbol, 'Stop loss orders are not supported for this symbol': BadSymbol, 'Stop loss limit orders are not supported for this symbol': BadSymbol, 'Take profit orders are not supported for this symbol': BadSymbol, 'Take profit limit orders are not supported for this symbol': BadSymbol, 'Price* QTY is zero or less': BadRequest, 'IcebergQty exceeds QTY': BadRequest, 'This action disabled is on this account': PermissionDenied, 'Unsupported order combination': InvalidOrder, 'Order would trigger immediately': InvalidOrder, 'Cancel order is invalid. Check origClOrdId and orderId': InvalidOrder, 'Order would immediately match and take': OrderImmediatelyFillable, 'PRICE_FILTER': InvalidOrder, 'LOT_SIZE': InvalidOrder, 'MIN_NOTIONAL': InvalidOrder, 'MAX_NUM_ORDERS': InvalidOrder, 'MAX_ALGO_ORDERS': InvalidOrder, 'BROKER_MAX_NUM_ORDERS': InvalidOrder, 'BROKER_MAX_ALGO_ORDERS': InvalidOrder, 'ICEBERG_PARTS': BadRequest, // Iceberg order would break into too many parts; icebergQty is too small. }, }, }); } /** * @method * @name coinsph#fetchCurrencies * @description fetches all available currencies on an exchange * @see https://docs.coins.ph/rest-api/#all-coins-information-user_data * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an associative dictionary of currencies */ async fetchCurrencies(params = {}) { if (!this.checkRequiredCredentials(false)) { return undefined; } const response = await this.privateGetOpenapiWalletV1ConfigGetall(params); // // [ // { // "coin": "PHP", // "name": "PHP", // "depositAllEnable": false, // "withdrawAllEnable": false, // "free": "0", // "locked": "0", // "transferPrecision": "2", // "transferMinQuantity": "0", // "networkList": [], // "legalMoney": true // }, // { // "coin": "USDT", // "name": "USDT", // "depositAllEnable": true, // "withdrawAllEnable": true, // "free": "0", // "locked": "0", // "transferPrecision": "8", // "transferMinQuantity": "0", // "networkList": [ // { // "addressRegex": "^0x[0-9a-fA-F]{40}$", // "memoRegex": " ", // "network": "ETH", // "name": "Ethereum (ERC20)", // "depositEnable": true, // "minConfirm": "12", // "unLockConfirm": "-1", // "withdrawDesc": "", // "withdrawEnable": true, // "withdrawFee": "6", // "withdrawIntegerMultiple": "0.000001", // "withdrawMax": "500000", // "withdrawMin": "10", // "sameAddress": false // }, // { // "addressRegex": "^T[0-9a-zA-Z]{33}$", // "memoRegex": "", // "network": "TRX", // "name": "TRON", // "depositEnable": true, // "minConfirm": "19", // "unLockConfirm": "-1", // "withdrawDesc": "", // "withdrawEnable": true, // "withdrawFee": "3", // "withdrawIntegerMultiple": "0.000001", // "withdrawMax": "1000000", // "withdrawMin": "20", // "sameAddress": false // } // ], // "legalMoney": false // } // ] // const result = {}; for (let i = 0; i < response.length; i++) { const entry = response[i]; const id = this.safeString(entry, 'coin'); const code = this.safeCurrencyCode(id); const isFiat = this.safeBool(entry, 'isLegalMoney'); const networkList = this.safeList(entry, 'networkList', []); const networks = {}; for (let j = 0; j < networkList.length; j++) { const networkItem = networkList[j]; const network = this.safeString(networkItem, 'network'); const networkCode = this.networkIdToCode(network); networks[networkCode] = { 'info': networkItem, 'id': network, 'network': networkCode, 'active': undefined, 'deposit': this.safeBool(networkItem, 'depositEnable'), 'withdraw': this.safeBool(networkItem, 'withdrawEnable'), 'fee': this.safeNumber(networkItem, 'withdrawFee'), 'precision': this.safeNumber(networkItem, 'withdrawIntegerMultiple'), 'limits': { 'withdraw': { 'min': this.safeNumber(networkItem, 'withdrawMin'), 'max': this.safeNumber(networkItem, 'withdrawMax'), }, 'deposit': { 'min': undefined, 'max': undefined, }, }, }; } result[code] = this.safeCurrencyStructure({ 'id': id, 'name': this.safeString(entry, 'name'), 'code': code, 'type': isFiat ? 'fiat' : 'crypto', 'precision': this.parseNumber(this.parsePrecision(this.safeString(entry, 'transferPrecision'))), 'info': entry, 'active': undefined, 'deposit': this.safeBool(entry, 'depositAllEnable'), 'withdraw': this.safeBool(entry, 'withdrawAllEnable'), 'networks': networks, 'fee': undefined, 'fees': undefined, 'limits': {}, }); } return result; } calculateRateLimiterCost(api, method, path, params, config = {}) { if (('noSymbol' in config) && !('symbol' in params)) { return config['noSymbol']; } else if (('noSymbolAndNoSymbols' in config) && !('symbol' in params) && !('symbols' in params)) { return config['noSymbolAndNoSymbols']; } else if (('byNumberOfSymbols' in config) && ('symbols' in params)) { const symbols = params['symbols']; const symbolsAmount = symbols.length; const byNumberOfSymbols = config['byNumberOfSymbols']; for (let i = 0; i < byNumberOfSymbols.length; i++) { const entry = byNumberOfSymbols[i]; if (symbolsAmount >= entry[0]) { return entry[1]; } } } 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); } /** * @method * @name coinsph#fetchStatus * @description the latest known information on the availability of the exchange API * @see https://coins-docs.github.io/rest-api/#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.publicGetOpenapiV1Ping(params); return { 'status': 'ok', 'updated': undefined, 'eta': undefined, 'url': undefined, 'info': response, }; } /** * @method * @name coinsph#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @see https://coins-docs.github.io/rest-api/#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.publicGetOpenapiV1Time(params); // // {"serverTime":1677705408268} // return this.safeInteger(response, 'serverTime'); } /** * @method * @name coinsph#fetchMarkets * @description retrieves data on all markets for coinsph * @see https://coins-docs.github.io/rest-api/#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 = {}) { const response = await this.publicGetOpenapiV1ExchangeInfo(params); // // { // "timezone": "UTC", // "serverTime": "1677449496897", // "exchangeFilters": [], // "symbols": [ // { // "symbol": "XRPPHP", // "status": "TRADING", // "baseAsset": "XRP", // "baseAssetPrecision": "2", // "quoteAsset": "PHP", // "quoteAssetPrecision": "4", // "orderTypes": [ // "LIMIT", // "MARKET", // "LIMIT_MAKER", // "STOP_LOSS_LIMIT", // "STOP_LOSS", // "TAKE_PROFIT_LIMIT", // "TAKE_PROFIT" // ], // "filters": [ // { // "minPrice": "0.01", // "maxPrice": "99999999.00000000", // "tickSize": "0.01", // "filterType": "PRICE_FILTER" // }, // { // "minQty": "0.01", // "maxQty": "99999999999.00000000", // "stepSize": "0.01", // "filterType": "LOT_SIZE" // }, // { minNotional: "50", filterType: "NOTIONAL" }, // { minNotional: "50", filterType: "MIN_NOTIONAL" }, // { // "priceUp": "99999999", // "priceDown": "0.01", // "filterType": "STATIC_PRICE_RANGE" // }, // { // "multiplierUp": "1.1", // "multiplierDown": "0.9", // "filterType": "PERCENT_PRICE_INDEX" // }, // { // "multiplierUp": "1.1", // "multiplierDown": "0.9", // "filterType": "PERCENT_PRICE_ORDER_SIZE" // }, // { maxNumOrders: "200", filterType: "MAX_NUM_ORDERS" }, // { maxNumAlgoOrders: "5", filterType: "MAX_NUM_ALGO_ORDERS" } // ] // }, // ] // } // const markets = this.safeList(response, 'symbols', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString(market, 'symbol'); const baseId = this.safeString(market, 'baseAsset'); const quoteId = this.safeString(market, 'quoteAsset'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const limits = this.indexBy(this.safeList(market, 'filters', []), 'filterType'); const amountLimits = this.safeValue(limits, 'LOT_SIZE', {}); const priceLimits = this.safeValue(limits, 'PRICE_FILTER', {}); const costLimits = this.safeValue(limits, 'NOTIONAL', {}); result.push({ 'id': id, 'symbol': base + '/' + quote, 'base': base, 'quote': quote, 'settle': undefined, 'baseId': baseId, 'quoteId': quoteId, 'settleId': undefined, 'type': 'spot', 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'active': this.safeStringLower(market, 'status') === 'trading', 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': undefined, 'maker': undefined, 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber(this.safeString(amountLimits, 'stepSize')), 'price': this.parseNumber(this.safeString(priceLimits, 'tickSize')), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.parseNumber(this.safeString(amountLimits, 'minQty')), 'max': this.parseNumber(this.safeString(amountLimits, 'maxQty')), }, 'price': { 'min': this.parseNumber(this.safeString(priceLimits, 'minPrice')), 'max': this.parseNumber(this.safeString(priceLimits, 'maxPrice')), }, 'cost': { 'min': this.parseNumber(this.safeString(costLimits, 'minNotional')), 'max': undefined, }, }, 'created': undefined, 'info': market, }); } this.setMarkets(result); return result; } /** * @method * @name coinsph#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://coins-docs.github.io/rest-api/#24hr-ticker-price-change-statistics * @see https://coins-docs.github.io/rest-api/#symbol-price-ticker * @see https://coins-docs.github.io/rest-api/#symbol-order-book-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(); const request = {}; if (symbols !== undefined) { const ids = []; for (let i = 0; i < symbols.length; i++) { const market = this.market(symbols[i]); const id = market['id']; ids.push(id); } request['symbols'] = ids; } const defaultMethod = 'publicGetOpenapiQuoteV1Ticker24hr'; const options = this.safeDict(this.options, 'fetchTickers', {}); const method = this.safeString(options, 'method', defaultMethod); let tickers = undefined; if (method === 'publicGetOpenapiQuoteV1TickerPrice') { tickers = await this.publicGetOpenapiQuoteV1TickerPrice(this.extend(request, params)); } else if (method === 'publicGetOpenapiQuoteV1TickerBookTicker') { tickers = await this.publicGetOpenapiQuoteV1TickerBookTicker(this.extend(request, params)); } else { tickers = await this.publicGetOpenapiQuoteV1Ticker24hr(this.extend(request, params)); } return this.parseTickers(tickers, symbols, params); } /** * @method * @name coinsph#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://coins-docs.github.io/rest-api/#24hr-ticker-price-change-statistics * @see https://coins-docs.github.io/rest-api/#symbol-price-ticker * @see https://coins-docs.github.io/rest-api/#symbol-order-book-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'], }; const defaultMethod = 'publicGetOpenapiQuoteV1Ticker24hr'; const options = this.safeDict(this.options, 'fetchTicker', {}); const method = this.safeString(options, 'method', defaultMethod); let ticker = undefined; if (method === 'publicGetOpenapiQuoteV1TickerPrice') { ticker = await this.publicGetOpenapiQuoteV1TickerPrice(this.extend(request, params)); } else if (method === 'publicGetOpenapiQuoteV1TickerBookTicker') { ticker = await this.publicGetOpenapiQuoteV1TickerBookTicker(this.extend(request, params)); } else { ticker = await this.publicGetOpenapiQuoteV1Ticker24hr(this.extend(request, params)); } return this.parseTicker(ticker, market); } parseTicker(ticker, market = undefined) { // // publicGetOpenapiQuoteV1Ticker24hr // { // "symbol": "ETHUSDT", // "priceChange": "41.440000000000000000", // "priceChangePercent": "0.0259", // "weightedAvgPrice": "1631.169825783972125436", // "prevClosePrice": "1601.520000000000000000", // "lastPrice": "1642.96", // "lastQty": "0.000001000000000000", // "bidPrice": "1638.790000000000000000", // "bidQty": "0.280075000000000000", // "askPrice": "1647.340000000000000000", // "askQty": "0.165183000000000000", // "openPrice": "1601.52", // "highPrice": "1648.28", // "lowPrice": "1601.52", // "volume": "0.000287", // "quoteVolume": "0.46814574", // "openTime": "1677417000000", // "closeTime": "1677503415200", // "firstId": "1364680572697591809", // "lastId": "1365389809203560449", // "count": "100" // } // // publicGetOpenapiQuoteV1TickerPrice // { "symbol": "ETHUSDT", "price": "1599.68" } // // publicGetOpenapiQuoteV1TickerBookTicker // { // "symbol": "ETHUSDT", // "bidPrice": "1596.57", // "bidQty": "0.246405", // "askPrice": "1605.12", // "askQty": "0.242681" // } // const marketId = this.safeString(ticker, 'symbol'); market = this.safeMarket(marketId, market); const timestamp = this.safeInteger(ticker, 'closeTime'); const bid = this.safeString(ticker, 'bidPrice'); const ask = this.safeString(ticker, 'askPrice'); const bidVolume = this.safeString(ticker, 'bidQty'); const askVolume = this.safeString(ticker, 'askQty'); const baseVolume = this.safeString(ticker, 'volume'); const quoteVolume = this.safeString(ticker, 'quoteVolume'); const open = this.safeString(ticker, 'openPrice'); const high = this.safeString(ticker, 'highPrice'); const low = this.safeString(ticker, 'lowPrice'); const prevClose = this.safeString(ticker, 'prevClosePrice'); const vwap = this.safeString(ticker, 'weightedAvgPrice'); const changeValue = this.safeString(ticker, 'priceChange'); let changePcnt = this.safeString(ticker, 'priceChangePercent'); changePcnt = Precise.stringMul(changePcnt, '100'); return this.safeTicker({ 'symbol': market['symbol'], 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'open': open, 'high': high, 'low': low, 'close': this.safeString2(ticker, 'lastPrice', 'price'), 'bid': bid, 'bidVolume': bidVolume, 'ask': ask, 'askVolume': askVolume, 'vwap': vwap, 'previousClose': prevClose, 'change': changeValue, 'percentage': changePcnt, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }, market); } /** * @method * @name coinsph#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://coins-docs.github.io/rest-api/#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 (default 100, max 200) * @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.publicGetOpenapiQuoteV1Depth(this.extend(request, params)); // // { // "lastUpdateId": "1667022157000699400", // "bids": [ // [ '1651.810000000000000000', '0.214556000000000000' ], // [ '1651.730000000000000000', '0.257343000000000000' ], // ], // "asks": [ // [ '1660.510000000000000000', '0.299092000000000000' ], // [ '1660.600000000000000000', '0.253667000000000000' ], // ] // } // const orderbook = this.parseOrderBook(response, symbol); orderbook['nonce'] = this.safeInteger(response, 'lastUpdateId'); return orderbook; } /** * @method * @name coinsph#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://coins-docs.github.io/rest-api/#klinecandlestick-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 (default 500, max 1000) * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] timestamp in ms of the latest candle to fetch * @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 interval = this.safeString(this.timeframes, timeframe); const until = this.safeInteger(params, 'until'); const request = { 'symbol': market['id'], 'interval': interval, }; if (limit === undefined) { limit = 1000; } if (since !== undefined) { request['startTime'] = since; // since work properly only when it is "younger" than last "limit" candle if (until !== undefined) { request['endTime'] = until; } else { const duration = this.parseTimeframe(timeframe) * 1000; const endTimeByLimit = this.sum(since, duration * (limit - 1)); const now = this.milliseconds(); request['endTime'] = Math.min(endTimeByLimit, now); } } else if (until !== undefined) { request['endTime'] = until; // since work properly only when it is "younger" than last "limit" candle const duration = this.parseTimeframe(timeframe) * 1000; request['startTime'] = until - (duration * (limit - 1)); } request['limit'] = limit; params = this.omit(params, 'until'); const response = await this.publicGetOpenapiQuoteV1Klines(this.extend(request, params)); // // [ // [ // 1499040000000, // Open time // "0.01634790", // Open // "0.80000000", // High // "0.01575800", // Low // "0.01577100", // Close // "148976.11427815", // Volume // 1499644799999, // Close time // "2434.19055334", // Quote asset volume // 308, // Number of trades // "1756.87402397", // Taker buy base asset volume // "28.46694368" // Taker buy quote asset volume // ] // ] // return this.parseOHLCVs(response, market, timeframe, since, limit); } parseOHLCV(ohlcv, market = undefined) { return [ this.safeInteger(ohlcv, 0), this.safeNumber(ohlcv, 1), th