From 9b1135b5ec3fceee439667b3871914de7dfe99da Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 15 Jun 2025 13:56:48 -0400 Subject: [PATCH] More changes to support BollingerBands. --- PortfolioManager/App.axaml.cs | 79 ++-- .../Extensions/StopLimitsExtensions.cs | 46 ++ PortfolioManager/Models/BollingerBandModel.cs | 4 +- PortfolioManager/Program.cs | 59 ++- .../ViewModels/BollingerBandViewModel.cs | 439 ++++++++++++++---- .../ViewModels/CMMomentumViewModel.cs | 6 + .../ViewModels/CMTrendViewModel.cs | 1 + .../ViewModels/GainLossViewModel.cs | 31 +- .../ViewModels/MGSHMomentumViewModel.cs | 3 +- .../ViewModels/MainWindowViewModel.cs | 45 +- .../ViewModels/MomentumViewModel.cs | 6 + .../Views/BollingerBandView.axaml | 89 +++- PortfolioManager/Views/CMMomentumView.axaml | 2 +- PortfolioManager/Views/CMTrendView.axaml | 2 +- PortfolioManager/Views/GainLossView.axaml | 13 +- PortfolioManager/Views/MGSHMomentumView.axaml | 2 +- PortfolioManager/Views/MomentumView.axaml | 2 +- PortfolioManager/saveparams.config | 23 + 18 files changed, 631 insertions(+), 221 deletions(-) create mode 100644 PortfolioManager/Extensions/StopLimitsExtensions.cs diff --git a/PortfolioManager/App.axaml.cs b/PortfolioManager/App.axaml.cs index 8e5b0b3..a30e6b1 100644 --- a/PortfolioManager/App.axaml.cs +++ b/PortfolioManager/App.axaml.cs @@ -22,48 +22,49 @@ public partial class App : Application public override void OnFrameworkInitializationCompleted() { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + // Avoid duplicate validations from both Avalonia and the CommunityToolkit. + // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins + DisableAvaloniaDataAnnotationValidation(); + MainWindowViewModel mainWindowViewModel = new MainWindowViewModel(); + desktop.MainWindow = new MainWindow { - // Avoid duplicate validations from both Avalonia and the CommunityToolkit. - // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins - DisableAvaloniaDataAnnotationValidation(); - MainWindowViewModel mainWindowViewModel = new MainWindowViewModel(); - desktop.MainWindow = new MainWindow - { - DataContext = mainWindowViewModel, - }; + DataContext = mainWindowViewModel, + }; - EventHandler requestCloseHandler = null; - requestCloseHandler = delegate - { - MDTrace.WriteLine(LogLevel.DEBUG, $"RequestCloseHandler"); - if (null != mainWindowViewModel) - { - mainWindowViewModel.RequestClose -= requestCloseHandler; - mainWindowViewModel.Dispose(); - mainWindowViewModel = null; - } - if (null != desktop.MainWindow) - { - desktop.MainWindow.Close(); - desktop.MainWindow = null; - } - }; - mainWindowViewModel.RequestClose += requestCloseHandler; + EventHandler requestCloseHandler = null; + requestCloseHandler = delegate + { + MDTrace.WriteLine(LogLevel.DEBUG, "App: Received RequestCloseHandler event"); + if (null != mainWindowViewModel) + { + mainWindowViewModel.RequestClose -= requestCloseHandler; + mainWindowViewModel.Dispose(); + mainWindowViewModel = null; + } + if (null != desktop.MainWindow) + { + desktop.MainWindow.Close(); + MDTrace.WriteLine(LogLevel.DEBUG, "App: Shutting down the desktop"); + desktop.Shutdown(); + desktop.MainWindow = null; + } + }; + mainWindowViewModel.RequestClose += requestCloseHandler; - EventHandler closingHandler = null; - closingHandler = delegate - { - MDTrace.WriteLine(LogLevel.DEBUG, $"ClosingHandler"); - if (null != mainWindowViewModel) - { - mainWindowViewModel.Dispose(); - } - }; - - desktop.MainWindow.Closing += closingHandler; - } - base.OnFrameworkInitializationCompleted(); + EventHandler closingHandler = null; + closingHandler = delegate + { + MDTrace.WriteLine(LogLevel.DEBUG, "App: Received ClosingHandler event"); + if (null != mainWindowViewModel) + { + mainWindowViewModel.Dispose(); + } + }; + desktop.MainWindow.Closing += closingHandler; + } + base.OnFrameworkInitializationCompleted(); } private void DisableAvaloniaDataAnnotationValidation() diff --git a/PortfolioManager/Extensions/StopLimitsExtensions.cs b/PortfolioManager/Extensions/StopLimitsExtensions.cs new file mode 100644 index 0000000..fb49ed3 --- /dev/null +++ b/PortfolioManager/Extensions/StopLimitsExtensions.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MarketData.MarketDataModel; +using PortfolioManager.ViewModels; +using MarketData.Utils; + +namespace PortfolioManager.Extensions +{ + public static class StopLimitsExtensions + { + public static StopLimits FromSaveParams(SaveParameters saveParameters) + { + StopLimits stopLimits=new StopLimits(); + int stopHistoryCount=int.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("StopHistoryCount") select item).FirstOrDefault().Value); + for(int index=0;index item in saveParameters where item.Key.Equals(strItemKey) select item).FirstOrDefault().Value; + NVPCollection nvpCollection=new NVPCollection(strStopHistoryItem); + StopLimit stopLimit=MarketData.MarketDataModel.StopLimit.FromNVPCollection(nvpCollection); + stopLimits.Add(stopLimit); + } + stopLimits=new StopLimits(stopLimits.OrderBy(x => x.EffectiveDate).ToList()); + return stopLimits; + } + + public static SaveParameters FromStopLimits(this StopLimits stopLimits) + { + StringBuilder sb = new StringBuilder(); + if(null==stopLimits || 0==stopLimits.Count)return new SaveParameters(); + NVPCollections nvpCollections = stopLimits.ToNVPCollections(); + sb.Append("StopHistoryCount").Append(",").Append(String.Format("{0}", nvpCollections.Count)).Append(","); + for (int index = 0; index < nvpCollections.Count; index++) + { + sb.Append(String.Format("StopHistory_{0}", index)).Append(",").Append(nvpCollections[index].ToString()); + if (index < nvpCollections.Count - 1) sb.Append(","); + } + return SaveParameters.Parse(sb.ToString()); + } + } +} + + + diff --git a/PortfolioManager/Models/BollingerBandModel.cs b/PortfolioManager/Models/BollingerBandModel.cs index 7b9bbf8..2148875 100644 --- a/PortfolioManager/Models/BollingerBandModel.cs +++ b/PortfolioManager/Models/BollingerBandModel.cs @@ -173,9 +173,9 @@ namespace PortfolioManager.Models LeastSquaresResult leastSquaresResult = bollingerBands.LeastSquaresFitClose(); SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter(); List sortedBollingerBands = bollingerBands.OrderBy(x => x.Date).ToList(); - for (int index = 0; index < bollingerBands.Count; index++) + for (int index = 0; index < sortedBollingerBands.Count; index++) { - BollingerBandElement element = bollingerBands[index]; + BollingerBandElement element = sortedBollingerBands[index]; int leastSquaresIndex = (leastSquaresResult.LeastSquares.Length - 1) - index; sortedDateTimeDataAdapter.Add(element.Date, leastSquaresResult.LeastSquares[leastSquaresIndex]); } diff --git a/PortfolioManager/Program.cs b/PortfolioManager/Program.cs index 26b8652..da0c32e 100644 --- a/PortfolioManager/Program.cs +++ b/PortfolioManager/Program.cs @@ -9,32 +9,45 @@ namespace PortfolioManager; sealed class Program { - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) + { + MDTrace.LogLevel = LogLevel.VERBOSE; + String strLogFile = "portfolio_manager.log"; + Trace.Listeners.Add(new TextWriterTraceListener(strLogFile)); + MDTrace.WriteLine(LogLevel.DEBUG, "[MAIN:STARTING]"); + + IConfigurationBuilder builder = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + IConfigurationRoot configurationRoot = builder.Build(); + GlobalConfig.Instance.Configuration = configurationRoot; // This call sets up configuration stuff so it needs to be first. + try { - MDTrace.LogLevel = LogLevel.VERBOSE; - String strLogFile = "portfolio_manager.log"; - Trace.Listeners.Add(new TextWriterTraceListener(strLogFile)); - MDTrace.WriteLine(LogLevel.DEBUG, "[STARTING]"); - - IConfigurationBuilder builder = new ConfigurationBuilder() - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); - IConfigurationRoot configurationRoot = builder.Build(); - GlobalConfig.Instance.Configuration = configurationRoot; // This call sets up configuration stuff so it needs to be first. - try { BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } - catch (Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString()); } + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } + catch (Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString()); } - // public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .WithInterFont() - .LogToTrace(); + ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads; + if (null != currentThreads && 0 != currentThreads.Count) + { + MDTrace.WriteLine(LogLevel.DEBUG, $"There were {currentThreads.Count} threads still running at application shutdown."); + } + MDTrace.WriteLine(LogLevel.DEBUG, "[MAIN:EXIT]"); + Environment.Exit(0); + } + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); } diff --git a/PortfolioManager/ViewModels/BollingerBandViewModel.cs b/PortfolioManager/ViewModels/BollingerBandViewModel.cs index a59aba3..b2aa84d 100644 --- a/PortfolioManager/ViewModels/BollingerBandViewModel.cs +++ b/PortfolioManager/ViewModels/BollingerBandViewModel.cs @@ -3,9 +3,12 @@ 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 Eremex.AvaloniaUI.Charts; +using MarketData; using MarketData.DataAccess; using MarketData.Generator; using MarketData.MarketDataModel; @@ -13,6 +16,7 @@ using MarketData.Numerical; using MarketData.Utils; using PortfolioManager.Cache; using PortfolioManager.DataSeriesViewModels; +using PortfolioManager.Extensions; using PortfolioManager.Models; using PortfolioManager.UIUtils; @@ -39,9 +43,10 @@ namespace PortfolioManager.ViewModels private bool syncTradeToBand = true; private String companyName = default; + private bool showTradeLabels = true; private bool useLeastSquaresFit = true; - + private bool showInsiderTransactions = true; private CompositeDataSource compositeDataSourceZeroPoint = null; private CompositeDataSource compositeDataSourceStopLimit = null; @@ -69,11 +74,18 @@ namespace PortfolioManager.ViewModels public BollingerBandViewModel() { + InitializeDataSources(); PropertyChanged += OnViewModelPropertyChanged; - DisplayName = "BollingerBand View"; + DisplayName = "Bollinger"; Initialize(); } + protected override void OnDispose() + { + MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose BollingerBandViewModel"); + base.OnDispose(); + } + private void Initialize() { Task workerTask = Task.Factory.StartNew(() => @@ -108,7 +120,36 @@ namespace PortfolioManager.ViewModels { get { - return DisplayName; + if (null == selectedSymbol) return DisplayName; + return "Bollinger " + "("+selectedSymbol+")"; + } + } + + public String GraphTitle + { + get + { + if (null == companyName || null == prices || 0 == prices.Count) 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(prices.Take(2).ToList()); + if(2==prices2day.Count)change=prices2day.GetReturns()[0]; + sb.Append(displayCompanyName); + sb.Append(" (").Append(selectedSymbol).Append(") "); + sb.Append(Utility.DateTimeToStringMMHDDHYYYY(prices[prices.Count-1].Date)); + sb.Append(" Thru "); + sb.Append(Utility.DateTimeToStringMMHDDHYYYY(prices[0].Date)); + sb.Append(" (").Append(Utility.FormatCurrency(prices[0].Close)); + sb.Append("/").Append(Utility.FormatCurrency(prices[0].Low)); + if(!float.IsNaN(change)) + { + sb.Append(","); + sb.Append(change>=0.00?"+":"").Append(Utility.FormatPercent((double)change)); + } + sb.Append(")"); + return sb.ToString(); } } @@ -116,10 +157,7 @@ namespace PortfolioManager.ViewModels { get { - return "BollingerBand Model"; - // if (null == pathFileName) return "MGSHMomentum Model"; - // String pureFileName = Utility.GetFileNameNoExtension(pathFileName); - // return "MGSHMomentum Model (" + pureFileName + ")"; + return "Bollinger Band"; } } @@ -178,6 +216,62 @@ namespace PortfolioManager.ViewModels } } + public bool SyncTradeToBand + { + get + { + return syncTradeToBand; + } + set + { + syncTradeToBand = value; + if (syncTradeToBand) showTradeLabels = true; + base.OnPropertyChanged("SyncTradeToBand"); + } + } + + public bool ShowTradeLabels + { + get + { + return showTradeLabels; + } + set + { + showTradeLabels = value; + base.OnPropertyChanged("ShowTradeLabels"); + } + } + + public Boolean CheckBoxShowInsiderTransactions + { + get + { + return showInsiderTransactions; + } + set + { + showInsiderTransactions = value; + base.OnPropertyChanged("CheckBoxShowInsiderTransactions"); + base.OnPropertyChanged("InsiderTransactionPointDisposedSmall"); + base.OnPropertyChanged("InsiderTransactionPointDisposedMedium"); + base.OnPropertyChanged("InsiderTransactionPointDisposedLarge"); + base.OnPropertyChanged("InsiderTransactionPointAcquiredSmall"); + base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium"); + base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge"); + + + // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge"); + + // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredSmall"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredMedium"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredLarge"); + base.OnPropertyChanged("LeastSquares"); + } + } + // ****************************************************** P R O P E R T I E S ************************************************ @@ -206,17 +300,101 @@ namespace PortfolioManager.ViewModels public override bool CanPersist() { - return false; + return true; } public override SaveParameters GetSaveParameters() { - return null; + SaveParameters saveParams = new SaveParameters(); + if (null == 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("ShowRiskFree", showRiskFree.ToString())); + // saveParams.Add(new KeyValuePair("IsLegendVisible", isLegendVisible.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(strItemKey,strStopHistoryItem)); + } + } + return saveParams; } public override void SetSaveParameters(SaveParameters saveParameters) { - return; + try + { + 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; + base.OnPropertyChanged("SelectedWatchList"); + selectedDayCount = Int32.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("SelectedDayCount") select item).FirstOrDefault().Value); + try + { + if(saveParameters.ContainsKey("SyncTradeToBand"))syncTradeToBand=Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("SyncTradeToBand") select item).FirstOrDefault().Value); + else syncTradeToBand=true; + } + catch (Exception) { syncTradeToBand = true; } + 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("IsLegendVisible"))isLegendVisible=Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("IsLegendVisible") select item).FirstOrDefault().Value); + // } + // catch (Exception){;} + 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{showRiskFree = Boolean.Parse((from KeyValuePair item in saveParameters where item.Key.Equals("ShowRiskFree") 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())); + } + base.OnPropertyChanged("SelectedSymbol"); + } + catch (Exception) + { + } + } + + // ****************************************************** R E L A Y S ******************************************************** + [RelayCommand] + public async Task Refresh() + { + base.OnPropertyChanged("SelectedSymbol"); + await Task.FromResult(true); } // ************************************************************************************************************************************* @@ -225,110 +403,108 @@ namespace PortfolioManager.ViewModels if (eventArgs.PropertyName.Equals("SelectedSymbol")) { InitializeDataSources(); - insiderTransactionSummaries = null; - zeroPrice = null; - prices = null; - portfolioTrades = null; - portfolioTradesLots = null; - stopLimit = null; + InitializeData(); } - if (eventArgs.PropertyName.Equals("SyncTradeToBand")|| - eventArgs.PropertyName.Equals("ShowTradeLabels")|| - eventArgs.PropertyName.Equals("SelectedSymbol")|| - eventArgs.PropertyName.Equals("ShowRiskFree")|| - eventArgs.PropertyName.Equals("LeastSquaresFit")|| - (eventArgs.PropertyName.Equals("SelectedDayCount") && null!=selectedSymbol)) + if (eventArgs.PropertyName.Equals("SyncTradeToBand") || + eventArgs.PropertyName.Equals("ShowTradeLabels") || + eventArgs.PropertyName.Equals("SelectedSymbol") || + eventArgs.PropertyName.Equals("ShowRiskFree") || + eventArgs.PropertyName.Equals("LeastSquaresFit") || + (eventArgs.PropertyName.Equals("SelectedDayCount") && null != selectedSymbol)) { - IsBusy=true; - Task workerTask=Task.Factory.StartNew(()=> - { - base.DisplayName="Bollinger("+selectedSymbol+")"; - base.OnPropertyChanged("DisplayName"); - -// DEBUG - stopLimit=PortfolioDA.GetStopLimit(selectedSymbol); - portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); - portfolioTradesLots=LotAggregator.CombineLots(portfolioTrades); - if (null != portfolioTrades && 0 != portfolioTrades.Count) + IsBusy = true; + Task workerTask = Task.Factory.StartNew(() => { - DateGenerator dateGenerator = new DateGenerator(); - DateTime earliestTrade = portfolioTrades[0].TradeDate; - earliestTrade = earliestTrade.AddDays(-30); - int daysBetween = dateGenerator.DaysBetween(earliestTrade, DateTime.Now); - if (daysBetween < selectedDayCount || !syncTradeToBand) prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); - else prices = PricingDA.GetPrices(selectedSymbol, earliestTrade); - - DateTime earliestInsiderTransactionDate=dateGenerator.GenerateFutureBusinessDate(prices[prices.Count-1].Date,30); - insiderTransactionSummaries=InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol,earliestInsiderTransactionDate); - -// calculate the break even price on the open trades for this symbol - PortfolioTrades openTrades=portfolioTrades.GetOpenTrades(); - DateTime latestPricingDate = PricingDA.GetLatestDate(selectedSymbol); - Price latestPrice = PricingDA.GetPrice(selectedSymbol, latestPricingDate); - zeroPrice=ParityGenerator.GenerateGainLossValue(openTrades,latestPrice); - - if (!syncTradeToBand) - { - DateTime earliestPricingDate = prices[prices.Count - 1].Date; - earliestPricingDate = earliestPricingDate.AddDays(30); - IEnumerable tradesInRange = (from portfolioTrade in portfolioTradesLots where portfolioTrade.TradeDate >= earliestPricingDate select portfolioTrade); - portfolioTrades = new PortfolioTrades(); - foreach (PortfolioTrade portfolioTrade in tradesInRange) portfolioTrades.Add(portfolioTrade); - portfolioTradesLots = portfolioTrades; - } - } - else - { - prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); - if (null != prices && 0 != prices.Count) + base.DisplayName = "Bollinger(" + selectedSymbol + ")"; + base.OnPropertyChanged("DisplayName"); + // InitializeDataSources(); + // InitializeData(); + stopLimit = PortfolioDA.GetStopLimit(selectedSymbol); + portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); + portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); + if (null != portfolioTrades && 0 != portfolioTrades.Count) { DateGenerator dateGenerator = new DateGenerator(); + DateTime earliestTrade = portfolioTrades[0].TradeDate; + earliestTrade = earliestTrade.AddDays(-30); + int daysBetween = dateGenerator.DaysBetween(earliestTrade, DateTime.Now); + if (daysBetween < selectedDayCount || !syncTradeToBand) prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); + else prices = PricingDA.GetPrices(selectedSymbol, earliestTrade); + DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30); insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate); + + // calculate the break even price on the open trades for this symbol + PortfolioTrades openTrades = portfolioTrades.GetOpenTrades(); + DateTime latestPricingDate = PricingDA.GetLatestDate(selectedSymbol); + Price latestPrice = PricingDA.GetPrice(selectedSymbol, latestPricingDate); + zeroPrice = ParityGenerator.GenerateGainLossValue(openTrades, latestPrice); + + if (!syncTradeToBand) + { + DateTime earliestPricingDate = prices[prices.Count - 1].Date; + earliestPricingDate = earliestPricingDate.AddDays(30); + IEnumerable tradesInRange = (from portfolioTrade in portfolioTradesLots where portfolioTrade.TradeDate >= earliestPricingDate select portfolioTrade); + portfolioTrades = new PortfolioTrades(); + foreach (PortfolioTrade portfolioTrade in tradesInRange) portfolioTrades.Add(portfolioTrade); + portfolioTradesLots = portfolioTrades; + } } - } - companyName = PricingDA.GetNameForSymbol(selectedSymbol); - bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); - CreateCompositeDataSources(); - }); - workerTask.ContinueWith((continuation)=> - { - IsBusy = false; - base.OnPropertyChanged("K"); - base.OnPropertyChanged("KL1"); - base.OnPropertyChanged("L"); - base.OnPropertyChanged("LP1"); - base.OnPropertyChanged("High"); - base.OnPropertyChanged("Low"); - base.OnPropertyChanged("Close"); - base.OnPropertyChanged("SMAN"); - base.OnPropertyChanged("Volume"); - base.OnPropertyChanged("LeastSquares"); - // base.OnPropertyChanged("Title"); - // base.OnPropertyChanged("TradePoints"); - // base.OnPropertyChanged("Markers"); - // base.OnPropertyChanged("ZeroPoint"); - // base.OnPropertyChanged("ZeroPointMarkers"); - // base.OnPropertyChanged("StopLimit"); - // base.OnPropertyChanged("StopLimitMarkers"); - // base.OnPropertyChanged("RiskFreeRatePoint"); - // base.OnPropertyChanged("RiskFreeRatePointMarkers"); + else + { + prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); + if (null != prices && 0 != prices.Count) + { + DateGenerator dateGenerator = new DateGenerator(); + DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30); + insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate); + } + } + companyName = PricingDA.GetNameForSymbol(selectedSymbol); + bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); + CreateCompositeDataSources(); + }); + workerTask.ContinueWith((continuation) => + { + IsBusy = false; + base.OnPropertyChanged("K"); + base.OnPropertyChanged("KL1"); + base.OnPropertyChanged("L"); + base.OnPropertyChanged("LP1"); + base.OnPropertyChanged("High"); + base.OnPropertyChanged("Low"); + base.OnPropertyChanged("Close"); + base.OnPropertyChanged("SMAN"); + base.OnPropertyChanged("Volume"); + base.OnPropertyChanged("LeastSquares"); + base.OnPropertyChanged("GraphTitle"); + base.OnPropertyChanged("Title"); + // base.OnPropertyChanged("TradePoints"); + // base.OnPropertyChanged("Markers"); + // base.OnPropertyChanged("ZeroPoint"); + // base.OnPropertyChanged("ZeroPointMarkers"); + // base.OnPropertyChanged("StopLimit"); + // base.OnPropertyChanged("StopLimitMarkers"); - // base.OnPropertyChanged("InsiderTransactionPointDisposedSmall"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall"); - // base.OnPropertyChanged("InsiderTransactionPointDisposedMedium"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium"); - // base.OnPropertyChanged("InsiderTransactionPointDisposedLarge"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge"); - // base.OnPropertyChanged("InsiderTransactionPointAcquiredSmall"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredSmall"); - // base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredMedium"); - // base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredLarge"); - }); + base.OnPropertyChanged("InsiderTransactionPointDisposedSmall"); + base.OnPropertyChanged("InsiderTransactionPointDisposedMedium"); + base.OnPropertyChanged("InsiderTransactionPointDisposedLarge"); + + base.OnPropertyChanged("InsiderTransactionPointAcquiredSmall"); + base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium"); + base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge"); + + + // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge"); + + // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredSmall"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredMedium"); + // base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredLarge"); + }); } else if (eventArgs.PropertyName.Equals("SelectedWatchList")) { @@ -433,7 +609,53 @@ namespace PortfolioManager.ViewModels } } + public CompositeDataSource InsiderTransactionPointDisposedSmall + { + get + { + return compositeDataSourceInsiderTransactionPointDisposedSmall; + } + } + public CompositeDataSource InsiderTransactionPointDisposedMedium + { + get + { + return compositeDataSourceInsiderTransactionPointDisposedMedium; + } + } + + public CompositeDataSource InsiderTransactionPointDisposedLarge + { + get + { + return compositeDataSourceInsiderTransactionPointDisposedLarge; + } + } + + public CompositeDataSource InsiderTransactionPointAcquiredSmall + { + get + { + return compositeDataSourceInsiderTransactionPointAcquiredSmall; + } + } + + public CompositeDataSource InsiderTransactionPointAcquiredMedium + { + get + { + return compositeDataSourceInsiderTransactionPointAcquiredMedium; + } + } + + public CompositeDataSource InsiderTransactionPointAcquiredLarge + { + get + { + return compositeDataSourceInsiderTransactionPointAcquiredLarge; + } + } // ********************************************************************************************************************************************* @@ -486,6 +708,17 @@ namespace PortfolioManager.ViewModels compositeDataSourceTradePoints = PortfolioTradeModel.PortfolioTrades(portfolioTradesLots); } + private void InitializeData() + { + + insiderTransactionSummaries = null; + zeroPrice = null; + prices = null; + portfolioTrades = null; + portfolioTradesLots = null; + stopLimit = null; + } + private void InitializeDataSources() { compositeDataSourceStopLimit = Empty(); diff --git a/PortfolioManager/ViewModels/CMMomentumViewModel.cs b/PortfolioManager/ViewModels/CMMomentumViewModel.cs index fb74d1b..140d6af 100644 --- a/PortfolioManager/ViewModels/CMMomentumViewModel.cs +++ b/PortfolioManager/ViewModels/CMMomentumViewModel.cs @@ -49,6 +49,12 @@ namespace PortfolioManager.ViewModels PropertyChanged += OnViewModelPropertyChanged; } + protected override void OnDispose() + { + MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose CMMomentumViewModel"); + base.OnDispose(); + } + private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { } diff --git a/PortfolioManager/ViewModels/CMTrendViewModel.cs b/PortfolioManager/ViewModels/CMTrendViewModel.cs index d3ea8c6..75de2b6 100644 --- a/PortfolioManager/ViewModels/CMTrendViewModel.cs +++ b/PortfolioManager/ViewModels/CMTrendViewModel.cs @@ -52,6 +52,7 @@ namespace PortfolioManager.ViewModels protected override void OnDispose() { + MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose CMTrendViewModel"); base.OnDispose(); } diff --git a/PortfolioManager/ViewModels/GainLossViewModel.cs b/PortfolioManager/ViewModels/GainLossViewModel.cs index 2f3763a..331eaba 100644 --- a/PortfolioManager/ViewModels/GainLossViewModel.cs +++ b/PortfolioManager/ViewModels/GainLossViewModel.cs @@ -7,6 +7,7 @@ using System.Reactive.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Avalonia.Controls; using CommunityToolkit.Mvvm.Input; using DynamicData; using MarketData; @@ -67,8 +68,9 @@ namespace PortfolioManager.ViewModels protected override void OnDispose() { + MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose GainLossViewModel"); base.OnDispose(); - } + } public override String DisplayName { @@ -737,6 +739,33 @@ namespace PortfolioManager.ViewModels await Task.FromResult(true); } + // *********************************************************** M E N U I T E M S **************************************************** + public ObservableCollection GainLossMenuItems + { + get + { + ObservableCollection collection = new ObservableCollection(); + collection.Add(new MenuItem() { Header = "Display Bollinger Band...", Command = BollingerBandsCommand, StaysOpenOnClick = false }); + return collection; + } + } + + [RelayCommand] + public async Task BollingerBands() + { + SaveParameters saveParams = SaveParameters.Parse("Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180,SyncTradeToBand,FALSE"); + saveParams.Referer = this; + WorkspaceInstantiator.Invoke(saveParams); + await Task.FromResult(true); + + // await Task.FromResult(() => + // { + // SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.BollingerBandViewModel,SelectedSymbol," + selectedGainLossSummaryItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180,SyncTradeToBand,FALSE"); + // saveParams.Referer = this; + // WorkspaceInstantiator.Invoke(saveParams); + // }); + } + // ****************************************************** T O O L T I P S ********************************************************* public String DollarChangePercent { diff --git a/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs b/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs index 9a9a38f..41a244a 100644 --- a/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs +++ b/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs @@ -52,8 +52,9 @@ namespace PortfolioManager.ViewModels protected override void OnDispose() { + MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose MGSHMomentumViewModel"); base.OnDispose(); - } + } private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { diff --git a/PortfolioManager/ViewModels/MainWindowViewModel.cs b/PortfolioManager/ViewModels/MainWindowViewModel.cs index ff15941..cb858f2 100644 --- a/PortfolioManager/ViewModels/MainWindowViewModel.cs +++ b/PortfolioManager/ViewModels/MainWindowViewModel.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Runtime.CompilerServices; using System.Text; using Avalonia.Threading; +using Axiom.Utils; using MarketData.Cache; using MarketData.DataAccess; using PortfolioManager.Command; @@ -31,6 +32,23 @@ namespace PortfolioManager.ViewModels Dispatcher.UIThread.InvokeAsync(() => LoadViewStateThreadProc()); } + + protected override void OnDispose() + { + MDTrace.WriteLine(LogLevel.DEBUG, "[MainWindowViewModel:OnDispose] "); + WorkspacePersistenceHelper.Save(WorkspacePersistenceHelper.PARAMS_FILE, this.Workspaces); + foreach (WorkspaceViewModel workspaceViewModel in this.Workspaces) + { + workspaceViewModel.Dispose(); + } + try { LocalPriceCache.GetInstance().Dispose(); } catch (Exception) {; } + try { GBPriceCache.GetInstance().Dispose(); } catch (Exception) {; } + //try{PriceCache.GetInstance().Dispose();}catch(Exception){;} + // try{SymbolCache.GetInstance().Dispose();}catch(Exception){;} + base.OnDispose(); + MDTrace.WriteLine(LogLevel.DEBUG, "[MainWindowViewModel:OnDispose] LEAVE"); + } + private static String GetTitle() { DataSourceEx dataSource = MainDataSource.Instance.LocateDataSource("market_data"); @@ -46,24 +64,6 @@ namespace PortfolioManager.ViewModels WorkspacePersistenceHelper.Load(WorkspacePersistenceHelper.PARAMS_FILE, this.Workspaces, InstantiateWorkspace); } - protected override void OnDispose() - { - WorkspacePersistenceHelper.Save(WorkspacePersistenceHelper.PARAMS_FILE, this.Workspaces); - - foreach (WorkspaceViewModel workspaceViewModel in this.Workspaces) - { - workspaceViewModel.Dispose(); - } - - try { LocalPriceCache.GetInstance().Dispose(); } catch (Exception) {; } - - try { GBPriceCache.GetInstance().Dispose(); } catch (Exception) {; } - - // try{PriceCache.GetInstance().Dispose();}catch(Exception){;} - // try{SymbolCache.GetInstance().Dispose();}catch(Exception){;} - base.OnDispose(); - } - public override bool CanPersist() { return false; @@ -264,10 +264,11 @@ namespace PortfolioManager.ViewModels } } - public void InstantiateWorkspace(SaveParameters saveParameters) - { - WorkspaceViewModel workspaceViewModel = WorkspacePersistenceHelper.Load(saveParameters, workspaces, InstantiateWorkspace); + public void InstantiateWorkspace(SaveParameters saveParameters) + { + WorkspaceViewModel workspaceViewModel = WorkspacePersistenceHelper.Load(saveParameters, workspaces, InstantiateWorkspace); + SetActiveWorkspace(workspaceViewModel); // AddMenuItem(workspaceViewModel); - } + } } } diff --git a/PortfolioManager/ViewModels/MomentumViewModel.cs b/PortfolioManager/ViewModels/MomentumViewModel.cs index 2c8e033..5696548 100644 --- a/PortfolioManager/ViewModels/MomentumViewModel.cs +++ b/PortfolioManager/ViewModels/MomentumViewModel.cs @@ -48,6 +48,12 @@ namespace PortfolioManager.ViewModels PropertyChanged += OnViewModelPropertyChanged; } + protected override void OnDispose() + { + MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose MomentumViewModel"); + base.OnDispose(); + } + private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { } diff --git a/PortfolioManager/Views/BollingerBandView.axaml b/PortfolioManager/Views/BollingerBandView.axaml index 2638d30..a1ae48e 100644 --- a/PortfolioManager/Views/BollingerBandView.axaml +++ b/PortfolioManager/Views/BollingerBandView.axaml @@ -59,87 +59,132 @@ - + + + + - - - - + + - + + + + + + + + + + - + + + + + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PortfolioManager/Views/CMMomentumView.axaml b/PortfolioManager/Views/CMMomentumView.axaml index 1d4b21d..9ed8d3a 100644 --- a/PortfolioManager/Views/CMMomentumView.axaml +++ b/PortfolioManager/Views/CMMomentumView.axaml @@ -86,7 +86,7 @@ - + diff --git a/PortfolioManager/Views/CMTrendView.axaml b/PortfolioManager/Views/CMTrendView.axaml index 5f72047..1640544 100644 --- a/PortfolioManager/Views/CMTrendView.axaml +++ b/PortfolioManager/Views/CMTrendView.axaml @@ -86,7 +86,7 @@ - + diff --git a/PortfolioManager/Views/GainLossView.axaml b/PortfolioManager/Views/GainLossView.axaml index 462b3d5..2ea946b 100644 --- a/PortfolioManager/Views/GainLossView.axaml +++ b/PortfolioManager/Views/GainLossView.axaml @@ -91,25 +91,25 @@ - + - + - + - + @@ -244,6 +244,11 @@ + + + + + diff --git a/PortfolioManager/Views/MGSHMomentumView.axaml b/PortfolioManager/Views/MGSHMomentumView.axaml index 98c0198..4b9618d 100644 --- a/PortfolioManager/Views/MGSHMomentumView.axaml +++ b/PortfolioManager/Views/MGSHMomentumView.axaml @@ -102,7 +102,7 @@ - + diff --git a/PortfolioManager/Views/MomentumView.axaml b/PortfolioManager/Views/MomentumView.axaml index 0b553e6..9490238 100644 --- a/PortfolioManager/Views/MomentumView.axaml +++ b/PortfolioManager/Views/MomentumView.axaml @@ -86,7 +86,7 @@ - + diff --git a/PortfolioManager/saveparams.config b/PortfolioManager/saveparams.config index 5982e26..e1c7dd5 100644 --- a/PortfolioManager/saveparams.config +++ b/PortfolioManager/saveparams.config @@ -2,3 +2,26 @@ Type,PortfolioManager.ViewModels.MGSHMomentumViewModel,PathFileName,C:\boneyard\ Type,PortfolioManager.ViewModels.MomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\MG20180131.TXT Type,PortfolioManager.ViewModels.CMMomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\CM20191031.TXT Type,PortfolioManager.ViewModels.CMTrendViewModel,PathFileName,C:\boneyard\marketdata\Sessions\CMT20200817.TXT +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,RNMBY,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,TSCDY,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,SPOT,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,GWRE,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,RGLD,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,PRIM,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,NRG,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,DBX,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,CRS,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,SXT,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,PLMR,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,DRD,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,HURN,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,IEFA,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,IDA,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,MD,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,PSO,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,OPRA,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,DORM,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,KEP,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,VSTCX,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,SPY,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,JFNNX,SelectedWatchList,Valuations,SelectedDayCount,1440,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True