From e633235407bc2e69385bf2b3b5e6c56ca8f3e711 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 27 Feb 2026 16:39:54 -0500 Subject: [PATCH] Optimize GetGainLossWithDetailByDateAnd Account GetGainLossWithDetailByDate --- .../Controllers/GainLossController.cs | 361 +++++++++++++++--- 1 file changed, 298 insertions(+), 63 deletions(-) diff --git a/MarketDataServer/Controllers/GainLossController.cs b/MarketDataServer/Controllers/GainLossController.cs index 0a87acf..b78b6e0 100755 --- a/MarketDataServer/Controllers/GainLossController.cs +++ b/MarketDataServer/Controllers/GainLossController.cs @@ -125,54 +125,68 @@ namespace MarketDataServer.Controllers } /// - /// GetGainLossWithDetailByDate - Optimizing calls to DA methods + /// GetGainLossWithDetailByDate2 - Refactored: cache primed once before loop. + /// GenerateGainLoss still called per symbol (GainLossCollection is a time series, + /// not keyed by symbol) but cache is already populated so DB fetches are skipped. /// - /// - /// - /// [HttpGet] - public IEnumerable GetGainLossWithDetailByDate(String token,DateTime selectedDate) + public IEnumerable GetGainLossWithDetailByDate(String token, DateTime selectedDate) { Profiler profiler = new Profiler(); try { - MDTrace.WriteLine(LogLevel.DEBUG,$"Start"); + MDTrace.WriteLine(LogLevel.DEBUG, $"Start"); if (!Authorizations.GetInstance().IsAuthorized(token)) return null; - GLPriceCache.GetInstance().Refresh(); + PortfolioTrades portfolioTrades = PortfolioDA.GetTrades(); PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate); + + // Prime cache once for all symbols before any calculation begins. + // Guarantees all symbols are cached and open positions have the latest intraday price. + // GenerateGainLoss calls Add(PortfolioTrades) internally per symbol — those calls + // will hit the cache and skip DB fetches since all symbols are already populated. + GLPriceCache.GetInstance().Add(tradesOnOrBefore); GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate); List symbols = gainLossSummaryItems.Select(x => x.Symbol).ToList(); - Dictionary latestDates = PricingDA.GetLatestDates(symbols); - + Dictionary latestDates = PricingDA.GetLatestDates(symbols); + if (null == gainLossGenerator) gainLossGenerator = new ActiveGainLossGenerator(); List gainLossSummaryItemDetailCollection = new List(); foreach (GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems) { GainLossSummaryItemDetail gainLossSummaryItemDetail = new GainLossSummaryItemDetail(gainLossSummaryItem); - portfolioTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol); - double weightAdjustedDividendYield = portfolioTrades.GetWeightAdjustedDividendYield(); + + PortfolioTrades symbolTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol); + if (null == symbolTrades || 0 == symbolTrades.Count) continue; + + double weightAdjustedDividendYield = symbolTrades.GetWeightAdjustedDividendYield(); DateTime currentDate = latestDates[gainLossSummaryItem.Symbol]; - if (null == portfolioTrades || 0 == portfolioTrades.Count) continue; - double shares = (from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum(); - double exposure = portfolioTrades.Sum(x => x.Exposure()); - if(null==gainLossGenerator) gainLossGenerator=new ActiveGainLossGenerator(); - GainLossCollection gainLoss=gainLossGenerator.GenerateGainLoss(portfolioTrades); // gainLoss contains the gain/loss from active positions. Never includes dividends .. just positions + double shares = symbolTrades.Sum(x => x.Shares); + double exposure = symbolTrades.Sum(x => x.Exposure()); + + // GainLossCollection is a date-based time series — must be called per symbol + // Cache is already primed so internal Add(PortfolioTrades) skips DB fetches + GainLossCollection gainLoss = gainLossGenerator.GenerateGainLoss(symbolTrades); GainLossItem gainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault(); - gainLossSummaryItemDetail.Lots = portfolioTrades.Count; + + gainLossSummaryItemDetail.Lots = symbolTrades.Count; gainLossSummaryItemDetail.Shares = shares; gainLossSummaryItemDetail.Exposure = exposure; + if (!double.IsNaN(weightAdjustedDividendYield)) { gainLossSummaryItemDetail.DividendYield = weightAdjustedDividendYield; gainLossSummaryItemDetail.AnnualDividend = exposure * weightAdjustedDividendYield; } - Prices prices = GLPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); // cache should be refreshed after GainLossSummaryItemCollection - Price p1 = prices.Count>0?prices[0]:default; - Price p2 = prices.Count>1?prices[1]:default; - PortfolioTrades symbolTrades = new PortfolioTrades(portfolioTrades.Where(x=>x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList()); - ParityElement parityElement = ParityGenerator.GenerateBreakEven(symbolTrades, p1); + // Cache is already primed — pure memory read + Prices prices = GLPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); + Price p1 = prices.Count > 0 ? prices[0] : default; + Price p2 = prices.Count > 1 ? prices[1] : default; + + PortfolioTrades tradesForSymbol = new PortfolioTrades(symbolTrades.Where(x => x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList()); + ParityElement parityElement = ParityGenerator.GenerateBreakEven(tradesForSymbol, p1); gainLossSummaryItemDetail.ParityElement = parityElement; - if (null != parityElement) + + if (null != parityElement && null != gainLossItem) { gainLossSummaryItemDetail.AllTimeGainLossPercent = gainLossItem.GainLossPercent; gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent = parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100); @@ -185,11 +199,11 @@ namespace MarketDataServer.Controllers } gainLossSummaryItemDetailCollection.Add(gainLossSummaryItemDetail); } - // **** Add an aggregate entry GainLossSummaryItemDetail gainLossSummaryTotals = new GainLossSummaryItemDetail(); gainLossSummaryTotals.Symbol = ""; gainLossSummaryTotals.CompanyName = "Account Summary"; + if (null != gainLossSummaryItemDetailCollection && gainLossSummaryItemDetailCollection.Count > 0) { gainLossSummaryTotals.Date = gainLossSummaryItemDetailCollection.Min(x => x.Date); @@ -215,71 +229,195 @@ namespace MarketDataServer.Controllers // **** return gainLossSummaryItemDetailCollection; } - catch(Exception exception) + catch (Exception exception) { - MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}"); + MDTrace.WriteLine(LogLevel.DEBUG, $"Exception:{exception.ToString()}"); return null; } finally { - MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)"); + MDTrace.WriteLine(LogLevel.DEBUG, $"Done, total took {profiler.End()} (ms)"); } } + // /// + // /// GetGainLossWithDetailByDate - Optimizing calls to DA methods + // /// + // /// + // /// + // /// + // [HttpGet] + // public IEnumerable GetGainLossWithDetailByDate1(String token,DateTime selectedDate) + // { + // Profiler profiler = new Profiler(); + // try + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Start"); + // if (!Authorizations.GetInstance().IsAuthorized(token)) return null; + // GLPriceCache.GetInstance().Refresh(); + // PortfolioTrades portfolioTrades = PortfolioDA.GetTrades(); + // PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate); + // GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate); + // List symbols = gainLossSummaryItems.Select(x => x.Symbol).ToList(); + // Dictionary latestDates = PricingDA.GetLatestDates(symbols); + + // List gainLossSummaryItemDetailCollection = new List(); + // foreach (GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems) + // { + // GainLossSummaryItemDetail gainLossSummaryItemDetail = new GainLossSummaryItemDetail(gainLossSummaryItem); + // portfolioTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol); + // double weightAdjustedDividendYield = portfolioTrades.GetWeightAdjustedDividendYield(); + // DateTime currentDate = latestDates[gainLossSummaryItem.Symbol]; + // if (null == portfolioTrades || 0 == portfolioTrades.Count) continue; + // double shares = (from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum(); + // double exposure = portfolioTrades.Sum(x => x.Exposure()); + // if(null==gainLossGenerator) gainLossGenerator=new ActiveGainLossGenerator(); + // GainLossCollection gainLoss=gainLossGenerator.GenerateGainLoss(portfolioTrades); // gainLoss contains the gain/loss from active positions. Never includes dividends .. just positions + // GainLossItem gainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault(); + // gainLossSummaryItemDetail.Lots = portfolioTrades.Count; + // gainLossSummaryItemDetail.Shares = shares; + // gainLossSummaryItemDetail.Exposure = exposure; + // if (!double.IsNaN(weightAdjustedDividendYield)) + // { + // gainLossSummaryItemDetail.DividendYield = weightAdjustedDividendYield; + // gainLossSummaryItemDetail.AnnualDividend = exposure * weightAdjustedDividendYield; + // } + // Prices prices = GLPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); // cache should be refreshed after GainLossSummaryItemCollection + // Price p1 = prices.Count>0?prices[0]:default; + // Price p2 = prices.Count>1?prices[1]:default; + // PortfolioTrades symbolTrades = new PortfolioTrades(portfolioTrades.Where(x=>x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList()); + // ParityElement parityElement = ParityGenerator.GenerateBreakEven(symbolTrades, p1); + // gainLossSummaryItemDetail.ParityElement = parityElement; + // if (null != parityElement) + // { + // gainLossSummaryItemDetail.AllTimeGainLossPercent = gainLossItem.GainLossPercent; + // gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent = parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100); + // } + // if (null != p1 && null != p2) + // { + // double change = (p1.Close - p2.Close) / p2.Close; + // gainLossSummaryItemDetail.LatestPrice = p1; + // gainLossSummaryItemDetail.PriceChange = change; + // } + // gainLossSummaryItemDetailCollection.Add(gainLossSummaryItemDetail); + // } + + // // **** Add an aggregate entry + // GainLossSummaryItemDetail gainLossSummaryTotals = new GainLossSummaryItemDetail(); + // gainLossSummaryTotals.Symbol = ""; + // gainLossSummaryTotals.CompanyName = "Account Summary"; + // if (null != gainLossSummaryItemDetailCollection && gainLossSummaryItemDetailCollection.Count > 0) + // { + // gainLossSummaryTotals.Date = gainLossSummaryItemDetailCollection.Min(x => x.Date); + // gainLossSummaryTotals.Exposure = gainLossSummaryItemDetailCollection.Sum(x => x.Exposure); + // gainLossSummaryTotals.Change = gainLossSummaryItemDetailCollection.Sum(x => x.Change); + // gainLossSummaryTotals.CurrentGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.CurrentGainLoss); + // gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.PreviousGainLoss); + // gainLossSummaryTotals.ChangePercent = ((gainLossSummaryTotals.CurrentGainLoss - gainLossSummaryTotals.PreviousGainLoss) / Math.Abs(gainLossSummaryTotals.PreviousGainLoss)) * 100.00; + // gainLossSummaryTotals.LatestPrice = new Price(); + // gainLossSummaryTotals.PriceChange = 0; + // } + // else + // { + // gainLossSummaryTotals.Date = selectedDate; + // gainLossSummaryTotals.Change = 0.00; + // gainLossSummaryTotals.CurrentGainLoss = 0.00; + // gainLossSummaryTotals.PreviousGainLoss = 0.00; + // gainLossSummaryTotals.ChangePercent = 0.00; + // gainLossSummaryTotals.LatestPrice = new Price(); + // gainLossSummaryTotals.PriceChange = 0; + // } + // gainLossSummaryItemDetailCollection.Insert(0, gainLossSummaryTotals); + // // **** + // return gainLossSummaryItemDetailCollection; + // } + // catch(Exception exception) + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}"); + // return null; + // } + // finally + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)"); + // } + // } + [HttpGet] public IEnumerable GetGainLossWithDetailByDateAndAccount(String token, DateTime selectedDate, String account) { Profiler profiler = new Profiler(); try { - MDTrace.WriteLine(LogLevel.DEBUG,$"Start"); + MDTrace.WriteLine(LogLevel.DEBUG, $"Start"); if (!Authorizations.GetInstance().IsAuthorized(token)) return null; - GLPriceCache.GetInstance().Refresh(); + PortfolioTrades portfolioTrades = PortfolioDA.GetTrades(); portfolioTrades = new PortfolioTrades(portfolioTrades.Where(x => x.Account.Equals(account)).ToList()); PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate); + + // Prime cache once for all symbols before any calculation begins. + // Guarantees all symbols are cached and open positions have the latest intraday price. + // GenerateGainLoss calls Add(PortfolioTrades) internally per symbol — those calls + // will hit the cache and skip DB fetches since all symbols are already populated. + GLPriceCache.GetInstance().Add(tradesOnOrBefore); + GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate); List symbols = gainLossSummaryItems.Select(x => x.Symbol).ToList(); - Dictionary latestDates = PricingDA.GetLatestDates(symbols); + Dictionary latestDates = PricingDA.GetLatestDates(symbols); - List gainLossSummaryItemDetailCollection=new List(); - foreach(GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems) + if (null == gainLossGenerator) gainLossGenerator = new ActiveGainLossGenerator(); + + List gainLossSummaryItemDetailCollection = new List(); + + foreach (GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems) { GainLossSummaryItemDetail gainLossSummaryItemDetail = new GainLossSummaryItemDetail(gainLossSummaryItem); - portfolioTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol); - double weightAdjustedDividendYield = portfolioTrades.GetWeightAdjustedDividendYield(); + + PortfolioTrades symbolTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol); + if (null == symbolTrades || 0 == symbolTrades.Count) continue; + + double weightAdjustedDividendYield = symbolTrades.GetWeightAdjustedDividendYield(); DateTime currentDate = latestDates[gainLossSummaryItem.Symbol]; - if(null==portfolioTrades||0==portfolioTrades.Count)continue; - double shares = (from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum(); - double exposure = portfolioTrades.Sum(x => x.Exposure()); - if(null==gainLossGenerator) gainLossGenerator=new ActiveGainLossGenerator(); - GainLossCollection gainLoss = gainLossGenerator.GenerateGainLoss(portfolioTrades); // gainLoss contains the gain/loss from active positions. Never includes dividends .. just positions + double shares = symbolTrades.Sum(x => x.Shares); + double exposure = symbolTrades.Sum(x => x.Exposure()); + + // GainLossCollection is a date-based time series — must be called per symbol + // Cache is already primed so internal Add(PortfolioTrades) skips DB fetches + GainLossCollection gainLoss = gainLossGenerator.GenerateGainLoss(symbolTrades); GainLossItem gainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault(); - gainLossSummaryItemDetail.Lots=portfolioTrades.Count; - gainLossSummaryItemDetail.Shares=shares; - gainLossSummaryItemDetail.Exposure=exposure; + + gainLossSummaryItemDetail.Lots = symbolTrades.Count; + gainLossSummaryItemDetail.Shares = shares; + gainLossSummaryItemDetail.Exposure = exposure; + if (!double.IsNaN(weightAdjustedDividendYield)) { - gainLossSummaryItemDetail.DividendYield=weightAdjustedDividendYield; - gainLossSummaryItemDetail.AnnualDividend=exposure * weightAdjustedDividendYield; + gainLossSummaryItemDetail.DividendYield = weightAdjustedDividendYield; + gainLossSummaryItemDetail.AnnualDividend = exposure * weightAdjustedDividendYield; } - Prices prices = GLPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); // cache should be refreshed after GainLossSummaryItemCollection - Price p1 = prices.Count>0?prices[0]:default; - Price p2 = prices.Count>1?prices[1]:default; - PortfolioTrades symbolTrades = new PortfolioTrades(portfolioTrades.Where(x=>x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList()); - ParityElement parityElement = ParityGenerator.GenerateBreakEven(symbolTrades, p1); - gainLossSummaryItemDetail.ParityElement=parityElement; - if (null != parityElement) + + // Cache is already primed — pure memory read + Prices prices = GLPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); + Price p1 = prices.Count > 0 ? prices[0] : default; + Price p2 = prices.Count > 1 ? prices[1] : default; + + PortfolioTrades tradesForSymbol = new PortfolioTrades(symbolTrades.Where(x => x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList()); + ParityElement parityElement = ParityGenerator.GenerateBreakEven(tradesForSymbol, p1); + gainLossSummaryItemDetail.ParityElement = parityElement; + + if (null != parityElement && null != gainLossItem) { - gainLossSummaryItemDetail.AllTimeGainLossPercent=gainLossItem.GainLossPercent; - gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent=parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100); + gainLossSummaryItemDetail.AllTimeGainLossPercent = gainLossItem.GainLossPercent; + gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent = parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100); } - if(null!=p1 && null!=p2) + + if (null != p1 && null != p2) { double change = (p1.Close - p2.Close) / p2.Close; - gainLossSummaryItemDetail.LatestPrice=p1; - gainLossSummaryItemDetail.PriceChange=change; + gainLossSummaryItemDetail.LatestPrice = p1; + gainLossSummaryItemDetail.PriceChange = change; } + gainLossSummaryItemDetailCollection.Add(gainLossSummaryItemDetail); } @@ -287,16 +425,17 @@ namespace MarketDataServer.Controllers GainLossSummaryItemDetail gainLossSummaryTotals = new GainLossSummaryItemDetail(); gainLossSummaryTotals.Symbol = ""; gainLossSummaryTotals.CompanyName = "Account Summary"; + if (null != gainLossSummaryItemDetailCollection && gainLossSummaryItemDetailCollection.Count > 0) { gainLossSummaryTotals.Date = gainLossSummaryItemDetailCollection.Min(x => x.Date); - gainLossSummaryTotals.Exposure=gainLossSummaryItemDetailCollection.Sum(x=>x.Exposure); + gainLossSummaryTotals.Exposure = gainLossSummaryItemDetailCollection.Sum(x => x.Exposure); gainLossSummaryTotals.Change = gainLossSummaryItemDetailCollection.Sum(x => x.Change); gainLossSummaryTotals.CurrentGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.CurrentGainLoss); gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.PreviousGainLoss); gainLossSummaryTotals.ChangePercent = ((gainLossSummaryTotals.CurrentGainLoss - gainLossSummaryTotals.PreviousGainLoss) / Math.Abs(gainLossSummaryTotals.PreviousGainLoss)) * 100.00; - gainLossSummaryTotals.LatestPrice=new Price(); - gainLossSummaryTotals.PriceChange=0; + gainLossSummaryTotals.LatestPrice = new Price(); + gainLossSummaryTotals.PriceChange = 0; } else { @@ -309,20 +448,116 @@ namespace MarketDataServer.Controllers gainLossSummaryTotals.PriceChange = 0; } gainLossSummaryItemDetailCollection.Insert(0, gainLossSummaryTotals); - // **** return gainLossSummaryItemDetailCollection; } - catch(Exception exception) + catch (Exception exception) { - MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}"); + MDTrace.WriteLine(LogLevel.DEBUG, $"Exception:{exception.ToString()}"); return null; } finally { - MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)"); + MDTrace.WriteLine(LogLevel.DEBUG, $"Done, total took {profiler.End()} (ms)"); } } + // [HttpGet] + // public IEnumerable GetGainLossWithDetailByDateAndAccount(String token, DateTime selectedDate, String account) + // { + // Profiler profiler = new Profiler(); + // try + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Start"); + // if (!Authorizations.GetInstance().IsAuthorized(token)) return null; + // GLPriceCache.GetInstance().Refresh(); + // PortfolioTrades portfolioTrades = PortfolioDA.GetTrades(); + // portfolioTrades = new PortfolioTrades(portfolioTrades.Where(x => x.Account.Equals(account)).ToList()); + // PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate); + // GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate); + // List symbols = gainLossSummaryItems.Select(x => x.Symbol).ToList(); + // Dictionary latestDates = PricingDA.GetLatestDates(symbols); + + // List gainLossSummaryItemDetailCollection=new List(); + // foreach(GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems) + // { + // GainLossSummaryItemDetail gainLossSummaryItemDetail = new GainLossSummaryItemDetail(gainLossSummaryItem); + // portfolioTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol); + // double weightAdjustedDividendYield = portfolioTrades.GetWeightAdjustedDividendYield(); + // DateTime currentDate = latestDates[gainLossSummaryItem.Symbol]; + // if(null==portfolioTrades||0==portfolioTrades.Count)continue; + // double shares = (from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum(); + // double exposure = portfolioTrades.Sum(x => x.Exposure()); + // if(null==gainLossGenerator) gainLossGenerator=new ActiveGainLossGenerator(); + // GainLossCollection gainLoss = gainLossGenerator.GenerateGainLoss(portfolioTrades); // gainLoss contains the gain/loss from active positions. Never includes dividends .. just positions + // GainLossItem gainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault(); + // gainLossSummaryItemDetail.Lots=portfolioTrades.Count; + // gainLossSummaryItemDetail.Shares=shares; + // gainLossSummaryItemDetail.Exposure=exposure; + // if (!double.IsNaN(weightAdjustedDividendYield)) + // { + // gainLossSummaryItemDetail.DividendYield=weightAdjustedDividendYield; + // gainLossSummaryItemDetail.AnnualDividend=exposure * weightAdjustedDividendYield; + // } + // Prices prices = GLPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); // cache should be refreshed after GainLossSummaryItemCollection + // Price p1 = prices.Count>0?prices[0]:default; + // Price p2 = prices.Count>1?prices[1]:default; + // PortfolioTrades symbolTrades = new PortfolioTrades(portfolioTrades.Where(x=>x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList()); + // ParityElement parityElement = ParityGenerator.GenerateBreakEven(symbolTrades, p1); + // gainLossSummaryItemDetail.ParityElement=parityElement; + // if (null != parityElement) + // { + // gainLossSummaryItemDetail.AllTimeGainLossPercent=gainLossItem.GainLossPercent; + // gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent=parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100); + // } + // if(null!=p1 && null!=p2) + // { + // double change = (p1.Close - p2.Close) / p2.Close; + // gainLossSummaryItemDetail.LatestPrice=p1; + // gainLossSummaryItemDetail.PriceChange=change; + // } + // gainLossSummaryItemDetailCollection.Add(gainLossSummaryItemDetail); + // } + + // // **** Add an aggregate entry + // GainLossSummaryItemDetail gainLossSummaryTotals = new GainLossSummaryItemDetail(); + // gainLossSummaryTotals.Symbol = ""; + // gainLossSummaryTotals.CompanyName = "Account Summary"; + // if (null != gainLossSummaryItemDetailCollection && gainLossSummaryItemDetailCollection.Count > 0) + // { + // gainLossSummaryTotals.Date = gainLossSummaryItemDetailCollection.Min(x => x.Date); + // gainLossSummaryTotals.Exposure=gainLossSummaryItemDetailCollection.Sum(x=>x.Exposure); + // gainLossSummaryTotals.Change = gainLossSummaryItemDetailCollection.Sum(x => x.Change); + // gainLossSummaryTotals.CurrentGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.CurrentGainLoss); + // gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.PreviousGainLoss); + // gainLossSummaryTotals.ChangePercent = ((gainLossSummaryTotals.CurrentGainLoss - gainLossSummaryTotals.PreviousGainLoss) / Math.Abs(gainLossSummaryTotals.PreviousGainLoss)) * 100.00; + // gainLossSummaryTotals.LatestPrice=new Price(); + // gainLossSummaryTotals.PriceChange=0; + // } + // else + // { + // gainLossSummaryTotals.Date = selectedDate; + // gainLossSummaryTotals.Change = 0.00; + // gainLossSummaryTotals.CurrentGainLoss = 0.00; + // gainLossSummaryTotals.PreviousGainLoss = 0.00; + // gainLossSummaryTotals.ChangePercent = 0.00; + // gainLossSummaryTotals.LatestPrice = new Price(); + // gainLossSummaryTotals.PriceChange = 0; + // } + // gainLossSummaryItemDetailCollection.Insert(0, gainLossSummaryTotals); + // // **** + // return gainLossSummaryItemDetailCollection; + // } + // catch(Exception exception) + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}"); + // return null; + // } + // finally + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)"); + // } + // } + [HttpGet(Name = "GetCompoundGainLoss")] public GainLossCompoundModelCollection GetCompoundGainLoss(String token, int selectedDays, bool includeDividends) {