UNPKG

sfccxt

Version:

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

1,159 lines (1,132 loc) 67 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': 50, // ms 'has': { 'CORS': true, 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'addMargin': false, 'cancelOrder': true, 'createMarketOrder': true, 'createOrder': true, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, 'createStopOrder': false, 'fetchBalance': true, 'fetchBorrowRate': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchBorrowRates': false, 'fetchBorrowRatesPerSymbol': false, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDepositAddresses': true, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchPosition': false, 'fetchPositionMode': false, 'fetchPositions': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransfer': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': false, 'reduceMargin': false, 'setLeverage': false, 'setMarginMode': false, 'setPositionMode': false, 'signIn': true, 'transfer': false, 'withdraw': 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': 1, 'currency': 1, 'currency_with_platform': 1, 'time': 1, 'ticker': 1, 'order_book': 1, 'trade': 1, 'candle': 1, }, }, 'private': { 'post': { 'new_order': 2, 'cancel_order': 1, 'withdrawal': 2, }, 'get': { 'balance': 1, 'order': 1, 'open_order': 1, 'order_history': 1, 'trade_history': 1, 'deposit_address': 1, }, }, 'accounts': { 'post': { 'token': 1, }, }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': this.parseNumber ('0.002'), 'taker': this.parseNumber ('0.002'), }, }, '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"} 'MARKET_NOT_FOUND': BadSymbol, // {"errorCode":"MARKET_NOT_FOUND","message":"8e2b8496-0a1e-5beb-b990-a205b902eabe","details":{}} '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 'invalid_grant': AuthenticationError, // {"error":"invalid_grant"} }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'precisionMode': TICK_SIZE, 'options': { 'createMarketBuyOrderRequiresPrice': true, 'timeInForce': { 'limit': 'gtc', 'market': 'ioc', }, 'networks': { 'BEP20': 'BSC', 'ERC20': 'ETH', 'TRC20': 'TRON', 'TRX': 'TRON', }, }, 'commonCurrencies': { 'AUTO': 'Cube', 'AZU': 'Azultec', 'BCC': 'BCC', 'BDP': 'BidiPass', 'BIRD': 'Birdchain', 'BTCBEAR': 'BEAR', 'BTCBULL': 'BULL', 'CBC': 'CryptoBharatCoin', 'CHE': 'Chellit', 'CLR': 'Color Platform', 'CTK': 'Cryptyk', 'CTT': 'Castweet', 'DIP': 'Dipper', 'DKT': 'DAKOTA', 'EGC': 'EcoG9coin', 'EPS': 'Epanus', // conflict with EPS Ellipsis https://github.com/ccxt/ccxt/issues/8909 'FX': 'Fanzy', 'GDT': 'Gorilla Diamond', 'GM': 'GM Holding', 'GOGOL': 'GOL', 'GOL': 'Goldofir', 'GRB': 'Global Reward Bank', 'HBC': 'Hybrid Bank Cash', 'HUSL': 'The Hustle App', 'LAND': 'Landbox', 'LBK': 'Legal Block', 'ORC': 'Oracle System', 'PXP': 'PIXSHOP COIN', 'PYE': 'CreamPYE', 'ROOK': 'Reckoon', 'SOC': 'Soda Coin', 'SST': 'SocialSwap', 'TCT': 'Top Coin Token', 'TOR': 'Torex', 'TPAY': 'Tetra Pay', 'UNI': 'UNICORN Token', 'UNISWAP': 'UNI', }, }); } async fetchMarkets (params = {}) { /** * @method * @name probit#fetchMarkets * @description retrieves data on all markets for probit * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ 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 closed = this.safeValue (market, 'closed', false); 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, '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': !closed, 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': this.parseNumber (taker), 'maker': this.parseNumber (maker), 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'quantity_precision'))), 'price': this.safeNumber (market, 'price_increment'), 'cost': this.parseNumber (this.parsePrecision (this.safeString (market, 'cost_precision'))), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, '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'), }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { /** * @method * @name probit#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the probit api endpoint * @returns {object} an associative dictionary of currencies */ 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 depositSuspended = this.safeValue (platform, 'deposit_suspended'); const withdrawalSuspended = this.safeValue (platform, 'withdrawal_suspended'); const deposit = !depositSuspended; const withdraw = !withdrawalSuspended; const active = deposit && withdraw; 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, 'deposit': deposit, 'withdraw': withdraw, 'fee': fee, 'precision': this.parseNumber (this.parsePrecision (this.safeString (platform, 'precision'))), 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': this.safeNumber (platform, 'min_deposit_amount'), 'max': undefined, }, 'withdraw': { 'min': this.safeNumber (platform, 'min_withdrawal_amount'), 'max': undefined, }, }, }; } return result; } parseBalance (response) { 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.safeBalance (result); } async fetchBalance (params = {}) { /** * @method * @name probit#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @param {object} params extra parameters specific to the probit api endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ await this.loadMarkets (); const response = await this.privateGetBalance (params); // // { // data: [ // { // "currency_id":"XRP", // "total":"100", // "available":"0", // } // ] // } // return this.parseBalance (response); } async fetchOrderBook (symbol, limit = undefined, params = {}) { /** * @method * @name probit#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @param {string} symbol unified symbol of the market to fetch the order book for * @param {int|undefined} limit the maximum amount of order book entries to return * @param {object} params extra parameters specific to the probit api endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols */ 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, market['symbol'], undefined, 'buy', 'sell', 'price', 'quantity'); } async fetchTickers (symbols = undefined, params = {}) { /** * @method * @name probit#fetchTickers * @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market * @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 probit api endpoint * @returns {object} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ 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 = {}) { /** * @method * @name probit#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} params extra parameters specific to the probit api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ 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.safeString (ticker, 'last'); const change = this.safeString (ticker, 'change'); const baseVolume = this.safeString (ticker, 'base_volume'); const quoteVolume = this.safeString (ticker, 'quote_volume'); return this.safeTicker ({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeString (ticker, 'high'), 'low': this.safeString (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': close, 'last': close, 'previousClose': undefined, // previous day close 'change': change, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }, market); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name probit#fetchMyTrades * @description fetch all trades made by the user * @param {string|undefined} symbol unified market symbol * @param {int|undefined} since the earliest time in ms to fetch trades for * @param {int|undefined} limit the maximum number of trades structures to retrieve * @param {object} params extra parameters specific to the probit api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure} */ 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 = {}) { /** * @method * @name probit#fetchTrades * @description get the list of most recent trades for a particular symbol * @param {string} symbol unified symbol of the market to fetch trades for * @param {int|undefined} since timestamp in ms of the earliest trade to fetch * @param {int|undefined} limit the maximum amount of trades to fetch * @param {object} params extra parameters specific to the probit api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades} */ 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 orderId = this.safeString (trade, 'order_id'); const feeCostString = this.safeString (trade, 'fee_amount'); let fee = undefined; if (feeCostString !== undefined) { const feeCurrencyId = this.safeString (trade, 'fee_currency_id'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCostString, 'currency': feeCurrencyCode, }; } return this.safeTrade ({ 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': fee, }, market); } async fetchTime (params = {}) { /** * @method * @name probit#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @param {object} params extra parameters specific to the probit api endpoint * @returns {int} the current integer timestamp in milliseconds from the exchange server */ 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 = Math.floor (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 = {}) { /** * @method * @name probit#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|undefined} since timestamp in ms of the earliest candle to fetch * @param {int|undefined} limit the maximum amount of candles to fetch * @param {object} params extra parameters specific to the probit api endpoint * @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume */ 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) { limit = requestLimit; } 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 = {}) { /** * @method * @name probit#fetchOpenOrders * @description fetch all unfilled currently open orders * @param {string|undefined} symbol unified market symbol * @param {int|undefined} since the earliest time in ms to fetch open orders for * @param {int|undefined} limit the maximum number of open orders structures to retrieve * @param {object} params extra parameters specific to the probit api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ 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 = {}) { /** * @method * @name probit#fetchClosedOrders * @description fetches information on multiple closed orders made by the user * @param {string|undefined} symbol unified market symbol of the market orders were made in * @param {int|undefined} since the earliest time in ms to fetch orders for * @param {int|undefined} limit the maximum number of orde structures to retrieve * @param {object} params extra parameters specific to the probit api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ 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 = {}) { /** * @method * @name probit#fetchOrder * @description fetches information on an order made by the user * @param {string} symbol unified symbol of the market the order was made in * @param {object} params extra parameters specific to the probit api endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ 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.safeString (order, 'limit_price'); const filled = this.safeString (order, 'filled_quantity'); let remaining = this.safeString (order, 'open_quantity'); const canceledAmount = this.safeString (order, 'cancelled_quantity'); if (canceledAmount !== undefined) { remaining = Precise.stringAdd (remaining, canceledAmount); } const amount = this.safeString (order, 'quantity', Precise.stringAdd (filled, remaining)); const cost = this.safeString2 (order, 'filled_cost', 'cost'); if (type === 'market') { price = undefined; } const clientOrderId = this.safeString (order, 'client_order_id'); 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, }, market); } costToPrecision (symbol, cost) { return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['cost'], this.precisionMode); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { /** * @method * @name probit#createOrder * @description create a trade order * @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|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders * @param {object} params extra parameters specific to the probit api endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ 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');