public void ParseTickers(JArray tickers)
        {
            foreach (var raw_ticker in tickers)
            {
                try
                {
                    string symbol = raw_ticker.Value <string>("s");
                    var    ticker = TickerFromString(symbol);

                    TickerData data = new TickerData();

                    data.Ticker                = ticker;
                    data.LastTrade             = double.Parse(raw_ticker.Value <string>("c"));
                    data.DailyChangePercentage = double.Parse(raw_ticker.Value <string>("P")) / 100d;
                    data.DailyHigh             = double.Parse(raw_ticker.Value <string>("h"));
                    data.DailyLow              = double.Parse(raw_ticker.Value <string>("l"));
                    data.DailyVolume           = double.Parse(raw_ticker.Value <string>("v"));
                    data.Retrieved             = DateTime.Now;

                    TickerData[ticker] = data;
                    TickerAge[ticker]  = DateTime.Now;

                    OnTickerUpdateReceived?.Invoke(this, data);
                }
                catch
                {
                }
            }
        }
        public void HandleChannelMessage(JArray update)
        {
            int chan_id = update[0].Value <int>();

            string channel_type = channel_ids.ContainsKey(chan_id) ? channel_ids[chan_id] : update[1].Value <string>();
            string identifier   = channel_type.Split('_')[0];

            if (update[1].Type == JTokenType.String && update[1].Value <string>() == "hb")
            {
                //Log.Info("{0} heartbeat", chan_id);
                return;
            }

            switch (identifier)
            {
            case "ws":
            {
                foreach (var wallet in update[2])
                {
                    if (wallet[4].Type == JTokenType.Null)
                    {
                        if (wallet[2].Value <float>() > 0)
                        {
                            Recalculate(update[0].Value <int>(), string.Format("wallet_{0}_{1}", wallet[0], wallet[1]));
                            continue;
                        }
                        else
                        {
                            wallet[4] = 0;
                        }
                    }

                    Log.Debug("{0} wallet of currency {1} has {2} balance available", wallet[0], wallet[1], wallet[4]);
                    ExchangeBalances[wallet[1].Value <string>()] = wallet[4].Value <double>();
                }
            }
            break;

            case "wu":
            {
                var wallet = update[2];
                Log.Debug("{0} wallet of currency {1} has {2} balance available", wallet[0], wallet[1], wallet[2]);
                ExchangeBalances[wallet[1].Value <string>()] = wallet[4].Value <double>();
            }
            break;

            case "n":
                Log.Debug(update);
                break;

            case "book":
                HandleBook(update);
                break;

            case "ticker":
                string ticker_name = channel_type.Split('_')[1];
                Ticker ticker      = TickerFromString(ticker_name);

                //Log.Info("Ticker update on {0}: ask={1:0.0000000}/{2:0.00}, bid={3:0.0000000}/{4:0.00}, last_price={5:0.0000000}", channel_type.Split('_')[1], update[1][0], GetDepth(10, BookEntryType.Ask), update[1][2], GetDepth(10, BookEntryType.Bid), update[1][6]);
                Log.Info("Ticker update on {0}: ask={1:0.0000000}, bid={2:0.0000000}, last_price={3:0.0000000}", ticker_name, update[1][0], update[1][2], update[1][6]);
                //TickerData[channel_type.Split('_')[1]] = update[1][6].Value<double>();
                TickerAge[ticker]  = DateTime.Now;
                TickerData[ticker] = new TickerData(ticker, update);

                OnTickerUpdateReceived?.Invoke(this, TickerData[ticker]);
                break;

            case "miu":
            case "fiu":
                break;

            default:
                Log.Warn("Unknown channel name \"{0}\"", channel_type);
                break;
            }
        }