Fix views and view models.

This commit is contained in:
2025-02-14 19:02:17 -05:00
parent 786f5f67df
commit 9f836a09f5
5 changed files with 1171 additions and 57 deletions

View File

@@ -0,0 +1,951 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using MarketData;
using MarketData.Numerical;
using MarketData.Utils;
using MarketData.MarketDataModel;
using MarketData.Generator;
using MarketData.Generator.GainLoss;
using MarketData.DataAccess;
using TradeBlotter.DataAccess;
using TradeBlotter.Command;
using TradeBlotter.Model;
using TradeBlotter.Cache;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Forms;
using TradeBlotter.UIUtils;
using TradeBlotter.Extensions;
using System.Runtime.InteropServices.WindowsRuntime;
// Author:Sean Kessler
// This view model is intended to be called by trading models as it will only displayed the data that is provided via the SaveParams and is not editable.
// Although this view accepts PortfolioTrades (a collection of PortfolioTrade) THAT collection should only contain a single position
namespace TradeBlotter.ViewModels
{
public class BollingerBandPositionViewModel : WorkspaceViewModel
{
private static readonly String BAND_MESSAGE="Generating Bollinger Band...";
private static readonly String DATA_MESSAGE="Loading Pricing Data...";
private static readonly int MIN_DAYS = 40; // This is the minimum nunber of pricing days for bollinger bands to work
private BollingerBands bollingerBands;
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 String symbol;
private String companyName;
private Prices prices = null;
private RelayCommand refreshCommand;
private List<Int32> dayCounts;
private PortfolioTrades portfolioTrades;
private InsiderTransactionSummaries insiderTransactionSummaries=null;
private bool showTradeLabels = true;
private bool showInsiderTransactions=true;
private bool busyIndicator = false;
private Price zeroPrice = null;
private bool isLegendVisible = false;
private bool useLeastSquaresFit=true;
private String busyContent = BAND_MESSAGE;
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;
public BollingerBandPositionViewModel()
{
base.DisplayName = "Bollinger Band Position";
dayCounts = new List<Int32>();
dayCounts.Add(60);
dayCounts.Add(90);
dayCounts.Add(180);
dayCounts.Add(360);
dayCounts.Add(720);
dayCounts.Add(1440);
dayCounts.Add(3600);
PropertyChanged += OnBollingerBandPositionViewModelPropertyChanged;
}
private PortfolioTrade PortfolioTrade
{
get
{
return portfolioTrades.Take(1).FirstOrDefault();
}
}
private int GetNearestDayCount(int desiredDayCount)
{
for(int index=0;index<dayCounts.Count;index++)
{
if(dayCounts[index]>=desiredDayCount)
{
return dayCounts[index];
}
}
return dayCounts[dayCounts.Count-1];
}
private Price GetZeroPrice()
{
if(null==PortfolioTrade)return null;
PortfolioTrades lots = CreateBuySellLots(PortfolioTrade);
DateTime latestPricingDate=Utility.Epoch;
Price zeroPrice = default;
if(PortfolioTrade.IsOpen)
{
PortfolioTrades openLots = new PortfolioTrades();
openLots.Add(lots.Where(x => x.IsOpen==true).First());
latestPricingDate=PricingDA.GetLatestDate(SelectedSymbol);
Price latestPrice = PricingDA.GetPrice(SelectedSymbol, latestPricingDate);
zeroPrice=ParityGenerator.GenerateGainLossValue(openLots, latestPrice);
}
return zeroPrice;
}
// SERIALIZE TO PARAMETERS
public override SaveParameters GetSaveParameters()
{
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>("ShowTradeLabels", showTradeLabels.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)
{
SaveParameters stopLimitSaveParams = StopLimitsExtensions.FromStopLimits(stopLimits);
saveParams.AddRange(stopLimitSaveParams);
}
if(null!=portfolioTrades && 0!=portfolioTrades.Count)
{
SaveParameters portfolioTradesSaveParams = PortfolioTradesExtensions.FromPortfolioTrades(portfolioTrades);
saveParams.AddRange(portfolioTradesSaveParams);
}
return saveParams;
}
// DESERIALZIE FROM PARAMETERS
public override void SetSaveParameters(SaveParameters saveParameters)
{
try
{
Referer=saveParameters.Referer;
symbol = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedSymbol") select item).FirstOrDefault().Value;
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 exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
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 exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
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 exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
try
{
if(saveParameters.ContainsKey("StopHistoryCount"))
{
stopLimits = StopLimitsExtensions.FromSaveParams(saveParameters);
if(stopLimits.Count>0)
{
stopLimit = stopLimits[stopLimits.Count-1]; // This is the latest stop.
}
}
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
try
{
if(saveParameters.ContainsKey("PortfolioTradesCount"))
{
portfolioTrades = PortfolioTradesExtensions.FromSaveParams(saveParameters);
portfolioTrades = new PortfolioTrades(portfolioTrades.Take(1).ToList()); // This view works on a single position provided by a model
}
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
base.OnPropertyChanged("SelectedSymbol");
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
}
public override bool CanPersist()
{
return true;
}
public bool BusyIndicator
{
get { return busyIndicator; }
set
{
busyIndicator = value;
base.OnPropertyChanged("BusyIndicator");
}
}
public String BusyContent
{
get { return busyContent; }
set
{
busyContent = value;
base.OnPropertyChanged("BusyContent");
}
}
private void OnBollingerBandPositionViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName.Equals("SelectedSymbol")||
eventArgs.PropertyName.Equals("ShowTradeLabels")||
eventArgs.PropertyName.Equals("SelectedSymbol")||
eventArgs.PropertyName.Equals("LeastSquaresFit")&&
null!=SelectedSymbol)
{
BusyIndicator=true;
InitializeDataSources();
Task workerTask=Task.Factory.StartNew(()=>
{
base.DisplayName="Position("+SelectedSymbol+")";
base.OnPropertyChanged("DisplayName");
BusyContent=DATA_MESSAGE;
// DEBUG
// if (null != portfolioTrades && 0 != portfolioTrades.Count)
if(null!=PortfolioTrade)
{
DateGenerator dateGenerator = new DateGenerator();
DateTime latestMarketDate = PricingDA.GetLatestDate(SelectedSymbol);
DateTime earliestDate = PortfolioTrade.TradeDate;
earliestDate = dateGenerator.FindPastBusinessDay(earliestDate, 30); // expand to the left a little bit
DateTime latestDate = PortfolioTrade.SellDate;
if(Utility.IsEpoch(latestDate))latestDate=latestMarketDate;
DateTime expandDate = dateGenerator.FindForwardBusinessDay(latestDate, 10); // try to expand to the right a little bit
if(expandDate < latestMarketDate)
{
latestDate=expandDate;
}
int marketDaysBetween = dateGenerator.TradingDaysBetween(earliestDate, latestDate);
int daysRequired = MIN_DAYS - marketDaysBetween;
if(marketDaysBetween < MIN_DAYS)
{
if(latestDate < latestMarketDate) // expand to the right
{
int daysBetween = dateGenerator.TradingDaysBetween(latestDate, latestMarketDate);
if(daysBetween>daysRequired) // we can expand to the right
{
latestDate = dateGenerator.FindForwardBusinessDay(latestDate, daysRequired);
}
else // we can't expand further to the right we need to expznd to the left
{
earliestDate = dateGenerator.FindPastBusinessDay(earliestDate, daysRequired);
}
}
else // expand to the left
{
earliestDate = dateGenerator.FindPastBusinessDay(earliestDate, daysRequired);
}
}
int nearestDayCount = GetNearestDayCount(marketDaysBetween);
prices = PricingDA.GetPrices(SelectedSymbol, latestDate, earliestDate);
DateTime earliestInsiderTransactionDate=dateGenerator.GenerateFutureBusinessDate(prices[prices.Count-1].Date,30);
insiderTransactionSummaries=InsiderTransactionDA.GetInsiderTransactionSummaries(SelectedSymbol,earliestInsiderTransactionDate);
if(PortfolioTrade.IsClosed)
{
insiderTransactionSummaries=new InsiderTransactionSummaries(insiderTransactionSummaries.Where(x => x.TransactionDate<=PortfolioTrade.SellDate).ToList());
}
zeroPrice=GetZeroPrice();
}
companyName = PricingDA.GetNameForSymbol(SelectedSymbol);
BusyContent=BAND_MESSAGE;
bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices);
CreateCompositeDataSources();
});
workerTask.ContinueWith((continuation)=>
{
BusyIndicator = 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("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");
});
}
}
// *************************************************************************** C U S T O M B E H A V I O R **************************************************
public Boolean LeastSquaresFit
{
get
{
return useLeastSquaresFit;
}
set
{
useLeastSquaresFit = value;
base.OnPropertyChanged("LeastSquaresFit");
}
}
public Boolean CheckBoxLegendVisible
{
get
{
return isLegendVisible;
}
set
{
isLegendVisible = value;
base.OnPropertyChanged("CheckBoxLegendVisible");
base.OnPropertyChanged("LegendVisible");
}
}
public String LegendVisible
{
get
{
if (isLegendVisible) return "true";
return "false";
}
set
{
isLegendVisible = Boolean.Parse(value);
base.OnPropertyChanged("LegendVisible");
}
}
public bool ShowTradeLabels
{
get
{
return showTradeLabels;
}
set
{
showTradeLabels = value;
base.OnPropertyChanged("ShowTradeLabels");
}
}
// ************************************************************ C o m p o s i t e D a t a S o u r c e s *************************************************
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);
if(null!=zeroPrice)
{
compositeDataSourceZeroPoint= GainLossModel.Price(zeroPrice);
}
// if(null!=PortfolioTrade && PortfolioTrade.IsOpen && null!=zeroPrice)
// {
// compositeDataSourceGainLoss=GainLossModel.CreateCompositeDataSource(zeroPrice.Date,stopLimit.StopPrice);
// compositeDataSourceGainLoss=GainLossModel.CreateCompositeDataSource(zeroPrice.Date,zeroPrice.Close);
// }
// else
// {
compositeDataSourceStopLimit=StopLimitCompositeModel.CreateCompositeDataSource(stopLimits);
// }
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(CreateBuySellLots(PortfolioTrade));
}
public void InitializeDataSources()
{
if(compositeDataSourceStopLimit!=null)compositeDataSourceStopLimit.Clear();
if(compositeDataSourceZeroPoint!=null)compositeDataSourceZeroPoint.Clear();
if(compositeDataSourceInsiderTransactionPointDisposedSmall!=null)compositeDataSourceInsiderTransactionPointDisposedSmall.Clear();
if(compositeDataSourceInsiderTransactionPointDisposedMedium!=null)compositeDataSourceInsiderTransactionPointDisposedMedium.Clear();
if(compositeDataSourceInsiderTransactionPointDisposedLarge!=null)compositeDataSourceInsiderTransactionPointDisposedLarge.Clear();
if(compositeDataSourceInsiderTransactionPointAcquiredSmall!=null)compositeDataSourceInsiderTransactionPointAcquiredSmall.Clear();
if(compositeDataSourceInsiderTransactionPointAcquiredMedium!=null)compositeDataSourceInsiderTransactionPointAcquiredMedium.Clear();
if(compositeDataSourceInsiderTransactionPointAcquiredLarge!=null)compositeDataSourceInsiderTransactionPointAcquiredLarge.Clear();
if(compositeDataSourceK!=null)compositeDataSourceK.Clear();
if(compositeDataSourceKL1!=null)compositeDataSourceKL1.Clear();
if(compositeDataSourceL!=null)compositeDataSourceL.Clear();
if(compositeDataSourceLP1!=null)compositeDataSourceLP1.Clear();
if(compositeDataSourceHigh!=null)compositeDataSourceHigh.Clear();
if(compositeDataSourceLow!=null)compositeDataSourceLow.Clear();
if(compositeDataSourceClose!=null)compositeDataSourceClose.Clear();
if(compositeDataSourceSMAN!=null)compositeDataSourceSMAN.Clear();
if(compositeDataSourceVolume!=null)compositeDataSourceVolume.Clear();
if(compositeDataSourceLeastSquares!=null)compositeDataSourceLeastSquares.Clear();
if (compositeDataSourceTradePoints != null) compositeDataSourceTradePoints.Clear();
}
public CompositeDataSource ZeroPoint
{
get
{
return compositeDataSourceZeroPoint;
}
}
public CompositeDataSource StopLimit
{
get
{
return compositeDataSourceStopLimit;
}
}
// **************************************************************** I N S I D E R T R A N S A C T I O N S *************************************************
public CompositeDataSource InsiderTransactionPointDisposedSmall
{
get
{
if (!showInsiderTransactions || null == insiderTransactionSummaries || null==prices) return null;
return compositeDataSourceInsiderTransactionPointDisposedSmall;
}
}
public CenteredTextMarker[] InsiderTransactionPointMarkersDisposedSmall
{
get
{
return null; // not displaying any text markers for disposed shares yet
}
}
public CompositeDataSource InsiderTransactionPointDisposedMedium
{
get
{
if (!showInsiderTransactions || null == insiderTransactionSummaries || null==prices) return null;
return compositeDataSourceInsiderTransactionPointDisposedMedium;
}
}
public CenteredTextMarker[] InsiderTransactionPointMarkersDisposedMedium
{
get
{
return null; // not displaying any text markers for disposed shares yet
}
}
public CompositeDataSource InsiderTransactionPointDisposedLarge
{
get
{
if (!showInsiderTransactions || null == insiderTransactionSummaries || null==prices) return null;
return compositeDataSourceInsiderTransactionPointDisposedLarge;
}
}
public CenteredTextMarker[] InsiderTransactionPointMarkersDisposedLarge
{
get
{
return null; // not displaying any text markers for disposed shares yet
}
}
// *****************************************************************************************************************************************************
public CompositeDataSource InsiderTransactionPointAcquiredSmall
{
get
{
if (!showInsiderTransactions || null == insiderTransactionSummaries || null==prices) return null;
return compositeDataSourceInsiderTransactionPointAcquiredSmall;
}
}
public CenteredTextMarker[] InsiderTransactionPointMarkersAcquiredSmall
{
get
{
return null; // not displaying any text markers for acquired shares yet.
}
}
public CompositeDataSource InsiderTransactionPointAcquiredMedium
{
get
{
if (!showInsiderTransactions || null == insiderTransactionSummaries || null==prices) return null;
return compositeDataSourceInsiderTransactionPointAcquiredMedium;
}
}
public CenteredTextMarker[] InsiderTransactionPointMarkersAcquiredMedium
{
get
{
return null; // not displaying any text markers for acquired shares yet.
}
}
public CompositeDataSource InsiderTransactionPointAcquiredLarge
{
get
{
if (!showInsiderTransactions || null == insiderTransactionSummaries || null==prices) return null;
return compositeDataSourceInsiderTransactionPointAcquiredLarge;
}
}
public CenteredTextMarker[] InsiderTransactionPointMarkersAcquiredLarge
{
get
{
return null; // not displaying any text markers for acquired shares yet.
}
}
// *********************************************************************************************************************************************************************
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;
}
}
// ********************************************************************* L E A S T S Q U A R E S *************************************************************
public CompositeDataSource LeastSquares
{
get
{
if(!useLeastSquaresFit||null==bollingerBands)return null;
return compositeDataSourceLeastSquares;
}
}
// ***************************************************************************************************************************************************************
public CompositeDataSource TradePoints
{
get
{
return compositeDataSourceTradePoints;
}
}
// ***************************************************************************** M A R K E R S *************************************************************
// BUY/SELL MARKERS
public CenteredTextMarker[] Markers
{
get
{
if(null == PortfolioTrade || !showTradeLabels)return null;
PortfolioTrades lots = CreateBuySellLots(PortfolioTrade);
TextMarkerOffsets textMarkerOffsets = new TextMarkerOffsets(35, 18);
List<CenteredTextMarker> centeredTextMarkers = new List<CenteredTextMarker>();
for (int index = 0; index < lots.Count; index++)
{
CenteredTextMarker centerTextMarker = new CenteredTextMarker();
PortfolioTrade portfolioTrade = lots[index];
StringBuilder sb = new StringBuilder();
sb.Append(portfolioTrade.BuySell.Equals("B") ? "Buy " : "Sell ");
sb.Append(Utility.FormatNumber(portfolioTrade.Shares));
sb.Append("@");
sb.Append(Utility.FormatCurrency(portfolioTrade.Price));
centerTextMarker.Text = sb.ToString();
centerTextMarker.HorizontalShift = "0";
centerTextMarker.VerticalShift = textMarkerOffsets.GetOffset(portfolioTrade).ToString();
centeredTextMarkers.Add(centerTextMarker);
}
return centeredTextMarkers.ToArray();
}
}
// ZERO POINT MARKERS
public CenteredTextMarker[] ZeroPointMarkers
{
get
{
if (null == zeroPrice || !showTradeLabels) return null;
List<CenteredTextMarker> centeredTextMarkers = new List<CenteredTextMarker>();
CenteredTextMarker centerTextMarker = new CenteredTextMarker();
StringBuilder sb = new StringBuilder();
sb.Append("Even ");
sb.Append(Utility.FormatCurrency(zeroPrice.Close));
Price latestPrice=prices[0];
double parityOffsetPercent=(latestPrice.Close-zeroPrice.Close)/zeroPrice.Close;
sb.Append("(").Append(parityOffsetPercent<0?"":"+").Append(Utility.FormatPercent(parityOffsetPercent)).Append(")");
centerTextMarker.Text = sb.ToString();
centeredTextMarkers.Add(centerTextMarker);
centerTextMarker.VerticalShift="40";
centerTextMarker.HorizontalShift="32";
return centeredTextMarkers.ToArray();
}
}
// STOP LIMIT MARKERS
public CenteredTextMarker[] StopLimitMarkers
{
get
{
List<CenteredTextMarker> centeredTextMarkers=new List<CenteredTextMarker>();
if(!showTradeLabels) return null;
if(null!=stopLimits)
{
for(int index=0;index<stopLimits.Count;index++)
{
StopLimit limit=stopLimits[index];
CenteredTextMarker centerTextMarker=new CenteredTextMarker();
StringBuilder sb=new StringBuilder();
sb.Append(limit.StopType).Append(" ");
sb.Append(Utility.FormatCurrency(limit.StopPrice));
if(index==stopLimits.Count-1)
{
Price latestPrice=prices[0]; // always use the most recent price when calculating the spread (in percent) from the active stop limit.
double percentOffsetFromLow=((latestPrice.Low-limit.StopPrice)/limit.StopPrice);
sb.Append(" (").Append(percentOffsetFromLow>0?"+":"").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")");
}
centerTextMarker.Text=sb.ToString();
if(0==index&&stopLimits.Count>1) centerTextMarker.VerticalShift="25";
else centerTextMarker.VerticalShift="40";
centerTextMarker.HorizontalShift="32";
centeredTextMarkers.Add(centerTextMarker);
}
}
else
{
if(null==zeroPrice)return null;
if(null==stopLimit||null==zeroPrice||!showTradeLabels) return null;
CenteredTextMarker centerTextMarker=new CenteredTextMarker();
Price latestPrice=prices[0];
double percentOffsetFromLow=((latestPrice.Low-stopLimit.StopPrice)/stopLimit.StopPrice);
StringBuilder sb=new StringBuilder();
sb.Append(stopLimit.StopType).Append(" ");
sb.Append(Utility.FormatCurrency(stopLimit.StopPrice));
sb.Append(" (").Append(percentOffsetFromLow>0?"+":"").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")");
centerTextMarker.Text=sb.ToString();
centeredTextMarkers.Add(centerTextMarker);
centerTextMarker.VerticalShift="40";
centerTextMarker.HorizontalShift="32";
}
return centeredTextMarkers.ToArray();
}
}
// ************************************************************************************************************************************************************************
// ************************************************************************************************************************************************************************
// ************************************************************************************************************************************************************************
// The approach here is to trick the marker and trade composites to line up a BUY and SELL position.
// The markers and trades are looking at TradeDate and Price so for the SELL logic we need to put the SellDate into TradeDate and the SellPrice into Price
private PortfolioTrades CreateBuySellLots(PortfolioTrade portfolioTrade)
{
PortfolioTrades portfolioTrades = new PortfolioTrades();
if(null == portfolioTrade)return portfolioTrades;
NVPCollection clonedTrade = portfolioTrade.ToNVPCollection();
PortfolioTrade buyTrade = PortfolioTrade.FromNVPCollection(clonedTrade);
PortfolioTrade sellTrade = PortfolioTrade.FromNVPCollection(clonedTrade);
// BUY position
buyTrade.SellDate=Utility.Epoch;
buyTrade.BuySell="B";
buyTrade.Status="OPEN";
portfolioTrades.Add(buyTrade);
// SELL position
if(Utility.IsEpoch(portfolioTrade.SellDate))return portfolioTrades;
sellTrade.BuySell="S";
sellTrade.Status="CLOSE";
sellTrade.TradeDate=portfolioTrade.SellDate;
sellTrade.Price=sellTrade.SellPrice;
portfolioTrades.Add(sellTrade);
return portfolioTrades;
}
// ************************************************************************************************************************************************************************
// ************************************************************************************************************************************************************************
// ************************************************************************************************************************************************************************
public override String Title
{
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();
}
}
public Boolean Visibility
{
get { return false; }
}
public List<Int32> DayCounts
{
get{return dayCounts;}
}
public String SelectedSymbol
{
get
{
return symbol;
}
set
{
if (value == symbol || String.IsNullOrEmpty(value)) return;
symbol = value;
base.OnPropertyChanged("SelectedSymbol");
}
}
private void Refresh()
{
base.OnPropertyChanged("SelectedSymbol");
}
private bool CanRefresh
{
get
{
return true;
}
}
public ICommand RefreshCommand
{
get
{
if (refreshCommand == null)
{
refreshCommand = new RelayCommand(param => this.Refresh(),param=>this.CanRefresh);
}
return refreshCommand;
}
}
public Boolean CheckBoxShowInsiderTransactions
{
get
{
return showInsiderTransactions;
}
set
{
showInsiderTransactions = value;
base.OnPropertyChanged("CheckBoxShowInsiderTransactions");
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("LeastSquares");
}
}
}
}

