void ITickerCollectionUpdateListener.OnUpdateTickerCollection(TickerCollection collection, bool useInvokeForUI)
        {
            ArbitrageInfo info = collection.Arbitrage;

            double prevProfits = info.MaxProfitUSD;
            double prevSpread  = info.Spread;

            collection.IsUpdating = true;
            info.Calculate();
            info.SaveExpectedProfitUSD();
            collection.IsUpdating = false;
            bool checkMaxProfits = true;

            if (info.AvailableProfitUSD > 20)
            {
                SelectedCollection     = collection;
                ShouldProcessArbitrage = true;
                checkMaxProfits        = false;
            }
            var action = new Action(() => {
                if (this.bbAllCurrencies.Checked && prevSpread * info.Spread < 0)
                {
                    RefreshGrid();
                }
                else
                {
                    RefreshGridRow(collection);
                }
                if (checkMaxProfits && info.MaxProfitUSD - prevProfits > 20)
                {
                    ShowNotification(collection, prevProfits);
                }
                for (int i = 0; i < collection.Count; i++)
                {
                    Ticker ticker = collection.Tickers[i];
                    if (ticker.OrderBook.BidHipeStarted || ticker.OrderBook.AskHipeStarted)
                    {
                        SendBoostNotification(ticker);
                    }
                    else if (ticker.OrderBook.BidHipeStopped || ticker.OrderBook.AskHipeStopped)
                    {
                        SendBoostStopNotification(ticker);
                    }
                }
            });

            if (useInvokeForUI && IsHandleCreated)
            {
                BeginInvoke(action);
            }
            else
            {
                action();
            }
        }
        void ITickerCollectionUpdateListener.OnUpdateTickerCollection(TickerCollection collection, bool useInvokeForUI)
        {
            collection.RequestOverdue = false;

            ArbitrageInfo info = collection.Arbitrage;

            double prevProfits = info.MaxProfitUSD;
            double prevSpread  = info.Spread;

            collection.IsUpdating = true;
            info.Calculate();
            info.SaveExpectedProfitUSD();
            RaiseArbitrageChanged(collection);
            collection.IsUpdating = false;


            //for(int i = 0; i < collection.Count; i++) {
            //    Ticker ticker = collection.Tickers[i];
            //    if(ticker.OrderBook.BidHipeStarted || ticker.OrderBook.AskHipeStarted)
            //        SendBoostNotification(ticker);
            //    else if(ticker.OrderBook.BidHipeStopped || ticker.OrderBook.AskHipeStopped)
            //        SendBoostStopNotification(ticker);
            //}
        }
        void ProcessSelectedArbitrageInfo()
        {
            ShouldProcessArbitrage = false;
            if (SelectedCollection == null)
            {
                LogManager.Default.Warning("There is no selected arbitrage info. Quit.");
                Invoke(new MethodInvoker(ShowLog));
                return;
            }
            ArbitrageInfo info = SelectedCollection.Arbitrage;

            LogManager.Default.Log("Update buy on market balance info.", info.LowestAskHost + " - " + info.LowestAskTicker.BaseCurrency);
            if (!info.LowestAskTicker.UpdateBalance(info.LowestAskTicker.BaseCurrency))
            {
                LogManager.Default.Error("Failed update buy on market currency balance. Quit.", SelectedCollection.Arbitrage.LowestAskTicker.BaseCurrency);
                Invoke(new MethodInvoker(ShowLog));
                return;
            }

            LogManager.Default.Log("Update buy on market balance info.", info.HighestBidHost + " - " + info.HighestBidTicker.MarketCurrency);
            if (!info.HighestBidTicker.UpdateBalance(info.HighestBidTicker.MarketCurrency))
            {
                LogManager.Default.Error("Failed update sell on market currency balance. Quit.", info.HighestBidTicker.MarketCurrency);
                Invoke(new MethodInvoker(ShowLog));
                return;
            }

            LogManager.Default.Log("Update arbitrage info values.", SelectedCollection.Name);
            if (!UpdateArbitrageInfoTask(SelectedCollection).Wait(5000))
            {
                LogManager.Default.Error("Failed arbitrage update info values. Timeout.", SelectedCollection.Name);
                Invoke(new MethodInvoker(ShowLog));
                return;
            }
            SelectedCollection.IsActual = true;

            info.Calculate();
            info.UpateAmountByBalance();
            if (info.ExpectedProfitUSD - info.MaxProfitUSD > 10)
            {
                LogManager.Default.Warning("Arbitrage amount reduced because of balance not enough.", "New Amount = " + info.Amount.ToString("0.00000000") + ", ProfitUSD = " + info.MaxProfitUSD);
            }

            if (info.AvailableProfitUSD <= 20)
            {
                LogManager.Default.Warning("Arbitrage Profit reduced since last time. Skip trading.", SelectedCollection.Name + " expected " + info.ExpectedProfitUSD + " but after update" + info.MaxProfitUSD);
                Invoke(new MethodInvoker(ShowLog));
                return;
            }

            if (!info.Buy())
            {
                LogManager.Default.Error("FATAL ERROR! Could not buy!", SelectedCollection.Name);
                return;
            }
            if (!info.Sell())
            {
                LogManager.Default.Error("FATAL ERROR! Could not sell!", SelectedCollection.Name);
                return;
            }

            string successText = "Arbitrage completed!!! Please check your balances." + SelectedCollection.Name + " earned <b>" + info.AvailableProfitUSD + "</b>";

            LogManager.Default.Success(successText);
            TelegramBot.Default.SendNotification(successText);
            UpdateBalanceNotification = true;
            Invoke(new MethodInvoker(ShowLog));
            return;
        }