text: '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': [ '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.safeFloat (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.safeFloat (market, 'QuantityIncrement'),
                  'price': this.safeFloat (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.safeFloat (market, 'MinimumQuantity'),
                          'max': undefined,
                      },
                      'price': {
                          'min': this.safeFloat (market, 'MinimumPrice'),
                          'max': undefined,
                      },
                      'cost': {
                          'min': undefined,
                          'max': undefined,
                      },
                  },
              });
          }
          return result;
      }
  
      parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 6, amountKey = 8) {
          let nonce = undefined;
          const result = {
              '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.safeValue (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);
      }
  
      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.safeFloat (ticker, 'LastTradedPx');
          const percentage = this.safeFloat (ticker, 'Rolling24HrPxChangePercent');
          const change = this.safeFloat (ticker, 'Rolling24HrPxChange');
          const open = this.safeFloat (ticker, 'SessionOpen');
          let average = undefined;
          if ((last !== undefined) && (change !== undefined)) {
              average = this.sum (last, open) / 2;
          }
          const baseVolume = this.safeFloat (ticker, 'Rolling24HrVolume');
          const quoteVolume = this.safeFloat (ticker, 'Rolling24HrNotional');
          const vwap = this.vwap (baseVolume, quoteVolume);
          return {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': this.iso8601 (timestamp),
              'high': this.safeFloat (ticker, 'SessionHigh'),
              'low': this.safeFloat (ticker, 'SessionLow'),
              'bid': this.safeFloat (ticker, 'BestBid'),
              'bidVolume': undefined, // this.safeFloat (ticker, 'BidQty'), always shows 0
              'ask': this.safeFloat (ticker, 'BestOffer'),
              'askVolume': undefined, // this.safeFloat (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.safeFloat (ohlcv, 3),
              this.safeFloat (ohlcv, 1),
              this.safeFloat (ohlcv, 2),
              this.safeFloat (ohlcv, 4),
              this.safeFloat (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 price = undefined;
          let amount = 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)) {
              price = this.safeFloat (trade, 3);
              amount = this.safeFloat (trade, 2);
              if ((price !== undefined) && (amount !== undefined)) {
                  cost = price * amount;
              }
              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');
              price = this.safeFloat (trade, 'Price');
              amount = this.safeFloat (trade, 'Quantity');
              cost = this.safeFloat2 (trade, 'Value', 'GrossValueExecuted');
              takerOrMaker = this.safeStringLower (trade, 'MakerTaker');
              side = this.safeStringLower (trade, 'Side');
              type = this.safeStringLower (trade, 'OrderType');
              const feeCost = this.safeFloat (trade, 'Fee');
              if (feeCost !== undefined) {
                  const feeCurrencyId = this.safeString (trade, 'FeeProductId');
                  const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
                  fee = {
                      'cost': feeCost,
                      'currency': feeCurrencyCode,
                  };
              }
          }
          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 };
          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.safeFloat (balance, 'Amount');
              account['used'] = this.safeFloat (balance, 'Hold');
              result[code] = account;
          }
          return this.parseBalance (result);
      }
  
      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.safeFloat (item, 'CR');
          const debit = this.safeFloat (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.safeFloat (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');
          let lastTradeTimestamp = undefined;
          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.safeFloat (order, 'Price', 0.0);
          price = (price > 0.0) ? price : undefined;
          const amount = this.safeFloat (order, 'OrigQuantity');
          const filled = this.safeFloat (order, 'QuantityExecuted');
          const cost = this.safeFloat (order, 'GrossValueExecuted');
          let remaining = undefined;
          let average = undefined;
          if (filled !== undefined) {
              if (amount !== undefined) {
                  remaining = Math.max (0, amount - filled);
              }
              if (filled > 0) {
                  lastTradeTimestamp = this.safeInteger (order, 'LastUpdatedTime');
                  average = this.safeFloat (order, 'AvgPrice', 0.0);
                  average = (average > 0) ? average : undefined;
              }
          }
          let stopPrice = this.safeFloat (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 {
              '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': remaining,
              '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'] = clientOrderId;
          }
          const response = await this.privatePostSendOrder (this.extend (request, params));
          //
          //     {
          //         "status":"Accepted",
          //         "errormsg":"",
          //         "OrderId": 2543565231
          //     }
          //
          return this.parseOrder (response, market);
      }
  
      async editOrder (id, 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 = {
              'OrderIdToReplace': parseInt (id),
              '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'] = clientOrderId;
          }
          const response = await this.privatePostCancelReplaceOrder (this.extend (request, params));
          //
          //     {
          //         "replacementOrderId": 1234,
          //         "replacementClOrdId": 1561,
          //         "origOrderId": 5678,
          //         "origClOrdId": 91011,
          //     }
          //
          return this.parseOrder (response, market);
      }
  
      async fetchMyTrades (symbol = 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,
              // 'InstrumentId': market['id'],
              // 'TradeId': 123, // If you specify TradeId, GetTradesHistory can return all states for a single trade
              // 'OrderId': 456, // If specified, the call returns all trades associated with the order
              // 'UserId': integer. The ID of the logged-in user. If not specified, the call returns trades associated with the users belonging to the default account for the logged-in user of this OMS.
              // 'StartTimeStamp': long integer. The historical date and time at which to begin the trade report, in POSIX format. If not specified, reverts to the start date of this account on the trading venue.
              // 'EndTimeStamp': long integer. Date at which to end the trade report, in POSIX format.
              // 'Depth': integer. In this case, the count of trades to return, counting from the StartIndex. If Depth is not specified, returns all trades between BeginTimeStamp and EndTimeStamp, beginning at StartIndex.
              // 'StartIndex': 0 // from the most recent trade 0 and moving backwards in time
              // 'ExecutionId': 123, // The ID of the individual buy or sell execution. If not specified, returns all.
          };
          let market = undefined;
          if (symbol !== undefined) {
              market = this.market (symbol);
              request['InstrumentId'] = market['id'];
          }
          if (since !== undefined) {
              request['StartTimeStamp'] = since;
          }
          if (limit !== undefined) {
              request['Depth'] = limit;
          }
          const response = await this.privateGetGetTradesHistory (this.extend (request, params));
          //
          //     [
          //         {
          //             "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
          //         }
          //     ]
          //
          return this.parseTrades (response, market, since, limit);
      }
  
      async cancelAllOrders (symbol = 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 (symbol !== undefined) {
              const market = this.market (symbol);
              request['IntrumentId'] = market['id'];
          }
          const response = await this.privatePostCancelAllOrders (this.extend (request, params));
          //
          //     {
          //         "result":true,
          //         "errormsg":null,
          //         "errorcode":0,
          //         "detail":null
          //     }
          //
          return response;
      }
  
      async cancelOrder (id, symbol = 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' ]);
          let market = undefined;
          if (symbol !== undefined) {
              market = this.market (symbol);
          }
          const request = {
              'omsId': omsId,
              // 'AccountId': accountId,
          };
          const clientOrderId = this.safeInteger2 (params, 'clientOrderId', 'ClOrderId');
          if (clientOrderId !== undefined) {
              request['ClOrderId'] = clientOrderId;
          } else {
              request['OrderId'] = parseInt (id);
          }
          params = this.omit (params, [ 'clientOrderId', 'ClOrderId' ]);
          const response = await this.privatePostCancelOrder (this.extend (request, params));
          const order = this.parseOrder (response, market);
          return this.extend (order, {
              'id': id,
              'clientOrderId': clientOrderId,
          });
      }
  
      async fetchOpenOrders (symbol = 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' ]);
          let market = undefined;
          if (symbol !== undefined) {
              market = this.market (symbol);
          }
          const request = {
              'omsId': omsId,
              'AccountId': accountId,
          };
          const response = await this.privateGetGetOpenOrders (this.extend (request, params));
          //
          //     [
          //         {
          //             "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
          //         }
          //     ]
          //
          return this.parseOrders (response, market, since, limit);
      }
  
      async fetchOrders (symbol = 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,
              // 'ClientOrderId': clientOrderId,
              // 'OriginalOrderId': id,
              // 'OriginalClientOrderId': long integer,
              // 'UserId': integer,
              // 'InstrumentId': market['id'],
              // 'StartTimestamp': since,
              // 'EndTimestamp': this.milliseconds (),
              // 'Depth': limit,
              // 'StartIndex': 0,
          };
          let market = undefined;
          if (symbol !== undefined) {
              market = this.market (symbol);
              request['InstrumentId'] = market['id'];
          }
          if (since !== undefined) {
              request['StartTimeStamp'] = since;
          }
          if (limit !== undefined) {
              request['Depth'] = limit;
          }
          const response = await this.privateGetGetOrdersHistory (this.extend (request, params));
          //
          //     [
          //         {
          //             "Side":"Buy",
          //             "OrderId":2543565233,
          //             "Price":19010.000000000000000000000000,
          //             "Quantity":0.0000000000000000000000000000,
          //             "DisplayQuantity":0.3450000000000000000000000000,
          //             "Instrument":8,
          //             "Account":449,
          //             "AccountName":"igor@ccxt.trade",
          //             "OrderType":"Limit",
          //             "ClientOrderId":0,
          //             "OrderState":"Canceled",
          //             "ReceiveTime":1607579326003,
          //             "ReceiveTimeTicks":637431761260028981,
          //             "LastUpdatedTime":1607580965346,
          //             "LastUpdatedTimeTicks":637431777653463754,
          //             "OrigQuantity":0.3450000000000000000000000000,
          //             "QuantityExecuted":0.0000000000000000000000000000,
          //             "GrossValueExecuted":0.0000000000000000000000000000,
          //             "ExecutableValue":0.0000000000000000000000000000,
          //             "AvgPrice":0.0000000000000000000000000000,
          //             "CounterPartyId":0,
          //             "ChangeReason":"UserModified",
          //             "OrigOrderId":2543565233,
          //             "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":"UserModified",
          //             "OrderFlag":"AddedToBook, RemovedFromBook",
          //             "UseMargin":false,
          //             "StopPrice":0.0000000000000000000000000000,
          //             "PegPriceType":"Unknown",
          //             "PegOffset":0.0000000000000000000000000000,
          //             "PegLimitOffset":0.0000000000000000000000000000,
          //             "IpAddress":"5.228.233.138",
          //             "ClientOrderIdUuid":null,
          //             "OMSId":1
          //         },
          //     ]
          //
          return this.parseOrders (response, market, since, limit);
      }
  
      async fetchOrder (id, symbol = 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' ]);
          let market = undefined;
          if (symbol !== undefined) {
              market = this.market (symbol);
          }
          const request = {
              'omsId': omsId,
              'AccountId': accountId,
              'OrderId': parseInt (id),
          };
          const response = await this.privateGetGetOrderStatus (this.extend (request, params));
          //
          //     {
          //         "Side":"Sell",
          //         "OrderId":2543565232,
          //         "Price":0.0000000000000000000000000000,
          //         "Quantity":0.0000000000000000000000000000,
          //         "DisplayQuantity":0.0000000000000000000000000000,
          //         "Instrument":8,
          //         "Account":449,
          //         "AccountName":"igor@ccxt.trade",
          //         "OrderType":"Market",
          //         "ClientOrderId":0,
          //         "OrderState":"FullyExecuted",
          //         "ReceiveTime":1607569475591,
          //         "ReceiveTimeTicks":637431662755912377,
          //         "LastUpdatedTime":1607569475596,
          //         "LastUpdatedTimeTicks":637431662755960902,
          //         "OrigQuantity":1.0000000000000000000000000000,
          //         "QuantityExecuted":1.0000000000000000000000000000,
          //         "GrossValueExecuted":19068.270478610000000000000000,
          //         "ExecutableValue":0.0000000000000000000000000000,
          //         "AvgPrice":19068.270478610000000000000000,
          //         "CounterPartyId":0,
          //         "ChangeReason":"Trade",
          //         "OrigOrderId":2543565232,
          //         "OrigClOrdId":0,
          //         "EnteredBy":446,
          //         "UserName":"igor@ccxt.trade",
          //         "IsQuote":false,
          //         "InsideAsk":19069.320000000000000000000000,
          //         "InsideAskSize":0.0997360000000000000000000000,
          //         "InsideBid":19069.310000000000000000000000,
          //         "InsideBidSize":0.2400950000000000000000000000,
          //         "LastTradePrice":19069.310000000000000000000000,
          //         "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
          //     }
          //
          return this.parseOrder (response, market);
      }
  
      async fetchOrderTrades (id, symbol = 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' ]);
          let market = undefined;
          if (symbol !== undefined) {
              market = this.market (symbol);
          }
          const request = {
              'OMSId': parseInt (omsId),
              // 'AccountId': accountId,
              'OrderId': id,
          };
          const response = await this.privatePostGetOrderHistoryByOrderId (this.extend (request, params));
          //
          //     [
          //         {
          //             "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
          //         },
          //     ]
          //
          const grouped = this.groupBy (response, 'ChangeReason');
          const trades = this.safeValue (grouped, 'Trade', []);
          return this.parseTrades (trades, market, since, limit);
      }
  
      async fetchDepositAddress (code, 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 currency = this.currency (code);
          const request = {
              'omsId': omsId,
              'AccountId': accountId,
              'ProductId': currency['id'],
              'GenerateNewKey': false,
          };
          const response = await this.privateGetGetDepositInfo (this.extend (request, params));
          //
          //     {
          //         "result":true,
          //         "errormsg":null,
          //         "statuscode":0,
          //         "AssetManagerId":1,
          //         "AccountId":57922,
          //         "AssetId":16,
          //         "ProviderId":23,
          //         "DepositInfo":"[\"0x8A27564b5c30b91C93B1591821642420F323a210\"]"
          //     }
          //
          return this.parseDepositAddress (response, currency);
      }
  
      parseDepositAddress (depositAddress, currency = undefined) {
          //
          // fetchDepositAddress, createDepositAddress
          //
          //     {
          //         "result":true,
          //         "errormsg":null,
          //         "statuscode":0,
          //         "AssetManagerId":1,
          //         "AccountId":449,
          //         "AssetId":1,
          //         "ProviderId":1,
          //         "DepositInfo":"[\"r3e95RwVsLH7yCbnMfyh7SA8FdwUJCB4S2?memo=241452010\"]"
          //     }
          //
          const depositInfoString = this.safeString (depositAddress, 'DepositInfo');
          const depositInfo = JSON.parse (depositInfoString);
          const depositInfoLength = depositInfo.length;
          const lastString = this.safeString (depositInfo, depositInfoLength - 1);
          const parts = lastString.split ('?memo=');
          const address = this.safeString (parts, 0);
          const tag = this.safeString (parts, 1);
          let code = undefined;
          if (currency !== undefined) {
              code = currency['code'];
          }
          this.checkAddress (address);
          return {
              'currency': code,
              'address': address,
              'tag': tag,
              'info': depositAddress,
          };
      }
  
      async createDepositAddress (code, params = {}) {
          const request = {
              'GenerateNewKey': true,
          };
          return await this.fetchDepositAddress (code, this.extend (request, params));
      }
  
      async fetchDeposits (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' ]);
          let currency = undefined;
          if (code !== undefined) {
              currency = this.currency (code);
          }
          const request = {
              'omsId': omsId,
              'AccountId': accountId,
          };
          const response = await this.privateGetGetDeposits (this.extend (request, params));
          //
          //     [
          //         {
          //             "OMSId":1,
          //             "DepositId":44,
          //             "AccountId":449,
          //             "SubAccountId":0,
          //             "ProductId":4,
          //             "Amount":200.00000000000000000000000000,
          //             "LastUpdateTimeStamp":637431291261187806,
          //             "ProductType":"CryptoCurrency",
          //             "TicketStatus":"FullyProcessed",
          //             "DepositInfo":"{}",
          //             "DepositCode":"ab0e23d5-a9ce-4d94-865f-9ab464fb1de3",
          //             "TicketNumber":71,
          //             "NotionalProductId":13,
          //             "NotionalValue":200.00000000000000000000000000,
          //             "FeeAmount":0.0000000000000000000000000000,
          //         },
          //     ]
          //
          return this.parseTransactions (response, currency, since, limit);
      }
  
      async fetchWithdrawals (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' ]);
          let currency = undefined;
          if (code !== undefined) {
              currency = this.currency (code);
          }
          const request = {
              'omsId': omsId,
              'AccountId': accountId,
          };
          const response = await this.privateGetGetWithdraws (this.extend (request, params));
          //
          //     [
          //         {
          //             "Amount": 0.0,
          //             "FeeAmount": 0.0,
          //             "NotionalValue": 0.0,
          //             "WithdrawId": 0,
          //             "AssetManagerId": 0,
          //             "AccountId": 0,
          //             "AssetId": 0,
          //             "TemplateForm": "{\"TemplateType\": \"TetherRPCWithdraw\",\"Comment\": \"TestWithdraw\",\"ExternalAddress\": \"ms6C3pKAAr8gRCcnVebs8VRkVrjcvqNYv3\"}",
          //             "TemplateFormType": "TetherRPCWithdraw",
          //             "omsId": 0,
          //             "TicketStatus": 0,
          //             "TicketNumber": 0,
          //             "WithdrawTransactionDetails": "",
          //             "WithdrawType": "",
          //             "WithdrawCode": "490b4fa3-53fc-44f4-bd29-7e16be86fba3",
          //             "AssetType": 0,
          //             "Reaccepted": true,
          //             "NotionalProductId": 0
          //         },
          //     ]
          //
          return this.parseTransactions (response, currency, since, limit);
      }
  
      parseTransactionStatusByType (status, type = undefined) {
          const statusesByType = {
              'deposit': {
                  'New': 'pending', // new ticket awaiting operator review
                  'AdminProcessing': 'pending', // an admin is looking at the ticket
                  'Accepted': 'pending', // an admin accepts the ticket
                  'Rejected': 'rejected', // admin rejects the ticket
                  'SystemProcessing': 'pending', // automatic processing; an unlikely status for a deposit
                  'FullyProcessed': 'ok', // the deposit has concluded
                  'Failed': 'failed', // the deposit has failed for some reason
                  'Pending': 'pending', // Account Provider has set status to pending
                  'Confirmed': 'pending', // Account Provider confirms the deposit
                  'AmlProcessing': 'pending', // anti-money-laundering process underway
                  'AmlAccepted': 'pending', // anti-money-laundering process successful
                  'AmlRejected': 'rejected', // deposit did not stand up to anti-money-laundering process
                  'AmlFailed': 'failed', // anti-money-laundering process failed/did not complete
                  'LimitsAccepted': 'pending', // deposit meets limits for fiat or crypto asset
                  'LimitsRejected': 'rejected', // deposit does not meet limits for fiat or crypto asset
              },
              'withdrawal': {
                  'New': 'pending', // awaiting operator review
                  'AdminProcessing': 'pending', // An admin is looking at the ticket
                  'Accepted': 'pending', // withdrawal will proceed
                  'Rejected': 'rejected', // admin or automatic rejection
                  'SystemProcessing': 'pending', // automatic processing underway
                  'FullyProcessed': 'ok', // the withdrawal has concluded
                  'Failed': 'failed', // the withdrawal failed for some reason
                  'Pending': 'pending', // the admin has placed the withdrawal in pending status
                  'Pending2Fa': 'pending', // user must click 2-factor authentication confirmation link
                  'AutoAccepted': 'pending', // withdrawal will be automatically processed
                  'Delayed': 'pending', // waiting for funds to be allocated for the withdrawal
                  'UserCanceled': 'canceled', // withdraw canceled by user or Superuser
                  'AdminCanceled': 'canceled', // withdraw canceled by Superuser
                  'AmlProcessing': 'pending', // anti-money-laundering process underway
                  'AmlAccepted': 'pending', // anti-money-laundering process complete
                  'AmlRejected': 'rejected', // withdrawal did not stand up to anti-money-laundering process
                  'AmlFailed': 'failed', // withdrawal did not complete anti-money-laundering process
                  'LimitsAccepted': 'pending', // withdrawal meets limits for fiat or crypto asset
                  'LimitsRejected': 'rejected', // withdrawal does not meet limits for fiat or crypto asset
                  'Submitted': 'pending', // withdrawal sent to Account Provider; awaiting blockchain confirmation
                  'Confirmed': 'pending', // Account Provider confirms that withdrawal is on the blockchain
                  'ManuallyConfirmed': 'pending', // admin has sent withdrawal via wallet or admin function directly; marks ticket as FullyProcessed; debits account
                  'Confirmed2Fa': 'pending', // user has confirmed withdraw via 2-factor authentication.
              },
          };
          const statuses = this.safeValue (statusesByType, type, {});
          return this.safeString (statuses, status, status);
      }
  
      parseTransaction (transaction, currency = undefined) {
          //
          // fetchDeposits
          //
          //     {
          //         "OMSId":1,
          //         "DepositId":44,
          //         "AccountId":449,
          //         "SubAccountId":0,
          //         "ProductId":4,
          //         "Amount":200.00000000000000000000000000,
          //         "LastUpdateTimeStamp":637431291261187806,
          //         "ProductType":"CryptoCurrency",
          //         "TicketStatus":"FullyProcessed",
          //         "DepositInfo":"{}",
          //         "DepositCode":"ab0e23d5-a9ce-4d94-865f-9ab464fb1de3",
          //         "TicketNumber":71,
          //         "NotionalProductId":13,
          //         "NotionalValue":200.00000000000000000000000000,
          //         "FeeAmount":0.0000000000000000000000000000,
          //     }
          //
          // fetchWithdrawals
          //
          //     {
          //         "Amount": 0.0,
          //         "FeeAmount": 0.0,
          //         "NotionalValue": 0.0,
          //         "WithdrawId": 0,
          //         "AssetManagerId": 0,
          //         "AccountId": 0,
          //         "AssetId": 0,
          //         "TemplateForm": "{\"TemplateType\": \"TetherRPCWithdraw\",\"Comment\": \"TestWithdraw\",\"ExternalAddress\": \"ms6C3pKAAr8gRCcnVebs8VRkVrjcvqNYv3\"}",
          //         "TemplateFormType": "TetherRPCWithdraw",
          //         "omsId": 0,
          //         "TicketStatus": 0,
          //         "TicketNumber": 0,
          //         "WithdrawTransactionDetails": "",
          //         "WithdrawType": "",
          //         "WithdrawCode": "490b4fa3-53fc-44f4-bd29-7e16be86fba3",
          //         "AssetType": 0,
          //         "Reaccepted": true,
          //         "NotionalProductId": 0
          //     }
          //
          const id = this.safeString (transaction, 'DepositId');
          let txid = undefined;
          const currencyId = this.safeString (transaction, 'ProductId');
          const code = this.safeCurrencyCode (currencyId, currency);
          let timestamp = undefined;
          let type = undefined;
          if ('DepositId' in transaction) {
              type = 'deposit';
          } else if ('WithdrawId' in transaction) {
              type = 'withdrawal';
          }
          const templateFormString = this.safeString (transaction, 'TemplateForm');
          let address = undefined;
          let updated = this.safeInteger (transaction, 'LastUpdateTimeStamp');
          if (templateFormString !== undefined) {
              const templateForm = JSON.parse (templateFormString);
              address = this.safeString (templateForm, 'ExternalAddress');
              txid = this.safeString (templateForm, 'TxId');
              timestamp = this.safeInteger (templateForm, 'TimeSubmitted');
              updated = this.safeInteger (templateForm, 'LastUpdated', updated);
          }
          const addressTo = address;
          const status = this.parseTransactionStatusByType (this.safeString (transaction, 'TicketStatus'), type);
          const amount = this.safeFloat (transaction, 'Amount');
          const feeCost = this.safeFloat (transaction, 'FeeAmount');
          let fee = undefined;
          if (feeCost !== undefined) {
              fee = { 'currency': code, 'cost': feeCost };
          }
          return {
              'info': transaction,
              'id': id,
              'txid': txid,
              'timestamp': timestamp,
              'datetime': this.iso8601 (timestamp),
              'address': address,
              'addressTo': addressTo,
              'addressFrom': undefined,
              'tag': undefined,
              'tagTo': undefined,
              'tagFrom': undefined,
              'type': type,
              'amount': amount,
              'currency': code,
              'status': status,
              'updated': updated,
              'fee': fee,
          };
      }
  
      nonce () {
          return this.milliseconds ();
      }
  
      sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
          let url = this.urls['api'][api] + '/' + this.implodeParams (path, params);
          const query = this.omit (params, this.extractParams (path));
          if (api === 'public') {
              if (Object.keys (query).length) {
                  url += '?' + this.urlencode (query);
              }
          } else if (api === 'private') {
              this.checkRequiredCredentials ();
              const nonce = this.nonce ().toString ();
              const auth = nonce + this.uid + this.apiKey;
              const signature = this.hmac (this.encode (auth), this.encode (this.secret));
              headers = {
                  'Nonce': nonce,
                  'APIKey': this.apiKey,
                  'Signature': signature,
                  'UserId': this.uid,
              };
              if (method === 'POST') {
                  headers['Content-Type'] = 'application/json';
                  body = this.json (query);
              } else {
                  if (Object.keys (query).length) {
                      url += '?' + this.urlencode (query);
                  }
              }
          }
          return { 'url': url, 'method': method, 'body': body, 'headers': headers };
      }
  
      handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
          if (code === 404) {
              throw new AuthenticationError (this.id + ' ' + body);
          }
          if (response === undefined) {
              return;
          }
          //
          //     {"status":"Rejected","errormsg":"Not_Enough_Funds","errorcode":101}
          //     {"result":false,"errormsg":"Server Error","errorcode":102,"detail":null}
          //
          const message = this.safeString (response, 'errormsg');
          if ((message !== undefined) && (message !== '')) {
              const feedback = this.id + ' ' + body;
              this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
              this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback);
              throw new ExchangeError (feedback);
          }
      }
  };
  