More changes to support BollingerBands.

This commit is contained in:
2025-06-15 13:56:48 -04:00
parent 9bbfc9831f
commit 9b1135b5ec
18 changed files with 631 additions and 221 deletions

View File

@@ -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<String, String>("Type",GetType().Namespace+"."+GetType().Name));
saveParams.Add(new KeyValuePair<String, String>("SelectedSymbol", selectedSymbol));
saveParams.Add(new KeyValuePair<String, String>("SelectedWatchList", selectedWatchList));
saveParams.Add(new KeyValuePair<String, String>("SelectedDayCount", selectedDayCount.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>("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>("ShowInsiderTransactions", showInsiderTransactions.ToString()));
if(null!=stopLimits && 0!=stopLimits.Count)
{
saveParams.Add(new KeyValuePair<String,String>("StopHistoryCount",stopLimits.Count.ToString()));
for(int index=0;index<stopLimits.Count;index++)
{
String strItemKey=String.Format("StopHistory_{0}",index);
StopLimit stopLimit=stopLimits[index];
NVPCollection nvpCollection=stopLimit.ToNVPCollection();
String strStopHistoryItem=nvpCollection.ToString();
saveParams.Add(new KeyValuePair<String,String>(strItemKey,strStopHistoryItem));
}
}
return saveParams;
}
public override void SetSaveParameters(SaveParameters saveParameters)
{
return;
try
{
Referer=saveParameters.Referer;
selectedSymbol = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedSymbol") select item).FirstOrDefault().Value;
selectedWatchList = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedWatchList") select item).FirstOrDefault().Value;
base.OnPropertyChanged("SelectedWatchList");
selectedDayCount = Int32.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedDayCount") select item).FirstOrDefault().Value);
try
{
if(saveParameters.ContainsKey("SyncTradeToBand"))syncTradeToBand=Boolean.Parse((from KeyValuePair<String,String> 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<String, String> 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<String,String> item in saveParameters where item.Key.Equals("IsLegendVisible") select item).FirstOrDefault().Value);
// }
// catch (Exception){;}
try
{
if(saveParameters.ContainsKey("UseLeastSquaresFit"))useLeastSquaresFit=Boolean.Parse((from KeyValuePair<String,String> item in saveParameters where item.Key.Equals("UseLeastSquaresFit") select item).FirstOrDefault().Value);
}
catch (Exception){;}
try
{
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){;}
// try{showRiskFree = Boolean.Parse((from KeyValuePair<String, String> 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<PortfolioTrade> 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<PortfolioTrade> 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();