consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,051 lines (1,022 loc) • 43.5 kB
JavaScript
'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);
}
}
};