UNPKG

ccxt

Version:

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

1,161 lines (1,159 loc) • 60.9 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/indodax.js'; import { ExchangeError, ArgumentsRequired, InsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, BadSymbol } from './base/errors.js'; import { TICK_SIZE } from './base/functions/number.js'; import { Precise } from './base/Precise.js'; import { sha512 } from './static_dependencies/noble-hashes/sha512.js'; // --------------------------------------------------------------------------- /** * @class indodax * @augments Exchange */ export default class indodax extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'indodax', 'name': 'INDODAX', 'countries': ['ID'], // 10 requests per second for making trades => 1000ms / 10 = 100ms // 180 requests per minute (public endpoints) = 2 requests per second => cost = (1000ms / rateLimit) / 2 = 5 'rateLimit': 50, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'addMargin': false, 'cancelAllOrders': false, 'cancelOrder': true, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, 'createOrder': true, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, 'createStopOrder': false, 'fetchBalance': true, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchClosedOrders': true, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchDeposit': false, 'fetchDepositAddress': 'emulated', 'fetchDepositAddresses': true, 'fetchDepositAddressesByNetwork': false, 'fetchDeposits': false, 'fetchDepositsWithdrawals': true, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchOpenInterestHistory': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': false, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, 'fetchPositions': false, 'fetchPositionsForSymbol': false, 'fetchPositionsHistory': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransactionFee': true, 'fetchTransactionFees': false, 'fetchTransactions': 'emulated', 'fetchTransfer': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': false, 'reduceMargin': false, 'setLeverage': false, 'setMargin': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, 'withdraw': true, }, 'version': '2.0', 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/87070508-9358c880-c221-11ea-8dc5-5391afbbb422.jpg', 'api': { 'public': 'https://indodax.com', 'private': 'https://indodax.com/tapi', }, 'www': 'https://www.indodax.com', 'doc': 'https://github.com/btcid/indodax-official-api-docs', 'referral': 'https://indodax.com/ref/testbitcoincoid/1', }, 'api': { 'public': { 'get': { 'api/server_time': 5, 'api/pairs': 5, 'api/price_increments': 5, 'api/summaries': 5, 'api/ticker/{pair}': 5, 'api/ticker_all': 5, 'api/trades/{pair}': 5, 'api/depth/{pair}': 5, 'tradingview/history_v2': 5, }, }, 'private': { 'post': { 'getInfo': 4, 'transHistory': 4, 'trade': 1, 'tradeHistory': 4, 'openOrders': 4, 'orderHistory': 4, 'getOrder': 4, 'cancelOrder': 4, 'withdrawFee': 4, 'withdrawCoin': 4, 'listDownline': 4, 'checkDownline': 4, 'createVoucher': 4, // partner only }, }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0, 'taker': 0.003, }, }, 'exceptions': { 'exact': { 'invalid_pair': BadSymbol, 'Insufficient balance.': InsufficientFunds, 'invalid order.': OrderNotFound, 'Invalid credentials. API not found or session has expired.': AuthenticationError, 'Invalid credentials. Bad sign.': AuthenticationError, }, 'broad': { 'Minimum price': InvalidOrder, 'Minimum order': InvalidOrder, }, }, // exchange-specific options 'options': { 'recvWindow': 5 * 1000, 'timeDifference': 0, 'adjustForTimeDifference': false, 'networks': { 'XLM': 'Stellar Token', 'BSC': 'bep20', 'TRC20': 'trc20', 'MATIC': 'polygon', // 'BEP2': 'bep2', // 'ARB': 'arb', // 'ERC20': 'erc20', // 'KIP7': 'kip7', // 'MAINNET': 'mainnet', // TODO: does mainnet just mean the default? // 'OEP4': 'oep4', // 'OP': 'op', // 'SPL': 'spl', // 'TRC10': 'trc10', // 'ZRC2': 'zrc2' // 'ETH': 'eth' // 'BASE': 'base' }, 'timeframes': { '1m': '1', '15m': '15', '30m': '30', '1h': '60', '4h': '240', '1d': '1D', '3d': '3D', '1w': '1W', }, }, 'features': { 'spot': { 'sandbox': false, 'createOrder': { 'marginMode': false, 'triggerPrice': false, 'triggerPriceType': undefined, 'triggerDirection': false, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'IOC': true, 'FOK': false, 'PO': false, 'GTD': false, }, 'hedged': false, 'selfTradePrevention': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': false, 'marketBuyRequiresPrice': false, 'iceberg': false, }, 'createOrders': undefined, 'fetchMyTrades': undefined, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': undefined, 'fetchClosedOrders': { 'marginMode': false, 'limit': 1000, 'daysBack': 100000, 'daysBackCanceled': 1, 'untilDays': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOHLCV': { 'limit': 2000, // todo: not in request }, }, 'swap': { 'linear': undefined, 'inverse': undefined, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, 'commonCurrencies': { 'STR': 'XLM', 'BCHABC': 'BCH', 'BCHSV': 'BSV', 'DRK': 'DASH', 'NEM': 'XEM', }, 'precisionMode': TICK_SIZE, }); } nonce() { return this.milliseconds() - this.options['timeDifference']; } /** * @method * @name indodax#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Public-RestAPI.md#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.publicGetApiServerTime(params); // // { // "timezone": "UTC", // "server_time": 1571205969552 // } // return this.safeInteger(response, 'server_time'); } /** * @method * @name indodax#fetchMarkets * @description retrieves data on all markets for indodax * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Public-RestAPI.md#pairs * @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.publicGetApiPairs(params); // // [ // { // "id": "btcidr", // "symbol": "BTCIDR", // "base_currency": "idr", // "traded_currency": "btc", // "traded_currency_unit": "BTC", // "description": "BTC/IDR", // "ticker_id": "btc_idr", // "volume_precision": 0, // "price_precision": 1000, // "price_round": 8, // "pricescale": 1000, // "trade_min_base_currency": 10000, // "trade_min_traded_currency": 0.00007457, // "has_memo": false, // "memo_name": false, // "has_payment_id": false, // "trade_fee_percent": 0.3, // "url_logo": "https://indodax.com/v2/logo/svg/color/btc.svg", // "url_logo_png": "https://indodax.com/v2/logo/png/color/btc.png", // "is_maintenance": 0 // } // ] // const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString(market, 'ticker_id'); const baseId = this.safeString(market, 'traded_currency'); const quoteId = this.safeString(market, 'base_currency'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const isMaintenance = this.safeInteger(market, 'is_maintenance'); 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': isMaintenance ? false : true, 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': this.safeNumber(market, 'trade_fee_percent'), 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'percentage': true, 'precision': { 'amount': this.parseNumber('1e-8'), 'price': this.parseNumber(this.parsePrecision(this.safeString(market, 'price_round'))), 'cost': this.parseNumber(this.parsePrecision(this.safeString(market, 'volume_precision'))), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.safeNumber(market, 'trade_min_traded_currency'), 'max': undefined, }, 'price': { 'min': this.safeNumber(market, 'trade_min_base_currency'), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'created': undefined, 'info': market, }); } return result; } parseBalance(response) { const balances = this.safeValue(response, 'return', {}); const free = this.safeValue(balances, 'balance', {}); const used = this.safeValue(balances, 'balance_hold', {}); const timestamp = this.safeTimestamp(balances, 'server_time'); const result = { 'info': response, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), }; const currencyIds = Object.keys(free); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode(currencyId); const account = this.account(); account['free'] = this.safeString(free, currencyId); account['used'] = this.safeString(used, currencyId); result[code] = account; } return this.safeBalance(result); } /** * @method * @name indodax#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#get-info-endpoint * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async fetchBalance(params = {}) { await this.loadMarkets(); const response = await this.privatePostGetInfo(params); // // { // "success":1, // "return":{ // "server_time":1619562628, // "balance":{ // "idr":167, // "btc":"0.00000000", // "1inch":"0.00000000", // }, // "balance_hold":{ // "idr":0, // "btc":"0.00000000", // "1inch":"0.00000000", // }, // "address":{ // "btc":"1KMntgzvU7iTSgMBWc11nVuJjAyfW3qJyk", // "1inch":"0x1106c8bb3172625e1f411c221be49161dac19355", // "xrp":"rwWr7KUZ3ZFwzgaDGjKBysADByzxvohQ3C", // "zrx":"0x1106c8bb3172625e1f411c221be49161dac19355" // }, // "user_id":"276011", // "name":"", // "email":"testbitcoincoid@mailforspam.com", // "profile_picture":null, // "verification_status":"unverified", // "gauth_enable":true // } // } // return this.parseBalance(response); } /** * @method * @name indodax#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Public-RestAPI.md#depth * @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 = { 'pair': market['base'] + market['quote'], }; const orderbook = await this.publicGetApiDepthPair(this.extend(request, params)); return this.parseOrderBook(orderbook, market['symbol'], undefined, 'buy', 'sell'); } parseTicker(ticker, market = undefined) { // // { // "high":"0.01951", // "low":"0.01877", // "vol_eth":"39.38839319", // "vol_btc":"0.75320886", // "last":"0.01896", // "buy":"0.01896", // "sell":"0.019", // "server_time":1565248908 // } // const symbol = this.safeSymbol(undefined, market); const timestamp = this.safeTimestamp(ticker, 'server_time'); const baseVolume = 'vol_' + market['baseId'].toLowerCase(); const quoteVolume = 'vol_' + market['quoteId'].toLowerCase(); const last = this.safeString(ticker, 'last'); return this.safeTicker({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'high': this.safeString(ticker, 'high'), 'low': this.safeString(ticker, 'low'), 'bid': this.safeString(ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeString(ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeString(ticker, baseVolume), 'quoteVolume': this.safeString(ticker, quoteVolume), 'info': ticker, }, market); } /** * @method * @name indodax#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Public-RestAPI.md#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 = { 'pair': market['base'] + market['quote'], }; const response = await this.publicGetApiTickerPair(this.extend(request, params)); // // { // "ticker": { // "high":"0.01951", // "low":"0.01877", // "vol_eth":"39.38839319", // "vol_btc":"0.75320886", // "last":"0.01896", // "buy":"0.01896", // "sell":"0.019", // "server_time":1565248908 // } // } // const ticker = this.safeDict(response, 'ticker', {}); return this.parseTicker(ticker, market); } /** * @method * @name indodax#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Public-RestAPI.md#ticker-all * @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(); // // { // "tickers": { // "btc_idr": { // "high": "120009000", // "low": "116735000", // "vol_btc": "218.13777777", // "vol_idr": "25800033297", // "last": "117088000", // "buy": "117002000", // "sell": "117078000", // "server_time": 1571207881 // } // } // } // const response = await this.publicGetApiTickerAll(params); const tickers = this.safeDict(response, 'tickers', {}); return this.parseTickers(tickers, symbols); } parseTrade(trade, market = undefined) { const timestamp = this.safeTimestamp(trade, 'date'); return this.safeTrade({ 'id': this.safeString(trade, 'tid'), 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': this.safeSymbol(undefined, market), 'type': undefined, 'side': this.safeString(trade, 'type'), 'order': undefined, 'takerOrMaker': undefined, 'price': this.safeString(trade, 'price'), 'amount': this.safeString(trade, 'amount'), 'cost': undefined, 'fee': undefined, }, market); } /** * @method * @name indodax#fetchTrades * @description get the list of most recent trades for a particular symbol * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Public-RestAPI.md#trades * @param {string} symbol unified symbol of the market to fetch trades for * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['base'] + market['quote'], }; const response = await this.publicGetApiTradesPair(this.extend(request, params)); return this.parseTrades(response, market, since, limit); } parseOHLCV(ohlcv, market = undefined) { // // { // "Time": 1708416900, // "Open": 51707.52, // "High": 51707.52, // "Low": 51707.52, // "Close": 51707.52, // "Volume": "0" // } // return [ this.safeTimestamp(ohlcv, 'Time'), this.safeNumber(ohlcv, 'Open'), this.safeNumber(ohlcv, 'High'), this.safeNumber(ohlcv, 'Low'), this.safeNumber(ohlcv, 'Close'), this.safeNumber(ohlcv, 'Volume'), ]; } /** * @method * @name indodax#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int} [since] timestamp in ms of the earliest candle to fetch * @param {int} [limit] the maximum amount of candles to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] 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 timeframes = this.options['timeframes']; const selectedTimeframe = this.safeString(timeframes, timeframe, timeframe); const now = this.seconds(); const until = this.safeInteger(params, 'until', now); params = this.omit(params, ['until']); const request = { 'to': until, 'tf': selectedTimeframe, 'symbol': market['base'] + market['quote'], }; if (limit === undefined) { limit = 1000; } if (since !== undefined) { request['from'] = Math.floor(since / 1000); } else { const duration = this.parseTimeframe(timeframe); request['from'] = now - limit * duration - 1; } const response = await this.publicGetTradingviewHistoryV2(this.extend(request, params)); // // [ // { // "Time": 1708416900, // "Open": 51707.52, // "High": 51707.52, // "Low": 51707.52, // "Close": 51707.52, // "Volume": "0" // } // ] // return this.parseOHLCVs(response, market, timeframe, since, limit); } parseOrderStatus(status) { const statuses = { 'open': 'open', 'filled': 'closed', 'cancelled': 'canceled', }; return this.safeString(statuses, status, status); } parseOrder(order, market = undefined) { // // { // "order_id": "12345", // "submit_time": "1392228122", // "price": "8000000", // "type": "sell", // "order_ltc": "100000000", // "remain_ltc": "100000000" // } // // market closed orders - note that the price is very high // and does not reflect actual price the order executed at // // { // "order_id": "49326856", // "type": "sell", // "price": "1000000000", // "submit_time": "1618314671", // "finish_time": "1618314671", // "status": "filled", // "order_xrp": "30.45000000", // "remain_xrp": "0.00000000" // } // // cancelOrder // // { // "order_id": 666883, // "client_order_id": "clientx-sj82ks82j", // "type": "sell", // "pair": "btc_idr", // "balance": { // "idr": "33605800", // "btc": "0.00000000", // ... // "frozen_idr": "0", // "frozen_btc": "0.00000000", // ... // } // } // let side = undefined; if ('type' in order) { side = order['type']; } const status = this.parseOrderStatus(this.safeString(order, 'status', 'open')); let symbol = undefined; let cost = undefined; const price = this.safeString(order, 'price'); let amount = undefined; let remaining = undefined; const marketId = this.safeString(order, 'pair'); market = this.safeMarket(marketId, market); if (market !== undefined) { symbol = market['symbol']; let quoteId = market['quoteId']; let baseId = market['baseId']; if ((market['quoteId'] === 'idr') && ('order_rp' in order)) { quoteId = 'rp'; } if ((market['baseId'] === 'idr') && ('remain_rp' in order)) { baseId = 'rp'; } cost = this.safeString(order, 'order_' + quoteId); if (!cost) { amount = this.safeString(order, 'order_' + baseId); remaining = this.safeString(order, 'remain_' + baseId); } } const timestamp = this.safeInteger(order, 'submit_time'); const fee = undefined; const id = this.safeString(order, 'order_id'); return this.safeOrder({ 'info': order, 'id': id, 'clientOrderId': this.safeString(order, 'client_order_id'), 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': 'limit', 'timeInForce': undefined, 'postOnly': undefined, 'side': side, 'price': price, 'triggerPrice': undefined, 'cost': cost, 'average': undefined, 'amount': amount, 'filled': undefined, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }); } /** * @method * @name indodax#fetchOrder * @description fetches information on an order made by the user * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#get-order-endpoints * @param {string} id order id * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchOrder(id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired(this.id + ' fetchOrder() requires a symbol argument'); } await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['id'], 'order_id': id, }; const response = await this.privatePostGetOrder(this.extend(request, params)); const orders = response['return']; const order = this.parseOrder(this.extend({ 'id': id }, orders['order']), market); order['info'] = response; return order; } /** * @method * @name indodax#fetchOpenOrders * @description fetch all unfilled currently open orders * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#open-orders-endpoints * @param {string} symbol unified market symbol * @param {int} [since] the earliest time in ms to fetch open orders for * @param {int} [limit] the maximum number of open orders structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market(symbol); request['pair'] = market['id']; } const response = await this.privatePostOpenOrders(this.extend(request, params)); const rawOrders = response['return']['orders']; // { success: 1, return: { orders: null }} if no orders if (!rawOrders) { return []; } // { success: 1, return: { orders: [ ... objects ] }} for orders fetched by symbol if (symbol !== undefined) { return this.parseOrders(rawOrders, market, since, limit); } // { success: 1, return: { orders: { marketid: [ ... objects ] }}} if all orders are fetched const marketIds = Object.keys(rawOrders); let exchangeOrders = []; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const marketOrders = rawOrders[marketId]; market = this.safeMarket(marketId); const parsedOrders = this.parseOrders(marketOrders, market, since, limit); exchangeOrders = this.arrayConcat(exchangeOrders, parsedOrders); } return exchangeOrders; } /** * @method * @name indodax#fetchClosedOrders * @description fetches information on multiple closed orders made by the user * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#order-history * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired(this.id + ' fetchClosedOrders() requires a symbol argument'); } await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['id'], }; const response = await this.privatePostOrderHistory(this.extend(request, params)); let orders = this.parseOrders(response['return']['orders'], market); orders = this.filterBy(orders, 'status', 'closed'); return this.filterBySymbolSinceLimit(orders, symbol, since, limit); } /** * @method * @name indodax#createOrder * @description create a trade order * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#trade-endpoints * @param {string} symbol unified symbol of the market to create an order in * @param {string} type 'market' or 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async createOrder(symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['id'], 'type': side, 'price': price, }; let priceIsRequired = false; let quantityIsRequired = false; if (type === 'market') { if (side === 'buy') { let quoteAmount = undefined; const cost = this.safeNumber(params, 'cost'); params = this.omit(params, 'cost'); if (cost !== undefined) { quoteAmount = this.costToPrecision(symbol, cost); } else { if (price === undefined) { throw new InvalidOrder(this.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend (amount * price).'); } const amountString = this.numberToString(amount); const priceString = this.numberToString(price); const costRequest = Precise.stringMul(amountString, priceString); quoteAmount = this.costToPrecision(symbol, costRequest); } request[market['quoteId']] = quoteAmount; } else { quantityIsRequired = true; } } else if (type === 'limit') { priceIsRequired = true; quantityIsRequired = true; if (side === 'buy') { request[market['quoteId']] = this.parseToNumeric(Precise.stringMul(this.numberToString(amount), this.numberToString(price))); } } if (priceIsRequired) { if (price === undefined) { throw new InvalidOrder(this.id + ' createOrder() requires a price argument for a ' + type + ' order'); } request['price'] = price; } if (quantityIsRequired) { request[market['baseId']] = this.amountToPrecision(symbol, amount); } const result = await this.privatePostTrade(this.extend(request, params)); const data = this.safeValue(result, 'return', {}); const id = this.safeString(data, 'order_id'); return this.safeOrder({ 'info': result, 'id': id, }, market); } /** * @method * @name indodax#cancelOrder * @description cancels an open order * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#cancel-order-endpoints * @param {string} id order id * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async cancelOrder(id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument'); } const side = this.safeValue(params, 'side'); if (side === undefined) { throw new ArgumentsRequired(this.id + ' cancelOrder() requires an extra "side" param'); } await this.loadMarkets(); const market = this.market(symbol); const request = { 'order_id': id, 'pair': market['id'], 'type': side, }; const response = await this.privatePostCancelOrder(this.extend(request, params)); // // { // "success": 1, // "return": { // "order_id": 666883, // "client_order_id": "clientx-sj82ks82j", // "type": "sell", // "pair": "btc_idr", // "balance": { // "idr": "33605800", // "btc": "0.00000000", // ... // "frozen_idr": "0", // "frozen_btc": "0.00000000", // ... // } // } // } // const data = this.safeDict(response, 'return'); return this.parseOrder(data); } /** * @method * @name indodax#fetchTransactionFee * @description fetch the fee for a transaction * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#withdraw-fee-endpoints * @param {string} code unified currency code * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [fee structure]{@link https://docs.ccxt.com/#/?id=fee-structure} */ async fetchTransactionFee(code, params = {}) { await this.loadMarkets(); const currency = this.currency(code); const request = { 'currency': currency['id'], }; const response = await this.privatePostWithdrawFee(this.extend(request, params)); // // { // "success": 1, // "return": { // "server_time": 1607923272, // "withdraw_fee": 0.005, // "currency": "eth" // } // } // const data = this.safeValue(response, 'return', {}); const currencyId = this.safeString(data, 'currency'); return { 'info': response, 'rate': this.safeNumber(data, 'withdraw_fee'), 'currency': this.safeCurrencyCode(currencyId, currency), }; } /** * @method * @name indodax#fetchDepositsWithdrawals * @description fetch history of deposits and withdrawals * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#transaction-history-endpoints * @param {string} [code] unified currency code for the currency of the deposit/withdrawals, default is undefined * @param {int} [since] timestamp in ms of the earliest deposit/withdrawal, default is undefined * @param {int} [limit] max number of deposit/withdrawals to return, default is undefined * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} */ async fetchDepositsWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const request = {}; if (since !== undefined) { const startTime = this.iso8601(since).slice(0, 10); request['start'] = startTime; request['end'] = this.iso8601(this.milliseconds()).slice(0, 10); } const response = await this.privatePostTransHistory(this.extend(request, params)); // // { // "success": 1, // "return": { // "withdraw": { // "idr": [ // { // "status": "success", // "type": "coupon", // "rp": "115205", // "fee": "500", // "amount": "114705", // "submit_time": "1539844166", // "success_time": "1539844189", // "withdraw_id": "1783717", // "tx": "BTC-IDR-RDTVVO2P-ETD0EVAW-VTNZGMIR-HTNTUAPI-84ULM9OI", // "sender": "boris", // "used_by": "viginia88" // }, // ... // ], // "btc": [], // "abyss": [], // ... // }, // "deposit": { // "idr": [ // { // "status": "success", // "type": "duitku", // "rp": "393000", // "fee": "5895", // "amount": "387105", // "submit_time": "1576555012", // "success_time": "1576555012", // "deposit_id": "3395438", // "tx": "Duitku OVO Settlement" // }, // ... // ], // "btc": [ // { // "status": "success", // "btc": "0.00118769", // "amount": "0.00118769", // "success_time": "1539529208", // "deposit_id": "3602369", // "tx": "c816aeb35a5b42f389970325a32aff69bb6b2126784dcda8f23b9dd9570d6573" // }, // ... // ], // "abyss": [], // ... // } // } // } // const data = this.safeValue(response, 'return', {}); const withdraw = this.safeValue(data, 'withdraw', {}); const deposit = this.safeValue(data, 'deposit', {}); let transactions = []; let currency = undefined; if (code === undefined) { let keys = Object.keys(withdraw); for (let i = 0; i < keys.length; i++) { const key = keys[i]; transactions = this.arrayConcat(transactions, withdraw[key]); } keys = Object.keys(deposit); for (let i = 0; i < keys.length; i++) { const key = keys[i]; transactions = this.arrayConcat(transactions, deposit[key]); } } else { currency = this.currency(code); const withdraws = this.safeValue(withdraw, currency['id'], []); const deposits = this.safeValue(deposit, currency['id'], []); transactions = this.arrayConcat(withdraws, deposits); } return this.parseTransactions(transactions, currency, since, limit); } /** * @method * @name indodax#withdraw * @description make a withdrawal * @see https://github.com/btcid/indodax-official-api-docs/blob/master/Private-RestAPI.md#withdraw-coin-endpoints * @param {string} code unified currency code * @param {float} amount the amount to withdraw * @param {string} address the address to withdraw to * @param {string} tag * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} */ async withdraw(code, amount, address, tag = undefined, params = {}) { [tag, params] = this.handleWithdrawTagAndParams(tag, params); this.checkAddress(address); await this.loadMarkets(); const currency = this.currency(code); // Custom string you need to provide to identify each withdrawal. // Will be