consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,171 lines (1,149 loc) • 90.5 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');
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'] =