Commit Latest

This commit is contained in:
2025-06-25 21:05:14 -04:00
parent bf6d6ee6d7
commit 27881870f6
4 changed files with 115 additions and 93 deletions

View File

@@ -33,8 +33,30 @@ namespace PortfolioManager.Renderers
public class OffsetDictionary public class OffsetDictionary
{ {
enum OffsetType {VerticalOffset5PC, VerticalOffset3PC, VerticalOffset1PC,HorizontalOffset5PC,HorizontalOffset3PC,HorizontalOffset1PC}; public enum OffsetType
private Dictionary<int, double> offsetDictionary; {
VerticalOffset5PC, VerticalOffset3PC, VerticalOffset1PC, HorizontalOffset5PC, HorizontalOffset3PC, HorizontalOffset1PC,
MinBollingerDate, MaxBollingerDate, MinBollingerValue, MaxBollingerValue
};
private Dictionary<OffsetType, double> offsetDictionary = new Dictionary<OffsetType, double>();
public void Add(OffsetType offsetType, double value)
{
if (offsetDictionary.ContainsKey(offsetType))
{
offsetDictionary[offsetType] = value;
}
else
{
offsetDictionary.Add(offsetType, value);
}
}
public double Offset(OffsetType offsetType)
{
return offsetDictionary[offsetType];
}
} }
@@ -58,14 +80,7 @@ namespace PortfolioManager.Renderers
private BollingerBands bollingerBands; private BollingerBands bollingerBands;
private InsiderTransactionSummaries insiderTransactionSummaries = null; private InsiderTransactionSummaries insiderTransactionSummaries = null;
// double maxBollingerDate = 0.00; private OffsetDictionary offsets = new OffsetDictionary();
// double minBollingerDate = 0.00;
//double spread = 0.00;
double percentShiftHorz3PC = 0.00;
double percentShiftHorz1PC = 0.00;
double percentShiftVert3PC = 0.00;
double percentShiftVert1PC = 0.00;
double percentShiftVert5PC = 0.00;
public BollingerBandRenderer(AvaPlot plotter) public BollingerBandRenderer(AvaPlot plotter)
{ {
@@ -91,10 +106,10 @@ namespace PortfolioManager.Renderers
if (!ShowLegend) Plotter.Plot.HideLegend(); if (!ShowLegend) Plotter.Plot.HideLegend();
else Plotter.Plot.ShowLegend(); else Plotter.Plot.ShowLegend();
} }
else if (eventArgs.PropertyName.Equals("ShowInsiderTransactions")) // else if (eventArgs.PropertyName.Equals("ShowInsiderTransactions"))
{ // {
SetData(selectedSymbol, selectedDayCount); // SetData(selectedSymbol, selectedDayCount);
} // }
} }
public void Render() public void Render()
@@ -152,20 +167,7 @@ namespace PortfolioManager.Renderers
} }
} }
bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices);
CalculateOffsets();
double maxBollingerDate = bollingerBands.Max(x => x.Date).ToOADate();
double minBollingerDate = bollingerBands.Min(x => x.Date).ToOADate();
double maxBollingerValue = bollingerBands.Max(x => x.K);
double minBollingerValue = bollingerBands.Min(x => x.L);
double spreadHorz = (maxBollingerDate - minBollingerDate);
double spreadVert = (maxBollingerValue - minBollingerValue);
percentShiftHorz3PC = spreadHorz * .03;
percentShiftHorz1PC = spreadHorz * .01;
percentShiftVert3PC = spreadVert * .03;
percentShiftVert1PC = spreadVert * .01;
percentShiftVert5PC = spreadVert * .05;
GenerateBollingerBands(); GenerateBollingerBands();
GenerateZeroPoint(zeroPrice); GenerateZeroPoint(zeroPrice);
GenerateInsiderTransactions(); GenerateInsiderTransactions();
@@ -173,6 +175,28 @@ namespace PortfolioManager.Renderers
GenerateTradePoints(); GenerateTradePoints();
} }
private void CalculateOffsets()
{
double maxBollingerDate = bollingerBands.Max(x => x.Date).ToOADate();
double minBollingerDate = bollingerBands.Min(x => x.Date).ToOADate();
double maxBollingerValue = bollingerBands.Max(x => x.K);
double minBollingerValue = bollingerBands.Min(x => x.L);
offsets.Add(OffsetDictionary.OffsetType.MaxBollingerDate,maxBollingerDate);
offsets.Add(OffsetDictionary.OffsetType.MinBollingerDate,minBollingerDate);
offsets.Add(OffsetDictionary.OffsetType.MaxBollingerValue,maxBollingerValue);
offsets.Add(OffsetDictionary.OffsetType.MinBollingerValue,minBollingerValue);
double spreadHorz = (maxBollingerDate - minBollingerDate);
double spreadVert = (maxBollingerValue - minBollingerValue);
offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset1PC,spreadHorz * .01);
offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset3PC,spreadHorz * .03);
offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset5PC,spreadHorz * .05);
offsets.Add(OffsetDictionary.OffsetType.VerticalOffset1PC,spreadVert * .01);
offsets.Add(OffsetDictionary.OffsetType.VerticalOffset3PC,spreadVert *.03);
offsets.Add(OffsetDictionary.OffsetType.VerticalOffset5PC,spreadVert * .05);
}
/// <summary> /// <summary>
/// Generate the ZeroPoint marker and text /// Generate the ZeroPoint marker and text
/// </summary> /// </summary>
@@ -184,21 +208,21 @@ namespace PortfolioManager.Renderers
Coordinates coordinates = default; Coordinates coordinates = default;
Image image = default; Image image = default;
ZeroPoint = GainLossModel.Price(zeroPrice); ZeroPoint = GainLossModel.Price(zeroPrice);
(DateTime[] dates, double[] values) = ZeroPoint.ToXYData(); (DateTime[] dates, double[] values) = ZeroPoint.ToXYData(); // There is only a single value in this collection
Scatter scatter = Plotter.Plot.Add.Scatter(dates, values, ScottPlot.Color.FromSKColor(SKColors.Blue));
// Place the triangle marker
image = TextMarkerImageGenerator.ToSPImage(ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp)); image = TextMarkerImageGenerator.ToSPImage(ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp));
coordinates = new Coordinates(dates[0].ToOADate(), values[0]); coordinates = new Coordinates(dates[0].ToOADate(), values[0]);
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image, SizeFactor.Normal); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image, SizeFactor.Normal);
// Place the text marker
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.Append("Even "); sb.Append("Even ");
sb.Append(Utility.FormatCurrency(zeroPrice.Close)); sb.Append(Utility.FormatCurrency(zeroPrice.Close));
double parityOffsetPercent = (latestPrice.Close - zeroPrice.Close) / zeroPrice.Close; double parityOffsetPercent = (latestPrice.Close - zeroPrice.Close) / zeroPrice.Close;
sb.Append("(").Append(parityOffsetPercent < 0 ? "" : "+").Append(Utility.FormatPercent(parityOffsetPercent)).Append(")"); sb.Append("(").Append(parityOffsetPercent < 0 ? "" : "+").Append(Utility.FormatPercent(parityOffsetPercent)).Append(")");
image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 130, 24, FontFactor.FontSize); image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 130, 24, FontFactor.FontSize);
coordinates = new Coordinates(dates[0].ToOADate(), values[0] - 5.00); coordinates = new Coordinates(dates[0].ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC), values[0] - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
} }
@@ -245,8 +269,7 @@ namespace PortfolioManager.Renderers
sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")"); sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")");
} }
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize); Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize);
// Coordinates coordinates = new Coordinates(dates[0].ToOADate()-1, values[0] - 5.00); Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(), limit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(), limit.StopPrice - 5.00);
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
} }
} }
@@ -261,10 +284,8 @@ namespace PortfolioManager.Renderers
sb.Append(stopLimit.StopType).Append(" "); sb.Append(stopLimit.StopType).Append(" ");
sb.Append(Utility.FormatCurrency(stopLimit.StopPrice)); sb.Append(Utility.FormatCurrency(stopLimit.StopPrice));
sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")"); sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")");
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize); Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize);
// Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate()/(1+onePercent), stopLimit.StopPrice - 5.00); Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC), stopLimit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate() - percentShiftHorz3PC, stopLimit.StopPrice - 5.00);
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
} }
} }
@@ -295,7 +316,7 @@ namespace PortfolioManager.Renderers
sb.Append(Utility.FormatCurrency(portfolioTrade.Price)); sb.Append(Utility.FormatCurrency(portfolioTrade.Price));
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 150, 24, FontFactor.FontSize); Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 150, 24, FontFactor.FontSize);
Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(), portfolioTrade.Price - percentShiftVert5PC); Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(), portfolioTrade.Price - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
} }
} }
@@ -337,7 +358,7 @@ namespace PortfolioManager.Renderers
{ {
DateTime date = dates[index]; DateTime date = dates[index];
double value = values[index]; double value = values[index];
coordinates = new Coordinates(date.ToOADate(), value); coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Small); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Small);
} }
} }
@@ -348,7 +369,7 @@ namespace PortfolioManager.Renderers
{ {
DateTime date = dates[index]; DateTime date = dates[index];
double value = values[index]; double value = values[index];
coordinates = new Coordinates(date.ToOADate(), value); coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Normal); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Normal);
} }
} }
@@ -359,7 +380,7 @@ namespace PortfolioManager.Renderers
{ {
DateTime date = dates[index]; DateTime date = dates[index];
double value = values[index]; double value = values[index];
coordinates = new Coordinates(date.ToOADate(), value); coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Large); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Large);
} }
} }
@@ -371,7 +392,7 @@ namespace PortfolioManager.Renderers
{ {
DateTime date = dates[index]; DateTime date = dates[index];
double value = values[index]; double value = values[index];
coordinates = new Coordinates(date.ToOADate(), value); coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Small); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Small);
} }
} }
@@ -382,7 +403,7 @@ namespace PortfolioManager.Renderers
{ {
DateTime date = dates[index]; DateTime date = dates[index];
double value = values[index]; double value = values[index];
coordinates = new Coordinates(date.ToOADate(), value); coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Normal); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Normal);
} }
} }
@@ -393,7 +414,7 @@ namespace PortfolioManager.Renderers
{ {
DateTime date = dates[index]; DateTime date = dates[index];
double value = values[index]; double value = values[index];
coordinates = new Coordinates(date.ToOADate(), value); coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC));
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Large); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Large);
} }
} }

