using System; using System.ComponentModel; using System.Collections.Generic; using System.Text; using System.Linq; using System.Windows.Input; using System.Windows.Threading; using System.Threading.Tasks; using System.Collections.ObjectModel; using System.Threading; using System.Windows; using Microsoft.Research.DynamicDataDisplay.DataSources; using MarketData; using MarketData.MarketDataModel; using MarketData.Generator; using MarketData.DataAccess; using MarketData.Utils; using TradeBlotter.UIUtils; using TradeBlotter.Model; using TradeBlotter.Command; using TradeBlotter.Cache; using MarketData.Numerical; using MarketData.Generator.GainLoss; using MarketData.MarketDataModel.GainLoss; using MarketData.Cache; using MarketData.Helper; using MarketData.Generator.MovingAverage; namespace TradeBlotter.ViewModels { public class GainLossViewModel : WorkspaceViewModel { private enum Tasks{Accounts,SelectedSymbol,SelectedAccounts,LeastSquaresFit,UseDividends}; private Dictionary semaphorePool=new Dictionary(); private ObservableCollection gainLossSummaryItemCollection=new ObservableCollection(); private ITotalGainLossGenerator gainLossGenerator=null; private IActiveGainLossGenerator activeGainLossGenerator=null; private ObservableCollection gainLossCompoundModelCollection=new ObservableCollection(); private GainLossCompoundModelCollection gainLossModelCollection=null; private GainLossCompoundModel selectedGainLossCompoundItem=null; private GainLossSummaryItem selectedGainLossSummaryItem=null; private String selectedSymbol =Constants.CONST_ALL; private TotalGainLossCollection totalGainLoss=null; // total gain/loss private PortfolioTrades portfolioTrades = null; private String selectedCompanyName; private bool suspendUpdate=false; private String selectedAccounts; private List accounts=null; private DateTime latestMarketDate=Utility.Epoch; // RelayCommands private RelayCommand refreshCommand=null; private RelayCommand toggleReturnOrPercentCommand=null; private RelayCommand toggleActiveOrTotalCommand=null; // RelayCommand for menu private RelayCommand saveCommand=null; private RelayCommand stochasticsCommand=null; private RelayCommand relativeStrengthCommand=null; private RelayCommand macdCommand=null; private RelayCommand movingAverageCommand; private RelayCommand bollingerBandCommand=null; private RelayCommand priceHistoryCommand=null; private RelayCommand stickerValuationCommand=null; private RelayCommand dcfValuationCommand=null; private RelayCommand displayAnalystRatingsCommand=null; private RelayCommand displayHeadlinesCommand=null; private RelayCommand rollPriceForwardCommand=null; private RelayCommand dividendHistoryCommand=null; private RelayCommand displayHistoricalCommand=null; private RelayCommand proformaDividendRiskCommand = null; // private ObservableCollection symbols = new ObservableCollection(); private bool busyIndicator = false; private bool isLegendVisible = true; private bool useLeastSquaresFit=true; private bool useCumulativeGainLoss=true; private Boolean showAsGainLoss = true; private Boolean showActiveGainLoss=false; // show the total gain/loss graph in the view by default private Boolean includeDividends=false; // include dividends in the calculation of TOTAL gain/loss, ACTIVE gain/loss never includes dividends private String busyContent = "Generating Gain/Loss..."; public GainLossViewModel() { latestMarketDate=PremarketDA.GetLatestMarketDate(); semaphorePool.Add(Tasks.SelectedSymbol,new Semaphore(1,1)); semaphorePool.Add(Tasks.SelectedAccounts,new Semaphore(1,1)); semaphorePool.Add(Tasks.LeastSquaresFit,new Semaphore(1,1)); semaphorePool.Add(Tasks.Accounts,new Semaphore(1,1)); semaphorePool.Add(Tasks.UseDividends,new Semaphore(1,1)); base.DisplayName = "GainLossView"; PropertyChanged += OnGainLossViewModelPropertyChanged; BusyIndicator = true; suspendUpdate=true; Task workerTask = Task.Factory.StartNew(() => { semaphorePool[Tasks.Accounts].WaitOne(); portfolioTrades = PortfolioDA.GetTrades(); if(null!=portfolioTrades&&0!=portfolioTrades.Count) { accounts = portfolioTrades.Accounts; } }); workerTask.ContinueWith((continuation) => { BusyIndicator = false; SelectedAccounts=AccountsToString(accounts); suspendUpdate=false; SetSymbols(); semaphorePool[Tasks.Accounts].Release(); }); } public override SaveParameters GetSaveParameters() { return null; } public override void SetSaveParameters(SaveParameters saveParameters) { } public override bool CanPersist() { return false; } public bool BusyIndicator { get { return busyIndicator; } set { busyIndicator = value; base.OnPropertyChanged("BusyIndicator"); } } public String BusyContent { get { return busyContent; } set { busyContent = value; base.OnPropertyChanged("BusyContent"); } } private void OnGainLossViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { if (eventArgs.PropertyName.Equals("SelectedSymbol"))HandleSelectedSymbol(); else if (eventArgs.PropertyName.Equals("SelectedAccounts"))HandleSelectedAccounts(); else if (eventArgs.PropertyName.Equals("LeastSquaresFit"))HandleLeastSquaresFit(); else if (eventArgs.PropertyName.Equals("SelectedGainLossCompoundItem"))HandleSelectedGainLossCompoundItem(); else if(eventArgs.PropertyName.Equals("CheckBoxUseCumulativeReturns")) HandleCheckBoxUseCumulativeReturns(); } public void HandleCheckBoxUseCumulativeReturns() { gainLossGenerator=null; activeGainLossGenerator=null; base.OnPropertyChanged("SelectedSymbol"); } public void HandleSelectedGainLossCompoundItem() { if(null==selectedGainLossCompoundItem)return; BusyIndicator = true; Task workerTask = Task.Factory.StartNew(() => { DateTime selectedDate=selectedGainLossCompoundItem.Date; PortfolioTrades tradesOnOrBefore=portfolioTrades.GetTradesOnOrBefore(selectedDate); GainLossSummaryItemCollection gainLossSummaryItems=new GainLossSummaryItemCollection(tradesOnOrBefore,gainLossGenerator,activeGainLossGenerator,selectedDate); gainLossSummaryItemCollection=new ObservableCollection(gainLossSummaryItems); }); workerTask.ContinueWith((continuation) => { BusyIndicator = false; base.OnPropertyChanged("GainLossSummaryItemCollection"); }); } public void InstantiateGenerators() { if(null==gainLossGenerator) { if(useCumulativeGainLoss)gainLossGenerator=new GainLossGeneratorCum(); else gainLossGenerator=new GainLossGenerator(); } if(null==activeGainLossGenerator) activeGainLossGenerator=new ActiveGainLossGenerator(); } public void HandleLeastSquaresFit() { try { semaphorePool[Tasks.LeastSquaresFit].WaitOne(); base.OnPropertyChanged("LeastSquares"); } finally { semaphorePool[Tasks.LeastSquaresFit].Release(); } } public void HandleSelectedAccounts() { try { semaphorePool[Tasks.SelectedAccounts].WaitOne(); if(suspendUpdate)return; MDTrace.WriteLine(String.Format("[GainLossViewModel::OnGainLossViewModelPropertyChanged]SelectedAccounts '{0}'",selectedAccounts)); portfolioTrades = PortfolioDA.GetTradesForAccounts(ParseAccounts(selectedAccounts)); SetSymbols(); } finally { semaphorePool[Tasks.SelectedAccounts].Release(); } } public void HandleSelectedSymbol() { try { semaphorePool[Tasks.SelectedSymbol].WaitOne(); gainLossCompoundModelCollection=null; gainLossSummaryItemCollection=null; if(suspendUpdate)return; if (null == selectedSymbol||"".Equals(selectedAccounts)) { UpdateProperties(); return; } MDTrace.WriteLine(String.Format("[GainLossViewModel::OnGainLossViewModelPropertyChanged]SelectedSymbol '{0}'",selectedSymbol)); BusyIndicator = true; Task workerTask = Task.Factory.StartNew(() => { DividendPayments dividendPayments=null; if(Constants.CONST_ALL.Equals(selectedSymbol)) { portfolioTrades = PortfolioDA.GetTrades(); if(includeDividends)dividendPayments=DividendPaymentDA.GetDividendPayments(); selectedCompanyName=""; } else { portfolioTrades = PortfolioDA.GetTrades(selectedSymbol); if(includeDividends)dividendPayments=DividendPaymentDA.GetDividendPaymentsForSymbol(selectedSymbol); selectedCompanyName=PricingDA.GetNameForSymbol(selectedSymbol); } if(null!=portfolioTrades&&0!=portfolioTrades.Count) { accounts = portfolioTrades.Accounts; } portfolioTrades=portfolioTrades.FilterAccount(ParseAccounts(selectedAccounts)); if(null!=dividendPayments)dividendPayments=dividendPayments.FilterAccounts(selectedAccounts); // check the TotalGainLoss generator and build appropriately InstantiateGenerators(); LocalPriceCache.GetInstance().Refresh(); GainLossCollection gainLoss = null; gainLoss=activeGainLossGenerator.GenerateGainLoss(portfolioTrades); // gainLoss contains the gain/loss from active positions. Never includes dividends .. just positions // Call the appropriate TotalGainLoss method depending on whether we should include dividends or not. if(includeDividends)totalGainLoss=gainLossGenerator.GenerateTotalGainLossWithDividends(portfolioTrades,dividendPayments); else totalGainLoss=gainLossGenerator.GenerateTotalGainLoss(portfolioTrades); gainLossModelCollection=null; gainLossModelCollection=new GainLossCompoundModelCollection(gainLoss,totalGainLoss); if(null!=gainLossModelCollection) { gainLossModelCollection.Sort(new SortGainLossCompoundModelYearDescendingOrder()); gainLossCompoundModelCollection=new ObservableCollection(gainLossModelCollection); } GainLossSummaryItemCollection gainLossSummaryItems=new GainLossSummaryItemCollection(portfolioTrades,gainLossGenerator,activeGainLossGenerator); gainLossSummaryItemCollection=new ObservableCollection(gainLossSummaryItems); }); workerTask.ContinueWith((continuation) => { BusyIndicator = false; UpdateProperties(); }); } finally { semaphorePool[Tasks.SelectedSymbol].Release(); } } public void UpdateProperties() { base.OnPropertyChanged("Accounts"); base.OnPropertyChanged("Data"); base.OnPropertyChanged("MA21"); base.OnPropertyChanged("MA55"); base.OnPropertyChanged("MA90"); base.OnPropertyChanged("Title"); base.OnPropertyChanged("GainLossCompoundModelCollection"); base.OnPropertyChanged("GainLossSummaryItemCollection"); base.OnPropertyChanged("LeastSquares"); base.OnPropertyChanged("LeastSquaresTitle"); } public void SetSymbols() { symbols = new ObservableCollection(); symbols.Add(Constants.CONST_ALL); List tradedSymbols = portfolioTrades.Symbols; foreach (String symbol in tradedSymbols) symbols.Add(symbol); base.OnPropertyChanged("Symbols"); SelectedSymbol=symbols[0]; } public ObservableCollection Symbols { get { return symbols; } set { symbols = value; base.OnPropertyChanged("Symbols"); } } public String SelectedSymbol { get { MDTrace.WriteLine(String.Format("[GainLossViewModel::SelectedSymbol] Get {0}",selectedSymbol)); return selectedSymbol; } set { MDTrace.WriteLine(String.Format("[GainLossViewModel::SelectedSymbol] Set {0}",selectedSymbol)); selectedSymbol = value; base.OnPropertyChanged("SelectedSymbol"); } } public List Accounts { get { semaphorePool[Tasks.Accounts].WaitOne(); MDTrace.WriteLine(String.Format("[GainLossViewModel::Accounts] {0}",accounts==null?"null":"non-null")); semaphorePool[Tasks.Accounts].Release(); return accounts; } } public String SelectedAccounts { get { MDTrace.WriteLine(String.Format("[GainLossViewModel:SelectedAccounts]Get '{0}'",selectedAccounts)); return selectedAccounts; } set { MDTrace.WriteLine(String.Format("[GainLossViewModel:SelectedAccounts]Set '{0}'",value)); selectedAccounts = value; base.OnPropertyChanged("SelectedAccounts"); portfolioTrades = PortfolioDA.GetTradesForAccounts(ParseAccounts(selectedAccounts)); if(null==portfolioTrades)return; accounts = portfolioTrades.Accounts; SetSymbols(); } } private static String AccountsToString(List accounts) { StringBuilder sb=new StringBuilder(); for(int index=0;index ParseAccounts(String strAccounts) { String[] accounts=strAccounts.Split(','); return accounts.ToList(); } // ************************************************************************************************************************************************************************* // *************************************************************************** L I N E G R A P H D A T A *************************************************************** // ************************************************************************************************************************************************************************* public CompositeDataSource Data { get { if (null == gainLossModelCollection) return null; CompositeDataSource compositeDataSource=null; if(showActiveGainLoss)compositeDataSource = GainLossModel.GainLoss(gainLossModelCollection,showAsGainLoss); else compositeDataSource = GainLossModel.TotalGainLoss(gainLossModelCollection,showAsGainLoss); return compositeDataSource; } } public CompositeDataSource LeastSquares { get { if(!useLeastSquaresFit||null==gainLossModelCollection)return null; if(showActiveGainLoss)return GainLossModel.LeastSquares(gainLossModelCollection,showAsGainLoss); else return GainLossModel.TotalLeastSquares(gainLossModelCollection,showAsGainLoss); } } public CompositeDataSource MA21 { get { if (null == gainLossModelCollection) return null; CompositeDataSource compositeDataSource = null; DMAValues dmaValues=null; if(showActiveGainLoss) { if(showAsGainLoss)dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesActiveGainLoss,21); else dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesActiveGainLossPercent,21); compositeDataSource = MovingAverageModel.CreateCompositeDataSource(dmaValues); } else { if(showAsGainLoss)dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesTotalGainLoss,21); else dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesTotalGainLossPercent,21); compositeDataSource = MovingAverageModel.CreateCompositeDataSource(dmaValues); } return compositeDataSource; } } public CompositeDataSource MA55 { get { if (null == gainLossModelCollection) return null; CompositeDataSource compositeDataSource = null; DMAValues dmaValues=null; if(showActiveGainLoss) { if(showAsGainLoss)dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesActiveGainLoss,55); else dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesActiveGainLossPercent,55); compositeDataSource = MovingAverageModel.CreateCompositeDataSource(dmaValues); } else { if(showAsGainLoss)dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesTotalGainLoss,55); else dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesTotalGainLossPercent,55); compositeDataSource = MovingAverageModel.CreateCompositeDataSource(dmaValues); } return compositeDataSource; } } public CompositeDataSource MA90 { get { if (null == gainLossModelCollection) return null; CompositeDataSource compositeDataSource = null; DMAValues dmaValues=null; if(showActiveGainLoss) { if(showAsGainLoss)dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesActiveGainLoss,90); else dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesActiveGainLossPercent,90); compositeDataSource = MovingAverageModel.CreateCompositeDataSource(dmaValues); } else { if(showAsGainLoss)dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesTotalGainLoss,90); else dmaValues=MovingAverageGenerator.GenerateMovingAverage(gainLossModelCollection.DMAValuesTotalGainLossPercent,90); compositeDataSource = MovingAverageModel.CreateCompositeDataSource(dmaValues); } return compositeDataSource; } } // *********************************************************************************************************************************************************************************** // ****************************************************************************** C H E C K B O X E S ****************************************************************************** // *********************************************************************************************************************************************************************************** public Boolean CheckBoxLegendVisible { get { return isLegendVisible; } set { isLegendVisible = value; base.OnPropertyChanged("CheckBoxLegendVisible"); base.OnPropertyChanged("LegendVisible"); } } public Boolean CheckBoxIncludeDividends { get { return includeDividends; } set { includeDividends = value; base.OnPropertyChanged("CheckBoxIncludeDividends"); base.OnPropertyChanged("SelectedSymbol"); } } public Boolean CheckBoxUseCumulativeReturns { get { return useCumulativeGainLoss; } set { useCumulativeGainLoss=value; base.OnPropertyChanged("CheckBoxUseCumulativeReturns"); } } public Boolean CheckBoxSuspendUpdate { get { return suspendUpdate; } set { suspendUpdate = value; base.OnPropertyChanged("CheckBoxSuspendUpdate"); if(!suspendUpdate)base.OnPropertyChanged("SelectedSymbol"); } } public String LeastSquaresTitle { get { if(null==gainLossModelCollection)return "LeastSquares"; LeastSquaresResult leastSquaresResult=GainLossModel.LeastSquaresFit(gainLossModelCollection,showAsGainLoss); return "LeastSquares="+Utility.FormatNumber(leastSquaresResult.Slope,2); } } public Boolean LeastSquaresFit { get { return useLeastSquaresFit; } set { useLeastSquaresFit = value; base.OnPropertyChanged("LeastSquaresFit"); } } public String LegendVisible { get { if (isLegendVisible) return "true"; return "false"; } set { isLegendVisible = Boolean.Parse(value); base.OnPropertyChanged("LegendVisible"); } } public override String Title { get { if(null==gainLossModelCollection||0==gainLossModelCollection.Count)return ""; StringBuilder sb = new StringBuilder(); sb.Append(showActiveGainLoss?"Active":"Total").Append(" "); sb.Append("Gain/Loss "); if (showAsGainLoss) sb.Append("($)"); else sb.Append("(%)"); if (!Constants.CONST_ALL.Equals(selectedSymbol))sb.Append(" (").Append(selectedAccounts).Append(") ").Append(" - ").Append(selectedCompanyName).Append(" (").Append(selectedSymbol).Append(") "); else sb.Append(" (").Append(selectedAccounts).Append(") ").Append(" - ").Append(selectedCompanyName); DateTime fromDate=gainLossModelCollection.Select(x => x.Date).Min(); DateTime toDate = gainLossModelCollection.Select(x => x.Date).Max(); sb.Append(" from ").Append(Utility.DateTimeToStringMMHDDHYYYY(fromDate)); sb.Append(" Thru ").Append(Utility.DateTimeToStringMMHDDHYYYY(toDate)); return sb.ToString(); } } private void Refresh() { latestMarketDate = PremarketDA.GetLatestMarketDate(); base.OnPropertyChanged("SelectedSymbol"); } public ICommand RefreshCommand { get { if (refreshCommand == null) { refreshCommand = new RelayCommand(param => this.Refresh(), param => { return true;}); } return refreshCommand; } } // This is the button that controls the display in terms of percentages or dollar values public String PercentButtonText { get { if (!showAsGainLoss) return "Show $"; else return "Show %"; } } public ICommand ToggleReturnOrPercentCommand { get { if (toggleReturnOrPercentCommand == null) { toggleReturnOrPercentCommand = new RelayCommand(action => { showAsGainLoss = !showAsGainLoss; base.OnPropertyChanged("SelectedSymbol"); base.OnPropertyChanged("PercentButtonText"); }, action => { return true; }); } return toggleReturnOrPercentCommand; } } // This is the button that controls the display in terms of active vs total return display on the graph public String ActiveTotalButtonText { get { if (!showActiveGainLoss) return "Show Active G/L"; else return "Show Total G/L"; } } public ICommand ToggleActiveOrTotalCommand { get { if (toggleActiveOrTotalCommand == null) { toggleActiveOrTotalCommand = new RelayCommand(action => { showActiveGainLoss=!showActiveGainLoss; base.OnPropertyChanged("SelectedSymbol"); base.OnPropertyChanged("ActiveTotalButtonText"); }, action => { return true; }); } return toggleActiveOrTotalCommand; } } // ************************************************************************************************************************************************************************* // ***************************************************************************** T A B L E D A T A *********************************************************************** // ************************************************************************************************************************************************************************* // The compound model collection is the data behind the views left-hand grid. This view shows the compound gain/loss since inception public ObservableCollection GainLossCompoundModelCollection { get { return gainLossCompoundModelCollection; } } // The summary item collection is the data behind the views right-hand grid. This view shows the gain/loss for the date that is selected in the compound model public ObservableCollection GainLossSummaryItemCollection { get { return gainLossSummaryItemCollection; } } // ****************************************************************************** G R I D S E L E C T I O N S *********************************************** // The selected item in the compoint model public GainLossCompoundModel SelectedGainLossCompoundItem { get { return selectedGainLossCompoundItem; } set { selectedGainLossCompoundItem = value; base.OnPropertyChanged("SelectedGainLossCompoundItem"); } } // The selected item in the summary model public GainLossSummaryItem SelectedGainLossSummaryItem { get { return selectedGainLossSummaryItem; } set { selectedGainLossSummaryItem = value; base.OnPropertyChanged("SelectedGainLossSummaryItem"); } } // ************************************************************************************************************************************************************************************ // ************************************************************************************************************************************************************************************ // ************************************************************************************************************************************************************************************ public ObservableCollection MenuItems { get { ObservableCollection collection = new ObservableCollection(); collection.Add(new MenuItem() { Text = "Display Bollinger Band", MenuItemClickedCommand = DisplayBollingerBand, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Dividend History", MenuItemClickedCommand = DisplayDividendHistory, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Proforma Dividend Risk", MenuItemClickedCommand = DisplayProformaDividendRisk, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Sticker Valuation", MenuItemClickedCommand = DisplayStickerValuation, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Historical", MenuItemClickedCommand = DisplayHistorical, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Stochastics", MenuItemClickedCommand = DisplayStochastic, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Relative Strength", MenuItemClickedCommand = DisplayRelativeStrength, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display MACD", MenuItemClickedCommand = DisplayMACD, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Moving Average", MenuItemClickedCommand = DisplayMovingAverage, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Price History", MenuItemClickedCommand = DisplayPriceHistory, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display DCF Valuation", MenuItemClickedCommand = DisplayDCFValuation, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Analyst Ratings", MenuItemClickedCommand = DisplayAnalystRatings, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Display Headlines", MenuItemClickedCommand = DisplayHeadlines, StaysOpenOnClick = false }); collection.Add(new MenuItem() { Text = "Roll Price Forward", MenuItemClickedCommand = RollPriceForward, StaysOpenOnClick = false }); return collection; } } public ICommand DisplayBollingerBand { get { if (bollingerBandCommand == null) { bollingerBandCommand = new RelayCommand(param => this.DisplayBollingerBandCommand(), param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return bollingerBandCommand; } } public ICommand DisplayDividendHistory { get { if (dividendHistoryCommand == null) { dividendHistoryCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.DividendHistoryViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All}"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null != selectedGainLossSummaryItem.Symbol; }); } return dividendHistoryCommand; } } public ICommand DisplayProformaDividendRisk { get { if (proformaDividendRiskCommand == null) { proformaDividendRiskCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.DividendRiskParityViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ""); saveParams.Referer = this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null != selectedGainLossSummaryItem.Symbol; }); } return proformaDividendRiskCommand; } } public ICommand DisplayStickerValuation { get { if (null == stickerValuationCommand) { stickerValuationCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.StickerPriceViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All}"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return stickerValuationCommand; } } public ICommand DisplayHistorical { get { if (displayHistoricalCommand == null) { displayHistoricalCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.HistoricalViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All}"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null != selectedGainLossSummaryItem.Symbol; }); } return displayHistoricalCommand; } } public ICommand DisplayStochastic { get { if (stochasticsCommand == null) { stochasticsCommand = new RelayCommand(param => this.DisplayStochasticCommand(), param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return stochasticsCommand; } } public ICommand DisplayRelativeStrength { get { if (relativeStrengthCommand == null) { relativeStrengthCommand = new RelayCommand(param => this.DisplayRelativeStrengthCommand(), param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return relativeStrengthCommand; } } public ICommand DisplayMACD { get { if (macdCommand == null) { macdCommand = new RelayCommand(param => this.DisplayMACDCommand(), param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return macdCommand; } } public ICommand DisplayMovingAverage { get { if (movingAverageCommand == null) { movingAverageCommand = new RelayCommand(param => this.DisplayMovingAverageCommand(), param => { return null != selectedGainLossSummaryItem && null != selectedGainLossSummaryItem.Symbol; }); } return movingAverageCommand; } } public ICommand DisplayPriceHistory { get { if (priceHistoryCommand == null) { priceHistoryCommand = new RelayCommand(param => this.DisplayPriceHistoryCommand(), param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return priceHistoryCommand; } } public ICommand DisplayDCFValuation { get { if (null == dcfValuationCommand) { dcfValuationCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.DCFValuationViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All}"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return dcfValuationCommand; } } public ICommand DisplayAnalystRatings { get { if (null == displayAnalystRatingsCommand) { displayAnalystRatingsCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.AnalystRatingsViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All}"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return displayAnalystRatingsCommand; } } public ICommand DisplayHeadlines { get { if (null == displayHeadlinesCommand) { displayHeadlinesCommand = new RelayCommand(param => { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.HeadlinesViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,Valuations"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null != selectedGainLossSummaryItem && null!=selectedGainLossSummaryItem.Symbol; }); } return displayHeadlinesCommand; } } public ICommand RollPriceForward { get { if (null == rollPriceForwardCommand) { rollPriceForwardCommand = new RelayCommand(param => { Price previousPrice=PricingDA.GetPrice(selectedGainLossSummaryItem.Symbol); Price latestPrice=PricingMarketDataHelper.RollPriceForward(selectedGainLossSummaryItem.Symbol); System.Windows.MessageBox.Show(String.Format("{0} price rolled forward from {1} to {2}",selectedGainLossSummaryItem.Symbol,previousPrice.Date.ToShortDateString(),latestPrice.Date.ToShortDateString()),"Info",MessageBoxButton.OK,MessageBoxImage.Information); ClearCache(); HandleSelectedSymbol(); }, param => { if(null == selectedGainLossSummaryItem || null==selectedGainLossSummaryItem.Symbol)return false; Price symbolPrice=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(selectedGainLossSummaryItem.Symbol,latestMarketDate); if(null==symbolPrice)return false; if(symbolPrice.Date.Equals(latestMarketDate))return false; DateGenerator dateGenerator=new DateGenerator(); DateTime prevBusinessDay=dateGenerator.FindPrevBusinessDay(latestMarketDate); if(!symbolPrice.Date.Equals(prevBusinessDay))return false; // This method was slowing things down quite considerably as it was constantly hitting the pricingDA.GetLatestDate() // Refactored to the above code and seeing how that works out. //DateTime latestDate=PremarketDA.GetLatestMarketDate(); //DateTime latestDateSymbol=PricingDA.GetLatestDate(selectedGainLossSummaryItem.Symbol); //if(latestDate.Equals(latestDateSymbol))return false; //DateGenerator dateGenerator=new DateGenerator(); //DateTime prevBusinessDay=dateGenerator.FindPrevBusinessDay(latestDate); //if(!latestDateSymbol.Equals(prevBusinessDay))return false; return true; }); } return rollPriceForwardCommand; } } private void ClearCache() { try{LocalPriceCache.GetInstance().Clear();}catch(Exception){;} try{GBPriceCache.GetInstance().Clear();}catch(Exception){;} try{PriceCache.GetInstance().Clear();}catch(Exception){;} } // ************************************************************************************************************************************************************************************** // ******************************************************************************** C O M M A N D S ************************************************************************************* // ************************************************************************************************************************************************************************************** public void DisplayBollingerBandCommand() { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.BollingerBandViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180,SyncTradeToBand,FALSE"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); } public void DisplayRelativeStrengthCommand() { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.RSIViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,60,SelectedRSIDayCount,3"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); } public void DisplayStochasticCommand() { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.StochasticsViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,90"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); } public void DisplayMACDCommand() { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.MACDViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,90"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); } public void DisplayMovingAverageCommand() { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.MovingAverageViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,360"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); } public void DisplayPriceHistoryCommand() { SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.PricingViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,90"); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); } // ************************************************************************************************************************************************** // ************************************************************* T O O L T I P C A L L O U T******************************************************* // ************************************************************************************************************************************************** public String DollarChangePercent { get { return "'$ Change(%)' is calculated as a percentage of dollar change in market value from the previous period to the current period.\nThis number may appear skewed if share count has changed day over day."; } } public String Parity { get { if(null== selectedGainLossSummaryItem || null==selectedGainLossSummaryItem.Symbol)return "No row selected."; StringBuilder sb=new StringBuilder(); PortfolioTrades portfolioTrades=PortfolioDA.GetOpenTradesSymbol(selectedGainLossSummaryItem.Symbol); double weightAdjustedDividendYield=portfolioTrades.GetWeightAdjustedDividendYield(); DateTime currentDate=PricingDA.GetLatestDate(selectedGainLossSummaryItem.Symbol); String companyName=PricingDA.GetNameForSymbol(selectedGainLossSummaryItem.Symbol); if(null!=companyName)sb.Append(companyName).Append("\n"); CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(selectedGainLossSummaryItem.Symbol); if(null!=companyProfile) { sb.Append(companyProfile.Sector??Constants.CONST_QUESTION).Append("/").Append(companyProfile.Industry??Constants.CONST_QUESTION).Append("\n"); } if(null!=portfolioTrades&&0!=portfolioTrades.Count) { double shares=(from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum(); double exposure=portfolioTrades.Sum(x=>x.Exposure()); // Calculate the gain loss so that we can show the difference between our all time high percentage and where we are right now InstantiateGenerators(); GainLossCollection gainLoss=activeGainLossGenerator.GenerateGainLoss(portfolioTrades); // gainLoss contains the gain/loss from active positions. Never includes dividends .. just positions GainLossItem maxGainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault(); GainLossItem minGainLossItem=gainLoss.OrderBy(x => x.GainLossPercent).FirstOrDefault(); String accounts=Utility.ListToString(portfolioTrades.Accounts); sb.Append("You own '").Append(selectedGainLossSummaryItem.Symbol).Append("' in ").Append(portfolioTrades.Count).Append(" lot(s) (").Append(Utility.FormatNumber(shares,0,true)).Append(" shares) ").Append("Exposure: ").Append(Utility.FormatCurrency(exposure)); sb.Append("\n").Append("Accounts: ").Append(accounts); if(!double.IsNaN(weightAdjustedDividendYield)) { sb.Append("\n").Append("Dividend Yield: ").Append(Utility.FormatPercent(weightAdjustedDividendYield)).Append(" "); sb.Append("(").Append(Utility.FormatCurrency(exposure*weightAdjustedDividendYield)).Append("/Yr.)"); } ParityElement parityElement=ParityGenerator.GenerateBreakEven(selectedGainLossSummaryItem.Symbol); if(null!=parityElement) { sb.Append("\n").Append(parityElement.ToString()); sb.Append("\n").Append("All Time Gain/Loss: ").Append(maxGainLossItem.GainLossPercent<0?"":"+").Append(Utility.FormatPercent(maxGainLossItem.GainLossPercent/100)); sb.Append(" (").Append(minGainLossItem.GainLossPercent<0?"":"+").Append(Utility.FormatPercent(minGainLossItem.GainLossPercent/100)).Append(")"); } } else sb.Append("You don't hold any shares of '").Append(selectedGainLossSummaryItem.Symbol).Append("'"); sb.Append("."); DateGenerator dateGenerator=new DateGenerator(); DateTime priorDate=dateGenerator.FindPrevBusinessDay(currentDate); Price p1=PricingDA.GetPrice(selectedGainLossSummaryItem.Symbol,currentDate); Price p2=PricingDA.GetPrice(selectedGainLossSummaryItem.Symbol,priorDate); if(null==p2&&null!=p1) { priorDate=dateGenerator.FindPrevBusinessDay(priorDate); p2=PricingDA.GetPrice(selectedGainLossSummaryItem.Symbol,priorDate); } if(null==p1||null==p2)return sb.ToString(); sb.Append("\n"); double change=(p1.Close-p2.Close)/p2.Close; sb.Append(String.Format("Latest Price {0} {1} ({2}{3})",Utility.DateTimeToStringMMSDDSYYYY(p1.Date),Utility.FormatCurrency(p1.Close),change<0?"-":"+",Utility.FormatPercent(Math.Abs(change)))); if(companyProfile.FreezePricing)sb.Append(" ").Append("**Frozen**"); sb.Append("\n").Append(String.Format("Source: {0}",p1.SourceAsString())); return sb.ToString(); } } } }