UNPKG

sfccxt

Version:

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

1,157 lines (1,134 loc) 84.1 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, InvalidOrder, InsufficientFunds, AuthenticationError, RateLimitExceeded, BadSymbol } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class qtrade extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'qtrade', 'name': 'qTrade', 'countries': [ 'US' ], 'rateLimit': 1000, 'version': 'v1', 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/80491487-74a99c00-896b-11ea-821e-d307e832f13e.jpg', 'api': { 'rest': 'https://api.qtrade.io', }, 'www': 'https://qtrade.io', 'doc': 'https://qtrade-exchange.github.io/qtrade-docs', 'referral': 'https://qtrade.io/?ref=BKOQWVFGRH2C', 'fees': 'https://qtrade.io/fees', }, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'addMargin': false, 'cancelOrder': true, 'createMarketOrder': undefined, '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, 'fetchDeposit': true, 'fetchDepositAddress': true, 'fetchDeposits': 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, 'fetchOrders': true, 'fetchPosition': false, 'fetchPositionMode': false, 'fetchPositions': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': false, 'fetchTransactions': undefined, 'fetchTransfer': false, 'fetchTransfers': true, 'fetchWithdrawal': true, 'fetchWithdrawals': true, 'reduceMargin': false, 'setLeverage': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, 'withdraw': true, }, 'timeframes': { '5m': 'fivemin', '15m': 'fifteenmin', '30m': 'thirtymin', '1h': 'onehour', '2h': 'twohour', '4h': 'fourhour', '1d': 'oneday', }, 'api': { 'public': { 'get': [ 'ticker/{market_string}', 'tickers', 'currency/{code}', 'currencies', 'common', 'market/{market_string}', 'markets', 'market/{market_string}/trades', 'orderbook/{market_string}', 'market/{market_string}/ohlcv/{interval}', ], }, 'private': { 'get': [ 'me', 'balances', 'balances_all', // undocumented 'market/{market_string}', 'orders', 'order/{order_id}', 'trades', 'withdraw/{withdraw_id}', 'withdraws', 'deposit/{deposit_id}', 'deposits', 'transfers', ], 'post': [ 'cancel_order', 'withdraw', 'deposit_address/{currency}', 'sell_limit', 'buy_limit', ], }, }, 'fees': { 'trading': { 'feeSide': 'quote', 'tierBased': true, 'percentage': true, 'taker': this.parseNumber ('0.005'), 'maker': this.parseNumber ('0.0'), }, 'funding': { 'withdraw': {}, }, }, 'commonCurrencies': { 'BTM': 'Bitmark', }, 'precisionMode': TICK_SIZE, 'exceptions': { 'exact': { 'invalid_auth': AuthenticationError, 'insuff_funds': InsufficientFunds, 'market_not_found': BadSymbol, // {"errors":[{"code":"market_not_found","title":"Requested market does not exist"}]} 'too_small': InvalidOrder, 'limit_exceeded': RateLimitExceeded, // {"errors":[{"code":"limit_exceeded","title":"You have exceeded the windowed rate limit. Please see docs."}]} }, }, }); } async fetchMarkets (params = {}) { /** * @method * @name qtrade#fetchMarkets * @description retrieves data on all markets for qtrade * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ const response = await this.publicGetMarkets (params); // // { // "data":{ // "markets":[ // { // "id":5, // "market_currency":"BAC", // "base_currency":"BTC", // "maker_fee":"0.0025", // "taker_fee":"0.0025", // "metadata":{ // "delisting_date":"7/15/2018", // "market_notices":[ // { // "message":"Delisting Notice: This market has been delisted due to low volume. Please cancel your orders and withdraw your funds by 7/15/2018.", // "type":"warning" // } // ] // }, // "can_trade":false, // "can_cancel":true, // "can_view":false, // "market_string":"BAC_BTC", // "minimum_sell_amount":"0.0001", // "minimum_buy_value":"0.0001", // "market_precision":8, // note, they have reversed understanding of 'quote' vs 'base' concepts // "base_precision":8 // as noted in above comment // }, // ], // } // } // const data = this.safeValue (response, 'data', {}); const markets = this.safeValue (data, 'markets', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const marketId = this.safeString (market, 'market_string'); const numericId = this.safeInteger (market, 'id'); const baseId = this.safeString (market, 'market_currency'); const quoteId = this.safeString (market, 'base_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const canView = this.safeValue (market, 'can_view', false); const canTrade = this.safeValue (market, 'can_trade', false); const active = canTrade && canView; result.push ({ 'id': marketId, 'numericId': numericId, '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': active, 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': this.safeNumber (market, 'taker_fee'), 'maker': this.safeNumber (market, 'maker_fee'), 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'market_precision'))), 'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'base_precision'))), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.safeNumber (market, 'minimum_sell_value'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber (market, 'minimum_buy_value'), 'max': undefined, }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { /** * @method * @name qtrade#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the qtrade api endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.publicGetCurrencies (params); // // { // "data":{ // "currencies":[ // { // "code":"DGB", // "long_name":"Digibyte", // "type":"bitcoin_like", // "precision":8, // "config":{ // "price":0.0035, // "withdraw_fee":"10", // "deposit_types":[ // { // "label":"Address", // "lookup_mode":"address", // "render_type":"address", // "deposit_type":"address", // "lookup_config":{} // } // ], // "default_signer":103, // "address_version":30, // "satoshi_per_byte":300, // "required_confirmations":200, // "required_generate_confirmations":300 // }, // "metadata":{}, // "minimum_order":"0.0001", // "status":"ok", // "can_withdraw":true, // "delisted":false, // "deposit_disabled":false, // "withdraw_disabled":false, // "deposit_warn_codes":[], // "withdraw_warn_codes":[] // }, // ], // } // } // const data = this.safeValue (response, 'data', {}); const currencies = this.safeValue (data, 'currencies', []); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'code'); const code = this.safeCurrencyCode (id); const name = this.safeString (currency, 'long_name'); const type = this.safeString (currency, 'type'); const canWithdraw = this.safeValue (currency, 'can_withdraw', true); const withdrawDisabled = this.safeValue (currency, 'withdraw_disabled', false); const depositDisabled = this.safeValue (currency, 'deposit_disabled', false); const deposit = !depositDisabled; const withdraw = canWithdraw && !withdrawDisabled; const config = this.safeValue (currency, 'config', {}); const status = this.safeString (currency, 'status'); const active = withdraw && deposit && (status === 'ok'); result[code] = { 'id': id, 'code': code, 'info': currency, 'type': type, 'name': name, 'fee': this.safeNumber (config, 'withdraw_fee'), 'precision': this.parseNumber (this.parsePrecision (this.safeString (currency, 'precision'))), 'active': active, 'deposit': deposit, 'withdraw': withdraw, 'limits': { 'amount': { 'min': this.safeNumber (currency, 'minimum_order'), 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, }; } return result; } parseOHLCV (ohlcv, market = undefined) { // // { // "time":"2019-12-07T22:55:00Z", // "open":"0.00197", // "high":"0.00197", // "low":"0.00197", // "close":"0.00197", // "volume":"0.00016676", // "market_volume":"0.08465047" // } // return [ this.parse8601 (this.safeString (ohlcv, 'time')), this.safeNumber (ohlcv, 'open'), this.safeNumber (ohlcv, 'high'), this.safeNumber (ohlcv, 'low'), this.safeNumber (ohlcv, 'close'), this.safeNumber (ohlcv, 'market_volume'), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { /** * @method * @name qtrade#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 qtrade 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 request = { 'market_string': market['id'], 'interval': this.timeframes[timeframe], }; const response = await this.publicGetMarketMarketStringOhlcvInterval (this.extend (request, params)); // // { // "data":{ // "slices":[ // {"time":"2019-12-07T22:55:00Z","open":"0.00197","high":"0.00197","low":"0.00197","close":"0.00197","volume":"0.00016676","market_volume":"0.08465047"}, // {"time":"2019-12-07T23:00:00Z","open":"0.00197","high":"0.00197","low":"0.00197","close":"0.00197","volume":"0","market_volume":"0"}, // {"time":"2019-12-07T23:05:00Z","open":"0.00197","high":"0.00197","low":"0.00197","close":"0.00197","volume":"0","market_volume":"0"}, // ] // } // } // const data = this.safeValue (response, 'data', {}); const ohlcvs = this.safeValue (data, 'slices', []); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { /** * @method * @name qtrade#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 qtrade 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_string': market['id'] }; const response = await this.publicGetOrderbookMarketString (this.extend (request, params)); // // { // "data":{ // "buy":{ // "0.00700015":"4.76196367", // "0.00700017":"1.89755391", // "0.00700018":"2.13214088", // }, // "last_change":1588539869958811, // "sell":{ // "0.02418662":"0.19513696", // "0.02465627":"0.2439212", // "0.02530277":"0.663475931274359255", // } // } // } // const data = this.safeValue (response, 'data', {}); const orderbook = {}; const sides = { 'buy': 'bids', 'sell': 'asks' }; const keys = Object.keys (sides); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const side = sides[key]; const bidasks = this.safeValue (data, key, {}); const prices = Object.keys (bidasks); const result = []; for (let j = 0; j < prices.length; j++) { const priceAsString = prices[j]; const price = this.safeNumber (prices, j); const amount = this.safeNumber (bidasks, priceAsString); result.push ([ price, amount ]); } orderbook[side] = result; } const timestamp = this.safeIntegerProduct (data, 'last_change', 0.001); return this.parseOrderBook (orderbook, market['symbol'], timestamp); } parseTicker (ticker, market = undefined) { // // fetchTicker, fetchTickers // // { // "ask":"0.02423119", // "bid":"0.0230939", // "day_avg_price":"0.0247031874349301", // "day_change":"-0.0237543162270376", // "day_high":"0.02470552", // "day_low":"0.02470172", // "day_open":"0.02530277", // "day_volume_base":"0.00268074", // "day_volume_market":"0.10851798", // "id":41, // "id_hr":"ETH_BTC", // "last":"0.02470172", // "last_change":1588533365354609 // } // const marketId = this.safeString (ticker, 'id_hr'); const symbol = this.safeSymbol (marketId, market, '_'); const timestamp = this.safeIntegerProduct (ticker, 'last_change', 0.001); const previous = this.safeString (ticker, 'day_open'); const last = this.safeString (ticker, 'last'); const day_change = this.safeString (ticker, 'day_change'); const average = this.safeString (ticker, 'day_avg_price'); const baseVolume = this.safeString (ticker, 'day_volume_market'); const quoteVolume = this.safeString (ticker, 'day_volume_base'); const percentage = Precise.stringMul (day_change, '100'); const change = Precise.stringMul (day_change, previous); return this.safeTicker ({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeString (ticker, 'day_high'), 'low': this.safeString (ticker, 'day_low'), 'bid': this.safeString (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeString (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': previous, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }, market); } async fetchTickers (symbols = undefined, params = {}) { /** * @method * @name qtrade#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 qtrade api endpoint * @returns {object} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ await this.loadMarkets (); symbols = this.marketSymbols (symbols); const response = await this.publicGetTickers (params); // // { // "data":{ // "markets":[ // { // "ask":"0.0000003", // "bid":"0.00000029", // "day_avg_price":"0.0000002999979728", // "day_change":"0.0344827586206897", // "day_high":"0.0000003", // "day_low":"0.0000003", // "day_open":"0.00000029", // "day_volume_base":"0.00591958", // "day_volume_market":"19732.06666665", // "id":36, // "id_hr":"DOGE_BTC", // "last":"0.0000003", // "last_change":1588534202130778 // }, // ] // } // } // const data = this.safeValue (response, 'data', {}); const tickers = this.safeValue (data, 'markets', []); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = this.parseTicker (tickers[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return this.filterByArray (result, 'symbol', symbols); } async fetchTicker (symbol, params = {}) { /** * @method * @name qtrade#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 qtrade 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_string': market['id'], }; const response = await this.publicGetTickerMarketString (this.extend (request, params)); // // { // "data":{ // "ask":"0.02423119", // "bid":"0.0230939", // "day_avg_price":"0.0247031874349301", // "day_change":"-0.0237543162270376", // "day_high":"0.02470552", // "day_low":"0.02470172", // "day_open":"0.02530277", // "day_volume_base":"0.00268074", // "day_volume_market":"0.10851798", // "id":41, // "id_hr":"ETH_BTC", // "last":"0.02470172", // "last_change":1588533365354609 // } // } // const data = this.safeValue (response, 'data', {}); return this.parseTicker (data, market); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name qtrade#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 qtrade 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_string': market['id'], // 'older_than': 123, // returns trades with id < older_than // 'newer_than': 123, // returns trades with id > newer_than }; const response = await this.publicGetMarketMarketStringTrades (this.extend (request, params)); // // { // "data":{ // "trades":[ // { // "id":85507, // "amount":"0.09390502", // "price":"0.02556325", // "base_volume":"0.00240051", // "seller_taker":true, // "side":"sell", // "created_at":"0001-01-01T00:00:00Z", // "created_at_ts":1581560391338718 // }, // ] // } // } // const data = this.safeValue (response, 'data', {}); const trades = this.safeValue (data, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name qtrade#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 qtrade api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure} */ await this.loadMarkets (); const request = { 'desc': true, // Returns newest trades first when true // 'older_than': 123, // returns trades with id < older_than // 'newer_than': 123, // returns trades with id > newer_than }; let market = undefined; const numericId = this.safeValue (params, 'market_id'); if (numericId !== undefined) { request['market_id'] = numericId; // mutually exclusive with market_string } else if (symbol !== undefined) { market = this.market (symbol); request['market_string'] = market['id']; } const response = await this.privateGetTrades (this.extend (request, params)); // // { // "data":{ // "trades":[ // { // "id":107331, // "market_amount":"0.1082536946986", // "price":"0.0230939", // "base_amount":"0.00249999", // "order_id":13790596, // "market_id":41, // "market_string":"ETH_BTC", // "taker":true, // "base_fee":"0.00001249", // "side":"sell", // "created_at":"2020-05-04T06:08:18.513413Z" // } // ] // } // } // const data = this.safeValue (response, 'data', {}); const trades = this.safeValue (data, 'trades', []); return this.parseTrades (trades, market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "id":85507, // "amount":"0.09390502", // "price":"0.02556325", // "base_volume":"0.00240051", // "seller_taker":true, // "side":"sell", // "created_at":"0001-01-01T00:00:00Z", // "created_at_ts":1581560391338718 // } // // fetchMyTrades (private) // // { // "id":107331, // "market_amount":"0.1082536946986", // "price":"0.0230939", // "base_amount":"0.00249999", // "order_id":13790596, // "market_id":41, // "market_string":"ETH_BTC", // "taker":true, // "base_fee":"0.00001249", // "side":"sell", // "created_at":"2020-05-04T06:08:18.513413Z" // } // // createOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders // // { // "base_amount": "9.58970687", // "base_fee": "0.02397426", // "created_at": "0001-01-01T00:00:00Z", // "id": 0, // "market_amount": "0.97179355", // "price": "9.86804952", // "taker": true // } // const id = this.safeString (trade, 'id'); let timestamp = this.safeIntegerProduct (trade, 'created_at_ts', 0.001); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (trade, 'created_at')); } const side = this.safeString (trade, 'side'); const marketId = this.safeString (trade, 'market_string'); market = this.safeMarket (marketId, market); const cost = this.safeString2 (trade, 'base_volume', 'base_amount'); const price = this.safeString (trade, 'price'); const amount = this.safeString2 (trade, 'market_amount', 'amount'); let fee = undefined; const feeCost = this.safeString (trade, 'base_fee'); if (feeCost !== undefined) { const feeCurrencyCode = (market === undefined) ? undefined : market['quote']; fee = { 'currency': feeCurrencyCode, 'cost': feeCost, }; } const taker = this.safeValue (trade, 'taker', true); const takerOrMaker = taker ? 'taker' : 'maker'; const orderId = this.safeString (trade, 'order_id'); return this.safeTrade ({ 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': market['symbol'], 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }, market); } async fetchTradingFee (symbol, params = {}) { /** * @method * @name qtrade#fetchTradingFee * @description fetch the trading fees for a market * @param {string} symbol unified market symbol * @param {object} params extra parameters specific to the qtrade api endpoint * @returns {object} a [fee structure]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure} */ await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_string': market['id'], }; const response = await this.publicGetMarketMarketString (this.extend (request, params)); // // { // data: { // market: { // id: '41', // market_currency: 'ETH', // base_currency: 'BTC', // maker_fee: '0', // taker_fee: '0.005', // metadata: {}, // can_trade: true, // can_cancel: true, // can_view: true, // market_string: 'ETH_BTC', // minimum_sell_amount: '0.001', // minimum_buy_value: '0.0001', // market_precision: '18', // base_precision: '8' // }, // recent_trades: [] // } // } // const data = this.safeValue (response, 'data', {}); const marketData = this.safeValue (data, 'market', {}); return { 'info': marketData, 'symbol': market['symbol'], 'maker': this.safeNumber (marketData, 'maker_fee'), 'taker': this.safeNumber (marketData, 'taker_fee'), 'percentage': true, 'tierBased': true, }; } parseBalance (response) { const data = this.safeValue (response, 'data', {}); let balances = this.safeValue (data, 'balances', []); const result = { 'info': response, 'timestamp': undefined, 'datetime': undefined, }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = (code in result) ? result[code] : this.account (); account['free'] = this.safeString (balance, 'balance'); account['used'] = '0'; result[code] = account; } balances = this.safeValue (data, 'order_balances', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = (code in result) ? result[code] : this.account (); account['used'] = this.safeString (balance, 'balance'); result[code] = account; } return this.safeBalance (result); } async fetchBalance (params = {}) { /** * @method * @name qtrade#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 qtrade 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.privateGetBalancesAll (params); // // { // "data":{ // "balances": [ // { "balance": "100000000", "currency": "BCH" }, // { "balance": "99992435.78253015", "currency": "LTC" }, // { "balance": "99927153.76074182", "currency": "BTC" }, // ], // "order_balances":[], // "limit_used":0, // "limit_remaining":4000, // "limit":4000 // } // } // return this.parseBalance (response); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { /** * @method * @name qtrade#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 qtrade api endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ if (type !== 'limit') { throw new InvalidOrder (this.id + ' createOrder() allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'amount': this.amountToPrecision (market['symbol'], amount), 'market_id': market['numericId'], 'price': this.priceToPrecision (market['symbol'], price), }; const method = (side === 'sell') ? 'privatePostSellLimit' : 'privatePostBuyLimit'; const response = await this[method] (this.extend (request, params)); // // { // "data": { // "order": { // "created_at": "2018-04-06T20:46:52.899248Z", // "id": 13253, // "market_amount": "1", // "market_amount_remaining": "0", // "market_id": 1, // "open": false, // "order_type": "sell_limit", // "price": "0.01", // "trades": [ // { // "base_amount": "0.27834267", // "base_fee": "0.00069585", // "created_at": "0001-01-01T00:00:00Z", // "id": 0, // "market_amount": "0.02820645", // "price": "9.86805058", // "taker": true // }, // { // "base_amount": "9.58970687", // "base_fee": "0.02397426", // "created_at": "0001-01-01T00:00:00Z", // "id": 0, // "market_amount": "0.97179355", // "price": "9.86804952", // "taker": true // } // ] // } // } // } // const data = this.safeValue (response, 'data', {}); const order = this.safeValue (data, 'order', {}); return this.parseOrder (order, market); } parseOrder (order, market = undefined) { // // createOrder // // { // "created_at": "2018-04-06T20:46:52.899248Z", // "id": 13253, // "market_amount": "1", // "market_amount_remaining": "0", // "market_id": 1, // "open": false, // "order_type": "sell_limit", // "price": "0.01", // "trades": [ // { // "base_amount": "0.27834267", // "base_fee": "0.00069585", // "created_at": "0001-01-01T00:00:00Z", // "id": 0, // "market_amount": "0.02820645", // "price": "9.86805058", // "taker": true // }, // { // "base_amount": "9.58970687", // "base_fee": "0.02397426", // "created_at": "0001-01-01T00:00:00Z", // "id": 0, // "market_amount": "0.97179355", // "price": "9.86804952", // "taker": true // } // ] // } // // fetchOrder // // { // id: 13790596, // market_amount: "0.15", // market_amount_remaining: "0", // created_at: "2020-05-04T06:08:18.513413Z", // price: "0.0230939", // base_amount: "0", // order_type: "sell_limit", // market_id: 41, // market_string: "ETH_BTC", // open: false, // trades: [ // { // id: 107331, // market_amount: "0.1082536946986", // price: "0.0230939", // base_amount: "0.00249999", // taker: true, // base_fee: "0.00001249", // created_at: "2020-05-04T06:08:18.513413Z", // } // ], // close_reason: "canceled" // } // const id = this.safeString (order, 'id'); const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const sideType = this.safeString (order, 'order_type'); let orderType = undefined; let side = undefined; if (sideType !== undefined) { const parts = sideType.split ('_'); side = this.safeString (parts, 0); orderType = this.safeString (parts, 1); } const price = this.safeString (order, 'price'); const amount = this.safeString (order, 'market_amount'); const remaining = this.safeString (order, 'market_amount_remaining'); const open = this.safeValue (order, 'open', false); const closeReason = this.safeString (order, 'close_reason'); let status = undefined; if (open) { status = 'open'; } else if (closeReason === 'canceled') { status = 'canceled'; } else { status = 'closed'; } const marketId = this.safeString (order, 'market_string'); market = this.safeMarket (marketId, market, '_'); const symbol = market['symbol']; const rawTrades = this.safeValue (order, 'trades', []); return this.safeOrder ({ 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': orderType, 'timeInForce': undefined, 'postOnly': undefined, 'side': side, 'price': price, 'stopPrice': undefined, 'average': undefined, 'amount': amount, 'remaining': remaining, 'filled': undefined, 'status': status, 'fee': undefined, 'fees': undefined, 'cost': undefined, 'trades': rawTrades, }, market); } async cancelOrder (id, symbol = undefined, params = {}) { /** * @method * @name qtrade#cancelOrder * @description cancels an open order * @param {string} id order id * @param {string|undefined} symbol not used by qtrade cancelOrder () * @param {object} params extra parameters specific to the qtrade api endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ const request = { 'id': parseInt (id), }; // successful cancellation returns 200 with no payload return await this.privatePostCancelOrder (this.extend (request, params)); } async fetchOrder (id, symbol = undefined, params = {}) { /** * @method * @name qtrade#fetchOrder * @description fetches information on an order made by the user * @param {string|undefined} symbol not used by qtrade fetchOrder * @param {object} params extra parameters specific to the qtrade api endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ await this.loadMarkets (); const request = { 'order_id': id }; const response = await this.privateGetOrderOrderId (this.extend (request, params)); // // { // "data":{ // "order":{ // "id":13790596, // "market_amount":"0.15", // "market_amount_remaining":"0.0417463053014", // "created_at":"2020-05-04T06:08:18.513413Z", // "price":"0.0230939", // "order_type":"sell_limit", // "market_id":41, // "market_string":"ETH_BTC", // "open":true, // "trades":[ // { // "id":107331, // "market_amount":"0.1082536946986", // "price":"0.0230939", // "base_amount":"0.00249999", // "taker":true, // "base_fee":"0.00001249", // "created_at":"2020-05-04T06:08:18.513413Z" // } // ] // } // } // } // const data = this.safeValue (response, 'data', {}); const order = this.safeValue (data, 'order', {}); return this.parseOrder (order); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name qtrade#fetchOrders * @description fetches information on multiple 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