Files
Avalonia/PortfolioManager/ViewModels/BollingerBandViewModel.cs
2025-06-13 21:13:11 -04:00

524 lines
19 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using DynamicData;
using Eremex.AvaloniaUI.Charts;
using MarketData.DataAccess;
using MarketData.Generator;
using MarketData.MarketDataModel;
using MarketData.Numerical;
using MarketData.Utils;
using PortfolioManager.Cache;
using PortfolioManager.DataSeriesViewModels;
using PortfolioManager.Models;
using PortfolioManager.UIUtils;
namespace PortfolioManager.ViewModels
{
public partial class BollingerBandViewModel : WorkspaceViewModel
{
private bool isBusy = false;
private List<String> watchListNames;
private String selectedWatchList;
private List<Int32> dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList<int>();
private int selectedDayCount = 90;
private ObservableCollection<String> symbols = new ObservableCollection<String>();
private String selectedSymbol = default;
private bool showMarkers = false;
private InsiderTransactionSummaries insiderTransactionSummaries = null;
private Price zeroPrice = null;
private PortfolioTrades portfolioTrades;
private PortfolioTrades portfolioTradesLots;
private StopLimit stopLimit; // This is the stop limit that is looked up in the database and displayed (if there is one)
private StopLimits stopLimits; // These stop limits might be passed in with the SaveParams. (i.e.) MMTRend model passes in StopLimits. If these are passsed in then they are displayed instead of stopLimit.
private Prices prices = null;
private bool syncTradeToBand = true;
private String companyName = default;
private bool useLeastSquaresFit = true;
private CompositeDataSource compositeDataSourceZeroPoint = null;
private CompositeDataSource compositeDataSourceStopLimit = null;
private CompositeDataSource compositeDataSourceInsiderTransactionPointDisposedSmall = null;
private CompositeDataSource compositeDataSourceInsiderTransactionPointDisposedMedium = null;
private CompositeDataSource compositeDataSourceInsiderTransactionPointDisposedLarge = null;
private CompositeDataSource compositeDataSourceInsiderTransactionPointAcquiredSmall = null;
private CompositeDataSource compositeDataSourceInsiderTransactionPointAcquiredMedium = null;
private CompositeDataSource compositeDataSourceInsiderTransactionPointAcquiredLarge = null;
private CompositeDataSource compositeDataSourceK = null;
private CompositeDataSource compositeDataSourceKL1 = null;
private CompositeDataSource compositeDataSourceL = null;
private CompositeDataSource compositeDataSourceLP1 = null;
private CompositeDataSource compositeDataSourceHigh = null;
private CompositeDataSource compositeDataSourceLow = null;
private CompositeDataSource compositeDataSourceClose = null;
private CompositeDataSource compositeDataSourceSMAN = null;
private CompositeDataSource compositeDataSourceVolume = null;
private CompositeDataSource compositeDataSourceLeastSquares = null;
private CompositeDataSource compositeDataSourceTradePoints = null;
private BollingerBands bollingerBands;
public BollingerBandViewModel()
{
PropertyChanged += OnViewModelPropertyChanged;
DisplayName = "BollingerBand View";
Initialize();
}
private void Initialize()
{
Task workerTask = Task.Factory.StartNew(() =>
{
watchListNames = WatchListDA.GetWatchLists();
watchListNames.Insert(0, UIConstants.CONST_ALL);
selectedWatchList = watchListNames.Find(x => x.Equals("Valuations"));
symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList));
});
workerTask.ContinueWith((continuation) =>
{
base.OnPropertyChanged("Symbols");
base.OnPropertyChanged("WatchListNames");
base.OnPropertyChanged("SelectedWatchList");
});
}
public bool IsBusy
{
get
{
return isBusy;
}
set
{
isBusy = value;
base.OnPropertyChanged("IsBusy");
}
}
public override String Title
{
get
{
return DisplayName;
}
}
public override String DisplayName
{
get
{
return "BollingerBand Model";
// if (null == pathFileName) return "MGSHMomentum Model";
// String pureFileName = Utility.GetFileNameNoExtension(pathFileName);
// return "MGSHMomentum Model (" + pureFileName + ")";
}
}
public bool ShowMarkers
{
get
{
return showMarkers;
}
set
{
showMarkers = value;
base.OnPropertyChanged("ShowMarkers");
}
}
public ObservableCollection<String> Symbols
{
get
{
return symbols;
}
}
public String SelectedSymbol
{
get
{
return selectedSymbol;
}
set
{
selectedSymbol = value;
base.OnPropertyChanged("SelectedSymbol");
}
}
public int SelectedDayCount
{
get
{
return selectedDayCount;
}
set
{
selectedDayCount = value;
base.OnPropertyChanged("SelectedDayCount");
}
}
public List<int> DayCounts
{
get
{
return dayCounts;
}
}
// ****************************************************** P R O P E R T I E S ************************************************
public List<String> WatchListNames
{
get
{
return watchListNames;
}
}
public String SelectedWatchList
{
get
{
return selectedWatchList;
}
set
{
selectedWatchList = value;
base.OnPropertyChanged("SelectedWatchList");
}
}
// ******************************************************************* P E R S I S T E N C E ***************************************************
public override bool CanPersist()
{
return false;
}
public override SaveParameters GetSaveParameters()
{
return null;
}
public override void SetSaveParameters(SaveParameters saveParameters)
{
return;
}
// *************************************************************************************************************************************
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName.Equals("SelectedSymbol"))
{
InitializeDataSources();
insiderTransactionSummaries = null;
zeroPrice = null;
prices = null;
portfolioTrades = null;
portfolioTradesLots = null;
stopLimit = null;
}
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)
{
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)
{
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("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");
// 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");
});
}
else if (eventArgs.PropertyName.Equals("SelectedWatchList"))
{
IsBusy = true;
Task workerTask = Task.Factory.StartNew(() =>
{
if (selectedWatchList.Equals(Constants.CONST_ALL))
{
symbols.Clear();
symbols.AddRange(SymbolCache.GetInstance().GetSymbols());
}
else
{
symbols.Clear();
symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList));
}
});
workerTask.ContinueWith((continuation) =>
{
IsBusy = false;
base.OnPropertyChanged("Symbols");
});
}
}
// ************************************************* C O M P O S I T E P R O P E R T I E S ********************************************
public CompositeDataSource K
{
get
{
return compositeDataSourceK;
}
}
public CompositeDataSource KL1
{
get
{
return compositeDataSourceKL1;
}
}
public CompositeDataSource L
{
get
{
return compositeDataSourceL;
}
}
public CompositeDataSource LP1
{
get
{
return compositeDataSourceLP1;
}
}
public CompositeDataSource High
{
get
{
return compositeDataSourceHigh;
}
}
public CompositeDataSource Low
{
get
{
return compositeDataSourceLow;
}
}
public CompositeDataSource Close
{
get
{
return compositeDataSourceClose;
}
}
public CompositeDataSource SMAN
{
get
{
return compositeDataSourceSMAN;
}
}
public CompositeDataSource Volume
{
get
{
return compositeDataSourceVolume;
}
}
public CompositeDataSource LeastSquares
{
get
{
if(!useLeastSquaresFit||null==bollingerBands)return null;
return compositeDataSourceLeastSquares;
}
}
// *********************************************************************************************************************************************
public void CreateCompositeDataSources()
{
if (null == prices || 0 == prices.Count) return;
double minClose = (from Price price in prices select price.Close).Min();
// get the maximum date in the bollinger band series
DateTime maxBollingerDate = (from BollingerBandElement bollingerBandElement in bollingerBands select bollingerBandElement.Date).Max();
// ensure that the insider transactions are clipped to the bollingerband max date. There are some items in insider transaction summaries (options dated in the future) that will throw the graphic out of proportion
InsiderTransactionSummaries disposedSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed < 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate select insiderTransactionSummary).ToList());
InsiderTransactionSummaries acquiredSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed > 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate select insiderTransactionSummary).ToList());
BinCollection<InsiderTransactionSummary> disposedSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(disposedSummaries), 3);
BinCollection<InsiderTransactionSummary> acquiredSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(acquiredSummaries), 3);
compositeDataSourceZeroPoint = GainLossModel.Price(zeroPrice);
if (null != stopLimits)
{
compositeDataSourceStopLimit = StopLimitCompositeModel.CreateCompositeDataSource(stopLimits);
}
else if (null != stopLimit && null != zeroPrice)
{
compositeDataSourceStopLimit = GainLossModel.CreateCompositeDataSource(zeroPrice.Date, stopLimit.StopPrice);
}
compositeDataSourceInsiderTransactionPointDisposedSmall = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(disposedSummariesBin[2]), minClose);
compositeDataSourceInsiderTransactionPointDisposedMedium = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(disposedSummariesBin[1]), minClose);
compositeDataSourceInsiderTransactionPointDisposedLarge = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(disposedSummariesBin[0]), minClose);
compositeDataSourceInsiderTransactionPointAcquiredSmall = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(acquiredSummariesBin[0]), minClose);
compositeDataSourceInsiderTransactionPointAcquiredMedium = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(acquiredSummariesBin[1]), minClose);
compositeDataSourceInsiderTransactionPointAcquiredLarge = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(acquiredSummariesBin[2]), minClose);
compositeDataSourceK = BollingerBandModel.K(bollingerBands);
compositeDataSourceKL1 = BollingerBandModel.KL1(bollingerBands);
compositeDataSourceL = BollingerBandModel.L(bollingerBands);
compositeDataSourceLP1 = BollingerBandModel.LP1(bollingerBands);
compositeDataSourceHigh = BollingerBandModel.High(bollingerBands);
compositeDataSourceLow = BollingerBandModel.Low(bollingerBands);
compositeDataSourceClose = BollingerBandModel.Close(bollingerBands);
compositeDataSourceSMAN = BollingerBandModel.SMAN(bollingerBands);
compositeDataSourceVolume = BollingerBandModel.Volume(bollingerBands);
compositeDataSourceLeastSquares = BollingerBandModel.LeastSquares(bollingerBands);
compositeDataSourceTradePoints = PortfolioTradeModel.PortfolioTrades(portfolioTradesLots);
}
private void InitializeDataSources()
{
compositeDataSourceStopLimit = Empty();
compositeDataSourceZeroPoint = Empty();
compositeDataSourceInsiderTransactionPointDisposedSmall = Empty();
compositeDataSourceInsiderTransactionPointDisposedMedium = Empty();
compositeDataSourceInsiderTransactionPointDisposedLarge = Empty();
compositeDataSourceInsiderTransactionPointAcquiredSmall = Empty();
compositeDataSourceInsiderTransactionPointAcquiredMedium = Empty();
compositeDataSourceInsiderTransactionPointAcquiredLarge = Empty();
compositeDataSourceK = Empty();
compositeDataSourceKL1 = Empty();
compositeDataSourceL = Empty();
compositeDataSourceLP1 = Empty();
compositeDataSourceHigh = Empty();
compositeDataSourceLow = Empty();
compositeDataSourceClose = Empty();
compositeDataSourceSMAN = Empty();
compositeDataSourceVolume = Empty();
compositeDataSourceLeastSquares = Empty();
compositeDataSourceTradePoints = Empty();
}
private static CompositeDataSource Empty()
{
return new CompositeDataSource()
{
DataAdapter = new SortedDateTimeDataAdapter()
};
}
}
}