sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,160 lines (1,146 loc) • 159 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { AccountSuspended, BadRequest, BadResponse, NetworkError, NotSupported, DDoSProtection, AuthenticationError, PermissionDenied, ExchangeError, InsufficientFunds, InvalidOrder, InvalidNonce, OrderNotFound, InvalidAddress, RateLimitExceeded, BadSymbol } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class digifinex extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'digifinex',
'name': 'DigiFinex',
'countries': [ 'SG' ],
'version': 'v3',
'rateLimit': 900, // 300 for posts
'has': {
'CORS': undefined,
'spot': true,
'margin': true,
'swap': true,
'future': false,
'option': false,
'addMargin': false,
'cancelOrder': true,
'cancelOrders': true,
'createOrder': true,
'createPostOnlyOrder': true,
'createReduceOnlyOrder': true,
'createStopLimitOrder': false,
'createStopMarketOrder': false,
'createStopOrder': false,
'fetchBalance': true,
'fetchBorrowInterest': true,
'fetchBorrowRate': true,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchLedger': true,
'fetchLeverage': false,
'fetchLeverageTiers': true,
'fetchMarginMode': false,
'fetchMarketLeverageTiers': true,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchPosition': true,
'fetchPositionMode': false,
'fetchPositions': true,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchStatus': true,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': false,
'fetchTransfers': true,
'fetchWithdrawals': true,
'reduceMargin': false,
'setLeverage': true,
'setMargin': false,
'setMarginMode': false,
'setPositionMode': false,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1',
'5m': '5',
'15m': '15',
'30m': '30',
'1h': '60',
'4h': '240',
'12h': '720',
'1d': '1D',
'1w': '1W',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/87443315-01283a00-c5fe-11ea-8628-c2a0feaf07ac.jpg',
'api': {
'rest': 'https://openapi.digifinex.com',
},
'www': 'https://www.digifinex.com',
'doc': [
'https://docs.digifinex.com',
],
'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328422-Fee-Structure-on-DigiFinex',
'referral': 'https://www.digifinex.com/en-ww/from/DhOzBg?channelCode=ljaUPp',
},
'api': {
'public': {
'spot': {
'get': [
'{market}/symbols',
'kline',
'margin/currencies',
'margin/symbols',
'markets',
'order_book',
'ping',
'spot/symbols',
'time',
'trades',
'trades/symbols',
'ticker',
'currencies',
],
},
'swap': {
'get': [
'public/api_weight',
'public/candles',
'public/candles_history',
'public/depth',
'public/funding_rate',
'public/funding_rate_history',
'public/instrument',
'public/instruments',
'public/ticker',
'public/tickers',
'public/time',
'public/trades',
],
},
},
'private': {
'spot': {
'get': [
'{market}/financelog',
'{market}/mytrades',
'{market}/order',
'{market}/order/detail',
'{market}/order/current',
'{market}/order/history',
'margin/assets',
'margin/financelog',
'margin/mytrades',
'margin/order',
'margin/order/current',
'margin/order/history',
'margin/positions',
'otc/financelog',
'spot/assets',
'spot/financelog',
'spot/mytrades',
'spot/order',
'spot/order/current',
'spot/order/history',
'deposit/address',
'deposit/history',
'withdraw/history',
],
'post': [
'{market}/order/cancel',
'{market}/order/new',
'{market}/order/batch_new',
'margin/order/cancel',
'margin/order/new',
'margin/position/close',
'spot/order/cancel',
'spot/order/new',
'transfer',
'withdraw/new',
'withdraw/cancel',
],
},
'swap': {
'get': [
'account/balance',
'account/positions',
'account/finance_record',
'account/trading_fee_rate',
'account/transfer_record',
'trade/history_orders',
'trade/history_trades',
'trade/open_orders',
'trade/order_info',
],
'post': [
'account/leverage',
'trade/batch_cancel_order',
'trade/batch_order',
'trade/cancel_order',
'trade/order_place',
],
},
},
},
'fees': {
'trading': {
'tierBased': true,
'percentage': true,
'maker': this.parseNumber ('0.002'),
'taker': this.parseNumber ('0.002'),
},
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {
'10001': [ BadRequest, "Wrong request method, please check it's a GET ot POST request" ],
'10002': [ AuthenticationError, 'Invalid ApiKey' ],
'10003': [ AuthenticationError, "Sign doesn't match" ],
'10004': [ BadRequest, 'Illegal request parameters' ],
'10005': [ DDoSProtection, 'Request frequency exceeds the limit' ],
'10006': [ PermissionDenied, 'Unauthorized to execute this request' ],
'10007': [ PermissionDenied, 'IP address Unauthorized' ],
'10008': [ InvalidNonce, 'Timestamp for this request is invalid, timestamp must within 1 minute' ],
'10009': [ NetworkError, 'Unexist endpoint, please check endpoint URL' ],
'10011': [ AccountSuspended, 'ApiKey expired. Please go to client side to re-create an ApiKey' ],
'20001': [ PermissionDenied, 'Trade is not open for this trading pair' ],
'20002': [ PermissionDenied, 'Trade of this trading pair is suspended' ],
'20003': [ InvalidOrder, 'Invalid price or amount' ],
'20007': [ InvalidOrder, 'Price precision error' ],
'20008': [ InvalidOrder, 'Amount precision error' ],
'20009': [ InvalidOrder, 'Amount is less than the minimum requirement' ],
'20010': [ InvalidOrder, 'Cash Amount is less than the minimum requirement' ],
'20011': [ InsufficientFunds, 'Insufficient balance' ],
'20012': [ BadRequest, 'Invalid trade type, valid value: buy/sell)' ],
'20013': [ InvalidOrder, 'No order info found' ],
'20014': [ BadRequest, 'Invalid date, Valid format: 2018-07-25)' ],
'20015': [ BadRequest, 'Date exceeds the limit' ],
'20018': [ PermissionDenied, 'Your trading rights have been banned by the system' ],
'20019': [ BadSymbol, 'Wrong trading pair symbol. Correct format:"usdt_btc". Quote asset is in the front' ],
'20020': [ DDoSProtection, "You have violated the API operation trading rules and temporarily forbid trading. At present, we have certain restrictions on the user's transaction rate and withdrawal rate." ],
'50000': [ ExchangeError, 'Exception error' ],
'20021': [ BadRequest, 'Invalid currency' ],
'20022': [ BadRequest, 'The ending timestamp must be larger than the starting timestamp' ],
'20023': [ BadRequest, 'Invalid transfer type' ],
'20024': [ BadRequest, 'Invalid amount' ],
'20025': [ BadRequest, 'This currency is not transferable at the moment' ],
'20026': [ InsufficientFunds, 'Transfer amount exceed your balance' ],
'20027': [ PermissionDenied, 'Abnormal account status' ],
'20028': [ PermissionDenied, 'Blacklist for transfer' ],
'20029': [ PermissionDenied, 'Transfer amount exceed your daily limit' ],
'20030': [ BadRequest, 'You have no position on this trading pair' ],
'20032': [ PermissionDenied, 'Withdrawal limited' ],
'20033': [ BadRequest, 'Wrong Withdrawal ID' ],
'20034': [ PermissionDenied, 'Withdrawal service of this crypto has been closed' ],
'20035': [ PermissionDenied, 'Withdrawal limit' ],
'20036': [ ExchangeError, 'Withdrawal cancellation failed' ],
'20037': [ InvalidAddress, 'The withdrawal address, Tag or chain type is not included in the withdrawal management list' ],
'20038': [ InvalidAddress, 'The withdrawal address is not on the white list' ],
'20039': [ ExchangeError, "Can't be canceled in current status" ],
'20040': [ RateLimitExceeded, 'Withdraw too frequently; limitation: 3 times a minute, 100 times a day' ],
'20041': [ PermissionDenied, 'Beyond the daily withdrawal limit' ],
'20042': [ BadSymbol, 'Current trading pair does not support API trading' ],
'400002': [ BadRequest, 'Invalid Parameter' ],
},
'broad': {
},
},
'options': {
'defaultType': 'spot',
'types': [ 'spot', 'margin', 'otc' ],
'accountsByType': {
'spot': '1',
'margin': '2',
'OTC': '3',
},
'networks': {
'ARBITRUM': 'Arbitrum',
'AVALANCEC': 'AVAX-CCHAIN',
'AVALANCEX': 'AVAX-XCHAIN',
'BEP20': 'BEP20',
'BSC': 'BEP20',
'CARDANO': 'Cardano',
'CELO': 'Celo',
'CHILIZ': 'Chiliz',
'COSMOS': 'COSMOS',
'CRC20': 'Crypto.com',
'CRONOS': 'Crypto.com',
'DOGECOIN': 'DogeChain',
'ERC20': 'ERC20',
'ETH': 'ERC20',
'ETHW': 'ETHW',
'IOTA': 'MIOTA',
'KLAYTN': 'KLAY',
'MATIC': 'Polygon',
'METIS': 'MetisDAO',
'MOONBEAM': 'GLMR',
'MOONRIVER': 'Moonriver',
'OPTIMISM': 'OPETH',
'POLYGON': 'Polygon',
'RIPPLE': 'XRP',
'SOLANA': 'SOL', // SOL & SPL
'STELLAR': 'Stella', // XLM
'TERRACLASSIC': 'TerraClassic',
'TERRA': 'Terra',
'TON': 'Ton',
'TRC20': 'TRC20',
'TRON': 'TRC20',
'TRX': 'TRC20',
'VECHAIN': 'Vechain', // VET
},
'networksById': {
'Arbitrum': 'ARBITRUM',
'AVAX-CCHAIN': 'AVALANCEC',
'AVAX-XCHAIN': 'AVALANCEX',
'BEP20': 'BEP20',
'Cardano': 'CARDANO',
'Celo': 'CELO',
'Chiliz': 'CHILIZ',
'COSMOS': 'COSMOS',
'Crypto.com': 'CRC20', // CRONOS
'DogeChain': 'DOGECOIN',
'ERC20': 'ERC20',
'ETHW': 'ETHW',
'MIOTA': 'IOTA',
'KLAY': 'KLAYTN',
'Polygon': 'POLYGON',
'MetisDAO': 'METIS',
'Moonriver': 'MOONRIVER',
'GLMR': 'MOONBEAM',
'OPETH': 'OPTIMISM',
'XRP': 'RIPPLE',
'SOL': 'SOLANA',
'Stella': 'STELLAR',
'Terra': 'TERRA',
'TerraClassic': 'TERRACLASSIC',
'Ton': 'TON',
'TRC20': 'TRC20',
'Vechain': 'VECHAIN',
},
},
'commonCurrencies': {
'BHT': 'Black House Test',
'EPS': 'Epanus',
'FREE': 'FreeRossDAO',
'MBN': 'Mobilian Coin',
'TEL': 'TEL666',
},
});
}
async fetchCurrencies (params = {}) {
/**
* @method
* @name digifinex#fetchCurrencies
* @description fetches all available currencies on an exchange
* @param {object} params extra parameters specific to the digifinex api endpoint
* @returns {object} an associative dictionary of currencies
*/
const response = await this.publicSpotGetCurrencies (params);
//
// {
// "data":[
// {
// "deposit_status":1,
// "min_deposit_amount":10,
// "withdraw_fee_rate":0,
// "min_withdraw_amount":10,
// "min_withdraw_fee":5,
// "currency":"USDT",
// "withdraw_status":0,
// "chain":"OMNI"
// },
// {
// "deposit_status":1,
// "min_deposit_amount":10,
// "withdraw_fee_rate":0,
// "min_withdraw_amount":10,
// "min_withdraw_fee":3,
// "currency":"USDT",
// "withdraw_status":1,
// "chain":"ERC20"
// },
// {
// "deposit_status":0,
// "min_deposit_amount":0,
// "withdraw_fee_rate":0,
// "min_withdraw_amount":0,
// "min_withdraw_fee":0,
// "currency":"DGF13",
// "withdraw_status":0,
// "chain":""
// },
// ],
// "code":200
// }
//
const data = this.safeValue (response, 'data', []);
const result = {};
for (let i = 0; i < data.length; i++) {
const currency = data[i];
const id = this.safeString (currency, 'currency');
const code = this.safeCurrencyCode (id);
const depositStatus = this.safeInteger (currency, 'deposit_status', 1);
const withdrawStatus = this.safeInteger (currency, 'withdraw_status', 1);
const deposit = depositStatus > 0;
const withdraw = withdrawStatus > 0;
const active = deposit && withdraw;
const feeString = this.safeString (currency, 'min_withdraw_fee'); // withdraw_fee_rate was zero for all currencies, so this was the worst case scenario
const fee = this.parseNumber (feeString);
const minWithdrawString = this.safeString (currency, 'min_withdraw_amount');
const minWithdraw = this.parseNumber (minWithdrawString);
const minDepositString = this.safeString (currency, 'min_deposit_amount');
const minDepositPrecisionLength = this.precisionFromString (minDepositString);
// define precision with temporary way
const feePrecisionLength = this.precisionFromString (feeString);
const minWithdrawPrecisionLength = this.precisionFromString (minWithdrawString);
const minDeposit = this.parseNumber (minDepositString);
const maxFoundPrecision = Math.max (feePrecisionLength, Math.max (minWithdrawPrecisionLength, minDepositPrecisionLength));
const precision = this.parseNumber (this.parsePrecision (this.numberToString (maxFoundPrecision)));
const networkId = this.safeString (currency, 'chain');
const networkCode = this.networkIdToCode (networkId);
const network = {
'info': currency,
'id': networkId,
'network': networkCode,
'active': active,
'fee': this.parseNumber (feeString),
'precision': precision,
'deposit': deposit,
'withdraw': withdraw,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': minWithdraw,
'max': undefined,
},
'deposit': {
'min': minDeposit,
'max': undefined,
},
},
};
if (code in result) {
if (Array.isArray (result[code]['info'])) {
result[code]['info'].push (currency);
} else {
result[code]['info'] = [ result[code]['info'], currency ];
}
if (withdraw) {
result[code]['withdraw'] = true;
result[code]['limits']['withdraw']['min'] = Math.min (result[code]['limits']['withdraw']['min'], minWithdraw);
}
if (deposit) {
result[code]['deposit'] = true;
result[code]['limits']['deposit']['min'] = Math.min (result[code]['limits']['deposit']['min'], minDeposit);
}
if (active) {
result[code]['active'] = true;
}
} else {
result[code] = {
'id': id,
'code': code,
'info': currency,
'type': undefined,
'name': undefined,
'active': active,
'deposit': deposit,
'withdraw': withdraw,
'fee': fee,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': minWithdraw,
'max': undefined,
},
'deposit': {
'min': minDeposit,
'max': undefined,
},
},
'networks': {},
};
}
if (networkId !== undefined) {
result[code]['networks'][networkId] = network;
} else {
result[code]['active'] = active;
result[code]['fee'] = this.parseNumber (feeString);
result[code]['deposit'] = deposit;
result[code]['withdraw'] = withdraw;
result[code]['limits'] = {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': minWithdraw,
'max': undefined,
},
'deposit': {
'min': minDeposit,
'max': undefined,
},
};
}
result[code]['precision'] = (result[code]['precision'] === undefined) ? precision : Math.max (result[code]['precision'], precision);
}
return result;
}
async fetchMarkets (params = {}) {
/**
* @method
* @name digifinex#fetchMarkets
* @description retrieves data on all markets for digifinex
* @param {object} params extra parameters specific to the exchange api endpoint
* @returns {[object]} an array of objects representing market data
*/
const options = this.safeValue (this.options, 'fetchMarkets', {});
const method = this.safeString (options, 'method', 'fetch_markets_v2');
return await this[method] (params);
}
async fetchMarketsV2 (params = {}) {
const defaultType = this.safeString (this.options, 'defaultType');
const [ marginMode, query ] = this.handleMarginModeAndParams ('fetchMarketsV2', params);
const method = (marginMode !== undefined) ? 'publicSpotGetMarginSymbols' : 'publicSpotGetTradesSymbols';
let promises = [ this[method] (query), this.publicSwapGetPublicInstruments (params) ];
promises = await Promise.all (promises);
const spotMarkets = promises[0];
const swapMarkets = promises[1];
//
// spot and margin
//
// {
// "symbol_list":[
// {
// "order_types":["LIMIT","MARKET"],
// "quote_asset":"USDT",
// "minimum_value":2,
// "amount_precision":4,
// "status":"TRADING",
// "minimum_amount":0.0001,
// "symbol":"BTC_USDT",
// "is_allow":1,
// "zone":"MAIN",
// "base_asset":"BTC",
// "price_precision":2
// }
// ],
// "code":0
// }
//
// swap
//
// {
// "code": 0,
// "data": [
// {
// "instrument_id": "BTCUSDTPERP",
// "type": "REAL",
// "contract_type": "PERPETUAL",
// "base_currency": "BTC",
// "quote_currency": "USDT",
// "clear_currency": "USDT",
// "contract_value": "0.001",
// "contract_value_currency": "BTC",
// "is_inverse": false,
// "is_trading": true,
// "status": "ONLINE",
// "price_precision": 4,
// "tick_size": "0.0001",
// "min_order_amount": 1,
// "open_max_limits": [
// {
// "leverage": "50",
// "max_limit": "1000000"
// }
// ]
// },
// ]
// }
//
const spotData = this.safeValue (spotMarkets, 'symbol_list', []);
const swapData = this.safeValue (swapMarkets, 'data', []);
const response = this.arrayConcat (spotData, swapData);
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const id = this.safeString2 (market, 'symbol', 'instrument_id');
const baseId = this.safeString2 (market, 'base_asset', 'base_currency');
const quoteId = this.safeString2 (market, 'quote_asset', 'quote_currency');
const settleId = this.safeString (market, 'clear_currency');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const settle = this.safeCurrencyCode (settleId);
//
// The status is documented in the exchange API docs as follows:
// TRADING, HALT (delisted), BREAK (trading paused)
// https://docs.digifinex.vip/en-ww/v3/#/public/spot/symbols
// However, all spot markets actually have status === 'HALT'
// despite that they appear to be active on the exchange website.
// Apparently, we can't trust this status.
// const status = this.safeString (market, 'status');
// const active = (status === 'TRADING');
//
let isAllowed = this.safeInteger (market, 'is_allow', 1);
let type = (defaultType === 'margin') ? 'margin' : 'spot';
const spot = settle === undefined;
const swap = !spot;
const margin = (marginMode !== undefined) ? true : undefined;
let symbol = base + '/' + quote;
let isInverse = undefined;
let isLinear = undefined;
if (swap) {
type = 'swap';
symbol = base + '/' + quote + ':' + settle;
isInverse = this.safeValue (market, 'is_inverse');
isLinear = (!isInverse) ? true : false;
const isTrading = this.safeValue (market, 'isTrading');
if (isTrading) {
isAllowed = true;
}
}
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': type,
'spot': spot,
'margin': margin,
'swap': swap,
'future': false,
'option': false,
'active': isAllowed ? true : false,
'contract': swap,
'linear': isLinear,
'inverse': isInverse,
'contractSize': this.safeNumber (market, 'contract_value'),
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'amount_precision'))),
'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'price_precision'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber2 (market, 'minimum_amount', 'min_order_amount'),
'max': undefined,
},
'price': {
'min': this.safeNumber (market, 'tick_size'),
'max': undefined,
},
'cost': {
'min': this.safeNumber (market, 'minimum_value'),
'max': undefined,
},
},
'info': market,
});
}
return result;
}
async fetchMarketsV1 (params = {}) {
const response = await this.publicSpotGetMarkets (params);
//
// {
// "data": [
// {
// "volume_precision":4,
// "price_precision":2,
// "market":"btc_usdt",
// "min_amount":2,
// "min_volume":0.0001
// },
// ],
// "date":1564507456,
// "code":0
// }
//
const markets = this.safeValue (response, 'data', []);
const result = [];
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const id = this.safeString (market, 'market');
const [ baseId, quoteId ] = id.split ('_');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
result.push ({
'id': id,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': undefined,
'swap': false,
'future': false,
'option': false,
'active': undefined,
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'price_precision'))),
'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'volume_precision'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber (market, 'min_volume'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': this.safeNumber (market, 'min_amount'),
'max': undefined,
},
},
'info': market,
});
}
return result;
}
parseBalance (response) {
//
// spot and margin
//
// {
// "currency": "BTC",
// "free": 4723846.89208129,
// "total": 0
// }
//
// swap
//
// {
// "equity": "0",
// "currency": "BTC",
// "margin": "0",
// "frozen_margin": "0",
// "frozen_money": "0",
// "margin_ratio": "0",
// "realized_pnl": "0",
// "avail_balance": "0",
// "unrealized_pnl": "0",
// "time_stamp": 1661487402396
// }
//
const result = { 'info': response };
for (let i = 0; i < response.length; i++) {
const balance = response[i];
const currencyId = this.safeString (balance, 'currency');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
const free = this.safeString2 (balance, 'free', 'avail_balance');
const total = this.safeString2 (balance, 'total', 'equity');
account['free'] = free;
account['used'] = Precise.stringSub (total, free);
account['total'] = total;
result[code] = account;
}
return this.safeBalance (result);
}
async fetchBalance (params = {}) {
/**
* @method
* @name digifinex#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://docs.digifinex.com/en-ww/spot/v3/rest.html#spot-account-assets
* @see https://docs.digifinex.com/en-ww/spot/v3/rest.html#margin-assets
* @see https://docs.digifinex.com/en-ww/swap/v2/rest.html#accountbalance
* @param {object} params extra parameters specific to the digifinex api endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
await this.loadMarkets ();
let marketType = undefined;
[ marketType, params ] = this.handleMarketTypeAndParams ('fetchBalance', undefined, params);
let method = this.getSupportedMapping (marketType, {
'spot': 'privateSpotGetSpotAssets',
'margin': 'privateSpotGetMarginAssets',
'swap': 'privateSwapGetAccountBalance',
});
const [ marginMode, query ] = this.handleMarginModeAndParams ('fetchBalance', params);
if (marginMode !== undefined) {
method = 'privateSpotGetMarginAssets';
marketType = 'margin';
}
const response = await this[method] (query);
//
// spot and margin
//
// {
// "code": 0,
// "list": [
// {
// "currency": "BTC",
// "free": 4723846.89208129,
// "total": 0
// },
// ...
// ]
// }
//
// swap
//
// {
// "code": 0,
// "data": [
// {
// "equity": "0",
// "currency": "BTC",
// "margin": "0",
// "frozen_margin": "0",
// "frozen_money": "0",
// "margin_ratio": "0",
// "realized_pnl": "0",
// "avail_balance": "0",
// "unrealized_pnl": "0",
// "time_stamp": 1661487402396
// },
// ...
// ]
// }
//
const balanceRequest = (marketType === 'swap') ? 'data' : 'list';
const balances = this.safeValue (response, balanceRequest, []);
return this.parseBalance (balances);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name digifinex#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://docs.digifinex.com/en-ww/spot/v3/rest.html#get-orderbook
* @see https://docs.digifinex.com/en-ww/swap/v2/rest.html#orderbook
* @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 digifinex 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 [ marketType, query ] = this.handleMarketTypeAndParams ('fetchOrderBook', market, params);
const request = {};
let method = undefined;
if (marketType === 'swap') {
method = 'publicSwapGetPublicDepth';
request['instrument_id'] = market['id'];
} else {
method = 'publicSpotGetOrderBook';
request['symbol'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this[method] (this.extend (request, query));
//
// spot
//
// {
// "bids": [
// [9605.77,0.0016],
// [9605.46,0.0003],
// [9602.04,0.0127],
// ],
// "asks": [
// [9627.22,0.025803],
// [9627.12,0.168543],
// [9626.52,0.0011529],
// ],
// "date":1564509499,
// "code":0
// }
//
// swap
//
// {
// "code": 0,
// "data": {
// "instrument_id": "BTCUSDTPERP",
// "timestamp": 1667975290425,
// "asks": [
// ["18384.7",3492],
// ["18402.7",5000],
// ["18406.7",5000],
// ],
// "bids": [
// ["18366.2",4395],
// ["18364.3",3070],
// ["18359.4",5000],
// ]
// }
// }
//
let timestamp = undefined;
let orderBook = undefined;
if (marketType === 'swap') {
orderBook = this.safeValue (response, 'data', {});
timestamp = this.safeInteger (orderBook, 'timestamp');
} else {
orderBook = response;
timestamp = this.safeTimestamp (response, 'date');
}
return this.parseOrderBook (orderBook, market['symbol'], timestamp);
}
async fetchTickers (symbols = undefined, params = {}) {
/**
* @method
* @name digifinex#fetchTickers
* @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
* @see https://docs.digifinex.com/en-ww/spot/v3/rest.html#ticker-price
* @see https://docs.digifinex.com/en-ww/swap/v2/rest.html#tickers
* @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 digifinex 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 first = this.safeString (symbols, 0);
let market = undefined;
if (first !== undefined) {
market = this.market (first);
}
let type = undefined;
[ type, params ] = this.handleMarketTypeAndParams ('fetchTickers', market, params);
let method = 'publicSpotGetTicker';
const request = {};
if (type === 'swap') {
method = 'publicSwapGetPublicTickers';
}
const response = await this[method] (this.extend (request, params));
//
// spot
//
// {
// "ticker": [{
// "vol": 40717.4461,
// "change": -1.91,
// "base_vol": 392447999.65374,
// "sell": 9592.23,
// "last": 9592.22,
// "symbol": "btc_usdt",
// "low": 9476.24,
// "buy": 9592.03,
// "high": 9793.87
// }],
// "date": 1589874294,
// "code": 0
// }
//
// swap
//
// {
// "code": 0,
// "data": [
// {
// "instrument_id": "SUSHIUSDTPERP",
// "index_price": "1.1297",
// "mark_price": "1.1289",
// "max_buy_price": "1.1856",
// "min_sell_price": "1.0726",
// "best_bid": "1.1278",
// "best_bid_size": "500",
// "best_ask": "1.1302",
// "best_ask_size": "471",
// "high_24h": "1.2064",
// "open_24h": "1.1938",
// "low_24h": "1.1239",
// "last": "1.1302",
// "last_qty": "29",
// "volume_24h": "4946163",
// "price_change_percent": "-0.053275255486681085",
// "open_interest": "-",
// "timestamp": 1663222782100
// },
// ...
// ]
// }
//
const result = {};
const tickers = this.safeValue2 (response, 'ticker', 'data', []);
const date = this.safeInteger (response, 'date');
for (let i = 0; i < tickers.length; i++) {
const rawTicker = this.extend ({
'date': date,
}, tickers[i]);
const ticker = this.parseTicker (rawTicker);
const symbol = ticker['symbol'];
result[symbol] = ticker;
}
return this.filterByArray (result, 'symbol', symbols);
}
async fetchTicker (symbol, params = {}) {
/**
* @method
* @name digifinex#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://docs.digifinex.com/en-ww/spot/v3/rest.html#ticker-price
* @see https://docs.digifinex.com/en-ww/swap/v2/rest.html#ticker
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} params extra parameters specific to the digifinex 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);
let method = 'publicSpotGetTicker';
const request = {};
if (market['swap']) {
method = 'publicSwapGetPublicTicker';
request['instrument_id'] = market['id'];
} else {
request['symbol'] = market['id'];
}
const response = await this[method] (this.extend (request, params));
//
// spot
//
// {
// "ticker": [{
// "vol": 40717.4461,
// "change": -1.91,
// "base_vol": 392447999.65374,
// "sell": 9592.23,
// "last": 9592.22,
// "symbol": "btc_usdt",
// "low": 9476.24,
// "buy": 9592.03,
// "high": 9793.87
// }],
// "date": 1589874294,
// "code": 0
// }
//
// swap
//
// {
// "code": 0,
// "data": {
// "instrument_id": "BTCUSDTPERP",
// "index_price": "20141.9967",
// "mark_price": "20139.3404",
// "max_buy_price": "21146.4838",
// "min_sell_price": "19132.2725",
// "best_bid": "20140.0998",
// "best_bid_size": "3116",
// "best_ask": "20140.0999",
// "best_ask_size": "9004",
// "high_24h": "20410.6496",
// "open_24h": "20308.6998",
// "low_24h": "19600",
// "last": "20140.0999",
// "last_qty": "2",
// "volume_24h": "49382816",
// "price_change_percent": "-0.008301855936636448",
// "open_interest": "-",
// "timestamp": 1663221614998
// }
// }
//
const date = this.safeInteger (response, 'date');
const tickers = this.safeValue (response, 'ticker', []);
const data = this.safeValue (response, 'data', {});
const firstTicker = this.safeValue (tickers, 0, {});
let result = undefined;
if (market['swap']) {
result = data;
} else {
result = this.extend ({ 'date': date }, firstTicker);
}
return this.parseTicker (result, market);
}
parseTicker (ticker, market = undefined) {
//
// spot: fetchTicker, fetchTickers
//
// {
// "last":0.021957,
// "symbol": "btc_usdt",
// "base_vol":2249.3521732227,
// "change":-0.6,
// "vol":102443.5111,
// "sell":0.021978,
// "low":0.021791,
// "buy":0.021946,
// "high":0.022266,
// "date"1564518452, // injected from fetchTicker/fetchTickers
// }
//
// swap: fetchTicker, fetchTickers
//
// {
// "instrument_id": "BTCUSDTPERP",
// "index_price": "20141.9967",
// "mark_price": "20139.3404",
// "max_buy_price": "21146.4838",
// "min_sell_price": "19132.2725",
// "best_bid": "20140.0998",
// "best_bid_size": "3116",
// "best_ask": "20140.0999",
// "best_ask_size": "9004",
// "high_24h": "20410.6496",
// "open_24h": "20308.6998",
// "low_24h": "19600",
// "last": "20140.0999",
// "last_qty": "2",
// "volume_24h": "49382816",
// "price_change_percent": "-0.008301855936636448",
// "open_interest": "-",
// "timestamp": 1663221614998
// }
//
const marketId = this.safeStringUpper2 (ticker, 'symbol', 'instrument_id');
const symbol = this.safeSymbol (marketId, market);
market = this.safeMarket (marketId);
let timestamp = this.safeTimestamp (ticker, 'da