View File

@@ -20,53 +20,51 @@ namespace PortfolioManager.ViewModels
{ {
public partial class ScottPlotViewModel : PlotterWorkspaceViewModel public partial class ScottPlotViewModel : PlotterWorkspaceViewModel
{ {
// private AvaPlot plotter = default; private List<String> watchListNames;
private List<String> watchListNames; private String selectedWatchList;
private String selectedWatchList; private ObservableCollection<String> symbols = new ObservableCollection<String>();
private ObservableCollection<String> symbols = new ObservableCollection<String>(); private List<Int32> dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList<int>();
private List<Int32> dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList<int>(); private int selectedDayCount = 90;
private int selectedDayCount = 90; private bool isBusy = false;
private bool isBusy = false; private String selectedSymbol = default;
private String selectedSymbol = default; private String companyName = default;
private String companyName = default; private BollingerBandRenderer bollingerBandRenderer = default;
BollingerBandRenderer bollingerBandRenderer = default; private bool showInsiderTransactions = true;
public ScottPlotViewModel() public ScottPlotViewModel()
{ {
DisplayName = "Bollinger"; DisplayName = "Bollinger";
// Plotter = new AvaPlot(); OnPlotterLoadedEventHandler += PlotterLoadedEvent;
// base.OnPropertyChanged("Plotter"); PropertyChanged += OnViewModelPropertyChanged;
OnPlotterLoadedEventHandler += PlotterLoadedEvent; Initialize();
PropertyChanged += OnViewModelPropertyChanged; }
Initialize();
}
protected override void OnDispose() protected override void OnDispose()
{ {
MDTrace.WriteLine(LogLevel.DEBUG, $"Dispose BollingerBandViewModel"); MDTrace.WriteLine(LogLevel.DEBUG, $"Dispose BollingerBandViewModel");
base.OnDispose(); base.OnDispose();
} }
private void Initialize(bool executePropertyChanged = true) private void Initialize(bool executePropertyChanged = true)
{ {
Task workerTask = Task.Factory.StartNew(() => Task workerTask = Task.Factory.StartNew(() =>
{ {
MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::Initialize()"); MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::Initialize()");
watchListNames = WatchListDA.GetWatchLists(); watchListNames = WatchListDA.GetWatchLists();
watchListNames.Insert(0, UIConstants.CONST_ALL); watchListNames.Insert(0, UIConstants.CONST_ALL);
selectedWatchList = watchListNames.Find(x => x.Equals("Valuations")); selectedWatchList = watchListNames.Find(x => x.Equals("Valuations"));
symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList)); symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList));
}); });
workerTask.ContinueWith((continuation) => workerTask.ContinueWith((continuation) =>
{ {
if (executePropertyChanged) if (executePropertyChanged)
{ {
base.OnPropertyChanged("Symbols"); base.OnPropertyChanged("Symbols");
base.OnPropertyChanged("WatchListNames"); base.OnPropertyChanged("WatchListNames");
base.OnPropertyChanged("SelectedWatchList"); base.OnPropertyChanged("SelectedWatchList");
} }
}); });
} }
// ******************************************************************************************************************************************* // *******************************************************************************************************************************************
@@ -78,7 +76,7 @@ namespace PortfolioManager.ViewModels
public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e) public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e)
{ {
base.OnPropertyChanged("Plotter"); base.OnPropertyChanged("Plotter");
} }
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
{ {
@@ -88,6 +86,7 @@ namespace PortfolioManager.ViewModels
eventArgs.PropertyName.Equals("SelectedSymbol") || eventArgs.PropertyName.Equals("SelectedSymbol") ||
eventArgs.PropertyName.Equals("ShowRiskFree") || eventArgs.PropertyName.Equals("ShowRiskFree") ||
eventArgs.PropertyName.Equals("LeastSquaresFit") || eventArgs.PropertyName.Equals("LeastSquaresFit") ||
eventArgs.PropertyName.Equals("CheckBoxShowInsiderTransactions") ||
eventArgs.PropertyName.Equals("SelectedDayCount")) eventArgs.PropertyName.Equals("SelectedDayCount"))
&& !String.IsNullOrEmpty(selectedSymbol) && !String.IsNullOrEmpty(selectedSymbol)
&& default != Plotter) && default != Plotter)
@@ -97,6 +96,7 @@ namespace PortfolioManager.ViewModels
{ {
companyName = PricingDA.GetNameForSymbol(selectedSymbol); companyName = PricingDA.GetNameForSymbol(selectedSymbol);
bollingerBandRenderer = new BollingerBandRenderer(Plotter); bollingerBandRenderer = new BollingerBandRenderer(Plotter);
bollingerBandRenderer.ShowInsiderTransactions=showInsiderTransactions;
bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount);
bollingerBandRenderer.Render(); bollingerBandRenderer.Render();
}); });
@@ -276,12 +276,12 @@ namespace PortfolioManager.ViewModels
{ {
get get
{ {
if (null == bollingerBandRenderer) return false; return showInsiderTransactions;
return bollingerBandRenderer.ShowInsiderTransactions;
} }
set set
{ {
bollingerBandRenderer.ShowInsiderTransactions = value; showInsiderTransactions = value;
base.OnPropertyChanged("CheckBoxShowInsiderTransactions");
} }
} }

View File

@@ -22,7 +22,7 @@ public partial class ScottPlotView : UserControl
{ {
ScottPlotView view = (sender as ScottPlotView); ScottPlotView view = (sender as ScottPlotView);
PlotterWorkspaceViewModel viewModel = (DataContext as PlotterWorkspaceViewModel); PlotterWorkspaceViewModel viewModel = (DataContext as PlotterWorkspaceViewModel);
if (default == viewModel.Plotter) if (null!=viewModel && default == viewModel.Plotter)
{ {
viewModel.Plotter = new AvaPlot(); viewModel.Plotter = new AvaPlot();
viewModel.OnPlotterLoaded(viewModel.Plotter); viewModel.OnPlotterLoaded(viewModel.Plotter);

View File

@@ -0,0 +1 @@
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,KEP,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True