async Task ReloadDbDataIfChangedImpl() // if necessary it reloads Historical and Realtime data { Console.WriteLine("*MemDb is not yet ready! ReloadDbData is in progress..."); DateTime startTime = DateTime.UtcNow; // GA.IM.NAV assets have user_id data, so User data has to be reloaded too before Assets (bool isDbReloadNeeded, User[]? newUsers, List <Asset>?sqCoreAssets) = m_Db.GetDataIfReloadNeeded(); if (!isDbReloadNeeded) { return; } // to minimize the time memDb is not consintent we create everything into new pointers first, then update them quickly var newAssetCache = AssetsCache.CreateAssetCache(sqCoreAssets !); // var newPortfolios = GeneratePortfolios(); if (IsInitialized) { // if this is the periodic (not initial) reload of RedisDb, then we don't surprise clients by emptying HistPrices // and not having HistPrices for 20minutes. So, we download HistPrices before swapping m_memData pointer DateTime startTimeHist = DateTime.UtcNow; var newDailyHist = await CreateDailyHist(m_Db, newUsers !, newAssetCache); // downloads historical prices from YF. Assume it takes 20min if (newDailyHist == null) { newDailyHist = new CompactFinTimeSeries <DateOnly, uint, float, uint>(); } m_lastHistoricalDataReload = DateTime.UtcNow; m_lastHistoricalDataReloadTs = DateTime.UtcNow - startTimeHist; var newMemData = new MemData(newUsers !, newAssetCache, newDailyHist); m_memData = newMemData; // swap pointer in atomic operation Console.WriteLine($"*MemDb is ready! (#Assets: {AssetsCache.Assets.Count}, #HistoricalAssets: {DailyHist.GetDataDirect().Data.Count}) in {m_lastHistoricalDataReloadTs.TotalSeconds:0.000}sec"); } else { // if this is the first time to load DB from Redis, then we don't demand HistData. Assume HistData crawling takes 20min // many clients can survive without historical data first. MarketDashboard. However, they need Asset and User data immediately. // BrAccInfo is fine wihout historical. It will send NaN as a LastClose. Fine. Client will handle it. // So, we don't need to wait for Historical to finish InitDb (that might take 20 minutes in the future). // !!! Also, in development, we don't want to wait until All HistData arrives, but start Debugging code right away after starting the WebServer. // Clients of MemDb should handle properly if HistData is not yet ready (NaN and later Refresh). var newMemData = new MemData(newUsers !, newAssetCache, new CompactFinTimeSeries <DateOnly, uint, float, uint>()); m_memData = newMemData; // swap pointer in atomic operation Console.WriteLine($"*MemDb is half-ready! (#Assets: {AssetsCache.Assets.Count}, #HistoricalAssets: 0)"); } m_lastDbReload = DateTime.UtcNow; m_lastDbReloadTs = DateTime.UtcNow - startTime; foreach (var brAccount in BrAccounts) { UpdateBrAccPosAssetIds(brAccount.AccPoss); } OnReloadAssetData_ReloadRtDataAndSetTimer(); // downloads realtime prices from YF or IEX OnReloadAssetData_ReloadRtNavDataAndSetTimer(); // downloads realtime NAVs from VBrokers EvDbDataReloaded?.Invoke(); }
TsDateData <TKey, TAssetId, TValue1, TValue2> m_data; // this m_data pointer can be swapped in an atomic instruction after update static void HowToUseThisClassExamples() { DateOnly[] dates = new DateOnly[2] { new DateOnly(2020, 05, 05), new DateOnly(2020, 05, 06) }; var dict1 = new Dictionary <TickType, float[]>() { { TickType.SplitDivAdjClose, new float[2] { 10.1f, 12.1f } } }; var dict2 = new Dictionary <TickType, uint[]>(); uint assetId = 1; var data = new Dictionary <uint, Tuple <Dictionary <TickType, float[]>, Dictionary <TickType, uint[]> > >() { { assetId, new Tuple <Dictionary <TickType, float[]>, Dictionary <TickType, uint[]> >(dict1, dict2) } }; var ts1 = new CompactFinTimeSeries <DateOnly, uint, float, uint>(); ts1.ChangeData(dates, data); }
public MemData(User[] newUsers, AssetsCache newAssetCache, CompactFinTimeSeries <DateOnly, uint, float, uint> newDailyHist) { Users = newUsers; AssetsCache = newAssetCache; DailyHist = newDailyHist; }