using System; using System.Collections.Generic; using System.Text; using MarketData.MarketDataModel; using MarketData.DataAccess; using MarketData.Utils; using System.Linq; using MarketData.Helper; using MarketData.Numerical; using System.Threading; // This cache is mainly used by the models. It is a short lived cache that gets cleared out every 2 minutes. // This cache will attempt to load a price from the database if it is found in the cache. namespace MarketData.Cache { public class GBPriceCache { private Thread cacheMonitorThread=null; private volatile bool threadRun=true; private Object thisLock=new Object(); private Dictionary priceCache=new Dictionary(); // the main cache private Dictionary realTimePriceCache=new Dictionary(); // short lived cache of realtime prices gets cleared out every cacheRefreshAfter(ms) private Dictionary nullCache=new Dictionary(); private DateGenerator dateGenerator=new DateGenerator(); private static GBPriceCache priceCacheInstance=null; private int cacheRefreshAfter=120000; // the cache will be cleaned up after 2 minutes protected GBPriceCache() { cacheMonitorThread=new Thread(new ThreadStart(ThreadProc)); cacheMonitorThread.Start(); } public static GBPriceCache GetInstance() { lock(typeof(GBPriceCache)) { if(null==priceCacheInstance) { priceCacheInstance=new GBPriceCache(); } return priceCacheInstance; } } public void Clear() { lock(thisLock) { priceCache=new Dictionary(); realTimePriceCache=new Dictionary(); nullCache=new Dictionary(); } } public void Dispose() { lock(thisLock) { if(null==priceCacheInstance || false==threadRun)return; threadRun=false; if(null!=cacheMonitorThread) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[GBPriceCache:Dispose]Thread state is '{0}'. Joining main thread...",Utility.ThreadStateToString(cacheMonitorThread))); cacheMonitorThread.Join(5000); this.cacheMonitorThread=null; } MDTrace.WriteLine(LogLevel.DEBUG,"[GBPriceCache:Dispose] End."); priceCacheInstance=null; } } public void ClearCacheOnOrBefore(DateTime onOrBeforeDate,bool collect=false) { lock(thisLock) { MDTrace.WriteLine(LogLevel.DEBUG,"Clearing GBPriceCache cache."); List symbols=new List(priceCache.Keys); foreach(String symbol in symbols) { PricesByDate pricesByDate=priceCache[symbol]; List symbolDates=new List(pricesByDate.Keys); foreach(DateTime symbolDate in symbolDates) { if(symbolDate historicalDates=dateGenerator.GenerateHistoricalDates(startDate,dayCount+60); Prices prices=null; List missingDates=null; foreach(DateTime historicalDate in historicalDates) { if(!ContainsPrice(symbol,historicalDate)) { String key=symbol+Utility.DateTimeToStringMMHDDHYYYY(historicalDate); if(nullCache.ContainsKey(key)) continue; if(null==missingDates)missingDates=new List(); missingDates.Add(historicalDate); } } if(null!=missingDates) { DateTime minDate=(from DateTime date in missingDates select date).Min(); DateTime maxDate=(from DateTime date in missingDates select date).Max(); prices=PricingDA.GetPrices(symbol,maxDate,minDate); foreach(Price price in prices) AddPrice(price); prices=new Prices(); foreach(DateTime historicalDate in historicalDates) { if(!ContainsPrice(symbol,historicalDate)) { String key=symbol+Utility.DateTimeToStringMMHDDHYYYY(historicalDate); if(!nullCache.ContainsKey(key)) nullCache.Add(key,true); } else { if(!priceCache.ContainsKey(symbol)) continue; PricesByDate pricesByDate=priceCache[symbol]; if(!pricesByDate.ContainsKey(historicalDate.Date)) continue; prices.Add(pricesByDate[historicalDate]); } } } else { prices=new Prices(); foreach(DateTime historicalDate in historicalDates) { if(!priceCache.ContainsKey(symbol)) continue; if(!priceCache[symbol].ContainsKey(historicalDate.Date)) { continue; } prices.Add((priceCache[symbol])[historicalDate]); } } return new Prices(prices.OrderByDescending(x => x.Date).ToList().Take(dayCount).ToList()); } } private void AddPrice(Price price) { lock(thisLock) { if(null==price) return; PricesByDate pricesByDate=null; if(!priceCache.ContainsKey(price.Symbol)) { pricesByDate=new PricesByDate(); pricesByDate.Add(price.Date,price); priceCache.Add(price.Symbol,pricesByDate); } else { pricesByDate=priceCache[price.Symbol]; if(pricesByDate.ContainsKey(price.Date.Date)) return; pricesByDate.Add(price.Date.Date,price); } } } public bool ContainsPrice(String symbol,DateTime date) { if(!priceCache.ContainsKey(symbol)) return false; PricesByDate pricesByDate=priceCache[symbol]; if(!pricesByDate.ContainsKey(date.Date)) return false; return true; } private void ThreadProc() { int quantums=0; int quantumInterval=1000; while(threadRun) { Thread.Sleep(quantumInterval); if(!threadRun) break; quantums+=quantumInterval; if(quantums>cacheRefreshAfter) { quantums=0; lock(thisLock) { realTimePriceCache.Clear(); MDTrace.WriteLine(LogLevel.DEBUG,"Clearing GBPriceCache price cache."); } } } MDTrace.WriteLine(LogLevel.DEBUG,"[GBPriceCache:ThreadProc]Thread ended."); } } }