public Dictionary<int, decimal> GetPricesByRegion(IEnumerable<int> typeIDs, int regionID, PriceStat stat) { Dictionary<int, decimal> result; // do the query result = GetPriceHelper(typeIDs, regionID, stat); // save the cache sooner rather than later, just in case! SaveCache(); // return it return result; }
private Dictionary<int, decimal> GetPriceHelper(IEnumerable<int> typeIDs, int regionID, PriceStat stat) { Dictionary<int, MarketStat> parsed; Dictionary<int, MarketStat> cached = new Dictionary<int, MarketStat>(); Dictionary<int, decimal> answer; List<int> uncachedTypeIDs = new List<int>(); List<KeyValuePair<string, string>> parameters; string resultPath = null; // check the cache, add cache misses to the query parameters foreach (int typeID in typeIDs) { MarketStat value; if (TryGetCachedMarketStat(typeID, regionID, out value)) cached[typeID] = value; else uncachedTypeIDs.Add(typeID); } try { // EVE Central allows us only 100 types per request for (int i = 0; i * MAX_TYPES_PER_QUERY < uncachedTypeIDs.Count; ++i) { // progress feedback OnUpdateProgress(i * MAX_TYPES_PER_QUERY, uncachedTypeIDs.Count); // rate limit queries lock (m_rateLock) { TimeSpan elapsed = DateTime.Now.Subtract(m_lastQuery); if (elapsed < m_rateLimit) System.Threading.Thread.Sleep(m_rateLimit.Subtract(elapsed)); m_lastQuery = DateTime.Now; } // generate typeid parameters for this iteration parameters = new List<KeyValuePair<string, string>>(); for (int j = i * MAX_TYPES_PER_QUERY; j < uncachedTypeIDs.Count && j < (i + 1) * MAX_TYPES_PER_QUERY; ++j) parameters.Add(new KeyValuePair<string, string>("typeid", uncachedTypeIDs[j].ToString())); // if regionID has a value, fill in our single region limit if (regionID > 0) parameters.Add(new KeyValuePair<string, string>("regionlimit", regionID.ToString())); try { // fetch the data we want resultPath = Resources.DownloadUrlPost(EVECENTRAL_MARKETSTAT_URL, parameters); // parse it parsed = ParseMarketStat(resultPath); } finally { // clean up temp file if (resultPath != null) { try { File.Delete(resultPath); } catch { /* pass */ } } } // cache the new stuff and copy to the cached dictionary for output below foreach (KeyValuePair<int, MarketStat> entry in parsed) { CacheMarketStat(entry.Key, regionID, entry.Value); cached[entry.Key] = entry.Value; } } // progress update the last OnUpdateProgress(uncachedTypeIDs.Count, uncachedTypeIDs.Count); // convert output to the form we want answer = new Dictionary<int, decimal>(cached.Count); foreach (KeyValuePair<int, MarketStat> entry in cached) { decimal price; // read the requested value from the struct switch (stat) { case PriceStat.Mean: price = entry.Value.All.Avg; break; case PriceStat.Median: price = entry.Value.All.Median; break; case PriceStat.High: price = entry.Value.All.Max; break; case PriceStat.Low: price = entry.Value.All.Min; break; default: throw new ArgumentException("Don't know how to process PriceStat " + stat); } // add the converted value answer[entry.Key] = price; } // return the answer return answer; } catch (Exception ex) { throw new PriceProviderException(PriceProviderFailureReason.UnexpectedError, "Unexpected error while querying EVE-Central prices", ex); } }
public Dictionary<int, decimal> GetPrices(IEnumerable<int> typeIDs, PriceStat stat) { return GetPricesByRegion(typeIDs, PriceRegion.ALL, stat); }
public Dictionary<int, decimal> GetPricesHighSec(IEnumerable<int> typeIDs, PriceStat stat) { throw new NotImplementedException(); }
public decimal GetPriceHighSec(int typeID, PriceStat stat) { throw new NotImplementedException(); }
public decimal GetPriceByRegion(int typeID, int regionID, PriceStat stat) { Dictionary<int, decimal> answer = GetPriceHelper(new int[] { typeID }, regionID, stat); decimal price; if (answer.TryGetValue(typeID, out price)) return price; else throw new PriceProviderException(PriceProviderFailureReason.PriceMissing, "Answer did not contain the requested price"); }
public void UpdatePrice(PriceEntryBase priceEntryBase) { DateTime now = DateTime.Now; decimal price = priceEntryBase.NetEarn; decimal totalPrice = price; int totalCount = PriceList.Count; List <decimal> window = new List <decimal> { price }; decimal windowedPrice = price; int windowedCount = 0; bool outlier = false; if (!PriceList.ContainsKey(priceEntryBase)) { PriceList.Add(priceEntryBase, new List <PriceStat>()); } foreach (PriceStat stat in PriceList[priceEntryBase]) { decimal historicPrice = stat.CurrentPrice; totalPrice += historicPrice; if (stat.Time == now) { return; } if (stat.Time >= now - _statWindow) { window.Add(historicPrice); windowedPrice += historicPrice; windowedCount++; } } decimal totalAveragePrice = totalPrice / (totalCount + 1); decimal windowedAveragePrice = windowedPrice / (windowedCount + 1); if (windowedCount >= 10) // Makes sure at least ten entries are in there { window.Sort(); if (_iqrMultiplier > 0) { int iqrIndex = (int)Math.Truncate(window.Count * 0.75); decimal iqr = window[iqrIndex] - window[window.Count - iqrIndex]; outlier = price > windowedAveragePrice + (_iqrMultiplier * iqr); } else { // If the IQR multiplier is negative or zero, // it'll try to use percentiles for outlierdetection // Not advised! Will kill off trending profits, iqr-multiplying is preferable int outlierIndex = (int)Math.Truncate(window.Count * _percentile); decimal[] outliers = { window[outlierIndex], window[window.Count - outlierIndex] }; outlier = price > outliers.Max(); } } PriceStat priceStat = new PriceStat { Time = now, CurrentPrice = price, TotalAveragePrice = totalAveragePrice, WindowedAveragePrice = windowedAveragePrice, Outlier = outlier }; priceEntryBase.Outlier = outlier; priceEntryBase.NetAverage = windowedAveragePrice; PriceList[priceEntryBase].Add(priceStat); }
public decimal GetPrice(int typeID, PriceStat stat) { return GetPriceByRegion(typeID, PriceRegion.ALL, stat); }
public decimal GetPriceByRegion(int typeID, int regionID, PriceStat stat) { return GetPriceInternal(typeID, regionID, stat); }
public Dictionary<int, decimal> GetPricesByRegion(IEnumerable<int> typeIDs, int regionID, PriceStat stat) { return GetPricesInternal(typeIDs, regionID, stat); }
public decimal GetPriceHighSec(int typeID, PriceStat stat) { return GetPriceByRegion(typeID, PriceRegion.CHS, stat); }
private Dictionary<int, decimal> GetPricesInternal(IEnumerable<int> typeIDs, int regionID, PriceStat stat) { Dictionary<int, ZofuEntry> regionCache; Dictionary<int, decimal> result = new Dictionary<int, decimal>(); ZofuEntry entry; CacheResult cacheResult; // check and download the file if it's missing or out of date cacheResult = DownloadRegionFile(regionID); // all right, result, let's fill it lock (m_cache) { // see if we have prices for the region if (!m_cache.TryGetValue(regionID, out regionCache)) throw new PriceProviderException(PriceProviderFailureReason.CacheEmpty, "No prices available for region " + regionID.ToString(), cacheResult.Exception); // the easy part foreach (int typeID in typeIDs) { if (regionCache.TryGetValue(typeID, out entry)) { switch (stat) { case PriceStat.Mean: result[typeID] = entry.Avg; break; case PriceStat.Median: result[typeID] = entry.Median; break; case PriceStat.Low: result[typeID] = entry.Low; break; case PriceStat.High: result[typeID] = entry.High; break; default: throw new ApplicationException("Unknown stat " + stat.ToString()); } } } } // finally return result; }
private decimal GetPriceInternal(int typeID, int regionID, PriceStat stat) { Dictionary<int, decimal> result; decimal value; result = GetPricesInternal(new int[] { typeID }, regionID, stat); if (!result.TryGetValue(typeID, out value)) throw new PriceProviderException(PriceProviderFailureReason.PriceMissing, "No price for typeID " + typeID.ToString()); else return value; }
public void UpdatePrice(PriceEntryBase priceEntryBase) { DateTime now = DateTime.Now; decimal price = priceEntryBase.NetEarn; decimal totalPrice = price; int totalCount = PriceList.Count; List <decimal> window = new List <decimal> { price }; decimal windowedPrice = price; int windowedCount = 0; bool outlier = false; if (!PriceList.ContainsKey(priceEntryBase)) { PriceList.Add(priceEntryBase, new List <PriceStat>()); } foreach (PriceStat stat in PriceList[priceEntryBase]) { if (stat.CurrentPrice > 0) { decimal historicPrice = stat.CurrentPrice; totalPrice += historicPrice; if (stat.Time == now) { return; } if (stat.Time >= now - _statWindow) { window.Add(historicPrice); windowedPrice += historicPrice; windowedCount++; } } } decimal totalAveragePrice = totalPrice / (totalCount + 1); decimal windowedAveragePrice = windowedPrice / (windowedCount + 1); if (windowedCount >= 10) // Makes sure at least ten entries are in there { window.Sort(); if (_iqrMultiplier > 0) { double sumOfSquareOfDifferences = (double)PriceList[priceEntryBase].Where(stat => stat.Time >= now - _statWindow) .Sum(stat => (stat.CurrentPrice - windowedAveragePrice) * (stat.CurrentPrice - windowedAveragePrice)); decimal standardDeviation = (decimal)Math.Sqrt(sumOfSquareOfDifferences / windowedCount); decimal top = windowedAveragePrice + standardDeviation; decimal bottom = windowedAveragePrice - standardDeviation; decimal iqr = top - bottom; decimal max = windowedAveragePrice + (iqr * _iqrMultiplier); outlier = price > max; } else { // If the IQR multiplier is negative or zero, // it'll try to use percentiles for outlierdetection // Not advised! Will kill off trending profits, iqr-multiplying is preferable int outlierIndex = (int)Math.Truncate(window.Count * _percentile); decimal[] outliers = { window[outlierIndex], window[window.Count - outlierIndex] }; outlier = price > outliers.Max(); } } PriceStat priceStat = new PriceStat { Time = now, CurrentPrice = price, TotalAveragePrice = totalAveragePrice, WindowedAveragePrice = windowedAveragePrice, Outlier = outlier }; priceEntryBase.Outlier = outlier; priceEntryBase.NetAverage = windowedAveragePrice; // if (priceEntryBase.Enabled == true) PriceList[priceEntryBase].Add(priceStat); }
public void UpdatePrice(PriceEntryBase priceEntryBase) { DateTime now = DateTime.Now; decimal price = priceEntryBase.NetEarn; decimal totalPrice = price; int totalCount = PriceList.Count; List<decimal> window = new List<decimal> {price}; decimal windowedPrice = price; int windowedCount = 0; bool outlier = false; if (!PriceList.ContainsKey(priceEntryBase)) PriceList.Add(priceEntryBase, new List<PriceStat>()); foreach (PriceStat stat in PriceList[priceEntryBase]) { decimal historicPrice = stat.CurrentPrice; totalPrice += historicPrice; if (stat.Time == now) return; if (stat.Time >= now - _statWindow) { window.Add(historicPrice); windowedPrice += historicPrice; windowedCount++; } } decimal totalAveragePrice = totalPrice/(totalCount+1); decimal windowedAveragePrice = windowedPrice/(windowedCount+1); if (windowedCount >= 10) // Makes sure at least ten entries are in there { window.Sort(); if (_iqrMultiplier > 0) { double sumOfSquareOfDifferences = (double) PriceList[priceEntryBase].Where(stat => stat.Time >= now - _statWindow) .Sum(stat => (stat.CurrentPrice - windowedAveragePrice) * (stat.CurrentPrice - windowedAveragePrice)); decimal standardDeviation = (decimal) Math.Sqrt(sumOfSquareOfDifferences/windowedCount); decimal top = windowedAveragePrice + standardDeviation; decimal bottom = windowedAveragePrice - standardDeviation; decimal iqr = top - bottom; decimal max = windowedAveragePrice + (iqr * _iqrMultiplier); outlier = price > max; } else { // If the IQR multiplier is negative or zero, // it'll try to use percentiles for outlierdetection // Not advised! Will kill off trending profits, iqr-multiplying is preferable int outlierIndex = (int) Math.Truncate(window.Count*_percentile); decimal[] outliers = {window[outlierIndex], window[window.Count - outlierIndex]}; outlier = price > outliers.Max(); } } PriceStat priceStat = new PriceStat { Time = now, CurrentPrice = price, TotalAveragePrice = totalAveragePrice, WindowedAveragePrice = windowedAveragePrice, Outlier = outlier }; priceEntryBase.Outlier = outlier; priceEntryBase.NetAverage = windowedAveragePrice; PriceList[priceEntryBase].Add(priceStat); }