UNPKG

consequunturatque

Version:

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

1,171 lines (1,149 loc) 90.5 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, InsufficientFunds, BadSymbol, OrderNotFound } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class ndax extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'ndax', 'name': 'NDAX', 'countries': [ 'US' ], // United States 'rateLimit': 1000, 'pro': true, 'has': { 'cancelAllOrders': true, 'cancelOrder': true, 'createDepositAddress': true, 'createOrder': true, 'editOrder': true, 'fetchAccounts': true, 'fetchBalance': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchLedger': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrderTrades': true, 'fetchTicker': true, 'fetchTrades': 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', 'Authenticate2FA', 'AuthenticateUser', 'GetL2Snapshot', 'GetLevel1', 'GetValidate2FARequiredEndpoints', 'LogOut', 'GetTickerHistory', 'GetProduct', 'GetProducts', 'GetInstrument', 'GetInstruments', 'Ping', 'trades', // undocumented 'GetLastTrades', // undocumented 'SubscribeLevel1', 'SubscribeLevel2', 'SubscribeTicker', 'SubscribeTrades', 'SubscribeBlockTrades', 'UnsubscribeBlockTrades', 'UnsubscribeLevel1', 'UnsubscribeLevel2', 'UnsubscribeTicker', 'UnsubscribeTrades', 'Authenticate', // undocumented ], }, 'private': { 'get': [ 'GetUserAccountInfos', 'GetUserAccounts', 'GetUserAffiliateCount', 'GetUserAffiliateTag', 'GetUserConfig', 'GetAllUnredactedUserConfigsForUser', 'GetUnredactedUserConfigByKey', 'GetUserDevices', 'GetUserReportTickets', 'GetUserReportWriterResultRecords', 'GetAccountInfo', 'GetAccountPositions', 'GetAllAccountConfigs', 'GetTreasuryProductsForAccount', 'GetAccountTrades', 'GetAccountTransactions', 'GetOpenTradeReports', 'GetAllOpenTradeReports', 'GetTradesHistory', 'GetOpenOrders', 'GetOpenQuotes', 'GetOrderFee', 'GetOrderHistory', 'GetOrdersHistory', 'GetOrderStatus', 'GetOmsFeeTiers', 'GetAccountDepositTransactions', 'GetAccountWithdrawTransactions', 'GetAllDepositRequestInfoTemplates', 'GetDepositInfo', 'GetDepositRequestInfoTemplate', 'GetDeposits', 'GetDepositTicket', 'GetDepositTickets', 'GetOMSWithdrawFees', 'GetWithdrawFee', 'GetWithdraws', 'GetWithdrawTemplate', 'GetWithdrawTemplateTypes', 'GetWithdrawTicket', 'GetWithdrawTickets', ], 'post': [ 'AddUserAffiliateTag', 'CancelUserReport', 'RegisterNewDevice', 'SubscribeAccountEvents', 'UpdateUserAffiliateTag', 'GenerateTradeActivityReport', 'GenerateTransactionActivityReport', 'GenerateTreasuryActivityReport', 'ScheduleTradeActivityReport', 'ScheduleTransactionActivityReport', 'ScheduleTreasuryActivityReport', 'CancelAllOrders', 'CancelOrder', 'CancelQuote', 'CancelReplaceOrder', 'CreateQuote', 'ModifyOrder', 'SendOrder', 'SubmitBlockTrade', 'UpdateQuote', 'CancelWithdraw', 'CreateDepositTicket', 'CreateWithdrawTicket', 'SubmitDepositTicketComment', 'SubmitWithdrawTicketComment', 'GetOrderHistoryByOrderId', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.2 / 100, 'taker': 0.25 / 100, }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': 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} }, }, 'options': { 'omsId': 1, 'orderTypes': { 'Market': 1, 'Limit': 2, 'StopMarket': 3, 'StopLimit': 4, 'TrailingStopMarket': 5, 'TrailingStopLimit': 6, 'BlockTrade': 7, }, }, }); } async fetchCurrencies (params = {}) { 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 precision = this.safeNumber (currency, 'TickSize'); const isDisabled = this.safeValue (currency, 'IsDisabled'); const active = !isDisabled; result[code] = { 'id': id, 'name': name, 'code': code, 'type': type, 'precision': precision, 'info': currency, 'active': active, 'fee': undefined, 'limits': this.limits, }; } return result; } async fetchMarkets (params = {}) { 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 symbol = base + '/' + quote; const precision = { 'amount': this.safeNumber (market, 'QuantityIncrement'), 'price': this.safeNumber (market, 'PriceIncrement'), }; const sessionStatus = this.safeString (market, 'SessionStatus'); const isDisable = this.safeValue (market, 'IsDisable'); const sessionRunning = (sessionStatus === 'Running'); const active = (sessionRunning && !isDisable) ? true : false; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': this.safeNumber (market, 'MinimumQuantity'), 'max': undefined, }, 'price': { 'min': this.safeNumber (market, 'MinimumPrice'), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } 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 = {}) { 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'); const symbol = this.safeSymbol (marketId, market); const last = this.safeNumber (ticker, 'LastTradedPx'); const percentage = this.safeNumber (ticker, 'Rolling24HrPxChangePercent'); const change = this.safeNumber (ticker, 'Rolling24HrPxChange'); const open = this.safeNumber (ticker, 'SessionOpen'); let average = undefined; if ((last !== undefined) && (change !== undefined)) { average = this.sum (last, open) / 2; } const baseVolume = this.safeNumber (ticker, 'Rolling24HrVolume'); const quoteVolume = this.safeNumber (ticker, 'Rolling24HrNotional'); const vwap = this.vwap (baseVolume, quoteVolume); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'SessionHigh'), 'low': this.safeNumber (ticker, 'SessionLow'), 'bid': this.safeNumber (ticker, 'BestBid'), 'bidVolume': undefined, // this.safeNumber (ticker, 'BidQty'), always shows 0 'ask': this.safeNumber (ticker, 'BestOffer'), 'askVolume': undefined, // this.safeNumber (ticker, 'AskQty'), always shows 0 'vwap': vwap, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { 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 = {}) { 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.ymd (now - duration * limit * 1000); request['ToDate'] = this.ymd (now); } } else { request['FromDate'] = this.ymd (since); if (limit === undefined) { request['ToDate'] = this.ymd (now); } else { request['ToDate'] = this.ymd (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":"5.228.233.138", // "ClientOrderIdUuid":null, // "OMSId":1 // } // let priceString = undefined; let amountString = undefined; let cost = 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.safeInteger (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'); cost = this.safeNumber2 (trade, 'Value', 'GrossValueExecuted'); takerOrMaker = this.safeStringLower (trade, 'MakerTaker'); side = this.safeStringLower (trade, 'Side'); type = this.safeStringLower (trade, 'OrderType'); const feeCost = this.safeNumber (trade, 'Fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'FeeProductId'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } } const price = this.parseNumber (priceString); const amount = this.parseNumber (amountString); if (cost === undefined) { cost = this.parseNumber (Precise.stringMul (priceString, amountString)); } const symbol = this.safeSymbol (marketId, market); return { 'info': trade, 'id': id, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'order': orderId, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { 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 = {}) { const omsId = this.safeInteger (this.options, 'omsId', 1); this.checkRequiredCredentials (); const request = { 'omsId': omsId, 'UserId': this.uid, 'UserName': 'igor@ccxt.trade', }; 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; } async fetchBalance (params = {}) { 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 // }, // ] // 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'); 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.parseBalance (result, false); } 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 = undefined, params = {}) { 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, }; if (limit !== undefined) { request['Depth'] = limit; } const response = await this.privateGetGetAccountTransactions (this.extend (request, params)); // // [ // { // "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 // }, // ] // let currency = undefined; if (code !== undefined) { currency = this.currency (code); } return this.parseLedger (response, currency, since, limit); } parseOrderStatus (status) { const statuses = { 'Accepted': 'open', 'Rejected': 'rejected', 'Working': 'open', 'Canceled': 'canceled', 'Expired': 'expired', 'FullyExecuted': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "status":"Accepted", // "errormsg":"", // "OrderId": 2543565231 // } // // editOrder // // { // "ReplacementOrderId": 1234, // "ReplacementClOrdId": 1561, // "OrigOrderId": 5678, // "OrigClOrdId": 91011, // } // // fetchOpenOrders, fetchClosedOrders // // { // "Side":"Buy", // "OrderId":2543565233, // "Price":19010, // "Quantity":0.345, // "DisplayQuantity":0.345, // "Instrument":8, // "Account":449, // "AccountName":"igor@ccxt.trade", // "OrderType":"Limit", // "ClientOrderId":0, // "OrderState":"Working", // "ReceiveTime":1607579326003, // "ReceiveTimeTicks":637431761260028981, // "LastUpdatedTime":1607579326005, // "LastUpdatedTimeTicks":637431761260054714, // "OrigQuantity":0.345, // "QuantityExecuted":0, // "GrossValueExecuted":0, // "ExecutableValue":0, // "AvgPrice":0, // "CounterPartyId":0, // "ChangeReason":"NewInputAccepted", // "OrigOrderId":2543565233, // "OrigClOrdId":0, // "EnteredBy":446, // "UserName":"igor@ccxt.trade", // "IsQuote":false, // "InsideAsk":19069.32, // "InsideAskSize":0.099736, // "InsideBid":19068.25, // "InsideBidSize":1.330001, // "LastTradePrice":19068.25, // "RejectReason":"", // "IsLockedIn":false, // "CancelReason":"", // "OrderFlag":"AddedToBook", // "UseMargin":false, // "StopPrice":0, // "PegPriceType":"Unknown", // "PegOffset":0, // "PegLimitOffset":0, // "IpAddress":null, // "ClientOrderIdUuid":null, // "OMSId":1 // } // const id = this.safeString2 (order, 'ReplacementOrderId', 'OrderId'); const timestamp = this.safeInteger (order, 'ReceiveTime'); const lastTradeTimestamp = this.safeInteger (order, 'LastUpdatedTime'); const marketId = this.safeString (order, 'Instrument'); const symbol = this.safeSymbol (marketId, market); const side = this.safeStringLower (order, 'Side'); const type = this.safeStringLower (order, 'OrderType'); const clientOrderId = this.safeString2 (order, 'ReplacementClOrdId', 'ClientOrderId'); let price = this.safeNumber (order, 'Price', 0.0); price = (price > 0.0) ? price : undefined; const amount = this.safeNumber (order, 'OrigQuantity'); const filled = this.safeNumber (order, 'QuantityExecuted'); const cost = this.safeNumber (order, 'GrossValueExecuted'); let average = this.safeNumber (order, 'AvgPrice', 0.0); average = (average > 0) ? average : undefined; let stopPrice = this.safeNumber (order, 'StopPrice', 0.0); stopPrice = (stopPrice > 0.0) ? stopPrice : undefined; const timeInForce = undefined; const status = this.parseOrderStatus (this.safeString (order, 'OrderState')); const fee = undefined; const trades = undefined; return this.safeOrder ({ 'id': id, 'clientOrderId': clientOrderId, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': type, 'timeInForce': timeInForce, 'postOnly': undefined, 'side': side, 'price': price, 'stopPrice': stopPrice, 'cost': cost, 'amount': amount, 'filled': filled, 'average': average, 'remaining': undefined, 'fee': fee, 'trades': trades, }); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { 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); const clientOrderId = this.safeInteger2 (params, 'ClientOrderId', 'clientOrderId'); params = this.omit (params, [ 'accountId', 'AccountId', 'clientOrderId', 'ClientOrderId' ]); const market = this.market (symbol); const orderSide = (side === 'buy') ? 0 : 1; const request = { 'InstrumentId': parseInt (market['id']), 'omsId': omsId, 'AccountId': accountId, 'TimeInForce': 1, // 0 Unknown, 1 GTC by default, 2 OPG execute as close to opening price as possible, 3 IOC immediate or canceled, 4 FOK fill-or-kill, 5 GTX good 'til executed, 6 GTD good 'til date // 'ClientOrderId': clientOrderId, // defaults to 0 // If this order is order A, OrderIdOCO refers to the order ID of an order B (which is not the order being created by this call). // If order B executes, then order A created by this call is canceled. // You can also set up order B to watch order A in the same way, but that may require an update to order B to make it watch this one, which could have implications for priority in the order book. // See CancelReplaceOrder and ModifyOrder. // 'OrderIdOCO': 0, // The order ID if One Cancels the Other. // 'UseDisplayQuantity': false, // If you enter a Limit order with a reserve, you must set UseDisplayQuantity to true 'Side': orderSide, // 0 Buy, 1 Sell, 2 Short, 3 unknown an error condition 'Quantity': parseFloat (this.amountToPrecision (symbol, amount)), 'OrderType': this.safeInteger (this.options['orderTypes'], this.capitalize (type)), // 0 Unknown, 1 Market, 2 Limit, 3 StopMarket, 4 StopLimit, 5 TrailingStopMarket, 6 TrailingStopLimit, 7 BlockTrade // 'PegPriceType': 3, // 1 Last, 2 Bid, 3 Ask, 4 Midpoint // 'LimitPrice': parseFloat (this.priceToPrecision (symbol, price)), }; // If OrderType=1 (Market), Side=0 (Buy), and LimitPrice is supplied, the Market order will execute up to the value specified if (price !== undefined) { request['LimitPrice'] = parseFloat (this.priceToPrecision (symbol, price)); } if (clientOrderId !== undefined) { request['ClientOrderId'] =