using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using CommunityToolkit.Mvvm.Input; using DynamicData; using MarketData; using MarketData.DataAccess; using MarketData.MarketDataModel; using MarketData.Utils; using PortfolioManager.Extensions; using PortfolioManager.Renderers; using PortfolioManager.UIUtils; namespace PortfolioManager.ViewModels { public partial class BollingerBandViewModel : PlotterWorkspaceViewModel { private List watchListNames; private String selectedWatchList; private ObservableCollection symbols = new ObservableCollection(); private List dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList(); private int selectedDayCount = 90; private bool isBusy = false; private String selectedSymbol = default; private String companyName = default; private BollingerBandRenderer bollingerBandRenderer = default; private bool showInsiderTransactions = true; private bool showTradeLabels = true; private bool syncTradeToBand = false; private bool useLeastSquaresFit = true; private StopLimits stopLimits = default; public BollingerBandViewModel() { DisplayName = "Bollinger"; OnPlotterLoadedEventHandler += PlotterLoadedEvent; PropertyChanged += OnViewModelPropertyChanged; Initialize(); } protected override void OnDispose() { MDTrace.WriteLine(LogLevel.DEBUG, $"Dispose BollingerBandViewModel"); base.OnDispose(); } public override String Title { get { if (null == selectedSymbol) return DisplayName; return "Bollinger " + "(" + selectedSymbol + ")"; } } public override String DisplayName { get { return "Bollinger Band"; } } private void Initialize(bool executePropertyChanged = true) { Task workerTask = Task.Factory.StartNew(() => { MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::Initialize()"); watchListNames = WatchListDA.GetWatchLists(); watchListNames.Insert(0, UIConstants.CONST_ALL); selectedWatchList = watchListNames.Find(x => x.Equals("Valuations")); symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList)); }); workerTask.ContinueWith((continuation) => { if (executePropertyChanged) { base.OnPropertyChanged("Symbols"); base.OnPropertyChanged("WatchListNames"); base.OnPropertyChanged("SelectedWatchList"); } }); } // ******************************************************************************************************************************************* /// /// This event will be called the first time the view is rendered and each time the view comes back into focus (i.e.) Tab activated /// /// /// public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e) { base.OnPropertyChanged("Plotter"); if (default != selectedSymbol) { base.OnPropertyChanged("SelectedSymbol"); } } private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { if(eventArgs.PropertyName.Equals("SelectedWatchList")) { IsBusy=true; Task workerTask = Task.Factory.StartNew(()=> { symbols.Clear(); if(UIConstants.CONST_ALL.Equals(selectedWatchList)) { symbols.AddRange(PricingDA.GetSymbols()); } else { symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList)); } }); workerTask.ContinueWith((continuation)=> { IsBusy=false; base.OnPropertyChanged("Symbols"); }); } else if ((eventArgs.PropertyName.Equals("SyncTradeToBand") || eventArgs.PropertyName.Equals("ShowTradeLabels") || eventArgs.PropertyName.Equals("SelectedSymbol") || eventArgs.PropertyName.Equals("ShowRiskFree") || eventArgs.PropertyName.Equals("UseLeastSquaresFit") || eventArgs.PropertyName.Equals("CheckBoxShowInsiderTransactions") || eventArgs.PropertyName.Equals("SelectedDayCount")) && !String.IsNullOrEmpty(selectedSymbol) && default != Plotter) { IsBusy = true; Task workerTask = Task.Factory.StartNew(() => { companyName = PricingDA.GetNameForSymbol(selectedSymbol); bollingerBandRenderer = new BollingerBandRenderer(Plotter); bollingerBandRenderer.ShowLeastSquares = useLeastSquaresFit; bollingerBandRenderer.SyncTradeToBand = syncTradeToBand; bollingerBandRenderer.ShowInsiderTransactions = showInsiderTransactions; bollingerBandRenderer.ShowTradeLabels = showTradeLabels; bollingerBandRenderer.ExternalStopLimits = stopLimits; bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); bollingerBandRenderer.Render(); }); workerTask.ContinueWith((continuation) => { base.OnPropertyChanged("GraphTitle"); base.OnPropertyChanged("Title"); IsBusy = false; }); } } // ********************************************** P E R S I S T E N C E ************************* public override bool CanPersist() { return true; } public override SaveParameters GetSaveParameters() { SaveParameters saveParams = new SaveParameters(); if (String.IsNullOrEmpty(selectedSymbol)) return null; saveParams.Add(new KeyValuePair("Type", GetType().Namespace + "." + GetType().Name)); saveParams.Add(new KeyValuePair("SelectedSymbol", selectedSymbol)); saveParams.Add(new KeyValuePair("SelectedWatchList", selectedWatchList)); saveParams.Add(new KeyValuePair("SelectedDayCount", selectedDayCount.ToString())); saveParams.Add(new KeyValuePair("SyncTradeToBand", syncTradeToBand.ToString())); saveParams.Add(new KeyValuePair("ShowTradeLabels", showTradeLabels.ToString())); saveParams.Add(new KeyValuePair("UseLeastSquaresFit", useLeastSquaresFit.ToString())); saveParams.Add(new KeyValuePair("ShowInsiderTransactions", showInsiderTransactions.ToString())); if (null != stopLimits && 0 != stopLimits.Count) { saveParams.Add(new KeyValuePair("StopHistoryCount", stopLimits.Count.ToString())); for (int index = 0; index < stopLimits.Count; index++) { String strItemKey = String.Format("StopHistory_{0}", index); StopLimit stopLimit = stopLimits[index]; NVPCollection nvpCollection = stopLimit.ToNVPCollection(); String strStopHistoryItem = nvpCollection.ToString(); saveParams.Add(new KeyValuePair(strItemKey, strStopHistoryItem)); } } return saveParams; } public override void SetSaveParameters(SaveParameters saveParameters) { try { Task workerTask = Task.Factory.StartNew(() => { Referer = saveParameters.Referer; selectedSymbol = (from KeyValuePair item in saveParameters where item.Key.Equals("SelectedSymbol") select item).FirstOrDefault().Value; selectedWatchList = (from KeyValuePair item in saveParameters where item.Key.Equals("SelectedWatchList") select item).FirstOrDefault().Value; selectedDayCount = Int32.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("SelectedDayCount") select item).FirstOrDefault().Value); MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::SetSaveParameters('{selectedSymbol}','{selectedWatchList}','{selectedDayCount}')"); try { if (saveParameters.ContainsKey("SyncTradeToBand")) syncTradeToBand = Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("SyncTradeToBand") select item).FirstOrDefault().Value); else syncTradeToBand = false; } catch (Exception) { syncTradeToBand = false; } try { if (saveParameters.ContainsKey("ShowTradeLabels")) showTradeLabels = Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("ShowTradeLabels") select item).FirstOrDefault().Value); else showTradeLabels = true; } catch (Exception) { showTradeLabels = true; } try { if (saveParameters.ContainsKey("UseLeastSquaresFit")) useLeastSquaresFit = Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("UseLeastSquaresFit") select item).FirstOrDefault().Value); } catch (Exception) {; } try { if (saveParameters.ContainsKey("ShowInsiderTransactions")) showInsiderTransactions = Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("ShowInsiderTransactions") select item).FirstOrDefault().Value); } catch (Exception) {; } try { if (saveParameters.ContainsKey("StopHistoryCount")) { stopLimits = StopLimitsExtensions.FromSaveParams(saveParameters); } } catch (Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Exception:{0}", exception.ToString())); } }); workerTask.ContinueWith((continuation) => { base.OnPropertyChanged("SelectedWatchList"); base.OnPropertyChanged("SelectedSymbol"); base.OnPropertyChanged("Title"); }); } catch (Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString()); } } // ****************************************************** P R O P E R T I E S ************************************************ public String GraphTitle { get { if (null == companyName || null == bollingerBandRenderer || null == bollingerBandRenderer.Prices) return ""; String displayCompanyName = companyName; if (displayCompanyName.Length > 40) displayCompanyName = displayCompanyName.Substring(0, 40) + "..."; StringBuilder sb = new StringBuilder(); float change = float.NaN; Prices prices2day = new Prices(bollingerBandRenderer.Prices.Take(2).ToList()); if (2 == prices2day.Count) change = prices2day.GetReturns()[0]; sb.Append(displayCompanyName); sb.Append(" (").Append(selectedSymbol).Append(") "); sb.Append(Utility.DateTimeToStringMMHDDHYYYY(bollingerBandRenderer.Prices[bollingerBandRenderer.Prices.Count - 1].Date)); sb.Append(" Thru "); sb.Append(Utility.DateTimeToStringMMHDDHYYYY(bollingerBandRenderer.Prices[0].Date)); sb.Append(" (").Append(Utility.FormatCurrency(bollingerBandRenderer.Prices[0].Close)); sb.Append("/").Append(Utility.FormatCurrency(bollingerBandRenderer.Prices[0].Low)); if (!float.IsNaN(change)) { sb.Append(","); sb.Append(change >= 0.00 ? "+" : "").Append(Utility.FormatPercent((double)change)); } sb.Append(")"); return sb.ToString(); } } public List WatchListNames { get { return watchListNames; } } public String SelectedWatchList { get { return selectedWatchList; } set { selectedWatchList = value; base.OnPropertyChanged("SelectedWatchList"); } } public ObservableCollection Symbols { get { return symbols; } } public int SelectedDayCount { get { return selectedDayCount; } set { selectedDayCount = value; base.OnPropertyChanged("SelectedDayCount"); } } public List DayCounts { get { return dayCounts; } } public String SelectedSymbol { get { return selectedSymbol; } set { if (String.IsNullOrEmpty(value)) { return; } selectedSymbol = value; base.OnPropertyChanged("SelectedSymbol"); } } public bool IsBusy { get { return isBusy; } set { isBusy = value; base.OnPropertyChanged("IsBusy"); } } public bool SyncTradeToBand { get { return syncTradeToBand; } set { syncTradeToBand = value; base.OnPropertyChanged("SyncTradeToBand"); } } public bool ShowTradeLabels { get { return showTradeLabels; } set { showTradeLabels = value; base.OnPropertyChanged("ShowTradeLabels"); } } public bool UseLeastSquaresFit { get { return useLeastSquaresFit; } set { useLeastSquaresFit = value; base.OnPropertyChanged("UseLeastSquaresFit"); } } public Boolean CheckBoxShowInsiderTransactions { get { return showInsiderTransactions; } set { showInsiderTransactions = value; base.OnPropertyChanged("CheckBoxShowInsiderTransactions"); } } [RelayCommand] public async Task Refresh() { base.OnPropertyChanged("SelectedSymbol"); await Task.FromResult(true); } } }