Implement Tooltips in the model views

This commit is contained in:
2025-06-11 19:15:45 -04:00
parent 7e173bcf23
commit b337170c39
7 changed files with 864 additions and 42 deletions

View File

@@ -487,11 +487,18 @@ namespace PortfolioManager.ViewModels
base.OnPropertyChanged("NonTradeableCash");
base.OnPropertyChanged("ModelExpectation");
base.OnPropertyChanged("ExpectationColor");
base.OnPropertyChanged("ExpectationDescription");
UpdateTooltipProperties();
});
return true;
}
private void UpdateTooltipProperties()
{
base.OnPropertyChanged("ExpectationDescription");
base.OnPropertyChanged("BetaDescription");
base.OnPropertyChanged("CompanyDescriptionSelectedPosition");
}
private void UpdatePositionPrices(bool change = true)
{
try
@@ -578,30 +585,53 @@ namespace PortfolioManager.ViewModels
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Exception:{0}", exception.ToString()));
}
}
// ************************************************** T O O L T I P S *************************************************
public String ExpectationDescription
{
get
{
if(null==modelStatistics) return "";
StringBuilder sb=new StringBuilder();
if (null == modelStatistics) return "";
StringBuilder sb = new StringBuilder();
sb.Append("Expectancy is (percentage of winning trades * average gain) / (percentage of losing trades * average loss).").Append("\n");
sb.Append("Total Trades : ").Append(modelStatistics.TotalTrades).Append("\n");
sb.Append("Winning Trades : ").Append(modelStatistics.WinningTrades).Append("\n");
sb.Append("Losing Trades : ").Append(modelStatistics.LosingTrades).Append("\n");
sb.Append("Winning Trades : ").Append(Utility.FormatNumber(modelStatistics.WinningTradesPercent,2)).Append("%").Append("\n");
sb.Append("Losing Trades : ").Append(Utility.FormatNumber(modelStatistics.LosingTradesPercent,2)).Append("%").Append("\n");
sb.Append("Average Winning Trade Gain : ").Append(Utility.FormatNumber(modelStatistics.AverageWinningTradePercentGain,2)).Append("%").Append("\n");
sb.Append("Average Losing Trade Loss : ").Append(Utility.FormatNumber(modelStatistics.AverageLosingTradePercentLoss,2)).Append("%").Append("\n");
sb.Append("Expectancy : ").Append(Utility.FormatNumber(modelStatistics.Expectancy,2)).Append("\n");
sb.Append("Winning Trades : ").Append(Utility.FormatNumber(modelStatistics.WinningTradesPercent, 2)).Append("%").Append("\n");
sb.Append("Losing Trades : ").Append(Utility.FormatNumber(modelStatistics.LosingTradesPercent, 2)).Append("%").Append("\n");
sb.Append("Average Winning Trade Gain : ").Append(Utility.FormatNumber(modelStatistics.AverageWinningTradePercentGain, 2)).Append("%").Append("\n");
sb.Append("Average Losing Trade Loss : ").Append(Utility.FormatNumber(modelStatistics.AverageLosingTradePercentLoss, 2)).Append("%").Append("\n");
sb.Append("Expectancy : ").Append(Utility.FormatNumber(modelStatistics.Expectancy, 2)).Append("\n");
sb.Append("\n");
sb.Append("Maintain a positive Expectancy and you're a winner.");
sb.Append("\n");
sb.Append("The calculations are based on closed positions.");
return sb.ToString();
}
}
}
public String BetaDescription
{
get
{
if (null == selectedPosition || null == selectedPosition.Symbol) return "No row selected.";
return "A beta of less than 1 means that the security is theoretically less volatile than the market. A beta of greater than 1 indicates that the security's price is theoretically more volatile than the market. For example, if a stock's beta is 1.2, it's theoretically 20% more volatile than the market.";
}
}
public String CompanyDescriptionSelectedPosition
{
get
{
if (null == selectedPosition || null == selectedPosition.Symbol) return "No row selected.";
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(selectedPosition.Symbol);
if (null == companyProfile || null == companyProfile.Description || "".Equals(companyProfile.Description)) return "No description found.";
StringBuilder sb = new StringBuilder();
sb.Append(companyProfile.Symbol).Append(" - ").Append(companyProfile.CompanyName).Append("\n");
sb.Append(companyProfile.Sector).Append("/").Append(companyProfile.Industry).Append("\n").Append(companyProfile.Description);
return sb.ToString();
}
}
}
}

View File

