UNPKG

consequunturatque

Version:

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

1,051 lines (1,022 loc) 43.5 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, BadRequest } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class currencycom extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'currencycom', 'name': 'Currency.com', 'countries': [ 'BY' ], // Belarus 'rateLimit': 500, 'certified': true, 'pro': true, 'version': 'v1', // new metainfo interface 'has': { 'cancelOrder': true, 'CORS': false, 'createOrder': true, 'fetchAccounts': true, 'fetchBalance': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrderBook': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTradingFees': true, 'fetchTrades': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '1d': '1d', '1w': '1w', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg', 'api': { 'public': 'https://api-adapter.backend.currency.com/api', 'private': 'https://api-adapter.backend.currency.com/api', }, 'www': 'https://www.currency.com', 'referral': 'https://currency.com/trading/signup?c=362jaimv&pid=referral', 'doc': [ 'https://currency.com/api', ], 'fees': 'https://currency.com/fees-charges', }, 'api': { 'public': { 'get': [ 'time', 'exchangeInfo', 'depth', 'aggTrades', 'klines', 'ticker/24hr', ], }, 'private': { 'get': [ 'leverageSettings', 'openOrders', 'tradingPositions', 'account', 'myTrades', ], 'post': [ 'order', 'updateTradingPosition', 'updateTradingOrder', 'closeTradingPosition', ], 'delete': [ 'order', ], }, }, 'fees': { 'trading': { 'feeSide': 'get', 'tierBased': false, 'percentage': true, 'taker': 0.002, 'maker': 0.002, }, }, 'precisionMode': TICK_SIZE, // exchange-specific options 'options': { 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel, 'FOK' = Fill Or Kill 'warnOnFetchOpenOrdersWithoutSymbol': true, 'recvWindow': 5 * 1000, // 5 sec, default 'timeDifference': 0, // the difference between system clock and Binance clock 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision 'newOrderRespType': { 'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills 'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT' 'stop': 'RESULT', }, }, 'exceptions': { 'broad': { 'FIELD_VALIDATION_ERROR Cancel is available only for LIMIT order': InvalidOrder, 'API key does not exist': AuthenticationError, 'Order would trigger immediately.': InvalidOrder, 'Account has insufficient balance for requested action.': InsufficientFunds, 'Rest API trading is not enabled.': ExchangeNotAvailable, }, 'exact': { '-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."} '-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL '-1021': InvalidNonce, // 'your time is ahead of server' '-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."} '-1100': InvalidOrder, // createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price' '-1104': ExchangeError, // Not all sent parameters were read, read 8 parameters but was sent 9 '-1025': AuthenticationError, // {"code":-1025,"msg":"Invalid API-key, IP, or permissions for action"} '-1128': BadRequest, // {"code":-1128,"msg":"Combination of optional parameters invalid."} '-2010': ExchangeError, // generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc... '-2011': OrderNotFound, // cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER' '-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist' '-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." } '-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action." }, }, 'commonCurrencies': { 'IQ': 'iQIYI', }, }); } nonce () { return this.milliseconds () - this.options['timeDifference']; } async fetchTime (params = {}) { const response = await this.publicGetTime (params); // // { // "serverTime": 1590998366609 // } // return this.safeInteger (response, 'serverTime'); } async loadTimeDifference (params = {}) { const response = await this.publicGetTime (params); const after = this.milliseconds (); this.options['timeDifference'] = parseInt (after - response['serverTime']); return this.options['timeDifference']; } async fetchMarkets (params = {}) { const response = await this.publicGetExchangeInfo (params); // // { // "timezone":"UTC", // "serverTime":1603252990096, // "rateLimits":[ // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, // {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":1,"limit":10}, // {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":864000}, // ], // "exchangeFilters":[], // "symbols":[ // { // "symbol":"EVK", // "name":"Evonik", // "status":"BREAK", // "baseAsset":"EVK", // "baseAssetPrecision":3, // "quoteAsset":"EUR", // "quoteAssetId":"EUR", // "quotePrecision":3, // "orderTypes":["LIMIT","MARKET"], // "filters":[ // {"filterType":"LOT_SIZE","minQty":"1","maxQty":"27000","stepSize":"1"}, // {"filterType":"MIN_NOTIONAL","minNotional":"23"} // ], // "marketType":"SPOT", // "country":"DE", // "sector":"Basic Materials", // "industry":"Diversified Chemicals", // "tradingHours":"UTC; Mon 07:02 - 15:30; Tue 07:02 - 15:30; Wed 07:02 - 15:30; Thu 07:02 - 15:30; Fri 07:02 - 15:30", // "tickSize":0.005, // "tickValue":0.11125, // "exchangeFee":0.05 // }, // { // "symbol":"BTC/USD_LEVERAGE", // "name":"Bitcoin / USD", // "status":"TRADING", // "baseAsset":"BTC", // "baseAssetPrecision":3, // "quoteAsset":"USD", // "quoteAssetId":"USD_LEVERAGE", // "quotePrecision":3, // "orderTypes":["LIMIT","MARKET","STOP"], // "filters":[ // {"filterType":"LOT_SIZE","minQty":"0.001","maxQty":"100","stepSize":"0.001"}, // {"filterType":"MIN_NOTIONAL","minNotional":"13"} // ], // "marketType":"LEVERAGE", // "longRate":-0.01, // "shortRate":0.01, // "swapChargeInterval":480, // "country":"", // "sector":"", // "industry":"", // "tradingHours":"UTC; Mon - 21:00, 21:05 -; Tue - 21:00, 21:05 -; Wed - 21:00, 21:05 -; Thu - 21:00, 21:05 -; Fri - 21:00, 22:01 -; Sat - 21:00, 21:05 -; Sun - 20:00, 21:05 -", // "tickSize":0.05, // "tickValue":610.20875, // "makerFee":-0.025, // "takerFee":0.075 // }, // ] // } // if (this.options['adjustForTimeDifference']) { await this.loadTimeDifference (); } const markets = this.safeValue (response, 'symbols'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'baseAsset'); const quoteId = this.safeString (market, 'quoteAsset'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); let symbol = base + '/' + quote; if (id.indexOf ('/') >= 0) { symbol = id; } const filters = this.safeValue (market, 'filters', []); const filtersByType = this.indexBy (filters, 'filterType'); const precision = { 'amount': 1 / Math.pow (1, this.safeInteger (market, 'baseAssetPrecision')), 'price': this.safeNumber (market, 'tickSize'), }; const status = this.safeString (market, 'status'); const active = (status === 'TRADING'); let type = this.safeStringLower (market, 'marketType'); if (type === 'leverage') { type = 'margin'; } const spot = (type === 'spot'); const margin = (type === 'margin'); const entry = { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'type': type, 'spot': spot, 'margin': margin, 'info': market, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': -Math.log10 (precision['amount']), 'max': undefined, }, }, }; const exchangeFee = this.safeNumber2 (market, 'exchangeFee', 'tradingFee'); const makerFee = this.safeNumber (market, 'makerFee', exchangeFee); const takerFee = this.safeNumber (market, 'takerFee', exchangeFee); if (makerFee !== undefined) { entry['maker'] = makerFee / 100; } if (takerFee !== undefined) { entry['taker'] = takerFee / 100; } if ('PRICE_FILTER' in filtersByType) { const filter = this.safeValue (filtersByType, 'PRICE_FILTER', {}); entry['precision']['price'] = this.safeNumber (filter, 'tickSize'); // PRICE_FILTER reports zero values for maxPrice // since they updated filter types in November 2018 // https://github.com/ccxt/ccxt/issues/4286 // therefore limits['price']['max'] doesn't have any meaningful value except undefined entry['limits']['price'] = { 'min': this.safeNumber (filter, 'minPrice'), 'max': undefined, }; const maxPrice = this.safeNumber (filter, 'maxPrice'); if ((maxPrice !== undefined) && (maxPrice > 0)) { entry['limits']['price']['max'] = maxPrice; } } if ('LOT_SIZE' in filtersByType) { const filter = this.safeValue (filtersByType, 'LOT_SIZE', {}); entry['precision']['amount'] = this.safeNumber (filter, 'stepSize'); entry['limits']['amount'] = { 'min': this.safeNumber (filter, 'minQty'), 'max': this.safeNumber (filter, 'maxQty'), }; } if ('MARKET_LOT_SIZE' in filtersByType) { const filter = this.safeValue (filtersByType, 'MARKET_LOT_SIZE', {}); entry['limits']['market'] = { 'min': this.safeNumber (filter, 'minQty'), 'max': this.safeNumber (filter, 'maxQty'), }; } if ('MIN_NOTIONAL' in filtersByType) { const filter = this.safeValue (filtersByType, 'MIN_NOTIONAL', {}); entry['limits']['cost']['min'] = this.safeNumber (filter, 'minNotional'); } result.push (entry); } return result; } async fetchAccounts (params = {}) { const response = await this.privateGetAccount (params); // // { // "makerCommission":0.20, // "takerCommission":0.20, // "buyerCommission":0.20, // "sellerCommission":0.20, // "canTrade":true, // "canWithdraw":true, // "canDeposit":true, // "updateTime":1591056268, // "balances":[ // { // "accountId":5470306579272968, // "collateralCurrency":true, // "asset":"ETH", // "free":0.0, // "locked":0.0, // "default":false, // }, // ] // } // const accounts = this.safeValue (response, 'balances', []); const result = []; for (let i = 0; i < accounts.length; i++) { const account = accounts[i]; const accountId = this.safeInteger (account, 'accountId'); const currencyId = this.safeString (account, 'asset'); const currencyCode = this.safeCurrencyCode (currencyId); result.push ({ 'id': accountId, 'type': undefined, 'currency': currencyCode, 'info': response, }); } return result; } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccount (params); return { 'info': response, 'maker': this.safeNumber (response, 'makerCommission'), 'taker': this.safeNumber (response, 'takerCommission'), }; } parseBalanceResponse (response) { // // { // "makerCommission":0.20, // "takerCommission":0.20, // "buyerCommission":0.20, // "sellerCommission":0.20, // "canTrade":true, // "canWithdraw":true, // "canDeposit":true, // "updateTime":1591056268, // "balances":[ // { // "accountId":5470306579272968, // "collateralCurrency":true, // "asset":"ETH", // "free":0.0, // "locked":0.0, // "default":false, // }, // ] // } // const result = { 'info': response }; const balances = this.safeValue (response, 'balances', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeString (balance, 'free'); account['used'] = this.safeString (balance, 'locked'); result[code] = account; } return this.parseBalance (result, false); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccount (params); // // { // "makerCommission":0.20, // "takerCommission":0.20, // "buyerCommission":0.20, // "sellerCommission":0.20, // "canTrade":true, // "canWithdraw":true, // "canDeposit":true, // "updateTime":1591056268, // "balances":[ // { // "accountId":5470306579272968, // "collateralCurrency":true, // "asset":"ETH", // "free":0.0, // "locked":0.0, // "default":false, // }, // ] // } // return this.parseBalanceResponse (response); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000, 5000 } const response = await this.publicGetDepth (this.extend (request, params)); // // { // "lastUpdateId":1590999849037, // "asks":[ // [0.02495,60.0000], // [0.02496,120.0000], // [0.02497,240.0000], // ], // "bids":[ // [0.02487,60.0000], // [0.02486,120.0000], // [0.02485,240.0000], // ] // } // const orderbook = this.parseOrderBook (response, symbol); orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId'); return orderbook; } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // "symbol":"ETH/BTC", // "priceChange":"0.00030", // "priceChangePercent":"1.21", // "weightedAvgPrice":"0.02481", // "prevClosePrice":"0.02447", // "lastPrice":"0.02477", // "lastQty":"60.0", // "bidPrice":"0.02477", // "askPrice":"0.02484", // "openPrice":"0.02447", // "highPrice":"0.02524", // "lowPrice":"0.02438", // "volume":"11.97", // "quoteVolume":"0.298053", // "openTime":1590969600000, // "closeTime":1591000072693 // } // // fetchTickers // // { // "symbol":"EVK", // "highPrice":"22.57", // "lowPrice":"22.16", // "volume":"1", // "quoteVolume":"22.2", // "openTime":1590699364000, // "closeTime":1590785764000 // } // const timestamp = this.safeInteger (ticker, 'closeTime'); const marketId = this.safeString (ticker, 'symbol'); const symbol = this.safeSymbol (marketId, market); const last = this.safeNumber (ticker, 'lastPrice'); const open = this.safeNumber (ticker, 'openPrice'); let average = undefined; if ((open !== undefined) && (last !== undefined)) { average = this.sum (open, last) / 2; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'highPrice'), 'low': this.safeNumber (ticker, 'lowPrice'), 'bid': this.safeNumber (ticker, 'bidPrice'), 'bidVolume': this.safeNumber (ticker, 'bidQty'), 'ask': this.safeNumber (ticker, 'askPrice'), 'askVolume': this.safeNumber (ticker, 'askQty'), 'vwap': this.safeNumber (ticker, 'weightedAvgPrice'), 'open': open, 'close': last, 'last': last, 'previousClose': this.safeNumber (ticker, 'prevClosePrice'), // previous day close 'change': this.safeNumber (ticker, 'priceChange'), 'percentage': this.safeNumber (ticker, 'priceChangePercent'), 'average': average, 'baseVolume': this.safeNumber (ticker, 'volume'), 'quoteVolume': this.safeNumber (ticker, 'quoteVolume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTicker24hr (this.extend (request, params)); // // { // "symbol":"ETH/BTC", // "priceChange":"0.00030", // "priceChangePercent":"1.21", // "weightedAvgPrice":"0.02481", // "prevClosePrice":"0.02447", // "lastPrice":"0.02477", // "lastQty":"60.0", // "bidPrice":"0.02477", // "askPrice":"0.02484", // "openPrice":"0.02447", // "highPrice":"0.02524", // "lowPrice":"0.02438", // "volume":"11.97", // "quoteVolume":"0.298053", // "openTime":1590969600000, // "closeTime":1591000072693 // } // return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker24hr (params); // // [ // { // "symbol":"EVK", // "highPrice":"22.57", // "lowPrice":"22.16", // "volume":"1", // "quoteVolume":"22.2", // "openTime":1590699364000, // "closeTime":1590785764000 // } // ] // return this.parseTickers (response, symbols); } parseOHLCV (ohlcv, market = undefined) { // // [ // 1590971040000, // "0.02454", // "0.02456", // "0.02452", // "0.02456", // 249 // ] // return [ this.safeInteger (ohlcv, 0), this.safeNumber (ohlcv, 1), this.safeNumber (ohlcv, 2), this.safeNumber (ohlcv, 3), this.safeNumber (ohlcv, 4), this.safeNumber (ohlcv, 5), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'interval': this.timeframes[timeframe], }; if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 500, max 1000 } const response = await this.publicGetKlines (this.extend (request, params)); // // [ // [1590971040000,"0.02454","0.02456","0.02452","0.02456",249], // [1590971100000,"0.02455","0.02457","0.02452","0.02456",300], // [1590971160000,"0.02455","0.02456","0.02453","0.02454",286], // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public aggregate trades) // // { // "a":1658318071, // "p":"0.02476", // "q":"0.0", // "T":1591001423382, // "m":false // } // // createOrder fills (private) // // { // "price": "9807.05", // "qty": "0.01", // "commission": "0", // "commissionAsset": "dUSD" // } // // fetchMyTrades // // { // "symbol": "BNBBTC", // "id": 28457, // "orderId": 100234, // "price": "4.00000100", // "qty": "12.00000000", // "commission": "10.10000000", // "commissionAsset": "BNB", // "time": 1499865549590, // "isBuyer": true, // "isMaker": false, // "isBestMatch": true // } // const timestamp = this.safeInteger2 (trade, 'T', 'time'); const priceString = this.safeString2 (trade, 'p', 'price'); const amountString = this.safeString2 (trade, 'q', 'qty'); const price = this.parseNumber (priceString); const amount = this.parseNumber (amountString); const cost = this.parseNumber (Precise.stringMul (priceString, amountString)); const id = this.safeString2 (trade, 'a', 'id'); let side = undefined; const orderId = this.safeString (trade, 'orderId'); if ('m' in trade) { side = trade['m'] ? 'sell' : 'buy'; // this is reversed intentionally } else if ('isBuyerMaker' in trade) { side = trade['isBuyerMaker'] ? 'sell' : 'buy'; } else { if ('isBuyer' in trade) { side = (trade['isBuyer']) ? 'buy' : 'sell'; // this is a true side } } let fee = undefined; if ('commission' in trade) { fee = { 'cost': this.safeNumber (trade, 'commission'), 'currency': this.safeCurrencyCode (this.safeString (trade, 'commissionAsset')), }; } let takerOrMaker = undefined; if ('isMaker' in trade) { takerOrMaker = trade['isMaker'] ? 'maker' : 'taker'; } const marketId = this.safeString (trade, 'symbol'); const symbol = this.safeSymbol (marketId, market); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], // 'limit': 500, // default 500, max 1000 }; if (limit !== undefined) { request['limit'] = limit; // default 500, max 1000 } const response = await this.publicGetAggTrades (this.extend (request, params)); // // [ // { // "a":1658318071, // "p":"0.02476", // "q":"0.0", // "T":1591001423382, // "m":false // } // ] // return this.parseTrades (response, market, since, limit); } parseOrderStatus (status) { const statuses = { 'NEW': 'open', 'PARTIALLY_FILLED': 'open', 'FILLED': 'closed', 'CANCELED': 'canceled', 'PENDING_CANCEL': 'canceling', // currently unused 'REJECTED': 'rejected', 'EXPIRED': 'expired', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "symbol": "BTC/USD", // "orderId": "00000000-0000-0000-0000-0000000c0263", // "clientOrderId": "00000000-0000-0000-0000-0000000c0263", // "transactTime": 1589878206426, // "price": "9825.66210000", // "origQty": "0.01", // "executedQty": "0.01", // "status": "FILLED", // "timeInForce": "FOK", // "type": "MARKET", // "side": "BUY", // "fills": [ // { // "price": "9807.05", // "qty": "0.01", // "commission": "0", // "commissionAsset": "dUSD" // } // ] // } // const status = this.parseOrderStatus (this.safeString (order, 'status')); const marketId = this.safeString (order, 'symbol'); const symbol = this.safeSymbol (marketId, market, '/'); let timestamp = undefined; if ('time' in order) { timestamp = this.safeInteger (order, 'time'); } else if ('transactTime' in order) { timestamp = this.safeInteger (order, 'transactTime'); } const price = this.safeNumber (order, 'price'); const amount = this.safeNumber (order, 'origQty'); const filled = this.safeNumber (order, 'executedQty'); const remaining = undefined; const cost = this.safeNumber (order, 'cummulativeQuoteQty'); const id = this.safeString (order, 'orderId'); const type = this.safeStringLower (order, 'type'); const side = this.safeStringLower (order, 'side'); let trades = undefined; const fills = this.safeValue (order, 'fills'); if (fills !== undefined) { trades = this.parseTrades (fills, market); } const timeInForce = this.safeString (order, 'timeInForce'); return this.safeOrder ({ 'info': order, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'timeInForce': timeInForce, 'side': side, 'price': price, 'stopPrice': undefined, 'amount': amount, 'cost': cost, 'average': undefined, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': trades, }); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let accountId = undefined; if (market['margin']) { accountId = this.safeInteger (params, 'accountId'); if (accountId === undefined) { throw new ArgumentsRequired (this.id + ' createOrder() requires an accountId parameter for ' + market['type'] + ' market ' + symbol); } } const uppercaseType = type.toUpperCase (); const newOrderRespType = this.safeValue (this.options['newOrderRespType'], type, 'RESULT'); const request = { 'symbol': market['id'], 'quantity': this.amountToPrecision (symbol, amount), 'type': uppercaseType, 'side': side.toUpperCase (), 'newOrderRespType': newOrderRespType, // 'RESULT' for full order or 'FULL' for order with fills // 'leverage': 1, // 'accountId': 5470306579272968, // required for leverage markets // 'takeProfit': '123.45', // 'stopLoss': '54.321' // 'guaranteedStopLoss': '54.321', }; if (uppercaseType === 'LIMIT') { request['price'] = this.priceToPrecision (symbol, price); request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel, 'FOK' = Fill Or Kill } else if (uppercaseType === 'STOP') { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrder (this.extend (request, params)); // // { // "symbol": "BTC/USD", // "orderId": "00000000-0000-0000-0000-0000000c0263", // "clientOrderId": "00000000-0000-0000-0000-0000000c0263", // "transactTime": 1589878206426, // "price": "9825.66210000", // "origQty": "0.01", // "executedQty": "0.01", // "status": "FILLED", // "timeInForce": "FOK", // "type": "MARKET", // "side": "BUY", // "fills": [ // { // "price": "9807.05", // "qty": "0.01", // "commission": "0", // "commissionAsset": "dUSD" // } // ] // } // return this.parseOrder (response, market); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } else if (this.options['warnOnFetchOpenOrdersWithoutSymbol']) { const symbols = this.symbols; const numSymbols = symbols.length; const fetchOpenOrdersRateLimit = parseInt (numSymbols / 2); throw new ExchangeError (this.id + ' fetchOpenOrders() WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.'); } const response = await this.privateGetOpenOrders (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } 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 origClientOrderId = this.safeValue (params, 'origClientOrderId'); const request = { 'symbol': market['id'], // 'orderId': parseInt (id), // 'origClientOrderId': id, }; if (origClientOrderId === undefined) { request['orderId'] = id; } else { request['origClientOrderId'] = origClientOrderId; } const response = await this.privateDeleteOrder (this.extend (request, params)); // // { // "symbol":"ETH/USD", // "orderId":"00000000-0000-0000-0000-00000024383b", // "clientOrderId":"00000000-0000-0000-0000-00000024383b", // "price":"150", // "origQty":"0.1", // "executedQty":"0.0", // "status":"CANCELED", // "timeInForce":"GTC", // "type":"LIMIT", // "side":"BUY" // } // return this.parseOrder (response, market); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetMyTrades (this.extend (request, params)); // // [ // { // "symbol": "BNBBTC", // "id": 28457, // "orderId": 100234, // "price": "4.00000100", // "qty": "12.00000000", // "commission": "10.10000000", // "commissionAsset": "BNB", // "time": 1499865549590, // "isBuyer": true, // "isMaker": false, // "isBestMatch": true // } // ] // return this.parseTrades (response, market, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + this.version + '/' + path; if (path === 'historicalTrades') { headers = { 'X-MBX-APIKEY': this.apiKey, }; } if (api === 'private') { this.checkRequiredCredentials (); let query = this.urlencode (this.extend ({ 'timestamp': this.nonce (), 'recvWindow': this.options['recvWindow'], }, params)); const signature = this.hmac (this.encode (query), this.encode (this.secret)); query += '&' + 'signature=' + signature; headers = { 'X-MBX-APIKEY': this.apiKey, }; if ((method === 'GET') || (method === 'DELETE')) { url += '?' + query; } else { body = query; headers['Content-Type'] = 'application/x-www-form-urlencoded'; } } else { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if ((httpCode === 418) || (httpCode === 429)) { throw new DDoSProtection (this.id + ' ' + httpCode.toString () + ' ' + reason + ' ' + body); } // error response in a form: { "code": -1013, "msg": "Invalid quantity." } // following block cointains legacy checks against message patterns in "msg" property // will switch "code" checks eventually, when we know all of them if (httpCode >= 400) { if (body.indexOf ('Price * QTY is zero or less') >= 0) { throw new InvalidOrder (this.id + ' order cost = amount * price is zero or less ' + body); } if (body.indexOf ('LOT_SIZE') >= 0) { throw new InvalidOrder (this.id + ' order amount should be evenly divisible by lot size ' + body); } if (body.indexOf ('PRICE_FILTER') >= 0) { throw new InvalidOrder (this.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use this.priceToPrecision (symbol, amount) ' + body); } } if (response === undefined) { return; // fallback to default error handler } // // {"code":-1128,"msg":"Combination of optional parameters invalid."} // const errorCode = this.safeString (response, 'code'); if ((errorCode !== undefined) && (errorCode !== '0')) { const feedback = this.id + ' ' + this.json (response); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); const message = this.safeString (response, 'msg'); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); } } };