From 20d253d50e9d4da93383569e582d61561ef2d05c Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 15 Jun 2025 20:11:58 -0400 Subject: [PATCH] Add ability to show image markers. --- .gitignore | 3 +- PortfolioManager/Assets/BlueTriangleUp.png | Bin 0 -> 3188 bytes PortfolioManager/Assets/GreenTriangleDown.png | Bin 0 -> 2204 bytes PortfolioManager/Assets/GreenTriangleUp.png | Bin 0 -> 2496 bytes PortfolioManager/Assets/RedTriangleDown.png | Bin 0 -> 1995 bytes PortfolioManager/Assets/RedTriangleUp.png | Bin 0 -> 2047 bytes PortfolioManager/Assets/YellowTriangleUp.png | Bin 0 -> 2331 bytes PortfolioManager/Cache/ImageCache.cs | 79 ++++++++++++ PortfolioManager/PortfolioManager.csproj | 7 ++ PortfolioManager/UIUtils/UIUtils.cs | 33 ++++- .../ViewModels/BollingerBandViewModel.cs | 115 ++++++++++++++---- .../ViewModels/MainWindowViewModel.cs | 6 +- .../Views/BollingerBandView.axaml | 19 +++ PortfolioManager/saveparams.config | 28 +---- 14 files changed, 236 insertions(+), 54 deletions(-) create mode 100644 PortfolioManager/Assets/BlueTriangleUp.png create mode 100644 PortfolioManager/Assets/GreenTriangleDown.png create mode 100644 PortfolioManager/Assets/GreenTriangleUp.png create mode 100644 PortfolioManager/Assets/RedTriangleDown.png create mode 100644 PortfolioManager/Assets/RedTriangleUp.png create mode 100644 PortfolioManager/Assets/YellowTriangleUp.png create mode 100644 PortfolioManager/Cache/ImageCache.cs diff --git a/.gitignore b/.gitignore index d5cb544..77abf91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ PortfolioManager/bin/** PortfolioManager/obj/** PortfolioManager/*.log - - +PortfolioManager/saveparams.config diff --git a/PortfolioManager/Assets/BlueTriangleUp.png b/PortfolioManager/Assets/BlueTriangleUp.png new file mode 100644 index 0000000000000000000000000000000000000000..8565fecead3a5e7647b2c87acd4818049d89b1f8 GIT binary patch literal 3188 zcmZ`+XEfUl-2EqwilC@fN~uGi_)?>EX z+EueuA}N|$;q{#N>-&DVzkBY--#Pc5b8oVVkuEEY9|iybtG*t}^o(Qv1N8jaC90u^ z&j^Gu)kOleV*;CJ0^+H8UlRZt(wUFZ3}>1tP|pSf02liH1BhGPAO-+fnDkMa=1&~S zMOy&^U-%M>6~lkAA~n2^m{6%+52b8nYc#SYe`^`OGNAJsnBYyyMKtZM-V{bYV20?~ ziO81fv#^FhO}s7!6kh%%dgpo?r$A!Ae8Wn=zHfjFqG2kn`-ny)z4lnS+vG!W_n@hm ziiD2O$N%5NE^ba^%qCE&CDsOd9PYoC2cBwGiOj_sa1r(f z^zY89kG0U>MMQ}pA1n3yEygX*TjYtnX)_4<(Tzi-P2|&DXYl$ynrg<$2O@MgFp9{ z)lg?jZJ4+<2B5-f*o{lop{5{UG(YQ5W)N77fF*2Fl-1nQQ?0qEy2_&sPRhb3M#}^LxOs7My1XL}Vt5gp2Uy?)uTLnat;CZR6qM*At z23=ilN~FS6yvP$s4^H<`yNB*;`?LM4B`FWRrCN5piW_K;8qR^tf=8}vTiyf|Gtv|5 zvgj&LYT9KH5`hp~pr)94yycw5p=b21xNbEn(khpAw3h}2OT|2|c)!|^ZpuEourq#R zg{Ri9X4DAA70XSaoxABP2u0s*iv(~%QA$wf1azca^~s&3M@);HdZ5Bf_lAgbM3d*i zWP!k~FI==0*M}JHqJ?YzGm-k=A?)dFhLJ>;Zr2vWA5Tq%T~0A@a7Hlmc%hZAw^LRT z7gXu~s__My&J#CdNL8UOetBn!ay@Qjd7?Oda>Oyn? z-J|85N3PJSMzfxjRCFM!UhL_(Ka*k*T9)bHIRPr%(uyNjWCwSv`wabHz;#H^s~k@%F{@c{(3GiXkti4*7csQwnLt zmvgcUNVXecB2635rLZSyoG@WX$f;`fdyDSmI)?hAK$$uLF!$ptHUIXKFnI^CY+s*`;ACwQ9Oj#*^5OP zZBzrBOz3d?9OBV_@=zGXg%{yO~{*L2K$>Qy~sBucL2@Q)E zta^1hJAt3hH(wpo7tB1>yf2sZ*$O<}QJ2q_ZZ`|BLx!CA7NUe7wo(_a?3X-LGx9v3 zJ_9ZU7iZ&dS^1n()tEG7Mol;&63AFtzYj*gZuM|zRIf;O=vo+|9S4vo^8_JBs zi}|uH60p?Q%2!r;A5D21*!!OSp$f_6={NxvJ01Uo{d?JK@OemE$XYYJMkrEJ6`A^M zZaG6x?X`8cP0lc4zbp&SsoA7{%`Exip89GS9^%^*p5M8h6xQ)fH2@Sve+vCbGM3Yw z<6{^kJHoE@a~Qsil{rmOUkz4D^GQ+nd&CnDmE?Wg`!Qg>d!lOt zd*1nDp#=t;~lQ;m#7p_VM0KpbVFR~GC8WEiqjHX&3Kd1&jyym+ zP97A#ohYi2`UsqNXa-@bbWCx zRZ3JA58|`@)~64N6I7$=v@RFJP#-TDESH;w*OJ|vV<}qg1p>4bEj*Q8$Y+Kf^*xf2b|QfSjx5pum59{ zX+)9Ztc})U@+Z8EbR~d7wLC^mN0>$@VN8_OWC3dAGjKN|SZqQv*{!V+#F0o*V zxo^dVtL=fZMZZ?>Z^sT<$1;ZWm8un)!wmHya0O-A--s=HUfttuTKmxoT`6(%e?u7I3E7<^VY^KVEdKi8_dF3w%5~A}3 zRk?Ckd$GtX*E;A*L$!G|R(fU+$)0D%4D!kQ{@sm1uQTCtIx*U=$qZ)vfMFss!h5K@ zUErq)nohBt8bNdZx!K42KBW_BZ4T6VUjW% zmVMzeDS%6-wj;u&y)*LsL~{aQ$z>@N_jik1aFC{W8?fx!xccka;qFms(7f91|BU1Z zclc;Nyd=kb+EV`=ykDaKv%r{9Y2>YSTXlx*)oImgFE6=cG-v+Yy75t|d^>qfiZtt0 zOX8Qbh;3_0*0=*Xq7*G3-8T2Jy{!)O0AYu+x+ZOzCk@_uOr=O8&DMLez)kwG$K>`;?+=+q5c~MPg23n;MT@W9u;t3S8Bz zp#B;^1^=a4oU-h77^k8MJIHs@Y2dJDM{rr-I?9y+5^nXeV4=QdLBc9Z+8Rg<8yi~8 z_YSh9+?FmhTykX%4Dwp?yeMZ4G^A-q$S(&aHSy6yOPj#u3w@D9>b6+E&N#V*E%4cB;{(b*X1afTQEi87r(@S_oq;I<6*P;@7~YN|5E zS<3mFsN%F=#Y5+W?zbD2)5pAtJ3MCz&Q)seJyH$cz}Ci(rY!b-d{^rHM{`9Z>5z37 z5hWV{DooMccO6+tyko`A*BWT^d>CYSVO131$UJSfm_)o}+f~To8IJ4OttVwm@Cl4i zOn!EC{1phnZI7YtT1(5_zHzENhW|^=U4PHLE-VMyE4+ujQ%q@P5!KXm5ilykr1l?3 zhCW`~TfH+9-S0{w8dk!xv>7v-#+-LXfz#(#*{%-4ri?hW+e`V6AQ zMEmZ2IBA>)9&q{H(Ix?!`wc8HPFMMS)E WEiOe#aOi9)0Q%ZSs9L0R-2VU}!3K5! literal 0 HcmV?d00001 diff --git a/PortfolioManager/Assets/GreenTriangleDown.png b/PortfolioManager/Assets/GreenTriangleDown.png new file mode 100644 index 0000000000000000000000000000000000000000..7c1324e875bd4bf78bf14becfe7707348ac99231 GIT binary patch literal 2204 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 literal 0 HcmV?d00001 diff --git a/PortfolioManager/Assets/RedTriangleUp.png b/PortfolioManager/Assets/RedTriangleUp.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0500e51a4a5c2112a76256ce34f904d86c8687 GIT binary patch literal 2047 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 imageCache = new Dictionary(); + private Object thisLock=new Object(); + private static ImageCache imageCacheInstance=null; + + private ImageCache() + { + String currentDirectory = Directory.GetCurrentDirectory(); + String pathToAssets = currentDirectory; + if (!Directory.Exists(currentDirectory + "/Assets")) + { + pathToAssets = currentDirectory + "/publish" + "/Assets"; + } + else + { + pathToAssets = currentDirectory + "/Assets"; + } + MDTrace.WriteLine(LogLevel.DEBUG,$"Reading assets from {pathToAssets}"); + imageCache.Add(ImageCache.ImageType.BlueTriangleUp,new Bitmap(pathToAssets+"/BlueTriangleUp.png")); + imageCache.Add(ImageCache.ImageType.GreenTriangleUp,new Bitmap(pathToAssets+"/GreenTriangleUp.png")); + imageCache.Add(ImageCache.ImageType.GreenTriangleDown,new Bitmap(pathToAssets+"/GreenTriangleDown.png")); + imageCache.Add(ImageCache.ImageType.RedTriangleUp,new Bitmap(pathToAssets+"/RedTriangleUp.png")); + imageCache.Add(ImageCache.ImageType.RedTriangleDown,new Bitmap(pathToAssets+"/RedTriangleDown.png")); + imageCache.Add(ImageCache.ImageType.YellowTriangleUp,new Bitmap(pathToAssets+"/YellowTriangleUp.png")); + } + + public static ImageCache GetInstance() + { + lock (typeof(SymbolCache)) + { + if (null == imageCacheInstance) imageCacheInstance = new ImageCache(); + return imageCacheInstance; + } + } + + public void Clear() + { + lock (thisLock) + { + imageCache = new Dictionary(); + } + } + + public void Dispose() + { + lock (thisLock) + { + if (null == imageCacheInstance) return; + List bitmaps = imageCache.Values.ToList(); + foreach (Bitmap bitmap in bitmaps) + { + bitmap.Dispose(); + } + imageCache = null; + imageCacheInstance = null; + } + } + + public IImage GetImage(ImageCache.ImageType imageType) + { + lock(this) + { + return imageCache[imageType]; + } + } + } +} diff --git a/PortfolioManager/PortfolioManager.csproj b/PortfolioManager/PortfolioManager.csproj index 0ffba44..3300a68 100644 --- a/PortfolioManager/PortfolioManager.csproj +++ b/PortfolioManager/PortfolioManager.csproj @@ -52,6 +52,13 @@ + + + PreserveNewest + Assets\%(RecursiveDir)%(Filename)%(Extension) + + + diff --git a/PortfolioManager/UIUtils/UIUtils.cs b/PortfolioManager/UIUtils/UIUtils.cs index 3d6d13c..9840ad0 100644 --- a/PortfolioManager/UIUtils/UIUtils.cs +++ b/PortfolioManager/UIUtils/UIUtils.cs @@ -1,19 +1,48 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using Avalonia.Data.Converters; using MarketData.MarketDataModel; using MarketData.Utils; namespace PortfolioManager.UIUtils { + + /// + /// If the OS is not Window then multiply the first parameter. + /// For example: "30|.5" would multiply 30 * .5 if the OS is not Windows and return 15 as the result. Otherwise (if Windows) it will return 30 + /// I am doing this because I need to adjust image sizes across Windows and Unix platforms where Unix images appear quite larger than Windows + /// + public class OSValueConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + String[] parameters = parameter.ToString().Split('|'); + double valueItem = double.Parse(parameters[0]); + double multiplier = double.Parse(parameters[1]); + + if (Utility.IsOSWindows()) + { + return (int)valueItem; + } + else + { + return (int)(valueItem * multiplier); + } + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return null; + } + } + public class RMultipleValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { double doubleValue = (double)value; if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue)) return Constants.CONST_DASHES; - return Utility.FormatNumber(doubleValue,2)+"R"; + return Utility.FormatNumber(doubleValue, 2) + "R"; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { diff --git a/PortfolioManager/ViewModels/BollingerBandViewModel.cs b/PortfolioManager/ViewModels/BollingerBandViewModel.cs index b2aa84d..34d1928 100644 --- a/PortfolioManager/ViewModels/BollingerBandViewModel.cs +++ b/PortfolioManager/ViewModels/BollingerBandViewModel.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using Avalonia.Media; using CommunityToolkit.Mvvm.Input; using DynamicData; using Eremex.AvaloniaUI.Charts; @@ -227,6 +228,12 @@ namespace PortfolioManager.ViewModels syncTradeToBand = value; if (syncTradeToBand) showTradeLabels = true; base.OnPropertyChanged("SyncTradeToBand"); + base.OnPropertyChanged("TradePoints"); + base.OnPropertyChanged("ZeroPoint"); + base.OnPropertyChanged("StopLimits"); + base.OnPropertyChanged("TradePointMarkers"); + base.OnPropertyChanged("ZeroPointMarkers"); + base.OnPropertyChanged("StopLimitMarkers"); } } @@ -296,7 +303,21 @@ namespace PortfolioManager.ViewModels } } - // ******************************************************************* P E R S I S T E N C E *************************************************** + /// + /// See the XAML and also OSValueConverter in UIUtils for an explanation. + /// + public int MarkerSize + { + get + { + return 0; + } + } + + // ********************************************************************************************************************************************* + + + // ******************************************************************* P E R S I S T E N C E *************************************************** public override bool CanPersist() { @@ -313,8 +334,6 @@ namespace PortfolioManager.ViewModels 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) @@ -353,11 +372,6 @@ namespace PortfolioManager.ViewModels 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); @@ -368,9 +382,6 @@ namespace PortfolioManager.ViewModels 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")) @@ -418,8 +429,6 @@ namespace PortfolioManager.ViewModels { base.DisplayName = "Bollinger(" + selectedSymbol + ")"; base.OnPropertyChanged("DisplayName"); - // InitializeDataSources(); - // InitializeData(); stopLimit = PortfolioDA.GetStopLimit(selectedSymbol); portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); @@ -480,13 +489,14 @@ namespace PortfolioManager.ViewModels 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("TradePoints"); + base.OnPropertyChanged("ZeroPoint"); + base.OnPropertyChanged("StopLimits"); + + base.OnPropertyChanged("TradePointMarkers"); + base.OnPropertyChanged("ZeroPointMarkers"); + base.OnPropertyChanged("StopLimitMarkers"); base.OnPropertyChanged("InsiderTransactionPointDisposedSmall"); base.OnPropertyChanged("InsiderTransactionPointDisposedMedium"); @@ -496,7 +506,6 @@ namespace PortfolioManager.ViewModels base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium"); base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge"); - // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall"); // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium"); // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge"); @@ -528,9 +537,6 @@ namespace PortfolioManager.ViewModels base.OnPropertyChanged("Symbols"); }); } - - - } // ************************************************* C O M P O S I T E P R O P E R T I E S ******************************************** @@ -609,10 +615,65 @@ namespace PortfolioManager.ViewModels } } + public CompositeDataSource TradePoints + { + get + { + if (!showTradeLabels) return Empty(); + return compositeDataSourceTradePoints; + } + } + + public IImage TradePointMarkers + { + get + { + if (!showTradeLabels) return null; + return ImageCache.GetInstance().GetImage(ImageCache.ImageType.YellowTriangleUp); + } + } + + public CompositeDataSource ZeroPoint + { + get + { + if (!showTradeLabels) return Empty(); + return compositeDataSourceZeroPoint; + } + } + + public IImage ZeroPointMarkers + { + get + { + if (!showTradeLabels) return null; + return ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp); + } + } + + public CompositeDataSource StopLimits + { + get + { + if (!showTradeLabels) return Empty(); + return compositeDataSourceStopLimit; + } + } + + public IImage StopLimitMarkers + { + get + { + if (!showTradeLabels) return null; + return ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleUp); + } + } + public CompositeDataSource InsiderTransactionPointDisposedSmall { get { + if (!showInsiderTransactions) return Empty(); return compositeDataSourceInsiderTransactionPointDisposedSmall; } } @@ -621,6 +682,7 @@ namespace PortfolioManager.ViewModels { get { + if (!showInsiderTransactions) return Empty(); return compositeDataSourceInsiderTransactionPointDisposedMedium; } } @@ -629,6 +691,7 @@ namespace PortfolioManager.ViewModels { get { + if (!showInsiderTransactions) return Empty(); return compositeDataSourceInsiderTransactionPointDisposedLarge; } } @@ -637,6 +700,7 @@ namespace PortfolioManager.ViewModels { get { + if (!showInsiderTransactions) return Empty(); return compositeDataSourceInsiderTransactionPointAcquiredSmall; } } @@ -645,6 +709,7 @@ namespace PortfolioManager.ViewModels { get { + if (!showInsiderTransactions) return Empty(); return compositeDataSourceInsiderTransactionPointAcquiredMedium; } } @@ -653,14 +718,12 @@ namespace PortfolioManager.ViewModels { get { + if (!showInsiderTransactions) return Empty(); return compositeDataSourceInsiderTransactionPointAcquiredLarge; } } // ********************************************************************************************************************************************* - - - public void CreateCompositeDataSources() { if (null == prices || 0 == prices.Count) return; diff --git a/PortfolioManager/ViewModels/MainWindowViewModel.cs b/PortfolioManager/ViewModels/MainWindowViewModel.cs index cb858f2..996ec2f 100644 --- a/PortfolioManager/ViewModels/MainWindowViewModel.cs +++ b/PortfolioManager/ViewModels/MainWindowViewModel.cs @@ -8,6 +8,7 @@ using Avalonia.Threading; using Axiom.Utils; using MarketData.Cache; using MarketData.DataAccess; +using PortfolioManager.Cache; using PortfolioManager.Command; namespace PortfolioManager.ViewModels @@ -43,9 +44,10 @@ namespace PortfolioManager.ViewModels } try { LocalPriceCache.GetInstance().Dispose(); } catch (Exception) {; } try { GBPriceCache.GetInstance().Dispose(); } catch (Exception) {; } + try { ImageCache.GetInstance().Dispose(); } catch (Exception) {; } //try{PriceCache.GetInstance().Dispose();}catch(Exception){;} - // try{SymbolCache.GetInstance().Dispose();}catch(Exception){;} - base.OnDispose(); + // try{SymbolCache.GetInstance().Dispose();}catch(Exception){;} + base.OnDispose(); MDTrace.WriteLine(LogLevel.DEBUG, "[MainWindowViewModel:OnDispose] LEAVE"); } diff --git a/PortfolioManager/Views/BollingerBandView.axaml b/PortfolioManager/Views/BollingerBandView.axaml index a1ae48e..c5be122 100644 --- a/PortfolioManager/Views/BollingerBandView.axaml +++ b/PortfolioManager/Views/BollingerBandView.axaml @@ -19,6 +19,7 @@ + @@ -147,6 +148,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/PortfolioManager/saveparams.config b/PortfolioManager/saveparams.config index e1c7dd5..ceb2b95 100644 --- a/PortfolioManager/saveparams.config +++ b/PortfolioManager/saveparams.config @@ -2,26 +2,10 @@ 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,RNMBY,SelectedWatchList,Valuations,SelectedDayCount,90,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 +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,CRS,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,True,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,GWRE,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,RGLD,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True