Add ability to show image markers.

This commit is contained in:
2025-06-15 20:11:58 -04:00
parent 9b1135b5ec
commit 20d253d50e
14 changed files with 236 additions and 54 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,4 @@
PortfolioManager/bin/** PortfolioManager/bin/**
PortfolioManager/obj/** PortfolioManager/obj/**
PortfolioManager/*.log PortfolioManager/*.log
PortfolioManager/saveparams.config

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using System.Linq;
using System.IO;
using Axiom.Utils;
namespace PortfolioManager.Cache
{
public class ImageCache : IDisposable
{
public enum ImageType {BlueTriangleUp,GreenTriangleUp,GreenTriangleDown,RedTriangleUp,RedTriangleDown,YellowTriangleUp }
private Dictionary<ImageType,Bitmap> imageCache = new Dictionary<ImageType,Bitmap>();
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<ImageType, Bitmap>();
}
}
public void Dispose()
{
lock (thisLock)
{
if (null == imageCacheInstance) return;
List<Bitmap> 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];
}
}
}
}

View File

@@ -52,6 +52,13 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Assets\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>Assets\%(RecursiveDir)%(Filename)%(Extension)</Link>
</Content>
</ItemGroup>
<Target Name="PostClean" AfterTargets="Clean"> <Target Name="PostClean" AfterTargets="Clean">
<RemoveDir Directories="$(BaseIntermediateOutputPath)" /><!-- obj --> <RemoveDir Directories="$(BaseIntermediateOutputPath)" /><!-- obj -->
<RemoveDir Directories="$(BaseOutputPath)" /><!-- bin --> <RemoveDir Directories="$(BaseOutputPath)" /><!-- bin -->

View File

@@ -1,19 +1,48 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using MarketData.MarketDataModel; using MarketData.MarketDataModel;
using MarketData.Utils; using MarketData.Utils;
namespace PortfolioManager.UIUtils namespace PortfolioManager.UIUtils
{ {
/// <summary>
/// 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
/// </summary>
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 class RMultipleValueConverter : IValueConverter
{ {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ {
double doubleValue = (double)value; double doubleValue = (double)value;
if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue)) return Constants.CONST_DASHES; 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) public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ {

View File

@@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Media;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using DynamicData; using DynamicData;
using Eremex.AvaloniaUI.Charts; using Eremex.AvaloniaUI.Charts;
@@ -227,6 +228,12 @@ namespace PortfolioManager.ViewModels
syncTradeToBand = value; syncTradeToBand = value;
if (syncTradeToBand) showTradeLabels = true; if (syncTradeToBand) showTradeLabels = true;
base.OnPropertyChanged("SyncTradeToBand"); 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 *************************************************** /// <summary>
/// See the XAML and also OSValueConverter in UIUtils for an explanation.
/// </summary>
public int MarkerSize
{
get
{
return 0;
}
}
// *********************************************************************************************************************************************
// ******************************************************************* P E R S I S T E N C E ***************************************************
public override bool CanPersist() public override bool CanPersist()
{ {
@@ -313,8 +334,6 @@ namespace PortfolioManager.ViewModels
saveParams.Add(new KeyValuePair<String, String>("SelectedDayCount", selectedDayCount.ToString())); saveParams.Add(new KeyValuePair<String, String>("SelectedDayCount", selectedDayCount.ToString()));
saveParams.Add(new KeyValuePair<String, String>("SyncTradeToBand", syncTradeToBand.ToString())); saveParams.Add(new KeyValuePair<String, String>("SyncTradeToBand", syncTradeToBand.ToString()));
saveParams.Add(new KeyValuePair<String, String>("ShowTradeLabels", showTradeLabels.ToString())); saveParams.Add(new KeyValuePair<String, String>("ShowTradeLabels", showTradeLabels.ToString()));
// saveParams.Add(new KeyValuePair<String, String>("ShowRiskFree", showRiskFree.ToString()));
// saveParams.Add(new KeyValuePair<String, String>("IsLegendVisible", isLegendVisible.ToString()));
saveParams.Add(new KeyValuePair<String, String>("UseLeastSquaresFit", useLeastSquaresFit.ToString())); saveParams.Add(new KeyValuePair<String, String>("UseLeastSquaresFit", useLeastSquaresFit.ToString()));
saveParams.Add(new KeyValuePair<String, String>("ShowInsiderTransactions", showInsiderTransactions.ToString())); saveParams.Add(new KeyValuePair<String, String>("ShowInsiderTransactions", showInsiderTransactions.ToString()));
if(null!=stopLimits && 0!=stopLimits.Count) if(null!=stopLimits && 0!=stopLimits.Count)
@@ -353,11 +372,6 @@ namespace PortfolioManager.ViewModels
else showTradeLabels=true; else showTradeLabels=true;
} }
catch (Exception) { showTradeLabels = true; } catch (Exception) { showTradeLabels = true; }
// try
// {
// if(saveParameters.ContainsKey("IsLegendVisible"))isLegendVisible=Boolean.Parse((from KeyValuePair<String,String> item in saveParameters where item.Key.Equals("IsLegendVisible") select item).FirstOrDefault().Value);
// }
// catch (Exception){;}
try try
{ {
if(saveParameters.ContainsKey("UseLeastSquaresFit"))useLeastSquaresFit=Boolean.Parse((from KeyValuePair<String,String> item in saveParameters where item.Key.Equals("UseLeastSquaresFit") select item).FirstOrDefault().Value); if(saveParameters.ContainsKey("UseLeastSquaresFit"))useLeastSquaresFit=Boolean.Parse((from KeyValuePair<String,String> 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<String,String> item in saveParameters where item.Key.Equals("ShowInsiderTransactions") select item).FirstOrDefault().Value); if(saveParameters.ContainsKey("ShowInsiderTransactions"))showInsiderTransactions=Boolean.Parse((from KeyValuePair<String,String> item in saveParameters where item.Key.Equals("ShowInsiderTransactions") select item).FirstOrDefault().Value);
} }
catch (Exception){;} catch (Exception){;}
// try{showRiskFree = Boolean.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("ShowRiskFree") select item).FirstOrDefault().Value);}
// catch (Exception){;}
try try
{ {
if(saveParameters.ContainsKey("StopHistoryCount")) if(saveParameters.ContainsKey("StopHistoryCount"))
@@ -418,8 +429,6 @@ namespace PortfolioManager.ViewModels
{ {
base.DisplayName = "Bollinger(" + selectedSymbol + ")"; base.DisplayName = "Bollinger(" + selectedSymbol + ")";
base.OnPropertyChanged("DisplayName"); base.OnPropertyChanged("DisplayName");
// InitializeDataSources();
// InitializeData();
stopLimit = PortfolioDA.GetStopLimit(selectedSymbol); stopLimit = PortfolioDA.GetStopLimit(selectedSymbol);
portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol);
portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades);
@@ -480,13 +489,14 @@ namespace PortfolioManager.ViewModels
base.OnPropertyChanged("LeastSquares"); base.OnPropertyChanged("LeastSquares");
base.OnPropertyChanged("GraphTitle"); base.OnPropertyChanged("GraphTitle");
base.OnPropertyChanged("Title"); 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("InsiderTransactionPointDisposedSmall");
base.OnPropertyChanged("InsiderTransactionPointDisposedMedium"); base.OnPropertyChanged("InsiderTransactionPointDisposedMedium");
@@ -496,7 +506,6 @@ namespace PortfolioManager.ViewModels
base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium"); base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium");
base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge"); base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge");
// base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall"); // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall");
// base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium"); // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium");
// base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge"); // base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge");
@@ -528,9 +537,6 @@ namespace PortfolioManager.ViewModels
base.OnPropertyChanged("Symbols"); base.OnPropertyChanged("Symbols");
}); });
} }
} }
// ************************************************* C O M P O S I T E P R O P E R T I E S ******************************************** // ************************************************* 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 public CompositeDataSource InsiderTransactionPointDisposedSmall
{ {
get get
{ {
if (!showInsiderTransactions) return Empty();
return compositeDataSourceInsiderTransactionPointDisposedSmall; return compositeDataSourceInsiderTransactionPointDisposedSmall;
} }
} }
@@ -621,6 +682,7 @@ namespace PortfolioManager.ViewModels
{ {
get get
{ {
if (!showInsiderTransactions) return Empty();
return compositeDataSourceInsiderTransactionPointDisposedMedium; return compositeDataSourceInsiderTransactionPointDisposedMedium;
} }
} }
@@ -629,6 +691,7 @@ namespace PortfolioManager.ViewModels
{ {
get get
{ {
if (!showInsiderTransactions) return Empty();
return compositeDataSourceInsiderTransactionPointDisposedLarge; return compositeDataSourceInsiderTransactionPointDisposedLarge;
} }
} }
@@ -637,6 +700,7 @@ namespace PortfolioManager.ViewModels
{ {
get get
{ {
if (!showInsiderTransactions) return Empty();
return compositeDataSourceInsiderTransactionPointAcquiredSmall; return compositeDataSourceInsiderTransactionPointAcquiredSmall;
} }
} }
@@ -645,6 +709,7 @@ namespace PortfolioManager.ViewModels
{ {
get get
{ {
if (!showInsiderTransactions) return Empty();
return compositeDataSourceInsiderTransactionPointAcquiredMedium; return compositeDataSourceInsiderTransactionPointAcquiredMedium;
} }
} }
@@ -653,14 +718,12 @@ namespace PortfolioManager.ViewModels
{ {
get get
{ {
if (!showInsiderTransactions) return Empty();
return compositeDataSourceInsiderTransactionPointAcquiredLarge; return compositeDataSourceInsiderTransactionPointAcquiredLarge;
} }
} }
// ********************************************************************************************************************************************* // *********************************************************************************************************************************************
public void CreateCompositeDataSources() public void CreateCompositeDataSources()
{ {
if (null == prices || 0 == prices.Count) return; if (null == prices || 0 == prices.Count) return;

View File

@@ -8,6 +8,7 @@ using Avalonia.Threading;
using Axiom.Utils; using Axiom.Utils;
using MarketData.Cache; using MarketData.Cache;
using MarketData.DataAccess; using MarketData.DataAccess;
using PortfolioManager.Cache;
using PortfolioManager.Command; using PortfolioManager.Command;
namespace PortfolioManager.ViewModels namespace PortfolioManager.ViewModels
@@ -43,9 +44,10 @@ namespace PortfolioManager.ViewModels
} }
try { LocalPriceCache.GetInstance().Dispose(); } catch (Exception) {; } try { LocalPriceCache.GetInstance().Dispose(); } catch (Exception) {; }
try { GBPriceCache.GetInstance().Dispose(); } catch (Exception) {; } try { GBPriceCache.GetInstance().Dispose(); } catch (Exception) {; }
try { ImageCache.GetInstance().Dispose(); } catch (Exception) {; }
//try{PriceCache.GetInstance().Dispose();}catch(Exception){;} //try{PriceCache.GetInstance().Dispose();}catch(Exception){;}
// try{SymbolCache.GetInstance().Dispose();}catch(Exception){;} // try{SymbolCache.GetInstance().Dispose();}catch(Exception){;}
base.OnDispose(); base.OnDispose();
MDTrace.WriteLine(LogLevel.DEBUG, "[MainWindowViewModel:OnDispose] LEAVE"); MDTrace.WriteLine(LogLevel.DEBUG, "[MainWindowViewModel:OnDispose] LEAVE");
} }

View File

@@ -19,6 +19,7 @@
<local:DateValueConverter x:Key="DateFormat"/> <local:DateValueConverter x:Key="DateFormat"/>
<local:RMultipleValueConverter x:Key="RMultipleFormat"/> <local:RMultipleValueConverter x:Key="RMultipleFormat"/>
<local:BoolValueConverter x:Key="BoolFormat"/> <local:BoolValueConverter x:Key="BoolFormat"/>
<local:OSValueConverter x:Key="OSValueConverter"/>
</UserControl.Resources> </UserControl.Resources>
<Grid Background="LightGray"> <Grid Background="LightGray">
@@ -147,6 +148,24 @@
</mxc:CartesianSeries> </mxc:CartesianSeries>
</mxc:CartesianChart.Series> </mxc:CartesianChart.Series>
<mxc:CartesianChart.Series>
<mxc:CartesianSeries Name="TradeMarkersPointsGraph" DataAdapter="{Binding TradePoints.DataAdapter}" >
<mxc:CartesianPointSeriesView MarkerImage="{Binding Path=TradePointMarkers, Mode=OneWay}" ShowInCrosshair="False" MarkerSize="{Binding Path=MarkerSize, Converter={StaticResource OSValueConverter},ConverterParameter=20|.5}"/>
</mxc:CartesianSeries>
</mxc:CartesianChart.Series>
<mxc:CartesianChart.Series>
<mxc:CartesianSeries Name="ZeroPointMarkersPointsGraph" DataAdapter="{Binding ZeroPoint.DataAdapter}">
<mxc:CartesianPointSeriesView MarkerImage="{Binding Path=ZeroPointMarkers, Mode=OneWay}" ShowInCrosshair="False" MarkerSize="{Binding Path=MarkerSize, Converter={StaticResource OSValueConverter},ConverterParameter=20|.5}"/>
</mxc:CartesianSeries>
</mxc:CartesianChart.Series>
<mxc:CartesianChart.Series>
<mxc:CartesianSeries Name="StopLimitMarkersPointsGraph" DataAdapter="{Binding StopLimits.DataAdapter}">
<mxc:CartesianPointSeriesView MarkerImage="{Binding Path=StopLimitMarkers, Mode=OneWay}" ShowInCrosshair="False" MarkerSize="{Binding Path=MarkerSize, Converter={StaticResource OSValueConverter},ConverterParameter=20|.5}"/>
</mxc:CartesianSeries>
</mxc:CartesianChart.Series>
<mxc:CartesianChart.Series> <mxc:CartesianChart.Series>
<mxc:CartesianSeries Name="InsiderTransactionsPointMarkersPointsGraphDisposedSmall" DataAdapter="{Binding InsiderTransactionPointDisposedSmall.DataAdapter}" > <mxc:CartesianSeries Name="InsiderTransactionsPointMarkersPointsGraphDisposedSmall" DataAdapter="{Binding InsiderTransactionPointDisposedSmall.DataAdapter}" >
<mxc:CartesianPointSeriesView ShowInCrosshair="False" Color="red" MarkerSize="10"/> <mxc:CartesianPointSeriesView ShowInCrosshair="False" Color="red" MarkerSize="10"/>

View File

@@ -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.MomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\MG20180131.TXT
Type,PortfolioManager.ViewModels.CMMomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\CM20191031.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.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,RNMBY,SelectedWatchList,Valuations,SelectedDayCount,90,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 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