sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,161 lines (1,140 loc) • 113 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError, InsufficientFunds, BadSymbol, OrderNotFound } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
module.exports = class ndax extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'ndax',
'name': 'NDAX',
'countries': [ 'CA' ], // Canada
'rateLimit': 1000,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'addMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'createDepositAddress': true,
'createOrder': true,
'createReduceOnlyOrder': false,
'editOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBorrowRate': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': false,
'fetchBorrowRatesPerSymbol': false,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchLedger': true,
'fetchLeverage': false,
'fetchLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchOrderTrades': true,
'fetchPosition': false,
'fetchPositions': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': false,
'fetchTime': false,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchWithdrawals': true,
'reduceMargin': false,
'setLeverage': false,
'setMarginMode': false,
'setPositionMode': false,
'signIn': true,
'transfer': false,
'withdraw': true,
},
'timeframes': {
'1m': '60',
'5m': '300',
'15m': '900',
'30m': '1800',
'1h': '3600',
'2h': '7200',
'4h': '14400',
'6h': '21600',
'12h': '43200',
'1d': '86400',
'1w': '604800',
'1M': '2419200',
'4M': '9676800',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/108623144-67a3ef00-744e-11eb-8140-75c6b851e945.jpg',
'test': {
'public': 'https://ndaxmarginstaging.cdnhop.net:8443/AP',
'private': 'https://ndaxmarginstaging.cdnhop.net:8443/AP',
},
'api': {
'public': 'https://api.ndax.io:8443/AP',
'private': 'https://api.ndax.io:8443/AP',
},
'www': 'https://ndax.io',
'doc': [
'https://apidoc.ndax.io/',
],
'fees': 'https://ndax.io/fees',
'referral': 'https://one.ndax.io/bfQiSL',
},
'api': {
'public': {
'get': {
'Activate2FA': 1,
'Authenticate2FA': 1,
'AuthenticateUser': 1,
'GetL2Snapshot': 1,
'GetLevel1': 1,
'GetValidate2FARequiredEndpoints': 1,
'LogOut': 1,
'GetTickerHistory': 1,
'GetProduct': 1,
'GetProducts': 1,
'GetInstrument': 1,
'GetInstruments': 1,
'Ping': 1,
'trades': 1, // undocumented
'GetLastTrades': 1, // undocumented
'SubscribeLevel1': 1,
'SubscribeLevel2': 1,
'SubscribeTicker': 1,
'SubscribeTrades': 1,
'SubscribeBlockTrades': 1,
'UnsubscribeBlockTrades': 1,
'UnsubscribeLevel1': 1,
'UnsubscribeLevel2': 1,
'UnsubscribeTicker': 1,
'UnsubscribeTrades': 1,
'Authenticate': 1, // undocumented
},
},
'private': {
'get': {
'GetUserAccountInfos': 1,
'GetUserAccounts': 1,
'GetUserAffiliateCount': 1,
'GetUserAffiliateTag': 1,
'GetUserConfig': 1,
'GetAllUnredactedUserConfigsForUser': 1,
'GetUnredactedUserConfigByKey': 1,
'GetUserDevices': 1,
'GetUserReportTickets': 1,
'GetUserReportWriterResultRecords': 1,
'GetAccountInfo': 1,
'GetAccountPositions': 1,
'GetAllAccountConfigs': 1,
'GetTreasuryProductsForAccount': 1,
'GetAccountTrades': 1,
'GetAccountTransactions': 1,
'GetOpenTradeReports': 1,
'GetAllOpenTradeReports': 1,
'GetTradesHistory': 1,
'GetOpenOrders': 1,
'GetOpenQuotes': 1,
'GetOrderFee': 1,
'GetOrderHistory': 1,
'GetOrdersHistory': 1,
'GetOrderStatus': 1,
'GetOmsFeeTiers': 1,
'GetAccountDepositTransactions': 1,
'GetAccountWithdrawTransactions': 1,
'GetAllDepositRequestInfoTemplates': 1,
'GetDepositInfo': 1,
'GetDepositRequestInfoTemplate': 1,
'GetDeposits': 1,
'GetDepositTicket': 1,
'GetDepositTickets': 1,
'GetOMSWithdrawFees': 1,
'GetWithdrawFee': 1,
'GetWithdraws': 1,
'GetWithdrawTemplate': 1,
'GetWithdrawTemplateTypes': 1,
'GetWithdrawTicket': 1,
'GetWithdrawTickets': 1,
},
'post': {
'AddUserAffiliateTag': 1,
'CancelUserReport': 1,
'RegisterNewDevice': 1,
'SubscribeAccountEvents': 1,
'UpdateUserAffiliateTag': 1,
'GenerateTradeActivityReport': 1,
'GenerateTransactionActivityReport': 1,
'GenerateTreasuryActivityReport': 1,
'ScheduleTradeActivityReport': 1,
'ScheduleTransactionActivityReport': 1,
'ScheduleTreasuryActivityReport': 1,
'CancelAllOrders': 1,
'CancelOrder': 1,
'CancelQuote': 1,
'CancelReplaceOrder': 1,
'CreateQuote': 1,
'ModifyOrder': 1,
'SendOrder': 1,
'SubmitBlockTrade': 1,
'UpdateQuote': 1,
'CancelWithdraw': 1,
'CreateDepositTicket': 1,
'CreateWithdrawTicket': 1,
'SubmitDepositTicketComment': 1,
'SubmitWithdrawTicketComment': 1,
'GetOrderHistoryByOrderId': 1,
},
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': this.parseNumber ('0.002'),
'taker': this.parseNumber ('0.0025'),
},
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
'uid': true,
// these credentials are required for signIn() and withdraw()
'login': true,
'password': true,
// 'twofa': true,
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {
'Not_Enough_Funds': InsufficientFunds, // {"status":"Rejected","errormsg":"Not_Enough_Funds","errorcode":101}
'Server Error': ExchangeError, // {"result":false,"errormsg":"Server Error","errorcode":102,"detail":null}
'Resource Not Found': OrderNotFound, // {"result":false,"errormsg":"Resource Not Found","errorcode":104,"detail":null}
},
'broad': {
'Invalid InstrumentId': BadSymbol, // {"result":false,"errormsg":"Invalid InstrumentId: 10000","errorcode":100,"detail":null}
'This endpoint requires 2FACode along with the payload': AuthenticationError,
},
},
'options': {
'omsId': 1,
'orderTypes': {
'Market': 1,
'Limit': 2,
'StopMarket': 3,
'StopLimit': 4,
'TrailingStopMarket': 5,
'TrailingStopLimit': 6,
'BlockTrade': 7,
},
},
});
}
async signIn (params = {}) {
/**
* @method
* @name ndax#signIn
* @description sign in, must be called prior to using other authenticated methods
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns response from exchange
*/
this.checkRequiredCredentials ();
if (this.login === undefined || this.password === undefined) {
throw new AuthenticationError (this.id + ' signIn() requires exchange.login, exchange.password');
}
let request = {
'grant_type': 'client_credentials', // the only supported value
};
const response = await this.publicGetAuthenticate (this.extend (request, params));
//
// {
// "Authenticated":true,
// "Requires2FA":true,
// "AuthType":"Google",
// "AddtlInfo":"",
// "Pending2FaToken": "6f5c4e66-f3ee-493e-9227-31cc0583b55f"
// }
//
let sessionToken = this.safeString (response, 'SessionToken');
if (sessionToken !== undefined) {
this.options['sessionToken'] = sessionToken;
return response;
}
const pending2faToken = this.safeString (response, 'Pending2FaToken');
if (pending2faToken !== undefined) {
if (this.twofa === undefined) {
throw new AuthenticationError (this.id + ' signIn() requires exchange.twofa credentials');
}
this.options['pending2faToken'] = pending2faToken;
request = {
'Code': this.oath (),
};
const response = await this.publicGetAuthenticate2FA (this.extend (request, params));
//
// {
// "Authenticated": true,
// "UserId":57765,
// "SessionToken":"4a2a5857-c4e5-4fac-b09e-2c4c30b591a0"
// }
//
sessionToken = this.safeString (response, 'SessionToken');
this.options['sessionToken'] = sessionToken;
return response;
}
return response;
}
async fetchCurrencies (params = {}) {
/**
* @method
* @name ndax#fetchCurrencies
* @description fetches all available currencies on an exchange
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns {object} an associative dictionary of currencies
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
const request = {
'omsId': omsId,
};
const response = await this.publicGetGetProducts (this.extend (request, params));
//
// [
// {
// "OMSId":1,
// "ProductId":1,
// "Product":"BTC",
// "ProductFullName":"Bitcoin",
// "ProductType":"CryptoCurrency",
// "DecimalPlaces":8,
// "TickSize":0.0000000100000000000000000000,
// "NoFees":false,
// "IsDisabled":false,
// "MarginEnabled":false
// },
// ]
//
const result = {};
for (let i = 0; i < response.length; i++) {
const currency = response[i];
const id = this.safeString (currency, 'ProductId');
const name = this.safeString (currency, 'ProductFullName');
const type = this.safeString (currency, 'ProductType');
const code = this.safeCurrencyCode (this.safeString (currency, 'Product'));
const isDisabled = this.safeValue (currency, 'IsDisabled');
const active = !isDisabled;
result[code] = {
'id': id,
'name': name,
'code': code,
'type': type,
'precision': this.safeNumber (currency, 'TickSize'),
'info': currency,
'active': active,
'deposit': undefined,
'withdraw': undefined,
'fee': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': undefined,
},
},
};
}
return result;
}
async fetchMarkets (params = {}) {
/**
* @method
* @name ndax#fetchMarkets
* @description retrieves data on all markets for ndax
* @param {object} params extra parameters specific to the exchange api endpoint
* @returns {[object]} an array of objects representing market data
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
const request = {
'omsId': omsId,
};
const response = await this.publicGetGetInstruments (this.extend (request, params));
//
// [
// {
// "OMSId":1,
// "InstrumentId":3,
// "Symbol":"LTCBTC",
// "Product1":3,
// "Product1Symbol":"LTC",
// "Product2":1,
// "Product2Symbol":"BTC",
// "InstrumentType":"Standard",
// "VenueInstrumentId":3,
// "VenueId":1,
// "SortIndex":0,
// "SessionStatus":"Running",
// "PreviousSessionStatus":"Stopped",
// "SessionStatusDateTime":"2020-11-25T19:42:15.245Z",
// "SelfTradePrevention":true,
// "QuantityIncrement":0.0000000100000000000000000000,
// "PriceIncrement":0.0000000100000000000000000000,
// "MinimumQuantity":0.0100000000000000000000000000,
// "MinimumPrice":0.0000010000000000000000000000,
// "VenueSymbol":"LTCBTC",
// "IsDisable":false,
// "MasterDataId":0,
// "PriceCollarThreshold":0.0000000000000000000000000000,
// "PriceCollarPercent":0.0000000000000000000000000000,
// "PriceCollarEnabled":false,
// "PriceFloorLimit":0.0000000000000000000000000000,
// "PriceFloorLimitEnabled":false,
// "PriceCeilingLimit":0.0000000000000000000000000000,
// "PriceCeilingLimitEnabled":false,
// "CreateWithMarketRunning":true,
// "AllowOnlyMarketMakerCounterParty":false,
// "PriceCollarIndexDifference":0.0000000000000000000000000000,
// "PriceCollarConvertToOtcEnabled":false,
// "PriceCollarConvertToOtcClientUserId":0,
// "PriceCollarConvertToOtcAccountId":0,
// "PriceCollarConvertToOtcThreshold":0.0000000000000000000000000000,
// "OtcConvertSizeThreshold":0.0000000000000000000000000000,
// "OtcConvertSizeEnabled":false,
// "OtcTradesPublic":true,
// "PriceTier":0
// },
// ]
//
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const id = this.safeString (market, 'InstrumentId');
// const lowercaseId = this.safeStringLower (market, 'symbol');
const baseId = this.safeString (market, 'Product1');
const quoteId = this.safeString (market, 'Product2');
const base = this.safeCurrencyCode (this.safeString (market, 'Product1Symbol'));
const quote = this.safeCurrencyCode (this.safeString (market, 'Product2Symbol'));
const sessionStatus = this.safeString (market, 'SessionStatus');
const isDisable = this.safeValue (market, 'IsDisable');
const sessionRunning = (sessionStatus === 'Running');
result.push ({
'id': id,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'active': (sessionRunning && !isDisable),
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.safeNumber (market, 'QuantityIncrement'),
'price': this.safeNumber (market, 'PriceIncrement'),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber (market, 'MinimumQuantity'),
'max': undefined,
},
'price': {
'min': this.safeNumber (market, 'MinimumPrice'),
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
});
}
return result;
}
parseOrderBook (orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 6, amountKey = 8) {
let nonce = undefined;
const result = {
'symbol': symbol,
'bids': [],
'asks': [],
'timestamp': undefined,
'datetime': undefined,
'nonce': undefined,
};
for (let i = 0; i < orderbook.length; i++) {
const level = orderbook[i];
if (timestamp === undefined) {
timestamp = this.safeInteger (level, 2);
} else {
const newTimestamp = this.safeInteger (level, 2);
timestamp = Math.max (timestamp, newTimestamp);
}
if (nonce === undefined) {
nonce = this.safeInteger (level, 0);
} else {
const newNonce = this.safeInteger (level, 0);
nonce = Math.max (nonce, newNonce);
}
const bidask = this.parseBidAsk (level, priceKey, amountKey);
const levelSide = this.safeInteger (level, 9);
const side = levelSide ? asksKey : bidsKey;
result[side].push (bidask);
}
result['bids'] = this.sortBy (result['bids'], 0, true);
result['asks'] = this.sortBy (result['asks'], 0);
result['timestamp'] = timestamp;
result['datetime'] = this.iso8601 (timestamp);
result['nonce'] = nonce;
return result;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name ndax#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int|undefined} limit the maximum amount of order book entries to return
* @param {object} params extra parameters specific to the ndax 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
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
await this.loadMarkets ();
const market = this.market (symbol);
limit = (limit === undefined) ? 100 : limit; // default 100
const request = {
'omsId': omsId,
'InstrumentId': market['id'],
'Depth': limit, // default 100
};
const response = await this.publicGetGetL2Snapshot (this.extend (request, params));
//
// [
// [
// 0, // 0 MDUpdateId
// 1, // 1 Number of Unique Accounts
// 123, // 2 ActionDateTime in Posix format X 1000
// 0, // 3 ActionType 0 (New), 1 (Update), 2(Delete)
// 0.0, // 4 LastTradePrice
// 0, // 5 Number of Orders
// 0.0, // 6 Price
// 0, // 7 ProductPairCode
// 0.0, // 8 Quantity
// 0, // 9 Side
// ],
// [97244115,1,1607456142963,0,19069.32,1,19069.31,8,0.140095,0],
// [97244115,0,1607456142963,0,19069.32,1,19068.64,8,0.0055,0],
// [97244115,0,1607456142963,0,19069.32,1,19068.26,8,0.021291,0],
// [97244115,1,1607456142964,0,19069.32,1,19069.32,8,0.099636,1],
// [97244115,0,1607456142964,0,19069.32,1,19069.98,8,0.1,1],
// [97244115,0,1607456142964,0,19069.32,1,19069.99,8,0.141604,1],
// ]
//
return this.parseOrderBook (response, symbol);
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker
//
// {
// "OMSId":1,
// "InstrumentId":8,
// "BestBid":19069.31,
// "BestOffer":19069.32,
// "LastTradedPx":19069.32,
// "LastTradedQty":0.0001,
// "LastTradeTime":1607040406424,
// "SessionOpen":19069.32,
// "SessionHigh":19069.32,
// "SessionLow":19069.32,
// "SessionClose":19069.32,
// "Volume":0.0001,
// "CurrentDayVolume":0.0001,
// "CurrentDayNotional":1.906932,
// "CurrentDayNumTrades":1,
// "CurrentDayPxChange":0.00,
// "Rolling24HrVolume":0.000000000000000000000000000,
// "Rolling24HrNotional":0.00000000000000000000000,
// "Rolling24NumTrades":0,
// "Rolling24HrPxChange":0,
// "TimeStamp":"1607040406425",
// "BidQty":0,
// "AskQty":0,
// "BidOrderCt":0,
// "AskOrderCt":0,
// "Rolling24HrPxChangePercent":0,
// }
//
const timestamp = this.safeInteger (ticker, 'TimeStamp');
const marketId = this.safeString (ticker, 'InstrumentId');
market = this.safeMarket (marketId, market);
const symbol = this.safeSymbol (marketId, market);
const last = this.safeString (ticker, 'LastTradedPx');
const percentage = this.safeString (ticker, 'Rolling24HrPxChangePercent');
const change = this.safeString (ticker, 'Rolling24HrPxChange');
const open = this.safeString (ticker, 'SessionOpen');
const baseVolume = this.safeString (ticker, 'Rolling24HrVolume');
const quoteVolume = this.safeString (ticker, 'Rolling24HrNotional');
return this.safeTicker ({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeString (ticker, 'SessionHigh'),
'low': this.safeString (ticker, 'SessionLow'),
'bid': this.safeString (ticker, 'BestBid'),
'bidVolume': undefined, // this.safeNumber (ticker, 'BidQty'), always shows 0
'ask': this.safeString (ticker, 'BestOffer'),
'askVolume': undefined, // this.safeNumber (ticker, 'AskQty'), always shows 0
'vwap': undefined,
'open': open,
'close': last,
'last': last,
'previousClose': undefined,
'change': change,
'percentage': percentage,
'average': undefined,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'info': ticker,
}, market);
}
async fetchTicker (symbol, params = {}) {
/**
* @method
* @name ndax#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'omsId': omsId,
'InstrumentId': market['id'],
};
const response = await this.publicGetGetLevel1 (this.extend (request, params));
//
// {
// "OMSId":1,
// "InstrumentId":8,
// "BestBid":19069.31,
// "BestOffer":19069.32,
// "LastTradedPx":19069.32,
// "LastTradedQty":0.0001,
// "LastTradeTime":1607040406424,
// "SessionOpen":19069.32,
// "SessionHigh":19069.32,
// "SessionLow":19069.32,
// "SessionClose":19069.32,
// "Volume":0.0001,
// "CurrentDayVolume":0.0001,
// "CurrentDayNotional":1.906932,
// "CurrentDayNumTrades":1,
// "CurrentDayPxChange":0.00,
// "Rolling24HrVolume":0.000000000000000000000000000,
// "Rolling24HrNotional":0.00000000000000000000000,
// "Rolling24NumTrades":0,
// "Rolling24HrPxChange":0,
// "TimeStamp":"1607040406425",
// "BidQty":0,
// "AskQty":0,
// "BidOrderCt":0,
// "AskOrderCt":0,
// "Rolling24HrPxChangePercent":0,
// }
//
return this.parseTicker (response, market);
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1501603632000, // 0 DateTime
// 2700.33, // 1 High
// 2687.01, // 2 Low
// 2687.01, // 3 Open
// 2687.01, // 4 Close
// 24.86100992, // 5 Volume
// 0, // 6 Inside Bid Price
// 2870.95, // 7 Inside Ask Price
// 1 // 8 InstrumentId
// ]
//
return [
this.safeInteger (ohlcv, 0),
this.safeNumber (ohlcv, 3),
this.safeNumber (ohlcv, 1),
this.safeNumber (ohlcv, 2),
this.safeNumber (ohlcv, 4),
this.safeNumber (ohlcv, 5),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name ndax#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int|undefined} since timestamp in ms of the earliest candle to fetch
* @param {int|undefined} limit the maximum amount of candles to fetch
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'omsId': omsId,
'InstrumentId': market['id'],
'Interval': this.timeframes[timeframe],
};
const duration = this.parseTimeframe (timeframe);
const now = this.milliseconds ();
if (since === undefined) {
if (limit !== undefined) {
request['FromDate'] = this.ymdhms (now - duration * limit * 1000);
request['ToDate'] = this.ymdhms (now);
}
} else {
request['FromDate'] = this.ymdhms (since);
if (limit === undefined) {
request['ToDate'] = this.ymdhms (now);
} else {
request['ToDate'] = this.ymdhms (this.sum (since, duration * limit * 1000));
}
}
const response = await this.publicGetGetTickerHistory (this.extend (request, params));
//
// [
// [1607299260000,19069.32,19069.32,19069.32,19069.32,0,19069.31,19069.32,8,1607299200000],
// [1607299320000,19069.32,19069.32,19069.32,19069.32,0,19069.31,19069.32,8,1607299260000],
// [1607299380000,19069.32,19069.32,19069.32,19069.32,0,19069.31,19069.32,8,1607299320000],
// ]
//
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// [
// 6913253, // 0 TradeId
// 8, // 1 ProductPairCode
// 0.03340802, // 2 Quantity
// 19116.08, // 3 Price
// 2543425077, // 4 Order1
// 2543425482, // 5 Order2
// 1606935922416, // 6 Tradetime
// 0, // 7 Direction
// 1, // 8 TakerSide
// 0, // 9 BlockTrade
// 0, // 10 Either Order1ClientId or Order2ClientId
// ]
//
// fetchMyTrades (private)
//
// {
// "OMSId":1,
// "ExecutionId":16916567,
// "TradeId":14476351,
// "OrderId":2543565231,
// "AccountId":449,
// "AccountName":"igor@ccxt.trade",
// "SubAccountId":0,
// "ClientOrderId":0,
// "InstrumentId":8,
// "Side":"Sell",
// "OrderType":"Market",
// "Quantity":0.1230000000000000000000000000,
// "RemainingQuantity":0.0000000000000000000000000000,
// "Price":19069.310000000000000000000000,
// "Value":2345.5251300000000000000000000,
// "CounterParty":"7",
// "OrderTradeRevision":1,
// "Direction":"NoChange",
// "IsBlockTrade":false,
// "Fee":1.1727625650000000000000000000,
// "FeeProductId":8,
// "OrderOriginator":446,
// "UserName":"igor@ccxt.trade",
// "TradeTimeMS":1607565031569,
// "MakerTaker":"Taker",
// "AdapterTradeId":0,
// "InsideBid":19069.310000000000000000000000,
// "InsideBidSize":0.2400950000000000000000000000,
// "InsideAsk":19069.320000000000000000000000,
// "InsideAskSize":0.0997360000000000000000000000,
// "IsQuote":false,
// "CounterPartyClientUserId":1,
// "NotionalProductId":2,
// "NotionalRate":1.0000000000000000000000000000,
// "NotionalValue":2345.5251300000000000000000000,
// "NotionalHoldAmount":0,
// "TradeTime":637431618315686826
// }
//
// fetchOrderTrades
//
// {
// "Side":"Sell",
// "OrderId":2543565235,
// "Price":18600.000000000000000000000000,
// "Quantity":0.0000000000000000000000000000,
// "DisplayQuantity":0.0000000000000000000000000000,
// "Instrument":8,
// "Account":449,
// "AccountName":"igor@ccxt.trade",
// "OrderType":"Limit",
// "ClientOrderId":0,
// "OrderState":"FullyExecuted",
// "ReceiveTime":1607585844956,
// "ReceiveTimeTicks":637431826449564182,
// "LastUpdatedTime":1607585844959,
// "LastUpdatedTimeTicks":637431826449593893,
// "OrigQuantity":0.1230000000000000000000000000,
// "QuantityExecuted":0.1230000000000000000000000000,
// "GrossValueExecuted":2345.3947500000000000000000000,
// "ExecutableValue":0.0000000000000000000000000000,
// "AvgPrice":19068.250000000000000000000000,
// "CounterPartyId":0,
// "ChangeReason":"Trade",
// "OrigOrderId":2543565235,
// "OrigClOrdId":0,
// "EnteredBy":446,
// "UserName":"igor@ccxt.trade",
// "IsQuote":false,
// "InsideAsk":19069.320000000000000000000000,
// "InsideAskSize":0.0997360000000000000000000000,
// "InsideBid":19068.250000000000000000000000,
// "InsideBidSize":1.3300010000000000000000000000,
// "LastTradePrice":19068.250000000000000000000000,
// "RejectReason":"",
// "IsLockedIn":false,
// "CancelReason":"",
// "OrderFlag":"0",
// "UseMargin":false,
// "StopPrice":0.0000000000000000000000000000,
// "PegPriceType":"Unknown",
// "PegOffset":0.0000000000000000000000000000,
// "PegLimitOffset":0.0000000000000000000000000000,
// "IpAddress":"x.x.x.x",
// "ClientOrderIdUuid":null,
// "OMSId":1
// }
//
let priceString = undefined;
let amountString = undefined;
let costString = undefined;
let timestamp = undefined;
let id = undefined;
let marketId = undefined;
let side = undefined;
let orderId = undefined;
let takerOrMaker = undefined;
let fee = undefined;
let type = undefined;
if (Array.isArray (trade)) {
priceString = this.safeString (trade, 3);
amountString = this.safeString (trade, 2);
timestamp = this.safeInteger (trade, 6);
id = this.safeString (trade, 0);
marketId = this.safeString (trade, 1);
const takerSide = this.safeValue (trade, 8);
side = takerSide ? 'sell' : 'buy';
orderId = this.safeString (trade, 4);
} else {
timestamp = this.safeInteger2 (trade, 'TradeTimeMS', 'ReceiveTime');
id = this.safeString (trade, 'TradeId');
orderId = this.safeString2 (trade, 'OrderId', 'OrigOrderId');
marketId = this.safeString2 (trade, 'InstrumentId', 'Instrument');
priceString = this.safeString (trade, 'Price');
amountString = this.safeString (trade, 'Quantity');
costString = this.safeString2 (trade, 'Value', 'GrossValueExecuted');
takerOrMaker = this.safeStringLower (trade, 'MakerTaker');
side = this.safeStringLower (trade, 'Side');
type = this.safeStringLower (trade, 'OrderType');
const feeCostString = this.safeString (trade, 'Fee');
if (feeCostString !== undefined) {
const feeCurrencyId = this.safeString (trade, 'FeeProductId');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCostString,
'currency': feeCurrencyCode,
};
}
}
const symbol = this.safeSymbol (marketId, market);
return this.safeTrade ({
'info': trade,
'id': id,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'order': orderId,
'type': type,
'side': side,
'takerOrMaker': takerOrMaker,
'price': priceString,
'amount': amountString,
'cost': costString,
'fee': fee,
}, market);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name ndax#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int|undefined} since timestamp in ms of the earliest trade to fetch
* @param {int|undefined} limit the maximum amount of trades to fetch
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'omsId': omsId,
'InstrumentId': market['id'],
};
if (limit !== undefined) {
request['Count'] = limit;
}
const response = await this.publicGetGetLastTrades (this.extend (request, params));
//
// [
// [6913253,8,0.03340802,19116.08,2543425077,2543425482,1606935922416,0,1,0,0],
// [6913254,8,0.01391671,19117.42,2543427510,2543427811,1606935927998,1,1,0,0],
// [6913255,8,0.000006,19107.81,2543430495,2543430793,1606935933881,2,0,0,0],
// ]
//
return this.parseTrades (response, market, since, limit);
}
async fetchAccounts (params = {}) {
/**
* @method
* @name ndax#fetchAccounts
* @description fetch all the accounts associated with a profile
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns {object} a dictionary of [account structures]{@link https://docs.ccxt.com/en/latest/manual.html#account-structure} indexed by the account type
*/
if (!this.login) {
throw new AuthenticationError (this.id + ' fetchAccounts() requires exchange.login email credential');
}
const omsId = this.safeInteger (this.options, 'omsId', 1);
this.checkRequiredCredentials ();
const request = {
'omsId': omsId,
'UserId': this.uid,
'UserName': this.login,
};
const response = await this.privateGetGetUserAccounts (this.extend (request, params));
//
// [ 449 ] // comma-separated list of account ids
//
const result = [];
for (let i = 0; i < response.length; i++) {
const accountId = this.safeString (response, i);
result.push ({
'id': accountId,
'type': undefined,
'currency': undefined,
'info': accountId,
});
}
return result;
}
parseBalance (response) {
const result = {
'info': response,
'timestamp': undefined,
'datetime': undefined,
};
for (let i = 0; i < response.length; i++) {
const balance = response[i];
const currencyId = this.safeString (balance, 'ProductId');
if (currencyId in this.currencies_by_id) {
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['total'] = this.safeString (balance, 'Amount');
account['used'] = this.safeString (balance, 'Hold');
result[code] = account;
}
}
return this.safeBalance (result);
}
async fetchBalance (params = {}) {
/**
* @method
* @name ndax#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @param {object} params extra parameters specific to the ndax api endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
const omsId = this.safeInteger (this.options, 'omsId', 1);
await this.loadMarkets ();
await this.loadAccounts ();
const defaultAccountId = this.safeInteger2 (this.options, 'accountId', 'AccountId', parseInt (this.accounts[0]['id']));
const accountId = this.safeInteger2 (params, 'accountId', 'AccountId', defaultAccountId);
params = this.omit (params, [ 'accountId', 'AccountId' ]);
const request = {
'omsId': omsId,
'AccountId': accountId,
};
const response = await this.privateGetGetAccountPositions (this.extend (request, params));
//
// [
// {
// "OMSId":1,
// "AccountId":449,
// "ProductSymbol":"BTC",
// "ProductId":1,
// "Amount":10.000000000000000000000000000,
// "Hold":0,
// "PendingDeposits":0.0000000000000000000000000000,
// "PendingWithdraws":0.0000000000000000000000000000,
// "TotalDayDeposits":10.000000000000000000000000000,
// "TotalMonthDeposits":10.000000000000000000000000000,
// "TotalYearDeposits":10.000000000000000000000000000,
// "TotalDayDepositNotional":10.000000000000000000000000000,
// "TotalMonthDepositNotional":10.000000000000000000000000000,
// "TotalYearDepositNotional":10.000000000000000000000000000,
// "TotalDayWithdraws":0,
// "TotalMonthWithdraws":0,
// "TotalYearWithdraws":0,
// "TotalDayWithdrawNotional":0,
// "TotalMonthWithdrawNotional":0,
// "TotalYearWithdrawNotional":0,
// "NotionalProductId":8,
// "NotionalProductSymbol":"USDT",
// "NotionalValue":10.000000000000000000000000000,
// "NotionalHoldAmount":0,
// "NotionalRate":1
// },
// ]
//
return this.parseBalance (response);
}
parseLedgerEntryType (type) {
const types = {
'Trade': 'trade',
'Deposit': 'transaction',
'Withdraw': 'transaction',
'Transfer': 'transfer',
'OrderHold': 'trade',
'WithdrawHold': 'transaction',
'DepositHold': 'transaction',
'MarginHold': 'trade',
'ManualHold': 'trade',
'ManualEntry': 'trade',
'MarginAcquisition': 'trade',
'MarginRelinquish': 'trade',
'MarginQuoteHold': 'trade',
};
return this.safeString (types, type, type);
}
parseLedgerEntry (item, currency = undefined) {
//
// {
// "TransactionId":2663709493,
// "ReferenceId":68,
// "OMSId":1,
// "AccountId":449,
// "CR":10.000000000000000000000000000,
// "DR":0.0000000000000000000000000000,
// "Counterparty":3,
// "TransactionType":"Other",
// "ReferenceType":"Deposit",
// "ProductId":1,
// "Balance":10.000000000000000000000000000,
// "TimeStamp":1607532331591
// }
//
const id = this.safeString (item, 'TransactionId');
const account = this.safeString (item, 'AccountId');
const referenceId = this.safeString (item, 'ReferenceId');
const referenceAccount = this.safeString (item, 'Counterparty');
const type = this.parseLedgerEntryType (this.safeString (item, 'ReferenceType'));
const currencyId = this.safeString (item, 'ProductId');
const code = this.safeCurrencyCode (currencyId, currency);
const credit = this.safeNumber (item, 'CR');
const debit = this.safeNumber (item, 'DR');
let amount = undefined;
let direction = undefined;
if (credit > 0) {
amount = credit;
direction = 'in';
} else if (debit > 0) {
amount = debit;
direction = 'out';
}
const timestamp = this.safeInteger (item, 'TimeStamp');
let before = undefined;
const after = this.safeNumber (item, 'Balance');
if (direction === 'out') {
before = this.sum (after, amount);
} else if (direction === 'in') {
before = Math.max (0, after - amount);
}
const status = 'ok';
return {
'info': item,
'id': id,
'direction': direction,
'account': account,
'referenceId': referenceId,
'referenceAccount': referenceAccount,
'type': type,
'currency': code,
'amount': amount,
'before': before,
'after': after,
'status': status,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'fee': undefined,
};
}
async fetchLedger (code = undefined, since = undefined, limit = undefin