@@ -505,13 +505,28 @@ namespace PortfolioManager.ViewModels
base.OnPropertyChanged("NonTradeableCash");
base.OnPropertyChanged("ModelExpectation");
base.OnPropertyChanged("ExpectationColor");
base.OnPropertyChanged("ExpectationDescription");
base.OnPropertyChanged("ReloadEnabled");
base.OnPropertyChanged("TradeDate");
UpdateTooltipProperties();
});
return true;
}
private void UpdateTooltipProperties()
{
base.OnPropertyChanged("ExpectationDescription");
base.OnPropertyChanged("CompanyDescriptionSelectedPosition");
base.OnPropertyChanged("ToolTipPurchasePrice");
base.OnPropertyChanged("ToolTipCurrentPrice");
base.OnPropertyChanged("ToolTipCurrentPriceLow");
base.OnPropertyChanged("ToolTipInitialStop");
base.OnPropertyChanged("ToolTipTrailingStop");
base.OnPropertyChanged("ToolTipR");
base.OnPropertyChanged("ToolTipTotalRiskExposure");
base.OnPropertyChanged("ToolTipRMultiple");
base.OnPropertyChanged("ToolTipEdgeRatio");
}
private void RunPerformance()
{
if (null == sessionParams) return;
@@ -590,30 +605,208 @@ namespace PortfolioManager.ViewModels
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Exception:{0}", exception.ToString()));
}
}
// ************************************************** T O O L T I P S *************************************************
public String ExpectationDescription
{
get
{
if(null==modelStatistics) return "";
StringBuilder sb=new StringBuilder();
if (null == modelStatistics) return "";
StringBuilder sb = new StringBuilder();
sb.Append("Expectancy is (percentage of winning trades * average gain) / (percentage of losing trades * average loss).").Append("\n");
sb.Append("Total Trades : ").Append(modelStatistics.TotalTrades).Append("\n");
sb.Append("Winning Trades : ").Append(modelStatistics.WinningTrades).Append("\n");
sb.Append("Losing Trades : ").Append(modelStatistics.LosingTrades).Append("\n");
sb.Append("Winning Trades : ").Append(Utility.FormatNumber(modelStatistics.WinningTradesPercent,2)).Append("%").Append("\n");
sb.Append("Losing Trades : ").Append(Utility.FormatNumber(modelStatistics.LosingTradesPercent,2)).Append("%").Append("\n");
sb.Append("Average Winning Trade Gain : ").Append(Utility.FormatNumber(modelStatistics.AverageWinningTradePercentGain,2)).Append("%").Append("\n");
sb.Append("Average Losing Trade Loss : ").Append(Utility.FormatNumber(modelStatistics.AverageLosingTradePercentLoss,2)).Append("%").Append("\n");
sb.Append("Expectancy : ").Append(Utility.FormatNumber(modelStatistics.Expectancy,2)).Append("\n");
sb.Append("Winning Trades : ").Append(Utility.FormatNumber(modelStatistics.WinningTradesPercent, 2)).Append("%").Append("\n");
sb.Append("Losing Trades : ").Append(Utility.FormatNumber(modelStatistics.LosingTradesPercent, 2)).Append("%").Append("\n");
sb.Append("Average Winning Trade Gain : ").Append(Utility.FormatNumber(modelStatistics.AverageWinningTradePercentGain, 2)).Append("%").Append("\n");
sb.Append("Average Losing Trade Loss : ").Append(Utility.FormatNumber(modelStatistics.AverageLosingTradePercentLoss, 2)).Append("%").Append("\n");
sb.Append("Expectancy : ").Append(Utility.FormatNumber(modelStatistics.Expectancy, 2)).Append("\n");
sb.Append("\n");
sb.Append("Maintain a positive Expectancy and you're a winner.");
sb.Append("\n");
sb.Append("The calculations are based on closed positions.");
return sb.ToString();
}
}
}
public String CompanyDescriptionSelectedPosition
{
get
{
if (null == selectedPosition || null == selectedPosition.Symbol) return "No row selected.";
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(selectedPosition.Symbol);
if (null == companyProfile || null == companyProfile.Description || "".Equals(companyProfile.Description)) return "No description found.";
StringBuilder sb = new StringBuilder();
sb.Append(companyProfile.Symbol).Append(" - ").Append(companyProfile.CompanyName).Append("\n");
sb.Append(companyProfile.Sector).Append("/").Append(companyProfile.Industry).Append("\n").Append(companyProfile.Description);
return sb.ToString();
}
}
public String ToolTipPurchasePrice
{
get
{
if (null == selectedPosition) return "No row selected.";
StringBuilder sb = new StringBuilder();
sb.Append("[PurchasePrice]=([PurchasePrice])").Append("\n");
sb.Append(Utility.FormatCurrency(selectedPosition.PurchasePrice)).Append("=").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice));
return sb.ToString();
}
}
public String ToolTipCurrentPrice
{
get
{
if (null == selectedPosition) return "No row selected.";
StringBuilder sb = new StringBuilder();
sb.Append("[Price]=([CurrentPrice])").Append("\n");
sb.Append(Utility.FormatCurrency(selectedPosition.CurrentPrice)).Append("=").Append(Utility.FormatCurrency(selectedPosition.CurrentPrice));
return sb.ToString();
}
}
public String ToolTipCurrentPriceLow
{
get
{
if (null == selectedPosition) return "No row selected.";
StringBuilder sb = new StringBuilder();
sb.Append("[Low]=([CurrentLowPrice])").Append("\n");
sb.Append(Utility.FormatCurrency(selectedPosition.CurrentPriceLow)).Append("=").Append(Utility.FormatCurrency(selectedPosition.CurrentPriceLow));
return sb.ToString();
}
}
public String ToolTipInitialStop
{
get
{
if (null == selectedPosition) return "No row selected.";
StringBuilder sb = new StringBuilder();
sb.Append("[InitialStop]=([InitialStopLimit])").Append("\n");
sb.Append(Utility.FormatCurrency(selectedPosition.InitialStopLimit)).Append("=").Append(Utility.FormatCurrency(selectedPosition.InitialStopLimit));
return sb.ToString();
}
}
public String ToolTipTrailingStop
{
get
{
if (null == selectedPosition)return "No row selected.";
StringBuilder sb = new StringBuilder();
sb.Append("[TrailingStop]=([TrailingStopLimit])").Append("\n");
sb.Append(Utility.FormatCurrency(selectedPosition.TrailingStopLimit)).Append("=").Append(Utility.FormatCurrency(selectedPosition.TrailingStopLimit)).Append("\n");
List<MarketData.Generator.Model.StopLimit> stopLimits = GetHistoricalStopLimits();
if (selectedPosition.TrailingStopLimit > selectedPosition.PurchasePrice)
{
sb.Append("Riskless Gain").Append(" = ");
sb.Append(String.Format("(Trailing Stop - Purchase Price) * Shares"));
sb.Append("\n");
sb.Append(String.Format("{0}", Utility.FormatCurrency((selectedPosition.TrailingStopLimit - selectedPosition.PurchasePrice) * selectedPosition.Shares)));
sb.Append(String.Format("=({0}-{1})*{2}", Utility.FormatCurrency(selectedPosition.TrailingStopLimit), Utility.FormatCurrency(selectedPosition.PurchasePrice), Utility.FormatNumber(selectedPosition.Shares, 3)));
sb.Append("\n");
}
if (null != stopLimits && 0 != stopLimits.Count)
{
sb.Append("Stop History").Append("\n");
foreach (MarketData.Generator.Model.StopLimit stopLimit in stopLimits)
{
sb.Append(String.Format("Analysis Date:{0} New Stop:{1} Previous Stop:{2}", Utility.DateTimeToStringMMSDDSYYYY(stopLimit.AnalysisDate), Utility.FormatCurrency(stopLimit.NewStop), Utility.FormatCurrency(stopLimit.PreviousStop)));
sb.Append("\n");
}
}
return sb.ToString();
}
}
public String ToolTipR
{
get
{
if(null==selectedPosition)return "No row selected.";
StringBuilder sb=new StringBuilder();
sb.Append("Current R/Share. See RMultiple for initial risk assumptions.").Append("\n");
sb.Append("R/Share=TrailingStop>PurchasePrice?0.00:PurchasePrice-TrailingStop").Append("\n");
if(selectedPosition.TrailingStopLimit>selectedPosition.PurchasePrice)
{
sb.Append(Utility.FormatCurrency(0)).Append("=").Append(Utility.FormatCurrency(0));
}
else
{
sb.Append(Utility.FormatCurrency(selectedPosition.TrailingStopLimit>selectedPosition.PurchasePrice?0.00:selectedPosition.PurchasePrice-selectedPosition.TrailingStopLimit));
sb.Append("=").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice)).Append("-").Append(Utility.FormatCurrency(selectedPosition.TrailingStopLimit));
}
return sb.ToString();
}
}
public String ToolTipTotalRiskExposure
{
get
{
if(null==selectedPosition)return "No row selected.";
StringBuilder sb=new StringBuilder();
sb.Append("Current Risk. See RMultiple for initial risk assumptions.").Append("\n");
sb.Append("[TotalRiskExposure]=([R/Share]*[Shares])").Append("\n");
if(null==selectedPosition) return sb.ToString();
sb.Append(Utility.FormatCurrency(selectedPosition.TotalRiskExposure)).Append("=(").Append(Utility.FormatCurrency(selectedPosition.R)).Append("*").Append(Utility.FormatNumber(selectedPosition.Shares,0)).Append(")");
return sb.ToString();
}
}
public String ToolTipRMultiple
{
get
{
if(null==selectedPosition)return "No row selected.";
StringBuilder sb=new StringBuilder();
sb.Append("RMultiple is based on original position setup.").Append("\n");
sb.Append("Original Exposure=").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice*selectedPosition.Shares)).Append("\n");
sb.Append("Original R/Share=PurchasePrice-InitialStop").Append("\n");
sb.Append(Utility.FormatCurrency(selectedPosition.PurchasePrice-selectedPosition.InitialStopLimit)).Append("=").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice)).Append("-").Append(Utility.FormatCurrency(selectedPosition.InitialStopLimit)).Append("\n");
sb.Append("Original Risk=Original R/Share*Shares").Append("\n");
sb.Append(Utility.FormatCurrency((selectedPosition.PurchasePrice-selectedPosition.InitialStopLimit)*selectedPosition.Shares)).Append("=").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice-selectedPosition.InitialStopLimit)).Append("*").Append(selectedPosition.Shares).Append("\n");
sb.Append("RMultiple=(CurrentPrice-PurchasePrice)/(PurchasePrice-InitialStop)").Append("\n");
sb.Append(selectedPosition.RMultipleAsString).Append("=").Append("(").Append(Utility.FormatCurrency(selectedPosition.CurrentPrice)).Append("-").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice)).Append(")");
sb.Append("/").Append("(").Append(Utility.FormatCurrency(selectedPosition.PurchasePrice)).Append("-").Append(Utility.FormatCurrency(selectedPosition.InitialStopLimit)).Append(")");
return sb.ToString();
}
}
public String ToolTipEdgeRatio
{
get
{
if(null==selectedPosition)return "No row selected.";
StringBuilder sb=new StringBuilder();
sb.Append("[EdgeRatio]=AVG(SUM(MFE/ATR(14)))/AVG(SUM(MAE/ATR(14)))").Append("\n");
sb.Append("MFE=Maximum Favorable Excursion").Append("\n");
sb.Append("MAE=Maximum Adverse Excursion").Append("\n");
sb.Append("ATR=Average True Range. 14 days").Append("\n");
if(selectedPosition.EdgeRatio<1.00)
{
sb.Append(String.Format("You can expect a further {0} units more of adverse volatility.",Utility.FormatNumber(1.00-selectedPosition.EdgeRatio,2)));
}
else
{
sb.Append(String.Format("You can expect a further {0} units more of favorable volatility.",Utility.FormatNumber(selectedPosition.EdgeRatio-1.00,2)));
}
return sb.ToString();
}
}
// This getter returns MMTrend specific stop limits to show in the tooltips
public MarketData.Generator.Model.StopLimits GetHistoricalStopLimits()
{
if (null == sessionParams || null == selectedPosition) return null;
MarketData.Generator.Model.StopLimits stopLimits = new MarketData.Generator.Model.StopLimits((from MarketData.Generator.Model.StopLimit stopLimit in sessionParams.StopLimits where stopLimit.Symbol.Equals(selectedPosition.Symbol) select stopLimit).OrderByDescending(x => x.AnalysisDate).ToList());
return stopLimits;
}
}
}

