UNPKG

ccxt-look

Version:

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

1,145 lines (1,123 loc) 113 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, InvalidOrder, BadSymbol, PermissionDenied, BadRequest } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class ascendex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'ascendex', 'name': 'AscendEX', 'countries': [ 'SG' ], // Singapore // 8 requests per minute = 0.13333 per second => rateLimit = 750 // testing 400 works 'rateLimit': 400, 'certified': true, 'pro': true, // new metainfo interface 'has': { 'CORS': undefined, 'spot': true, 'margin': true, 'swap': true, 'future': true, 'option': false, 'addMargin': true, 'cancelAllOrders': true, 'cancelOrder': true, 'createOrder': true, 'createReduceOnlyOrder': true, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'fetchAccounts': true, 'fetchBalance': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, 'fetchDeposits': true, 'fetchFundingFee': false, 'fetchFundingFees': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': true, 'fetchIndexOHLCV': false, 'fetchLeverage': false, 'fetchLeverageTiers': true, 'fetchMarketLeverageTiers': 'emulated', 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': false, 'fetchPosition': false, 'fetchPositions': true, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': true, 'fetchTransactions': true, 'fetchTransfer': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': true, 'reduceMargin': true, 'setLeverage': true, 'setMarginMode': true, 'setPositionMode': false, 'transfer': true, }, 'timeframes': { '1m': '1', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '6h': '360', '12h': '720', '1d': '1d', '1w': '1w', '1M': '1m', }, 'version': 'v2', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/112027508-47984600-8b48-11eb-9e17-d26459cc36c6.jpg', 'api': { 'rest': 'https://ascendex.com', }, 'test': { 'rest': 'https://api-test.ascendex-sandbox.com', }, 'www': 'https://ascendex.com', 'doc': [ 'https://ascendex.github.io/ascendex-pro-api/#ascendex-pro-api-documentation', ], 'fees': 'https://ascendex.com/en/feerate/transactionfee-traderate', 'referral': { 'url': 'https://ascendex.com/en-us/register?inviteCode=EL6BXBQM', 'discount': 0.25, }, }, 'api': { 'v1': { 'public': { 'get': { 'assets': 1, 'products': 1, 'ticker': 1, 'barhist/info': 1, 'barhist': 1, 'depth': 1, 'trades': 1, 'cash/assets': 1, // not documented 'cash/products': 1, // not documented 'margin/assets': 1, // not documented 'margin/products': 1, // not documented 'futures/collateral': 1, 'futures/contracts': 1, 'futures/ref-px': 1, 'futures/market-data': 1, 'futures/funding-rates': 1, 'risk-limit-info': 1, }, }, 'private': { 'get': { 'info': 1, 'wallet/transactions': 1, 'wallet/deposit/address': 1, // not documented 'data/balance/snapshot': 1, 'data/balance/history': 1, }, 'accountCategory': { 'get': { 'balance': 1, 'order/open': 1, 'order/status': 1, 'order/hist/current': 1, 'risk': 1, }, 'post': { 'order': 1, 'order/batch': 1, }, 'delete': { 'order': 1, 'order/all': 1, 'order/batch': 1, }, }, 'accountGroup': { 'get': { 'cash/balance': 1, 'margin/balance': 1, 'margin/risk': 1, 'futures/collateral-balance': 1, 'futures/position': 1, 'futures/risk': 1, 'futures/funding-payments': 1, 'order/hist': 1, 'spot/fee': 1, }, 'post': { 'transfer': 1, 'futures/transfer/deposit': 1, 'futures/transfer/withdraw': 1, }, }, }, }, 'v2': { 'public': { 'get': { 'assets': 1, 'futures/contract': 1, 'futures/collateral': 1, 'futures/pricing-data': 1, }, }, 'private': { 'get': { 'account/info': 1, }, 'accountGroup': { 'get': { 'order/hist': 1, 'futures/position': 1, 'futures/free-margin': 1, 'futures/order/hist/current': 1, 'futures/order/open': 1, 'futures/order/status': 1, }, 'post': { 'futures/isolated-position-margin': 1, 'futures/margin-type': 1, 'futures/leverage': 1, 'futures/transfer/deposit': 1, 'futures/transfer/withdraw': 1, 'futures/order': 1, 'futures/order/batch': 1, 'futures/order/open': 1, 'subuser/subuser-transfer': 1, 'subuser/subuser-transfer-hist': 1, }, 'delete': { 'futures/order': 1, 'futures/order/batch': 1, 'futures/order/all': 1, }, }, }, }, }, 'fees': { 'trading': { 'feeSide': 'get', 'tierBased': true, 'percentage': true, 'taker': this.parseNumber ('0.002'), 'maker': this.parseNumber ('0.002'), }, }, 'precisionMode': TICK_SIZE, 'options': { 'account-category': 'cash', // 'cash', 'margin', 'futures' // obsolete 'account-group': undefined, 'fetchClosedOrders': { 'method': 'v1PrivateAccountGroupGetOrderHist', // 'v1PrivateAccountGroupGetAccountCategoryOrderHistCurrent' }, 'defaultType': 'spot', // 'spot', 'margin', 'swap' 'accountsByType': { 'spot': 'cash', 'future': 'futures', 'margin': 'margin', }, 'transfer': { 'fillResponseFromRequest': true, }, }, 'exceptions': { 'exact': { // not documented '1900': BadRequest, // {"code":1900,"message":"Invalid Http Request Input"} '2100': AuthenticationError, // {"code":2100,"message":"ApiKeyFailure"} '5002': BadSymbol, // {"code":5002,"message":"Invalid Symbol"} '6001': BadSymbol, // {"code":6001,"message":"Trading is disabled on symbol."} '6010': InsufficientFunds, // {'code': 6010, 'message': 'Not enough balance.'} '60060': InvalidOrder, // { 'code': 60060, 'message': 'The order is already filled or canceled.' } '600503': InvalidOrder, // {"code":600503,"message":"Notional is too small."} // documented '100001': BadRequest, // INVALID_HTTP_INPUT Http request is invalid '100002': BadRequest, // DATA_NOT_AVAILABLE Some required data is missing '100003': BadRequest, // KEY_CONFLICT The same key exists already '100004': BadRequest, // INVALID_REQUEST_DATA The HTTP request contains invalid field or argument '100005': BadRequest, // INVALID_WS_REQUEST_DATA Websocket request contains invalid field or argument '100006': BadRequest, // INVALID_ARGUMENT The arugment is invalid '100007': BadRequest, // ENCRYPTION_ERROR Something wrong with data encryption '100008': BadSymbol, // SYMBOL_ERROR Symbol does not exist or not valid for the request '100009': AuthenticationError, // AUTHORIZATION_NEEDED Authorization is require for the API access or request '100010': BadRequest, // INVALID_OPERATION The action is invalid or not allowed for the account '100011': BadRequest, // INVALID_TIMESTAMP Not a valid timestamp '100012': BadRequest, // INVALID_STR_FORMAT String format does not '100013': BadRequest, // INVALID_NUM_FORMAT Invalid number input '100101': ExchangeError, // UNKNOWN_ERROR Some unknown error '150001': BadRequest, // INVALID_JSON_FORMAT Require a valid json object '200001': AuthenticationError, // AUTHENTICATION_FAILED Authorization failed '200002': ExchangeError, // TOO_MANY_ATTEMPTS Tried and failed too many times '200003': ExchangeError, // ACCOUNT_NOT_FOUND Account not exist '200004': ExchangeError, // ACCOUNT_NOT_SETUP Account not setup properly '200005': ExchangeError, // ACCOUNT_ALREADY_EXIST Account already exist '200006': ExchangeError, // ACCOUNT_ERROR Some error related with error '200007': ExchangeError, // CODE_NOT_FOUND '200008': ExchangeError, // CODE_EXPIRED Code expired '200009': ExchangeError, // CODE_MISMATCH Code does not match '200010': AuthenticationError, // PASSWORD_ERROR Wrong assword '200011': ExchangeError, // CODE_GEN_FAILED Do not generate required code promptly '200012': ExchangeError, // FAKE_COKE_VERIFY '200013': ExchangeError, // SECURITY_ALERT Provide security alert message '200014': PermissionDenied, // RESTRICTED_ACCOUNT Account is restricted for certain activity, such as trading, or withdraw. '200015': PermissionDenied, // PERMISSION_DENIED No enough permission for the operation '300001': InvalidOrder, // INVALID_PRICE Order price is invalid '300002': InvalidOrder, // INVALID_QTY Order size is invalid '300003': InvalidOrder, // INVALID_SIDE Order side is invalid '300004': InvalidOrder, // INVALID_NOTIONAL Notional is too small or too large '300005': InvalidOrder, // INVALID_TYPE Order typs is invalid '300006': InvalidOrder, // INVALID_ORDER_ID Order id is invalid '300007': InvalidOrder, // INVALID_TIME_IN_FORCE Time In Force in order request is invalid '300008': InvalidOrder, // INVALID_ORDER_PARAMETER Some order parameter is invalid '300009': InvalidOrder, // TRADING_VIOLATION Trading violation on account or asset '300011': InsufficientFunds, // INVALID_BALANCE No enough account or asset balance for the trading '300012': BadSymbol, // INVALID_PRODUCT Not a valid product supported by exchange '300013': InvalidOrder, // INVALID_BATCH_ORDER Some or all orders are invalid in batch order request '300014': InvalidOrder, // {"code":300014,"message":"Order price doesn't conform to the required tick size: 0.1","reason":"TICK_SIZE_VIOLATION"} '300020': InvalidOrder, // TRADING_RESTRICTED There is some trading restriction on account or asset '300021': InvalidOrder, // TRADING_DISABLED Trading is disabled on account or asset '300031': InvalidOrder, // NO_MARKET_PRICE No market price for market type order trading '310001': InsufficientFunds, // INVALID_MARGIN_BALANCE No enough margin balance '310002': InvalidOrder, // INVALID_MARGIN_ACCOUNT Not a valid account for margin trading '310003': InvalidOrder, // MARGIN_TOO_RISKY Leverage is too high '310004': BadSymbol, // INVALID_MARGIN_ASSET This asset does not support margin trading '310005': InvalidOrder, // INVALID_REFERENCE_PRICE There is no valid reference price '510001': ExchangeError, // SERVER_ERROR Something wrong with server. '900001': ExchangeError, // HUMAN_CHALLENGE Human change do not pass }, 'broad': {}, }, 'commonCurrencies': { 'BOND': 'BONDED', 'BTCBEAR': 'BEAR', 'BTCBULL': 'BULL', 'BYN': 'BeyondFi', 'PLN': 'Pollen', }, }); } getAccount (params = {}) { // get current or provided bitmax sub-account const account = this.safeValue (params, 'account', this.options['account']); return account.toLowerCase ().capitalize (); } async fetchCurrencies (params = {}) { const assets = await this.v1PublicGetAssets (params); // // { // "code":0, // "data":[ // { // "assetCode" : "LTCBULL", // "assetName" : "3X Long LTC Token", // "precisionScale" : 9, // "nativeScale" : 4, // "withdrawalFee" : "0.2", // "minWithdrawalAmt" : "1.0", // "status" : "Normal" // }, // ] // } // const margin = await this.v1PublicGetMarginAssets (params); // // { // "code":0, // "data":[ // { // "assetCode":"BTT", // "borrowAssetCode":"BTT-B", // "interestAssetCode":"BTT-I", // "nativeScale":0, // "numConfirmations":1, // "withdrawFee":"100.0", // "minWithdrawalAmt":"1000.0", // "statusCode":"Normal", // "statusMessage":"", // "interestRate":"0.001" // } // ] // } // const cash = await this.v1PublicGetCashAssets (params); // // { // "code":0, // "data":[ // { // "assetCode":"LTCBULL", // "nativeScale":4, // "numConfirmations":20, // "withdrawFee":"0.2", // "minWithdrawalAmt":"1.0", // "statusCode":"Normal", // "statusMessage":"" // } // ] // } // const assetsData = this.safeValue (assets, 'data', []); const marginData = this.safeValue (margin, 'data', []); const cashData = this.safeValue (cash, 'data', []); const assetsById = this.indexBy (assetsData, 'assetCode'); const marginById = this.indexBy (marginData, 'assetCode'); const cashById = this.indexBy (cashData, 'assetCode'); const dataById = this.deepExtend (assetsById, marginById, cashById); const ids = Object.keys (dataById); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = dataById[id]; const code = this.safeCurrencyCode (id); const scale = this.safeString2 (currency, 'precisionScale', 'nativeScale'); const precision = this.parseNumber (this.parsePrecision (scale)); // why would the exchange API have different names for the same field const fee = this.safeNumber2 (currency, 'withdrawFee', 'withdrawalFee'); const status = this.safeString2 (currency, 'status', 'statusCode'); const active = (status === 'Normal'); const margin = ('borrowAssetCode' in currency); result[code] = { 'id': id, 'code': code, 'info': currency, 'type': undefined, 'margin': margin, 'name': this.safeString (currency, 'assetName'), 'active': active, 'deposit': undefined, 'withdraw': undefined, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': precision, 'max': undefined, }, 'withdraw': { 'min': this.safeNumber (currency, 'minWithdrawalAmt'), 'max': undefined, }, }, }; } return result; } async fetchMarkets (params = {}) { const products = await this.v1PublicGetProducts (params); // // { // "code":0, // "data":[ // { // "symbol":"LBA/BTC", // "baseAsset":"LBA", // "quoteAsset":"BTC", // "status":"Normal", // "minNotional":"0.000625", // "maxNotional":"6.25", // "marginTradable":false, // "commissionType":"Quote", // "commissionReserveRate":"0.001", // "tickSize":"0.000000001", // "lotSize":"1" // }, // ] // } // const cash = await this.v1PublicGetCashProducts (params); // // { // "code":0, // "data":[ // { // "symbol":"QTUM/BTC", // "domain":"BTC", // "tradingStartTime":1569506400000, // "collapseDecimals":"0.0001,0.000001,0.00000001", // "minQty":"0.000000001", // "maxQty":"1000000000", // "minNotional":"0.000625", // "maxNotional":"12.5", // "statusCode":"Normal", // "statusMessage":"", // "tickSize":"0.00000001", // "useTick":false, // "lotSize":"0.1", // "useLot":false, // "commissionType":"Quote", // "commissionReserveRate":"0.001", // "qtyScale":1, // "priceScale":8, // "notionalScale":4 // } // ] // } // const perpetuals = await this.v2PublicGetFuturesContract (params); // // { // "code":0, // "data":[ // { // "symbol":"BTC-PERP", // "status":"Normal", // "displayName":"BTCUSDT", // "settlementAsset":"USDT", // "underlying":"BTC/USDT", // "tradingStartTime":1579701600000, // "priceFilter":{"minPrice":"1","maxPrice":"1000000","tickSize":"1"}, // "lotSizeFilter":{"minQty":"0.0001","maxQty":"1000000000","lotSize":"0.0001"}, // "commissionType":"Quote", // "commissionReserveRate":"0.001", // "marketOrderPriceMarkup":"0.03", // "marginRequirements":[ // {"positionNotionalLowerBound":"0","positionNotionalUpperBound":"50000","initialMarginRate":"0.01","maintenanceMarginRate":"0.006"}, // {"positionNotionalLowerBound":"50000","positionNotionalUpperBound":"200000","initialMarginRate":"0.02","maintenanceMarginRate":"0.012"}, // {"positionNotionalLowerBound":"200000","positionNotionalUpperBound":"2000000","initialMarginRate":"0.04","maintenanceMarginRate":"0.024"}, // {"positionNotionalLowerBound":"2000000","positionNotionalUpperBound":"20000000","initialMarginRate":"0.1","maintenanceMarginRate":"0.06"}, // {"positionNotionalLowerBound":"20000000","positionNotionalUpperBound":"40000000","initialMarginRate":"0.2","maintenanceMarginRate":"0.12"}, // {"positionNotionalLowerBound":"40000000","positionNotionalUpperBound":"1000000000","initialMarginRate":"0.333333","maintenanceMarginRate":"0.2"} // ] // } // ] // } // const productsData = this.safeValue (products, 'data', []); const productsById = this.indexBy (productsData, 'symbol'); const cashData = this.safeValue (cash, 'data', []); const perpetualsData = this.safeValue (perpetuals, 'data', []); const cashAndPerpetualsData = this.arrayConcat (cashData, perpetualsData); const cashAndPerpetualsById = this.indexBy (cashAndPerpetualsData, 'symbol'); const dataById = this.deepExtend (productsById, cashAndPerpetualsById); const ids = Object.keys (dataById); const result = []; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = dataById[id]; let baseId = this.safeString (market, 'baseAsset'); let quoteId = this.safeString (market, 'quoteAsset'); const settleId = this.safeValue (market, 'settlementAsset'); let base = this.safeCurrencyCode (baseId); let quote = this.safeCurrencyCode (quoteId); const settle = this.safeCurrencyCode (settleId); const status = this.safeString (market, 'status'); const spot = settle === undefined; const swap = !spot; const linear = swap ? true : undefined; let minQty = this.safeNumber (market, 'minQty'); let maxQty = this.safeNumber (market, 'maxQty'); let minPrice = this.safeNumber (market, 'tickSize'); let maxPrice = undefined; let symbol = base + '/' + quote; if (swap) { const lotSizeFilter = this.safeValue (market, 'lotSizeFilter'); minQty = this.safeNumber (lotSizeFilter, 'minQty'); maxQty = this.safeNumber (lotSizeFilter, 'maxQty'); const priceFilter = this.safeValue (market, 'priceFilter'); minPrice = this.safeNumber (priceFilter, 'minPrice'); maxPrice = this.safeNumber (priceFilter, 'maxPrice'); const underlying = this.safeString (market, 'underlying'); const parts = underlying.split ('/'); baseId = this.safeString (parts, 0); quoteId = this.safeString (parts, 1); base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote + ':' + settle; } const fee = this.safeNumber (market, 'commissionReserveRate'); const marginTradable = this.safeValue (market, 'marginTradable', false); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': swap ? 'swap' : 'spot', 'spot': spot, 'margin': spot ? marginTradable : undefined, 'swap': swap, 'future': false, 'option': false, 'active': (status === 'Normal'), 'contract': swap, 'linear': linear, 'inverse': swap ? !linear : undefined, 'taker': fee, 'maker': fee, 'contractSize': swap ? this.parseNumber ('1') : undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.safeNumber (market, 'lotSize'), 'price': this.safeNumber (market, 'tickSize'), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': minQty, 'max': maxQty, }, 'price': { 'min': minPrice, 'max': maxPrice, }, 'cost': { 'min': this.safeNumber (market, 'minNotional'), 'max': this.safeNumber (market, 'maxNotional'), }, }, 'info': market, }); } return result; } async fetchAccounts (params = {}) { let accountGroup = this.safeString (this.options, 'account-group'); let response = undefined; if (accountGroup === undefined) { response = await this.v1PrivateGetInfo (params); // // { // "code":0, // "data":{ // "email":"igor.kroitor@gmail.com", // "accountGroup":8, // "viewPermission":true, // "tradePermission":true, // "transferPermission":true, // "cashAccount":["cshrHKLZCjlZ2ejqkmvIHHtPmLYqdnda"], // "marginAccount":["martXoh1v1N3EMQC5FDtSj5VHso8aI2Z"], // "futuresAccount":["futc9r7UmFJAyBY2rE3beA2JFxav2XFF"], // "userUID":"U6491137460" // } // } // const data = this.safeValue (response, 'data', {}); accountGroup = this.safeString (data, 'accountGroup'); this.options['account-group'] = accountGroup; } return [ { 'id': accountGroup, 'type': undefined, 'currency': undefined, 'info': response, }, ]; } parseBalance (response) { const result = { 'info': response, 'timestamp': undefined, 'datetime': undefined, }; const balances = this.safeValue (response, 'data', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode (this.safeString (balance, 'asset')); const account = this.account (); account['free'] = this.safeString (balance, 'availableBalance'); account['total'] = this.safeString (balance, 'totalBalance'); result[code] = account; } return this.safeBalance (result); } parseSwapBalance (response) { const timestamp = this.milliseconds (); const result = { 'info': response, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), }; const data = this.safeValue (response, 'data', {}); const collaterals = this.safeValue (data, 'collaterals', []); for (let i = 0; i < collaterals.length; i++) { const balance = collaterals[i]; const code = this.safeCurrencyCode (this.safeString (balance, 'asset')); const account = this.account (); account['total'] = this.safeString (balance, 'balance'); result[code] = account; } return this.safeBalance (result); } async fetchBalance (params = {}) { await this.loadMarkets (); await this.loadAccounts (); const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchBalance', undefined, params); const options = this.safeValue (this.options, 'fetchBalance', {}); const accountsByType = this.safeValue (this.options, 'accountsByType', {}); const accountCategory = this.safeString (accountsByType, marketType, 'cash'); const account = this.safeValue (this.accounts, 0, {}); const accountGroup = this.safeString (account, 'id'); const request = { 'account-group': accountGroup, }; const defaultMethod = this.safeString (options, 'method', 'v1PrivateAccountCategoryGetBalance'); const method = this.getSupportedMapping (marketType, { 'spot': defaultMethod, 'margin': defaultMethod, 'swap': 'v2PrivateAccountGroupGetFuturesPosition', }); if (accountCategory === 'cash') { request['account-category'] = accountCategory; } const response = await this[method] (this.extend (request, query)); // // cash // // { // 'code': 0, // 'data': [ // { // 'asset': 'BCHSV', // 'totalBalance': '64.298000048', // 'availableBalance': '64.298000048', // }, // ] // } // // margin // // { // 'code': 0, // 'data': [ // { // 'asset': 'BCHSV', // 'totalBalance': '64.298000048', // 'availableBalance': '64.298000048', // 'borrowed': '0', // 'interest': '0', // }, // ] // } // // swap // // { // "code": 0, // "data": { // "accountId": "fut2ODPhGiY71Pl4vtXnOZ00ssgD7QGn", // "ac": "FUTURES", // "collaterals": [ // {"asset":"ADA","balance":"0.355803","referencePrice":"1.05095","discountFactor":"0.9"}, // {"asset":"USDT","balance":"0.000014519","referencePrice":"1","discountFactor":"1"} // ], // }j // } // if (marketType === 'swap') { return this.parseSwapBalance (response); } else { return this.parseBalance (response); } } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.v1PublicGetDepth (this.extend (request, params)); // // { // "code":0, // "data":{ // "m":"depth-snapshot", // "symbol":"BTC-PERP", // "data":{ // "ts":1590223998202, // "seqnum":115444921, // "asks":[ // ["9207.5","18.2383"], // ["9207.75","18.8235"], // ["9208","10.7873"], // ], // "bids":[ // ["9207.25","0.4009"], // ["9207","0.003"], // ["9206.5","0.003"], // ] // } // } // } // const data = this.safeValue (response, 'data', {}); const orderbook = this.safeValue (data, 'data', {}); const timestamp = this.safeInteger (orderbook, 'ts'); const result = this.parseOrderBook (orderbook, symbol, timestamp); result['nonce'] = this.safeInteger (orderbook, 'seqnum'); return result; } parseTicker (ticker, market = undefined) { // // { // "symbol":"QTUM/BTC", // "open":"0.00016537", // "close":"0.00019077", // "high":"0.000192", // "low":"0.00016537", // "volume":"846.6", // "ask":["0.00018698","26.2"], // "bid":["0.00018408","503.7"], // "type":"spot" // } // const timestamp = undefined; const marketId = this.safeString (ticker, 'symbol'); const type = this.safeString (ticker, 'type'); const delimiter = (type === 'spot') ? '/' : undefined; const symbol = this.safeSymbol (marketId, market, delimiter); const close = this.safeString (ticker, 'close'); const bid = this.safeValue (ticker, 'bid', []); const ask = this.safeValue (ticker, 'ask', []); const open = this.safeString (ticker, 'open'); return this.safeTicker ({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': undefined, 'high': this.safeString (ticker, 'high'), 'low': this.safeString (ticker, 'low'), 'bid': this.safeString (bid, 0), 'bidVolume': this.safeString (bid, 1), 'ask': this.safeString (ask, 0), 'askVolume': this.safeString (ask, 1), 'vwap': undefined, 'open': open, 'close': close, 'last': close, 'previousClose': undefined, // previous day close 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeString (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }, market, false); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.v1PublicGetTicker (this.extend (request, params)); // // { // "code":0, // "data":{ // "symbol":"BTC-PERP", // or "BTC/USDT" // "open":"9073", // "close":"9185.75", // "high":"9185.75", // "low":"9185.75", // "volume":"576.8334", // "ask":["9185.75","15.5863"], // "bid":["9185.5","0.003"], // "type":"derivatives", // or "spot" // } // } // const data = this.safeValue (response, 'data', {}); return this.parseTicker (data, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const marketIds = this.marketIds (symbols); request['symbol'] = marketIds.join (','); } const response = await this.v1PublicGetTicker (this.extend (request, params)); // // { // "code":0, // "data":[ // { // "symbol":"QTUM/BTC", // "open":"0.00016537", // "close":"0.00019077", // "high":"0.000192", // "low":"0.00016537", // "volume":"846.6", // "ask":["0.00018698","26.2"], // "bid":["0.00018408","503.7"], // "type":"spot" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTickers (data, symbols); } parseOHLCV (ohlcv, market = undefined) { // // { // "m":"bar", // "s":"BTC/USDT", // "data":{ // "i":"1", // "ts":1590228000000, // "o":"9139.59", // "c":"9131.94", // "h":"9139.99", // "l":"9121.71", // "v":"25.20648" // } // } // const data = this.safeValue (ohlcv, 'data', {}); return [ this.safeInteger (data, 'ts'), this.safeNumber (data, 'o'), this.safeNumber (data, 'h'), this.safeNumber (data, 'l'), this.safeNumber (data, 'c'), this.safeNumber (data, 'v'), ]; } 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 and limit are not specified // the exchange will return just 1 last candle by default const duration = this.parseTimeframe (timeframe); const options = this.safeValue (this.options, 'fetchOHLCV', {}); const defaultLimit = this.safeInteger (options, 'limit', 500); if (since !== undefined) { request['from'] = since; if (limit === undefined) { limit = defaultLimit; } else { limit = Math.min (limit, defaultLimit); } request['to'] = this.sum (since, limit * duration * 1000, 1); } else if (limit !== undefined) { request['n'] = limit; // max 500 } const response = await this.v1PublicGetBarhist (this.extend (request, params)); // // { // "code":0, // "data":[ // { // "m":"bar", // "s":"BTC/USDT", // "data":{ // "i":"1", // "ts":1590228000000, // "o":"9139.59", // "c":"9131.94", // "h":"9139.99", // "l":"9121.71", // "v":"25.20648" // } // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // public fetchTrades // // { // "p":"9128.5", // price // "q":"0.0030", // quantity // "ts":1590229002385, // timestamp // "bm":false, // if true, the buyer is the market maker, we only use this field to "define the side" of a public trade // "seqnum":180143985289898554 // } // const timestamp = this.safeInteger (trade, 'ts'); const priceString = this.safeString2 (trade, 'price', 'p'); const amountString = this.safeString (trade, 'q'); const buyerIsMaker = this.safeValue (trade, 'bm', false); const makerOrTaker = buyerIsMaker ? 'maker' : 'taker'; const side = buyerIsMaker ? 'buy' : 'sell'; market = this.safeMarket (undefined, market); return this.safeTrade ({ 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': market['symbol'], 'id': undefined, 'order': undefined, 'type': undefined, 'takerOrMaker': makerOrTaker, 'side': side, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': undefined, }, market); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['n'] = limit; // max 100 } const response = await this.v1PublicGetTrades (this.extend (request, params)); // // { // "code":0, // "data":{ // "m":"trades", // "symbol":"BTC-PERP", // "data":[ // {"p":"9128.5","q":"0.0030","ts":1590229002385,"bm":false,"seqnum":180143985289898554}, // {"p":"9129","q":"0.0030","ts":1590229002642,"bm":false,"seqnum":180143985289898587}, // {"p":"9129.5","q":"0.0030","ts":1590229021306,"bm":false,"seqnum":180143985289899043} // ] // } // } // const records = this.safeValue (response, 'data', []); const trades = this.safeValue (records, 'data', []); return this.parseTrades (trades, market, since, limit); } parseOrderStatus (status) { const statuses = { 'PendingNew': 'open', 'New': 'open', 'PartiallyFilled': 'open', 'Filled': 'closed', 'Canceled': 'canceled', 'Rejected': 'rejected', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "id": "16e607e2b83a8bXHbAwwoqDo55c166fa", // "orderId": "16e85b4d9b9a8bXHbAwwoqDoc3d66830", // "orderType": "Market", // "symbol": "BTC/USDT", // "timestamp": 1573576916201 // } // // { // "ac": "FUTURES", // "accountId": "fut2ODPhGiY71Pl4vtXnOZ00ssgD7QGn", // "time": 1640819389454, // "orderId": "a17e0874ecbdU0711043490bbtcpDU5X", // "seqNum": -1, // "orderType": "Limit", // "execInst": "NULL_VAL", // "side": "Buy", // "symbol": "BTC-PERP", // "price": "30000", // "orderQty": "0.002", // "stopPrice": "0", // "stopBy": "ref-px", // "status": "Ack", // "lastExecTime": 1640819389454, // "lastQty": "0", // "lastPx": "0", // "avgFilledPx": "0", // "cumFilledQty": "0", // "fee": "0", // "cumFee": "0", // "feeAsset": "", // "errorCode": "", // "posStopLossPrice": "0", // "posStopLossTrigger": "market", // "posTakeProfitPrice": "0", // "posTakeProfitTrigger": "market", // "liquidityInd": "n" // } // // fetchOrder, fetchOpenOrders, fetchClosedOrders // // { // "symbol": "BTC/USDT", // "price": "8131.22", // "orderQty": "0.00082", // "orderType": "Market", // "avgPx": "7392.02", // "cumFee": "0.005152238", // "cumFilledQty": "0.00082", // "errorCode": "", // "feeAsset": "USDT", // "lastExecTime": 1575953151764, // "orderId": "a16eee20b6750866943712zWEDdAjt3", // "seqNum": 2623469, // "side": "Buy", // "status": "Filled", // "stopPrice": "", // "execInst": "NULL_VAL" // } // // { // "ac": "FUTURES", // "accountId": "testabcdefg", // "avgPx": "0", // "cumFee": "0", // "cumQty": "0", // "errorCode": "NULL_VAL", // "execInst": "NULL_VAL", // "feeAsset": "USDT", // "lastExecTime": 1584072844085, // "orderId": "r170d21956dd5450276356bbtcpKa74", // "orderQty": "1.1499", // "orderType": "Limit", // "price": "4000", // "sendingTime": 1584072841033, // "seqNum": 24105338, // "side": "Buy", // "status": "Canceled", // "stopPrice": "", // "symbol": "BTC-PERP" // }, // const status = this.parseOrderStatus (this.safeString (order, 'status')); const marketId = this.safeString (order, 'symbol'); const symbol = this.safeSymbol (marketId, market, '/');