View File

@@ -70,13 +70,15 @@ namespace TradeBlotter.ViewModels
private String selectedParameter=null;
private MGSHPositionModelCollection positions=null;
private MGSHPositionModel selectedPosition=null;
private RelayCommand loadFileCommand;
private RelayCommand reloadCommand;
private RelayCommand monitorCommand;
private ObservableCollection<String> monitorIntervals;
private String selectedMonitorInterval;
private bool monitorRunning=false;
private DispatcherTimer dispatcherTimer = new DispatcherTimer();
private RelayCommand loadFileCommand;
private RelayCommand reloadCommand;
private RelayCommand monitorCommand;
private RelayCommand stochasticsCommandPosition;
private RelayCommand relativeStrengthCommandPosition;
private RelayCommand macdCommandPosition;
@@ -130,6 +132,7 @@ namespace TradeBlotter.ViewModels
base.OnPropertyChanged("SelectedParameter");
base.OnPropertyChanged("ParameterValue");
}
protected override void OnDispose()
{
StopMonitor();
@@ -140,6 +143,7 @@ namespace TradeBlotter.ViewModels
{
return true;
}
public override SaveParameters GetSaveParameters()
{
SaveParameters saveParams = new SaveParameters();
@@ -148,6 +152,7 @@ namespace TradeBlotter.ViewModels
saveParams.Add(new KeyValuePair<String, String>("PathFileName", pathFileName));
return saveParams;
}
public override void SetSaveParameters(SaveParameters saveParameters)
{
try
@@ -173,6 +178,7 @@ namespace TradeBlotter.ViewModels
base.OnPropertyChanged("BusyIndicator");
}
}
public String BusyContent
{
get
@@ -207,6 +213,7 @@ namespace TradeBlotter.ViewModels
return collection;
}
}
public ObservableCollection<MenuItem> PositionsMenuItems
{
get
@@ -226,11 +233,12 @@ namespace TradeBlotter.ViewModels
collection.Add(new MenuItem() { Text = "Display DCF Valuation", MenuItemClickedCommand = DisplayDCFValuationPosition, StaysOpenOnClick = false });
collection.Add(new MenuItem() { Text = "Add to Watchlist", MenuItemClickedCommand = AddToWatchListPosition, StaysOpenOnClick = false });
collection.Add(new MenuItem() { Text = "Remove from Watchlist", MenuItemClickedCommand = RemoveFromWatchListPosition, StaysOpenOnClick = false });
collection.Add(new MenuItem() { Text="Close Position...",MenuItemClickedCommand=ClosePosition,StaysOpenOnClick=false });
collection.Add(new MenuItem() { Text="Edit Position...",MenuItemClickedCommand=EditPosition,StaysOpenOnClick=false });
collection.Add(new MenuItem() { Text = "Close Position...",MenuItemClickedCommand=ClosePosition,StaysOpenOnClick=false });
collection.Add(new MenuItem() { Text = "Edit Position...",MenuItemClickedCommand=EditPosition,StaysOpenOnClick=false });
return collection;
}
}
private void OnMomentumViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
{
}
@@ -248,6 +256,7 @@ namespace TradeBlotter.ViewModels
return "MGSHMomentum Model ("+pureFileName+")";
}
}
public override String Title
{
get
@@ -255,11 +264,13 @@ namespace TradeBlotter.ViewModels
return DisplayName;
}
}
public DateTime SelectableDateStart
{
get{return selectableDateStart;}
set{selectableDateStart=value;}
}
public DateTime SelectableDateEnd
{
get{return selectableDateEnd;}
@@ -309,6 +320,7 @@ namespace TradeBlotter.ViewModels
return sb.ToString();
}
}
public String VelocityDescription
{
get
@@ -317,6 +329,7 @@ namespace TradeBlotter.ViewModels
return "Velocity is the percentage range of the current price within the price history.";
}
}
public String BetaDescription
{
get
@@ -325,6 +338,7 @@ namespace TradeBlotter.ViewModels
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.";
}
}
// Position
public String CompanyDescriptionSelectedPosition
{
@@ -355,11 +369,13 @@ namespace TradeBlotter.ViewModels
base.OnPropertyChanged("SelectedItem");
}
}
public MGSHPositionModel SelectedPosition
{
get{return selectedPosition;}
set{selectedPosition=value;base.OnPropertyChanged("SelectedPosition");}
}
public String SelectedDate
{
get { return selectedDate; }
@@ -369,40 +385,49 @@ namespace TradeBlotter.ViewModels
base.OnPropertyChanged("SelectedDate");
}
}
public ObservableCollection<MGSHMomentumCandidate> AllItems
{
get{return momentumCandidates;}
}
public MGSHPositionModelCollection AllPositions
{
get{return positions;}
}
public ObservableCollection<String> Parameters
{
get{return nvpDictionaryKeys;}
}
public ObservableCollection<String> MonitorIntervals
{
get{return monitorIntervals;}
}
public bool CanMonitor
{
get{return IsTradeFileLoaded;}
}
public String MonitorStatus
{
get{return monitorRunning?"Stop Monitor":"Start Monitor";}
}
public String SelectedMonitorInterval
{
get{return selectedMonitorInterval;}
set{selectedMonitorInterval=value;base.OnPropertyChanged("SelectedMonitorInterval");}
}
public String SelectedParameter
{
get{return selectedParameter;}
set{selectedParameter=value;base.OnPropertyChanged("SelectedParameter");base.OnPropertyChanged("ParameterValue");}
}
public String ParameterValue
{
get
@@ -411,6 +436,7 @@ namespace TradeBlotter.ViewModels
return nvpDictionary[selectedParameter].Value;
}
}
public String CashBalance
{
get
@@ -419,6 +445,7 @@ namespace TradeBlotter.ViewModels
return Utility.FormatCurrency(sessionParams.CashBalance);
}
}
public String NonTradeableCash
{
get
@@ -427,6 +454,16 @@ namespace TradeBlotter.ViewModels
return Utility.FormatCurrency(sessionParams.NonTradeableCash);
}
}
public String HedgeCash
{
get
{
if(null==sessionParams) return "";
return Utility.FormatCurrency(sessionParams.HedgeCashBalance);
}
}
public String ModelExpectation
{
get
@@ -435,6 +472,7 @@ namespace TradeBlotter.ViewModels
return Utility.FormatNumber(modelStatistics.Expectancy,2);
}
}
public Brush ExpectationColor
{
get
@@ -444,6 +482,7 @@ namespace TradeBlotter.ViewModels
return UIUtils.BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
}
}
public String ExpectationDescription
{
get
@@ -465,10 +504,11 @@ namespace TradeBlotter.ViewModels
sb.Append("The calculations are based on closed positions.");
return sb.ToString();
}
}
// *************************************************************************************************************************************************************
// *********************************************************************** I C O M M A N D ********************************************************************
// *************************************************************************************************************************************************************
}
// *************************************************************************************************************************************************************
// *********************************************************************** I C O M M A N D ********************************************************************
// *************************************************************************************************************************************************************
public ICommand DisplayHistorical
{
get
@@ -485,6 +525,7 @@ namespace TradeBlotter.ViewModels
return displayHistoricalCommand;
}
}
public ICommand RunCommand
{
get
@@ -496,6 +537,7 @@ namespace TradeBlotter.ViewModels
return runCommand;
}
}
public ICommand DisplayMovingAverage
{
get
@@ -507,6 +549,7 @@ namespace TradeBlotter.ViewModels
return movingAverageCommand;
}
}
public ICommand DisplayAnalystRatings
{
get
@@ -518,6 +561,7 @@ namespace TradeBlotter.ViewModels
return analystRatingsCommand;
}
}
public ICommand DisplayMACD
{
get
@@ -529,6 +573,7 @@ namespace TradeBlotter.ViewModels
return macdCommand;
}
}
public ICommand DisplayStochastics
{
get
@@ -540,6 +585,7 @@ namespace TradeBlotter.ViewModels
return stochasticsCommand;
}
}
public ICommand DisplayRelativeStrength
{
get
@@ -551,6 +597,7 @@ namespace TradeBlotter.ViewModels
return relativeStrengthCommand;
}
}
public ICommand DisplayStickerValuation
{
get
@@ -567,6 +614,7 @@ namespace TradeBlotter.ViewModels
return stickerValuationCommand;
}
}
public ICommand DisplayDCFValuation
{
get
@@ -583,6 +631,7 @@ namespace TradeBlotter.ViewModels
return dcfValuationCommand;
}
}
public ICommand DisplayPriceHistory
{
get
@@ -594,6 +643,7 @@ namespace TradeBlotter.ViewModels
return priceHistoryCommand;
}
}
public ICommand DisplayDividendHistory
{
get
@@ -610,12 +660,14 @@ namespace TradeBlotter.ViewModels
return dividendHistoryCommand;
}
}
public void DisplayPriceHistoryCommand()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.PricingViewModel,SelectedSymbol," + selectedItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public ICommand DisplayBollingerBand
{
get
@@ -627,6 +679,7 @@ namespace TradeBlotter.ViewModels
return bollingerBandCommand;
}
}
public ICommand AddToWatchList
{
get
@@ -644,6 +697,7 @@ namespace TradeBlotter.ViewModels
return addToWatchListCommand;
}
}
public ICommand RemoveFromWatchList
{
get
@@ -680,6 +734,7 @@ namespace TradeBlotter.ViewModels
return displayHeadlinesCommandPosition;
}
}
public ICommand DisplayHistoricalPosition
{
get
@@ -696,6 +751,7 @@ namespace TradeBlotter.ViewModels
return displayHistoricalCommandPosition;
}
}
public ICommand DisplayAnalystRatingsPosition
{
get
@@ -707,6 +763,7 @@ namespace TradeBlotter.ViewModels
return analystRatingsCommandPosition;
}
}
public ICommand DisplayMovingAveragePosition
{
get
@@ -718,6 +775,7 @@ namespace TradeBlotter.ViewModels
return movingAverageCommandPosition;
}
}
public ICommand DisplayMACDPosition
{
get
@@ -729,6 +787,7 @@ namespace TradeBlotter.ViewModels
return macdCommandPosition;
}
}
public ICommand DisplayStochasticsPosition
{
get
@@ -740,6 +799,7 @@ namespace TradeBlotter.ViewModels
return stochasticsCommandPosition;
}
}
public ICommand DisplayRelativeStrengthPosition
{
get
@@ -751,6 +811,7 @@ namespace TradeBlotter.ViewModels
return relativeStrengthCommandPosition;
}
}
public ICommand DisplayStickerValuationPosition
{
get
@@ -767,6 +828,7 @@ namespace TradeBlotter.ViewModels
return stickerValuationCommandPosition;
}
}
public ICommand DisplayDCFValuationPosition
{
get
@@ -783,6 +845,7 @@ namespace TradeBlotter.ViewModels
return dcfValuationCommandPosition;
}
}
public ICommand DisplayPriceHistoryPosition
{
get
@@ -794,6 +857,7 @@ namespace TradeBlotter.ViewModels
return priceHistoryCommandPosition;
}
}
public ICommand DisplayDividendHistoryPosition
{
get
@@ -810,6 +874,7 @@ namespace TradeBlotter.ViewModels
return dividendHistoryCommandPosition;
}
}
public ICommand DisplayBollingerBandPosition
{
get
@@ -821,6 +886,7 @@ namespace TradeBlotter.ViewModels
return bollingerBandCommandPosition;
}
}
public ICommand AddToWatchListPosition
{
get
@@ -839,6 +905,7 @@ namespace TradeBlotter.ViewModels
return addToWatchListCommandPosition;
}
}
public ICommand RemoveFromWatchListPosition
{
get
@@ -904,6 +971,7 @@ namespace TradeBlotter.ViewModels
return reloadCommand;
}
}
public bool ReloadEnabled
{
get
@@ -911,6 +979,7 @@ namespace TradeBlotter.ViewModels
return !String.IsNullOrEmpty(pathFileName);
}
}
public ICommand LoadFile
{
get
@@ -922,6 +991,7 @@ namespace TradeBlotter.ViewModels
return loadFileCommand;
}
}
public ICommand Monitor
{
get
@@ -946,24 +1016,28 @@ namespace TradeBlotter.ViewModels
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayAnalystRatingsCommand()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.AnalystRatingsViewModel,SelectedSymbol," + selectedItem.Symbol + ",SelectedWatchList,{All}");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayStochasticsCommand()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.StochasticsViewModel,SelectedSymbol," + selectedItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayRelativeStrengthCommand()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.RSIViewModel,SelectedSymbol," + selectedItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,60,SelectedRSIDayCount,3");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayMACDCommand()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.MACDViewModel,SelectedSymbol," + selectedItem.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180");
@@ -1072,24 +1146,28 @@ namespace TradeBlotter.ViewModels
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayStochasticsCommandPosition()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.StochasticsViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayRelativeStrengthCommandPosition()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.RSIViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,60,SelectedRSIDayCount,3");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayMACDCommandPosition()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.MACDViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180");
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void DisplayPriceHistoryCommandPosition()
{
SaveParameters saveParams = SaveParameters.Parse("Type,TradeBlotter.ViewModels.PricingViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,180");
@@ -1110,6 +1188,7 @@ namespace TradeBlotter.ViewModels
saveParams.Referer=this;
WorkspaceInstantiator.Invoke(saveParams);
}
public void AddToWatchListCommand(String symbol)
{
if (WatchListDA.IsInWatchList(symbol))
@@ -1126,6 +1205,7 @@ namespace TradeBlotter.ViewModels
System.Windows.MessageBox.Show("Added '"+symbol+"'","Success", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
public void RemoveFromWatchListCommand(String symbol)
{
if (!WatchListDA.IsInWatchList(symbol))
@@ -1158,6 +1238,7 @@ namespace TradeBlotter.ViewModels
monitorRunning=false;
base.OnPropertyChanged("MonitorStatus");
}
private void StartMonitor()
{
if(monitorRunning)return;
@@ -1167,12 +1248,14 @@ namespace TradeBlotter.ViewModels
DispatcherTimerTick(null,null);
base.OnPropertyChanged("MonitorStatus");
}
private void DispatcherTimerTick(object sender, EventArgs e)
{
UpdatePositionPrices(false);
UpdatePositionRSI3(true);
RunPerformance();
}
private void UpdatePositionPrices(bool change=true)
{
try
@@ -1199,6 +1282,7 @@ namespace TradeBlotter.ViewModels
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
}
}
private void UpdatePositionRSI3(bool change=true)
{
try
@@ -1222,10 +1306,12 @@ namespace TradeBlotter.ViewModels
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
}
}
public void ReloadCommand()
{
LoadSessionFile();
}
public void LoadFileCommand()
{
Forms.OpenFileDialog openFileDialog=new Forms.OpenFileDialog();
@@ -1297,6 +1383,7 @@ namespace TradeBlotter.ViewModels
base.OnPropertyChanged("CanMonitor");
base.OnPropertyChanged("CashBalance");
base.OnPropertyChanged("NonTradeableCash");
base.OnPropertyChanged("HedgeCash");
base.OnPropertyChanged("ModelExpectation");
base.OnPropertyChanged("ExpectationColor");
base.OnPropertyChanged("ExpectationDescription");
@@ -1326,6 +1413,7 @@ namespace TradeBlotter.ViewModels
base.OnPropertyChanged("LegendVisible");
}
}
public String PercentButtonText
{
get
@@ -1334,6 +1422,7 @@ namespace TradeBlotter.ViewModels
else return "Show %";
}
}
public ICommand ToggleReturnOrPercentCommand
{
get
@@ -1351,6 +1440,7 @@ namespace TradeBlotter.ViewModels
return toggleReturnOrPercentCommand;
}
}
public CompositeDataSource Data
{
get
@@ -1361,6 +1451,7 @@ namespace TradeBlotter.ViewModels
return compositeDataSource;
}
}
public String GraphTitle
{
get
@@ -1480,6 +1571,7 @@ namespace TradeBlotter.ViewModels
return sb.ToString();
}
}
public String ToolTipInitialStop
{
get
@@ -1531,6 +1623,7 @@ namespace TradeBlotter.ViewModels
{
get
{
if(null==selectedPosition)return "Please select a position by clicking on a row.";
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");
@@ -1545,6 +1638,46 @@ namespace TradeBlotter.ViewModels
}
}
public String ToolTipSold
{
get
{
if(null==selectedPosition)return "Please select a position by clicking on a row.";
StringBuilder sb=new StringBuilder();
if(selectedPosition.IsActivePosition)
{
sb.Append($"{selectedPosition.Symbol} is currently active.");
}
else
{
sb.Append($"{selectedPosition.Symbol} {selectedPosition.Comment}.");
}
return sb.ToString();
}
}
public String ToolTipExposure
{
get
{
if(null==selectedPosition)return "Please select a position by clicking on a row.";
StringBuilder sb=new StringBuilder();
if(selectedPosition.IsActivePosition)
{
sb.Append("Exposure = PurchasePrice * Shares\n");
sb.Append($"{Utility.FormatCurrency(selectedPosition.Exposure)} = {Utility.FormatCurrency(selectedPosition.PurchasePrice)} * {Utility.FormatNumber(selectedPosition.Shares,3)}");
}
else
{
sb.Append("Original Exposure = PurchasePrice * Shares\n");
sb.Append($"{Utility.FormatCurrency(selectedPosition.PurchasePrice * selectedPosition.Shares)} = {Utility.FormatCurrency(selectedPosition.PurchasePrice)} * {Utility.FormatNumber(selectedPosition.Shares,3)}");
}
return sb.ToString();
}
}
// ************************************************************** T O O L T I P H E L P E R S **************************************************************
public MarketData.Generator.Model.StopLimits GetHistoricalStopLimits()
{

View File

@@ -53,10 +53,13 @@ namespace TradeBlotter.ViewModels
{
this.OnDispose();
}
public virtual String DisplayName { get; protected set; }
protected virtual void OnDispose()
{
}
#if DEBUG
~ViewModelBase()
{

View File

@@ -132,7 +132,7 @@
<d3:VerticalAxis Name="countAxis"/>
</d3:ChartPlotter.MainVerticalAxis>
<d3:LineGraph d3:NewLegend.Description="Gain/Loss" d3:Viewport2D.UsesApproximateContentBoundsComparison="False" x:Name="Data" DataSource="{Binding Path=Data}" Stroke="MidnightBlue" StrokeThickness="2"/>
<d3:LineGraph x:Name="LeastSquares" d3:NewLegend.Description="{Binding Path=LeastSquaresTitle}" DataSource="{Binding LeastSquares}" Stroke="Orange" StrokeThickness="2"/>
<!--<d3:LineGraph x:Name="LeastSquares" d3:NewLegend.Description="{Binding Path=LeastSquaresTitle}" DataSource="{Binding LeastSquares}" Stroke="Orange" StrokeThickness="2"/>-->
<d3:CursorCoordinateGraph Name="cursorGraph" dc:CoordinateGraphBehavior.XTextMappingProperty="MM/dd/yyyy" LineStrokeThickness="1"/>
<d3:Header FontFamily="Arial" Content="{Binding Path=GraphTitle}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="Gain/Loss"/>

View File

@@ -40,6 +40,7 @@
<StackPanel Orientation="Vertical" Grid.Row="2" Grid.RowSpan="3" Grid.Column="0" Margin="0,4.962,0.396,-5">
<Label Content="Date" HorizontalAlignment="Left" ></Label>
<telerik:RadDatePicker SelectableDateStart="{Binding Path=SelectableDateStart}" SelectableDateEnd="{Binding Path=SelectableDateEnd}" VerticalAlignment="Top" HorizontalAlignment="Left" SelectedDate="{Binding Path=SelectedDate,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Parameters" HorizontalAlignment="Left" ></Label>
<ComboBox ItemsSource="{Binding Path=Parameters, Mode=OneWay}" SelectedItem="{Binding Path=SelectedParameter, ValidatesOnDataErrors=True}" Validation.ErrorTemplate="{x:Null}" >
<ComboBox.ItemsPanel>
@@ -49,11 +50,19 @@
</ComboBox.ItemsPanel>
</ComboBox>
<TextBox Height="24" MinWidth="80" HorizontalAlignment="Stretch" IsReadOnly="true" Text="{Binding Path=ParameterValue, Mode=OneWay,ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" Validation.ErrorTemplate="{x:Null}"/>
<Label Content="Tradeable Cash" HorizontalAlignment="Left" ></Label>
<TextBox Height="24" MinWidth="80" HorizontalAlignment="Stretch" IsReadOnly="true" Text="{Binding Path=CashBalance, Mode=OneWay,ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" Validation.ErrorTemplate="{x:Null}"/>
<Label Content="Non-Tradeable Cash" HorizontalAlignment="Left" ></Label>
<TextBox Height="24" MinWidth="80" HorizontalAlignment="Stretch" IsReadOnly="true" Text="{Binding Path=NonTradeableCash, Mode=OneWay,ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" Validation.ErrorTemplate="{x:Null}"/>
<Button Margin="0,2" Content="Find Candidates" HorizontalAlignment="Stretch" Command="{Binding Path=RunCommand}"></Button>
<Label Content="Hedge Cash" HorizontalAlignment="Left" ></Label>
<TextBox Height="24" MinWidth="80" HorizontalAlignment="Stretch" IsReadOnly="true" Text="{Binding Path=HedgeCash, Mode=OneWay,ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" Validation.ErrorTemplate="{x:Null}"/>
<Button Margin="0,2" Content="Find Candidates" HorizontalAlignment="Stretch" Command="{Binding Path=RunCommand}"></Button>
<Label Content="Monitor Interval (sec)" HorizontalAlignment="Left" ></Label>
<ComboBox ItemsSource="{Binding Path=MonitorIntervals, Mode=OneWay}" SelectedItem="{Binding Path=SelectedMonitorInterval, ValidatesOnDataErrors=True}" Validation.ErrorTemplate="{x:Null}" >
<ComboBox.ItemsPanel>
@@ -77,7 +86,8 @@
</StackPanel>
<DockPanel x:Name="DockPanelA" Grid.Row="2" Grid.Column="2" Width="Auto" Height="Auto" >
<telerik:RadGridView SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" ItemContainerStyle="{StaticResource MomentumItemStyle}" AlternationCount="2" AlternateRowBackground="Bisque" ShowGroupFooters="True" ShowColumnFooters="True" ItemsSource="{Binding Path=AllItems, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" >
<telerik:RadGridView SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" ItemContainerStyle="{StaticResource MomentumItemStyle}" AlternationCount="2" AlternateRowBackground="Bisque" ShowGroupFooters="True" ShowColumnFooters="True" ItemsSource="{Binding Path=AllItems, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" >
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu x:Name="GridContextMenu" StaysOpen="False" ItemsSource="{Binding CandidateMenuItems}">
<telerik:RadContextMenu.ItemContainerStyle>
@@ -104,8 +114,6 @@
<telerik:GridViewDataColumn IsReadOnly="True" Header="DayCount" DataMemberBinding="{Binding Path=DayCount,StringFormat='{}{0:N0}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="IDIndicator" DataMemberBinding="{Binding Path=IDIndicator,StringFormat='{}{0:N2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Score" DataMemberBinding="{Binding Path=Score,Converter={StaticResource DoubleFormat},ConverterParameter=2}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="MaxDrawdown" DataMemberBinding="{Binding Path=MaxDrawdown,StringFormat='{}{0:P2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="MaxUpside" DataMemberBinding="{Binding Path=MaxUpside,StringFormat='{}{0:P2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="PE" DataMemberBinding="{Binding Path=PE,StringFormat='{}{0:N2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Beta" DataMemberBinding="{Binding Path=Beta,StringFormat='{}{0:N2}'}" >
@@ -128,8 +136,6 @@
</telerik:GridViewColumn.ToolTipTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="ZacksRank" DataMemberBinding="{Binding Path=ZacksRank}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Volume" DataMemberBinding="{Binding Path=Volume,StringFormat='{}{0:N2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Return1D" DataMemberBinding="{Binding Path=Return1D,StringFormat='{}{0:N3}'}" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
@@ -145,7 +151,7 @@
<d3:VerticalAxis Name="countAxis"/>
</d3:ChartPlotter.MainVerticalAxis>
<d3:LineGraph d3:NewLegend.Description="Gain/Loss" d3:Viewport2D.UsesApproximateContentBoundsComparison="False" x:Name="Data" DataSource="{Binding Path=Data}" Stroke="MidnightBlue" StrokeThickness="2"/>
<d3:LineGraph x:Name="LeastSquares" d3:NewLegend.Description="{Binding Path=LeastSquaresTitle}" DataSource="{Binding LeastSquares}" Stroke="Orange" StrokeThickness="2"/>
<!--<d3:LineGraph x:Name="LeastSquares" d3:NewLegend.Description="{Binding Path=LeastSquaresTitle}" DataSource="{Binding LeastSquares}" Stroke="Orange" StrokeThickness="2"/>-->
<d3:CursorCoordinateGraph Name="cursorGraph" dc:CoordinateGraphBehavior.XTextMappingProperty="MM/dd/yyyy" LineStrokeThickness="1"/>
<d3:Header FontFamily="Arial" Content="{Binding Path=GraphTitle}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="Gain/Loss"/>
@@ -195,6 +201,13 @@
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Sold" DataMemberBinding="{Binding Path=SellDate,StringFormat='{}{0:MM/dd/yyyy}'}" >
<telerik:GridViewColumn.ToolTipTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal" >
<TextBlock Background="LemonChiffon" MaxWidth="1000" TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type vw:MGSHMomentumView}},Path=DataContext.ToolTipSold}"/>
</StackPanel>
</DataTemplate>
</telerik:GridViewColumn.ToolTipTemplate>
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=SellDate,StringFormat='{}{0:MM/dd/yyyy}'}" Foreground="{Binding Path=SellDateColor}"/>
@@ -219,6 +232,26 @@
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="RSI3" >
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding Path=RSI3,StringFormat='{}{0:N2}'}"/>
<Setter Property="Foreground" Value="{Binding Path=RSI3Color}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=RSI3}" Value="NaN" >
<Setter Property="Text" Value="---"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Initial Stop" DataMemberBinding="{Binding Path=InitialStopLimit,StringFormat='{}{0:C}'}" >
<telerik:GridViewColumn.ToolTipTemplate>
<DataTemplate >
@@ -282,7 +315,6 @@
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<!--<telerik:GridViewDataColumn IsReadOnly="True" Header="RMultiple" DataMemberBinding="{Binding Path=RMultipleAsString,StringFormat='{}{0:S}'}" >-->
<telerik:GridViewDataColumn IsReadOnly="True" Header="RMultiple" DataMemberBinding="{Binding Path=RMultiple,Converter={StaticResource RMultipleFormat}}" >
<telerik:GridViewColumn.ToolTipTemplate>
<DataTemplate >
@@ -293,34 +325,19 @@
</telerik:GridViewColumn.ToolTipTemplate>
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=RMultipleAsString,StringFormat='{}{0:S}'}" Foreground="{Binding Path=RMultipleAsStringColor}"/>
<TextBlock Text="{Binding Path=RMultipleAsString,StringFormat='{}{0:S}'}" Foreground="{Binding Path=RMultipleColor}"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="RSI3" >
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding Path=RSI3,StringFormat='{}{0:N2}'}"/>
<Setter Property="Foreground" Value="{Binding Path=RSI3Color}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=RSI3}" Value="NaN" >
<Setter Property="Text" Value="---"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Updated" DataMemberBinding="{Binding Path=LastUpdated,StringFormat='{}{0:MM/dd/yyyy HH:mm:ss}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Exposure">
<telerik:GridViewColumn.ToolTipTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal" >
<TextBlock Background="LemonChiffon" MaxWidth="1000" TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type vw:MGSHMomentumView}},Path=DataContext.ToolTipExposure}"/>
</StackPanel>
</DataTemplate>
</telerik:GridViewColumn.ToolTipTemplate>
<telerik:GridViewDataColumn.AggregateFunctions>
<local:MGSHMomentumPositionSumFunctionExposure />
</telerik:GridViewDataColumn.AggregateFunctions>
@@ -331,17 +348,6 @@
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Mkt.Value">
<telerik:GridViewDataColumn.AggregateFunctions>
<local:MGSHMomentumPositionSumFunctionMarketValue />
</telerik:GridViewDataColumn.AggregateFunctions>
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ActiveMarketValue,StringFormat='{}{0:C}'}" Foreground="{Binding Path=ActiveMarketValueColor}"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="GainLoss" >
<telerik:GridViewDataColumn.AggregateFunctions>
<local:MGSHMomentumPositionSumFunctionGainLoss />
@@ -364,6 +370,17 @@
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Mkt.Value">
<telerik:GridViewDataColumn.AggregateFunctions>
<local:MGSHMomentumPositionSumFunctionMarketValue />
</telerik:GridViewDataColumn.AggregateFunctions>
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ActiveMarketValue,StringFormat='{}{0:C}'}" Foreground="{Binding Path=ActiveMarketValueColor}"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Last Stop Adj." DataMemberBinding="{Binding Path=LastStopAdjustment,Converter={StaticResource DateFormat}}" >
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
@@ -372,15 +389,25 @@
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="Days" DataMemberBinding="{Binding Path=DaysSinceLastStopAdjustment,Converter={StaticResource IntFormat},ConverterParameter=0}" >
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DaysSinceLastStopAdjustment,Converter={StaticResource IntFormat},ConverterParameter=0}" Foreground="{Binding Path=DaysSinceLastStopAdjustmentColor}"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn IsReadOnly="True" Header="PE" DataMemberBinding="{Binding Path=PE,StringFormat='{}{0:N2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Beta" DataMemberBinding="{Binding Path=Beta,StringFormat='{}{0:N2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="IDIndicator" DataMemberBinding="{Binding Path=IDIndicator,StringFormat='{}{0:N2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Score" DataMemberBinding="{Binding Path=Score,Converter={StaticResource DoubleFormat},ConverterParameter=2}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="MaxDrawdown" DataMemberBinding="{Binding Path=MaxDrawdown,StringFormat='{}{0:P2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="MaxUpside" DataMemberBinding="{Binding Path=MaxUpside,StringFormat='{}{0:P2}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="ZacksRank" DataMemberBinding="{Binding Path=ZacksRank}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Volume" DataMemberBinding="{Binding Path=Volume,StringFormat='{}{0:N0}'}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Comment" DataMemberBinding="{Binding Path=Comment}" />
<telerik:GridViewDataColumn IsReadOnly="True" Header="Updated" DataMemberBinding="{Binding Path=LastUpdated,StringFormat='{}{0:MM/dd/yyyy HH:mm:ss}'}" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</Grid>