UNPKG

consequunturatque

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

1,201 lines (1,166 loc) 52.2 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, BadResponse, BadRequest, InvalidOrder, InsufficientFunds, AuthenticationError, ArgumentsRequired, InvalidAddress, RateLimitExceeded, DDoSProtection, BadSymbol } = require ('./base/errors'); const { TRUNCATE, TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class probit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'probit', 'name': 'ProBit', 'countries': [ 'SC', 'KR' ], // Seychelles, South Korea 'rateLimit': 250, // ms 'has': { 'CORS': true, 'fetchTime': true, 'fetchMarkets': true, 'fetchCurrencies': true, 'fetchTickers': true, 'fetchTicker': true, 'fetchOHLCV': true, 'fetchOrderBook': true, 'fetchTrades': true, 'fetchBalance': true, 'createOrder': true, 'createMarketOrder': true, 'cancelOrder': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchDepositAddress': true, 'withdraw': true, 'signIn': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '10m': '10m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1D', '1w': '1W', '1M': '1M', }, 'version': 'v1', 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/79268032-c4379480-7ea2-11ea-80b3-dd96bb29fd0d.jpg', 'api': { 'accounts': 'https://accounts.probit.com', 'public': 'https://api.probit.com/api/exchange', 'private': 'https://api.probit.com/api/exchange', }, 'www': 'https://www.probit.com', 'doc': [ 'https://docs-en.probit.com', 'https://docs-ko.probit.com', ], 'fees': 'https://support.probit.com/hc/en-us/articles/360020968611-Trading-Fees', 'referral': 'https://www.probit.com/r/34608773', }, 'api': { 'public': { 'get': [ 'market', 'currency', 'currency_with_platform', 'time', 'ticker', 'order_book', 'trade', 'candle', ], }, 'private': { 'post': [ 'new_order', 'cancel_order', 'withdrawal', ], 'get': [ 'balance', 'order', 'open_order', 'order_history', 'trade_history', 'deposit_address', ], }, 'accounts': { 'post': [ 'token', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, }, 'exceptions': { 'exact': { 'UNAUTHORIZED': AuthenticationError, 'INVALID_ARGUMENT': BadRequest, // Parameters are not a valid format, parameters are empty, or out of range, or a parameter was sent when not required. 'TRADING_UNAVAILABLE': ExchangeNotAvailable, 'NOT_ENOUGH_BALANCE': InsufficientFunds, 'NOT_ALLOWED_COMBINATION': BadRequest, 'INVALID_ORDER': InvalidOrder, // Requested order does not exist, or it is not your order 'RATE_LIMIT_EXCEEDED': RateLimitExceeded, // You are sending requests too frequently. Please try it later. 'MARKET_UNAVAILABLE': ExchangeNotAvailable, // Market is closed today 'INVALID_MARKET': BadSymbol, // Requested market is not exist 'MARKET_CLOSED': BadSymbol, // {"errorCode":"MARKET_CLOSED"} 'INVALID_CURRENCY': BadRequest, // Requested currency is not exist on ProBit system 'TOO_MANY_OPEN_ORDERS': DDoSProtection, // Too many open orders 'DUPLICATE_ADDRESS': InvalidAddress, // Address already exists in withdrawal address list }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'precisionMode': TICK_SIZE, 'options': { 'createMarketBuyOrderRequiresPrice': true, 'timeInForce': { 'limit': 'gtc', 'market': 'ioc', }, }, 'commonCurrencies': { 'AUTO': 'Cube', 'BTCBEAR': 'BEAR', 'BTCBULL': 'BULL', 'CBC': 'CryptoBharatCoin', 'EPS': 'Epanus', // conflict with EPS Ellipsis https://github.com/ccxt/ccxt/issues/8909 'HBC': 'Hybrid Bank Cash', 'UNI': 'UNICORN Token', }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetMarket (params); // // { // "data":[ // { // "id":"MONA-USDT", // "base_currency_id":"MONA", // "quote_currency_id":"USDT", // "min_price":"0.001", // "max_price":"9999999999999999", // "price_increment":"0.001", // "min_quantity":"0.0001", // "max_quantity":"9999999999999999", // "quantity_precision":4, // "min_cost":"1", // "max_cost":"9999999999999999", // "cost_precision":8, // "taker_fee_rate":"0.2", // "maker_fee_rate":"0.2", // "show_in_ui":true, // "closed":false // }, // ] // } // const markets = this.safeValue (response, 'data', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'base_currency_id'); const quoteId = this.safeString (market, 'quote_currency_id'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const closed = this.safeValue (market, 'closed', false); const active = !closed; const amountPrecision = this.safeString (market, 'quantity_precision'); const costPrecision = this.safeString (market, 'cost_precision'); const amountTickSize = this.parsePrecision (amountPrecision); const costTickSize = this.parsePrecision (costPrecision); const precision = { 'amount': this.parseNumber (amountTickSize), 'price': this.safeNumber (market, 'price_increment'), 'cost': this.parseNumber (costTickSize), }; const takerFeeRate = this.safeString (market, 'taker_fee_rate'); const taker = Precise.stringDiv (takerFeeRate, '100'); const makerFeeRate = this.safeString (market, 'maker_fee_rate'); const maker = Precise.stringDiv (makerFeeRate, '100'); result.push ({ 'id': id, 'info': market, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'taker': this.parseNumber (taker), 'maker': this.parseNumber (maker), 'limits': { 'amount': { 'min': this.safeNumber (market, 'min_quantity'), 'max': this.safeNumber (market, 'max_quantity'), }, 'price': { 'min': this.safeNumber (market, 'min_price'), 'max': this.safeNumber (market, 'max_price'), }, 'cost': { 'min': this.safeNumber (market, 'min_cost'), 'max': this.safeNumber (market, 'max_cost'), }, }, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencyWithPlatform (params); // // { // "data":[ // { // "id":"USDT", // "display_name":{"ko-kr":"테더","en-us":"Tether"}, // "show_in_ui":true, // "platform":[ // { // "id":"ETH", // "priority":1, // "deposit":true, // "withdrawal":true, // "currency_id":"USDT", // "precision":6, // "min_confirmation_count":15, // "require_destination_tag":false, // "display_name":{"name":{"ko-kr":"ERC-20","en-us":"ERC-20"}}, // "min_deposit_amount":"0", // "min_withdrawal_amount":"1", // "withdrawal_fee":[ // {"amount":"0.01","priority":2,"currency_id":"ETH"}, // {"amount":"1.5","priority":1,"currency_id":"USDT"}, // ], // "deposit_fee":{}, // "suspended_reason":"", // "deposit_suspended":false, // "withdrawal_suspended":false // }, // { // "id":"OMNI", // "priority":2, // "deposit":true, // "withdrawal":true, // "currency_id":"USDT", // "precision":6, // "min_confirmation_count":3, // "require_destination_tag":false, // "display_name":{"name":{"ko-kr":"OMNI","en-us":"OMNI"}}, // "min_deposit_amount":"0", // "min_withdrawal_amount":"5", // "withdrawal_fee":[{"amount":"5","priority":1,"currency_id":"USDT"}], // "deposit_fee":{}, // "suspended_reason":"wallet_maintenance", // "deposit_suspended":false, // "withdrawal_suspended":false // } // ], // "stakeable":false, // "unstakeable":false, // "auto_stake":false, // "auto_stake_amount":"0" // } // ] // } // const currencies = this.safeValue (response, 'data'); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'id'); const code = this.safeCurrencyCode (id); const displayName = this.safeValue (currency, 'display_name'); const name = this.safeString (displayName, 'en-us'); const platforms = this.safeValue (currency, 'platform', []); const platformsByPriority = this.sortBy (platforms, 'priority'); const platform = this.safeValue (platformsByPriority, 0, {}); const precision = this.safeInteger (platform, 'precision'); const depositSuspended = this.safeValue (platform, 'deposit_suspended'); const withdrawalSuspended = this.safeValue (platform, 'withdrawal_suspended'); const active = !(depositSuspended && withdrawalSuspended); const withdrawalFees = this.safeValue (platform, 'withdrawal_fee', {}); const fees = []; // sometimes the withdrawal fee is an empty object // [ { 'amount': '0.015', 'priority': 1, 'currency_id': 'ETH' }, {} ] for (let j = 0; j < withdrawalFees.length; j++) { const withdrawalFee = withdrawalFees[j]; const amount = this.safeNumber (withdrawalFee, 'amount'); const priority = this.safeInteger (withdrawalFee, 'priority'); if ((amount !== undefined) && (priority !== undefined)) { fees.push (withdrawalFee); } } const withdrawalFeesByPriority = this.sortBy (fees, 'priority'); const withdrawalFee = this.safeValue (withdrawalFeesByPriority, 0, {}); const fee = this.safeNumber (withdrawalFee, 'amount'); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': name, 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'deposit': { 'min': this.safeNumber (platform, 'min_deposit_amount'), 'max': undefined, }, 'withdraw': { 'min': this.safeNumber (platform, 'min_withdrawal_amount'), 'max': undefined, }, }, }; } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalance (params); // // { // data: [ // { // "currency_id":"XRP", // "total":"100", // "available":"0", // } // ] // } // const result = { 'info': response, 'timestamp': undefined, 'datetime': undefined, }; const data = this.safeValue (response, 'data'); for (let i = 0; i < data.length; i++) { const balance = data[i]; const currencyId = this.safeString (balance, 'currency_id'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeString (balance, 'total'); account['free'] = this.safeString (balance, 'available'); result[code] = account; } return this.parseBalance (result, false); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_id': market['id'], }; const response = await this.publicGetOrderBook (this.extend (request, params)); // // { // data: [ // { side: 'buy', price: '0.000031', quantity: '10' }, // { side: 'buy', price: '0.00356007', quantity: '4.92156877' }, // { side: 'sell', price: '0.1857', quantity: '0.17' }, // ] // } // const data = this.safeValue (response, 'data', []); const dataBySide = this.groupBy (data, 'side'); return this.parseOrderBook (dataBySide, symbol, undefined, 'buy', 'sell', 'price', 'quantity'); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const marketIds = this.marketIds (symbols); request['market_ids'] = marketIds.join (','); } const response = await this.publicGetTicker (this.extend (request, params)); // // { // "data":[ // { // "last":"0.022902", // "low":"0.021693", // "high":"0.024093", // "change":"-0.000047", // "base_volume":"15681.986", // "quote_volume":"360.514403624", // "market_id":"ETH-BTC", // "time":"2020-04-12T18:43:38.000Z" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTickers (data, symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_ids': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); // // { // "data":[ // { // "last":"0.022902", // "low":"0.021693", // "high":"0.024093", // "change":"-0.000047", // "base_volume":"15681.986", // "quote_volume":"360.514403624", // "market_id":"ETH-BTC", // "time":"2020-04-12T18:43:38.000Z" // } // ] // } // const data = this.safeValue (response, 'data', []); const ticker = this.safeValue (data, 0); if (ticker === undefined) { throw new BadResponse (this.id + ' fetchTicker() returned an empty response'); } return this.parseTicker (ticker, market); } parseTicker (ticker, market = undefined) { // // { // "last":"0.022902", // "low":"0.021693", // "high":"0.024093", // "change":"-0.000047", // "base_volume":"15681.986", // "quote_volume":"360.514403624", // "market_id":"ETH-BTC", // "time":"2020-04-12T18:43:38.000Z" // } // const timestamp = this.parse8601 (this.safeString (ticker, 'time')); const marketId = this.safeString (ticker, 'market_id'); const symbol = this.safeSymbol (marketId, market, '-'); const close = this.safeNumber (ticker, 'last'); const change = this.safeNumber (ticker, 'change'); let percentage = undefined; let open = undefined; if (change !== undefined) { if (close !== undefined) { open = close - change; percentage = (change / open) * 100; } } const baseVolume = this.safeNumber (ticker, 'base_volume'); const quoteVolume = this.safeNumber (ticker, 'quote_volume'); const vwap = this.vwap (baseVolume, quoteVolume); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'high'), 'low': this.safeNumber (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': vwap, 'open': open, 'close': close, 'last': close, 'previousClose': undefined, // previous day close 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { 'limit': 100, 'start_time': this.iso8601 (0), 'end_time': this.iso8601 (this.milliseconds ()), }; if (symbol !== undefined) { market = this.market (symbol); request['market_id'] = market['id']; } if (since !== undefined) { request['start_time'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetTradeHistory (this.extend (request, params)); // // { // data: [ // { // "id":"BTC-USDT:183566", // "order_id":"17209376", // "side":"sell", // "fee_amount":"0.657396569175", // "fee_currency_id":"USDT", // "status":"settled", // "price":"6573.96569175", // "quantity":"0.1", // "cost":"657.396569175", // "time":"2018-08-10T06:06:46.000Z", // "market_id":"BTC-USDT" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_id': market['id'], 'limit': 100, 'start_time': '1970-01-01T00:00:00.000Z', 'end_time': this.iso8601 (this.milliseconds ()), }; if (since !== undefined) { request['start_time'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTrade (this.extend (request, params)); // // { // "data":[ // { // "id":"ETH-BTC:3331886", // "price":"0.022981", // "quantity":"12.337", // "time":"2020-04-12T20:55:42.371Z", // "side":"sell", // "tick_direction":"down" // }, // { // "id":"ETH-BTC:3331885", // "price":"0.022982", // "quantity":"6.472", // "time":"2020-04-12T20:55:39.652Z", // "side":"sell", // "tick_direction":"down" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "id":"ETH-BTC:3331886", // "price":"0.022981", // "quantity":"12.337", // "time":"2020-04-12T20:55:42.371Z", // "side":"sell", // "tick_direction":"down" // } // // fetchMyTrades (private) // // { // "id":"BTC-USDT:183566", // "order_id":"17209376", // "side":"sell", // "fee_amount":"0.657396569175", // "fee_currency_id":"USDT", // "status":"settled", // "price":"6573.96569175", // "quantity":"0.1", // "cost":"657.396569175", // "time":"2018-08-10T06:06:46.000Z", // "market_id":"BTC-USDT" // } // const timestamp = this.parse8601 (this.safeString (trade, 'time')); const id = this.safeString (trade, 'id'); let marketId = undefined; if (id !== undefined) { const parts = id.split (':'); marketId = this.safeString (parts, 0); } marketId = this.safeString (trade, 'market_id', marketId); const symbol = this.safeSymbol (marketId, market, '-'); const side = this.safeString (trade, 'side'); const priceString = this.safeString (trade, 'price'); const amountString = this.safeString (trade, 'quantity'); const price = this.parseNumber (priceString); const amount = this.parseNumber (amountString); const cost = this.parseNumber (Precise.stringMul (priceString, amountString)); const orderId = this.safeString (trade, 'order_id'); const feeCost = this.safeNumber (trade, 'fee_amount'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fee_currency_id'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTime (params = {}) { const response = await this.publicGetTime (params); // // { "data":"2020-04-12T18:54:25.390Z" } // const timestamp = this.parse8601 (this.safeString (response, 'data')); return timestamp; } normalizeOHLCVTimestamp (timestamp, timeframe, after = false) { const duration = this.parseTimeframe (timeframe); if (timeframe === '1M') { const iso8601 = this.iso8601 (timestamp); const parts = iso8601.split ('-'); const year = this.safeString (parts, 0); let month = this.safeInteger (parts, 1); if (after) { month = this.sum (month, 1); } if (month < 10) { month = '0' + month.toString (); } else { month = month.toString (); } return year + '-' + month + '-01T00:00:00.000Z'; } else if (timeframe === '1w') { timestamp = parseInt (timestamp / 1000); const firstSunday = 259200; // 1970-01-04T00:00:00.000Z const difference = timestamp - firstSunday; const numWeeks = this.integerDivide (difference, duration); let previousSunday = this.sum (firstSunday, numWeeks * duration); if (after) { previousSunday = this.sum (previousSunday, duration); } return this.iso8601 (previousSunday * 1000); } else { timestamp = parseInt (timestamp / 1000); timestamp = duration * parseInt (timestamp / duration); if (after) { timestamp = this.sum (timestamp, duration); } return this.iso8601 (timestamp * 1000); } } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const interval = this.timeframes[timeframe]; limit = (limit === undefined) ? 100 : limit; let requestLimit = this.sum (limit, 1); requestLimit = Math.min (1000, requestLimit); // max 1000 const request = { 'market_ids': market['id'], 'interval': interval, 'sort': 'asc', // 'asc' will always include the start_time, 'desc' will always include end_time 'limit': requestLimit, // max 1000 }; const now = this.milliseconds (); const duration = this.parseTimeframe (timeframe); let startTime = since; let endTime = now; if (since === undefined) { if (limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires either a since argument or a limit argument'); } else { startTime = now - limit * duration * 1000; } } else { if (limit === undefined) { endTime = now; } else { endTime = this.sum (since, this.sum (limit, 1) * duration * 1000); } } const startTimeNormalized = this.normalizeOHLCVTimestamp (startTime, timeframe); const endTimeNormalized = this.normalizeOHLCVTimestamp (endTime, timeframe, true); request['start_time'] = startTimeNormalized; request['end_time'] = endTimeNormalized; const response = await this.publicGetCandle (this.extend (request, params)); // // { // "data":[ // { // "market_id":"ETH-BTC", // "open":"0.02811", // "close":"0.02811", // "low":"0.02811", // "high":"0.02811", // "base_volume":"0.0005", // "quote_volume":"0.000014055", // "start_time":"2018-11-30T18:19:00.000Z", // "end_time":"2018-11-30T18:20:00.000Z" // }, // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } parseOHLCV (ohlcv, market = undefined) { // // { // "market_id":"ETH-BTC", // "open":"0.02811", // "close":"0.02811", // "low":"0.02811", // "high":"0.02811", // "base_volume":"0.0005", // "quote_volume":"0.000014055", // "start_time":"2018-11-30T18:19:00.000Z", // "end_time":"2018-11-30T18:20:00.000Z" // } // return [ this.parse8601 (this.safeString (ohlcv, 'start_time')), this.safeNumber (ohlcv, 'open'), this.safeNumber (ohlcv, 'high'), this.safeNumber (ohlcv, 'low'), this.safeNumber (ohlcv, 'close'), this.safeNumber (ohlcv, 'base_volume'), ]; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); since = this.parse8601 (since); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market_id'] = market['id']; } const response = await this.privateGetOpenOrder (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseOrders (data, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'start_time': this.iso8601 (0), 'end_time': this.iso8601 (this.milliseconds ()), 'limit': 100, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market_id'] = market['id']; } if (since) { request['start_time'] = this.iso8601 (since); } if (limit) { request['limit'] = limit; } const response = await this.privateGetOrderHistory (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseOrders (data, market, since, limit); } 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 = { 'market_id': market['id'], }; const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_order_id'); if (clientOrderId !== undefined) { request['client_order_id'] = clientOrderId; } else { request['order_id'] = id; } const query = this.omit (params, [ 'clientOrderId', 'client_order_id' ]); const response = await this.privateGetOrder (this.extend (request, query)); const data = this.safeValue (response, 'data', []); const order = this.safeValue (data, 0); return this.parseOrder (order, market); } parseOrderStatus (status) { const statuses = { 'open': 'open', 'cancelled': 'canceled', 'filled': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // id: string, // user_id: string, // market_id: string, // type: 'orderType', // side: 'side', // quantity: string, // limit_price: string, // time_in_force: 'timeInForce', // filled_cost: string, // filled_quantity: string, // open_quantity: string, // cancelled_quantity: string, // status: 'orderStatus', // time: 'date', // client_order_id: string, // } // const status = this.parseOrderStatus (this.safeString (order, 'status')); const id = this.safeString (order, 'id'); const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); const marketId = this.safeString (order, 'market_id'); const symbol = this.safeSymbol (marketId, market, '-'); const timestamp = this.parse8601 (this.safeString (order, 'time')); let price = this.safeNumber (order, 'limit_price'); const filled = this.safeNumber (order, 'filled_quantity'); let remaining = this.safeNumber (order, 'open_quantity'); const canceledAmount = this.safeNumber (order, 'cancelled_quantity'); if (canceledAmount !== undefined) { remaining = this.sum (remaining, canceledAmount); } const amount = this.safeNumber (order, 'quantity', this.sum (filled, remaining)); const cost = this.safeNumber2 (order, 'filled_cost', 'cost'); if (type === 'market') { price = undefined; } let clientOrderId = this.safeString (order, 'client_order_id'); if (clientOrderId === '') { clientOrderId = undefined; } const timeInForce = this.safeStringUpper (order, 'time_in_force'); return this.safeOrder ({ 'id': id, 'info': order, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'timeInForce': timeInForce, 'side': side, 'status': status, 'price': price, 'stopPrice': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'average': undefined, 'cost': cost, 'fee': undefined, 'trades': undefined, }); } costToPrecision (symbol, cost) { return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['cost'], this.precisionMode); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const options = this.safeValue (this.options, 'timeInForce'); const defaultTimeInForce = this.safeValue (options, type); const timeInForce = this.safeString2 (params, 'timeInForce', 'time_in_force', defaultTimeInForce); const request = { 'market_id': market['id'], 'type': type, 'side': side, 'time_in_force': timeInForce, }; const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_order_id'); if (clientOrderId !== undefined) { request['client_order_id'] = clientOrderId; } let costToPrecision = undefined; if (type === 'limit') { request['limit_price'] = this.priceToPrecision (symbol, price); request['quantity'] = this.amountToPrecision (symbol, amount); } else if (type === 'market') { // for market buy it requires the amount of quote currency to spend if (side === 'buy') { let cost = this.safeNumber (params, 'cost'); const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true); if (createMarketBuyOrderRequiresPrice) { if (price !== undefined) { if (cost === undefined) { cost = amount * price; } } else if (cost === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument for market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument or in the 'cost' extra parameter (the exchange-specific behaviour)"); } } else { cost = (cost === undefined) ? amount : cost; } costToPrecision = this.costToPrecision (symbol, cost); request['cost'] = costToPrecision; } else { request['quantity'] = this.amountToPrecision (symbol, amount); } } const query = this.omit (params, [ 'timeInForce', 'time_in_force', 'clientOrderId', 'client_order_id' ]); const response = await this.privatePostNewOrder (this.extend (request, query)); // // { // data: { // id: string, // user_id: string, // market_id: string, // type: 'orderType', // side: 'side', // quantity: string, // limit_price: string, // time_in_force: 'timeInForce', // filled_cost: string, // filled_quantity: string, // open_quantity: string, // cancelled_quantity: string, // status: 'orderStatus', // time: 'date', // client_order_id: string, // } // } // const data = this.safeValue (response, 'data'); const order = this.parseOrder (data, market); // a workaround for incorrect huge amounts // returned by the exchange on market buys if ((type === 'market') && (side === 'buy')) { order['amount'] = undefined; order['cost'] = parseFloat (costToPrecision); order['remaining'] = undefined; } return order; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_id': market['id'], 'order_id': id, }; const response = await this.privatePostCancelOrder (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseOrder (data); } parseDepositAddress (depositAddress, currency = undefined) { const address = this.safeString (depositAddress, 'address'); const tag = this.safeString (depositAddress, 'destination_tag'); const currencyId = this.safeString (depositAddress, 'currency_id'); const code = this.safeCurrencyCode (currencyId); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': depositAddress, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency_id': currency['id'], }; const response = await this.privateGetDepositAddress (this.extend (request, params)); // // { // "data":[ // { // "currency_id":"ETH", // "address":"0x12e2caf3c4051ba1146e612f532901a423a9898a", // "destination_tag":null // } // ] // } // const data = this.safeValue (response, 'data', []); const firstAddress = this.safeValue (data, 0); if (firstAddress === undefined) { throw new InvalidAddress (this.id + ' fetchDepositAddress returned an empty response'); } return this.parseDepositAddress (firstAddress, currency); } async fetchDepositAddresses (codes = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (codes) { const currencyIds = []; for (let i = 0; i < codes.length; i++) { const currency = this.currency (codes[i]); currencyIds.push (currency['id']); } request['currency_id'] = codes.join (','); } const response = await this.privateGetDepositAddress (this.extend (request, params)); const data = this.safeValue (response, 'data', []); return this.parseDepositAddresses (data); } async withdraw (code, amount, address, tag = undefined, params = {}) { // In order to use this method // you need to allow API withdrawal from the API Settings Page, and // and register the list of withdrawal addresses and destination tags on the API Settings page // you can only withdraw to the registered addresses using the API this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); if (tag === undefined) { tag = ''; } const request = { 'currency_id': currency['id'], // 'platform_id': 'ETH', // if omitted it will use the default platform for the currency 'address': address, 'destination_tag': tag, 'amount': this.currencyToPrecision (code, amount), // which currency to pay the withdrawal fees // only applicable for currencies that accepts multiple withdrawal fee options // 'fee_currency_id': 'ETH', // if omitted it will use the default fee policy for each currency // whether the amount field includes fees // 'include_fee': false, // makes sense only when fee_currency_id is equal to currency_id }; const response = await this.privatePostWithdrawal (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseTransaction (data, currency); } parseTransaction (transaction, currency = undefined) { const id = this.safeString (transaction, 'id'); const amount = this.safeNumber (transaction, 'amount'); const address = this.safeString (transaction, 'address'); const tag = this.safeString (transaction, 'destination_tag'); const txid = this.safeString (transaction, 'hash'); const timestamp = this.parse8601 (this.safeString (transaction, 'time')); const type = this.safeString (transaction, 'type'); const currencyId = this.safeString (transaction, 'currency_id'); const code = this.safeCurrencyCode (currencyId); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const feeCost = this.safeNumber (transaction, 'fee'); let fee = undefined; if (feeCost !== undefined && feeCost !== 0) { fee = { 'currency': code, 'cost': feeCost, }; } return { 'id': id, 'currency': code, 'amount': amount, 'addressFrom': undefined, 'address': address, 'addressTo': address, 'tagFrom': undefined, 'tag': tag, 'tagTo': tag, 'status': status, 'type': type, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': fee, 'info': transaction, }; } parseTransactionStatus (status) { const statuses = { 'requested': 'pending', 'pending': 'pending', 'confirming': 'pending', 'confirmed': 'pending', 'applying': 'pending', 'done': 'ok', 'cancelled': 'canceled', 'cancelling': 'canceled', }; return this.safeString (statuses, status, status); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/'; const query = this.omit (params, this.extractParams (path)); if (api === 'accounts') { this.checkRequiredCredentials (); url += this.implodeParams (path, params); const auth = this.apiKey + ':' + this.secret; const auth64 = this.stringToBase64 (auth); headers = { 'Authorization': 'Basic ' + this.decode (auth64), 'Content-Type': 'application/json', }; if (Object.keys (query).length) { body = this.json (query); } } else { url += this.version + '/'; if (api === 'public') { url += this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'private') { const now = this.milliseconds (); this.checkRequiredCredentials (); const expires = this.safeInteger (this.options, 'expires'); if ((expires === undefined) || (expires < now)) { throw new AuthenticationError (this.id + ' access token expired, call signIn() method'); } const accessToken = this.safeString (this.options, 'accessToken');