public void AddIndirectExchanges(params string[] currencyCodeRoute)
        {
            if (currencyCodeRoute.Length < 1)
            {
                return;
            }

            Dictionary <IExchange, ExchangePrice[]> routes = new Dictionary <IExchange, ExchangePrice[]>();

            for (int currencyIdx = 1; currencyIdx < currencyCodeRoute.Length; currencyIdx++)
            {
                foreach (MarketPrice price in this.GetPrices(currencyCodeRoute[currencyIdx - 1], currencyCodeRoute[currencyIdx]))
                {
                    ExchangePrice   exchangePrice = price as ExchangePrice;
                    ExchangePrice[] routePrices;

                    if (null == exchangePrice)
                    {
                        // Cannot route through non-exchange prices
                        continue;
                    }

                    if (!routes.TryGetValue(exchangePrice.Exchange, out routePrices))
                    {
                        routePrices = new ExchangePrice[currencyCodeRoute.Length - 1];
                        routes[exchangePrice.Exchange] = routePrices;
                    }

                    routePrices[currencyIdx - 1] = exchangePrice;
                }
            }

            List <MarketPrice> routedMarket = this.GetPrices(currencyCodeRoute[0], currencyCodeRoute[currencyCodeRoute.Length - 1]);

            foreach (IExchange exchange in routes.Keys)
            {
                bool            fullRoute = true;
                ExchangePrice[] route     = routes[exchange];

                // Check if we have steps for each part of the route.
                for (int routeIdx = 0; routeIdx < route.Length; routeIdx++)
                {
                    if (route[routeIdx] == null)
                    {
                        fullRoute = false;
                        break;
                    }
                }

                if (fullRoute)
                {
                    routedMarket.Add(new IndirectPrice(route));
                }
            }
        }
        public void UpdateAllPrices()
        {
            if (prices.Length == 0)
            {
                return;
            }

            List <Task> tasks         = new List <Task>();
            int         currencyCount = prices.GetLength(0);

            Dictionary <MarketId, ExchangePrice> vircurexPrices = new Dictionary <MarketId, ExchangePrice>();
            HashSet <string> vircurexQuoteCurrencyCodes         = new HashSet <string>();
            VircurexExchange vircurex = null;

            // Start the data fetch running in parallel; non-Vircurex first
            for (int baseCurrencyIdx = 0; baseCurrencyIdx < currencyCount; baseCurrencyIdx++)
            {
                for (int quoteCurrencyIdx = 0; quoteCurrencyIdx < currencyCount; quoteCurrencyIdx++)
                {
                    if (baseCurrencyIdx == quoteCurrencyIdx)
                    {
                        continue;
                    }

                    foreach (MarketPrice marketPrice in this.prices[baseCurrencyIdx, quoteCurrencyIdx])
                    {
                        // Can only update prices on markets which are directly tradable; other markets
                        // infer their prices from the underlying exchange prices.
                        // As such, ignore any non-exchange-price types.
                        ExchangePrice exchangePrice = marketPrice as ExchangePrice;

                        if (null == exchangePrice)
                        {
                            continue;
                        }

                        if (exchangePrice.Exchange is VircurexExchange)
                        {
                            VircurexMarketId marketId = new VircurexMarketId(currencyCodes[baseCurrencyIdx],
                                                                             currencyCodes[quoteCurrencyIdx]);

                            vircurexQuoteCurrencyCodes.Add(marketId.QuoteCurrencyCode);
                            vircurexPrices[marketId] = exchangePrice;
                            vircurex = (VircurexExchange)marketPrice.Exchange;
                        }
                        else
                        {
                            tasks.Add(exchangePrice.UpdatePriceAsync());
                        }
                    }
                }
            }

            // Perform data fetch for Vircurex currencies; these can be
            // done in a batch, so we do them once the rest of the data
            // requests are running
            foreach (string quoteCurrencyCode in vircurexQuoteCurrencyCodes)
            {
                Dictionary <MarketId, Book> books = vircurex.GetMarketOrdersAlt(quoteCurrencyCode).Result;

                foreach (MarketId marketId in books.Keys)
                {
                    ExchangePrice exchangePrice;

                    if (vircurexPrices.TryGetValue(marketId, out exchangePrice))
                    {
                        exchangePrice.MarketDepth = books[marketId];
                    }
                }
            }

            // Wait for all tasks to finish before we exit
            foreach (Task task in tasks)
            {
                task.Wait();
            }
        }
        public IndirectPrice(ExchangePrice[] setRoute)
        {
            AssertRouteValid(setRoute);

            this.Route = setRoute;
        }
        public void AddIndirectExchanges(params string[] currencyCodeRoute)
        {
            if (currencyCodeRoute.Length < 1)
            {
                return;
            }

            Dictionary<IExchange, ExchangePrice[]> routes = new Dictionary<IExchange, ExchangePrice[]>();

            for (int currencyIdx = 1; currencyIdx < currencyCodeRoute.Length; currencyIdx++)
            {
                foreach (MarketPrice price in this.GetPrices(currencyCodeRoute[currencyIdx - 1], currencyCodeRoute[currencyIdx]))
                {
                    ExchangePrice exchangePrice = price as ExchangePrice;
                    ExchangePrice[] routePrices;

                    if (null == exchangePrice)
                    {
                        // Cannot route through non-exchange prices
                        continue;
                    }

                    if (!routes.TryGetValue(exchangePrice.Exchange, out routePrices))
                    {
                        routePrices = new ExchangePrice[currencyCodeRoute.Length - 1];
                        routes[exchangePrice.Exchange] = routePrices;
                    }

                    routePrices[currencyIdx - 1] = exchangePrice;
                }
            }

            List<MarketPrice> routedMarket = this.GetPrices(currencyCodeRoute[0], currencyCodeRoute[currencyCodeRoute.Length - 1]);

            foreach (IExchange exchange in routes.Keys)
            {
                bool fullRoute = true;
                ExchangePrice[] route = routes[exchange];

                // Check if we have steps for each part of the route.
                for (int routeIdx = 0; routeIdx < route.Length; routeIdx++) {
                    if (route[routeIdx] == null) {
                        fullRoute = false;
                        break;
                    }
                }

                if (fullRoute)
                {
                    routedMarket.Add(new IndirectPrice(route));
                }
            }
        }
        public static void AssertRouteValid(ExchangePrice[] route)
        {
            if (route.Length == 0)
            {
                throw new InvalidRouteException("Trading route between two currencies cannot be empty.");
            }

            IExchange referenceExchange = route[0].Exchange;
            HashSet<ExchangePrice> breadcrumbs = new HashSet<ExchangePrice>();
            Market previousMarket = null;

            foreach (ExchangePrice routeElement in route)
            {
                if (!routeElement.Exchange.Equals(referenceExchange))
                {
                    throw new InvalidRouteException("All exchanges in the price path must be the same.");
                }

                if (breadcrumbs.Contains(routeElement))
                {
                    throw new InvalidRouteException("Route is cyclic; have seen the market \""
                        + routeElement.Market + "\" once already in the route.");
                }

                // TODO: Consider inverting the next route element, if it has a valid pair.
                if (null != previousMarket
                    && !previousMarket.QuoteCurrencyCode.Equals(routeElement.Market.BaseCurrencyCode))
                {
                    throw new InvalidRouteException("Route does not connect directly; cannot go from \""
                        + previousMarket + " to \""
                        + routeElement.Market + "\".");
                }

                breadcrumbs.Add(routeElement);
                previousMarket = routeElement.Market;
            }
        }