View File

@@ -499,11 +499,16 @@ namespace PortfolioManager.ViewModels
base.OnPropertyChanged("ModelExpectation");
base.OnPropertyChanged("ExpectationColor");
base.OnPropertyChanged("ReloadEnabled");
base.OnPropertyChanged("ExpectationDescription");
});
return true;
}
private void UpdateTooltipProperties()
{
base.OnPropertyChanged("ExpectationDescription");
base.OnPropertyChanged("CompanyDescriptionSelectedPosition");
}
private void UpdatePositionPrices(bool change = true)
{
try
@@ -591,9 +596,9 @@ namespace PortfolioManager.ViewModels
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Exception:{0}", exception.ToString()));
}
}
// ************************************************** T O O L T I P S *************************************************
public String ExpectationDescription
{
get
@@ -615,6 +620,20 @@ namespace PortfolioManager.ViewModels
sb.Append("The calculations are based on closed positions.");
return sb.ToString();
}
}
}
public String CompanyDescriptionSelectedPosition
{
get
{
if (null == selectedPosition || null == selectedPosition.Symbol) return "No row selected.";
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(selectedPosition.Symbol);
if (null == companyProfile || null == companyProfile.Description || "".Equals(companyProfile.Description)) return "No description found.";
StringBuilder sb = new StringBuilder();
sb.Append(companyProfile.Symbol).Append(" - ").Append(companyProfile.CompanyName).Append("\n");
sb.Append(companyProfile.Sector).Append("/").Append(companyProfile.Industry).Append("\n").Append(companyProfile.Description);
return sb.ToString();
}
}
}
}