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; } }