using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.Input;
using Eremex.AvaloniaUI.Controls;
using MarketData;
using MarketData.DataAccess;
using MarketData.Generator.CMTrend;
using MarketData.Generator.Interface;
using MarketData.Generator.Model;
using MarketData.Generator.ModelGenerators;
using MarketData.MarketDataModel;
using MarketData.Utils;
using PortfolioManager.DataSeriesViewModels;
using PortfolioManager.Dialogs;
using PortfolioManager.Models;
using PortfolioManager.UIUtils;
using Position=MarketData.Generator.CMTrend.Position;
using StopLimit = MarketData.MarketDataModel.StopLimit;

namespace PortfolioManager.ViewModels
{
  public partial class CMTrendViewModel : WorkspaceViewModel
  {
    private bool isBusy = false;
    private bool showMarkers = false;
    private ModelPerformanceSeries modelPerformanceSeries = null;
    private ModelStatistics modelStatistics = null;
    private bool showAsGainLoss = true;
    private CMTPositionModelCollection positions = null;
    private CMTPositionModel selectedPosition = null;
    private CMTParams configuration = null;
    private String pathFileName = null;
    private CMTSessionParams sessionParams;
    private NVPDictionary nvpDictionary = null;
    private ObservableCollection<String> nvpDictionaryKeys = null;
    private String selectedParameter = null;

    public CMTrendViewModel()
    {
      DisplayName = "CMTrend Model";
      PropertyChanged += OnViewModelPropertyChanged;
    }

    protected override void OnDispose()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,$"Dispose CMTrendViewModel");
      base.OnDispose();
    }

    private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
    {
    }

    public bool IsBusy
    {
      get
      {
        return isBusy;
      }
      set
      {
        isBusy = value;
        base.OnPropertyChanged("IsBusy");
      }
    }


    public bool ShowMarkers
    {
      get
      {
        return showMarkers;
      }
      set
      {
        showMarkers = value;
        base.OnPropertyChanged("ShowMarkers");
      }
    }

    public CompositeDataSource Data
    {
      get
      {
        if (null == modelPerformanceSeries) return GainLossModel.Empty();
        CompositeDataSource compositeDataSource = null;
        compositeDataSource = GainLossModel.GainLoss(modelPerformanceSeries, showAsGainLoss);
        return compositeDataSource;
      }
    }

    public String PercentButtonText
    {
      get
      {
        if (!showAsGainLoss) return "Show $";
        else return "Show %";
      }
    }


    public ObservableCollection<String> Parameters
    {
      get { return nvpDictionaryKeys; }
    }

    public String SelectedParameter
    {
      get
      {
        return selectedParameter;
      }
      set
      {
        selectedParameter = value;
        base.OnPropertyChanged("SelectedParameter");
        base.OnPropertyChanged("ParameterValue");
      }
    }

    public String ParameterValue
    {
      get
      {
        if (null == nvpDictionary || null == nvpDictionaryKeys || null == selectedParameter) return null;
        return nvpDictionary[selectedParameter].Value;
      }
    }

    public String TradeDate
    {
      get
      {
        if (null == sessionParams) return Constants.CONST_DASHES;
        DateGenerator dateGenerator = new DateGenerator();
        return Utility.DateTimeToStringMMSDDSYYYY(sessionParams.TradeDate);
      }
    }

    public String CashBalance
    {
      get
      {
        if (null == sessionParams) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(sessionParams.CashBalance);
      }
    }

    public String NonTradeableCash
    {
      get
      {
        if (null == sessionParams) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(sessionParams.NonTradeableCash);
      }
    }

    public String ModelExpectation
    {
      get
      {
        if (null == modelStatistics) return "";
        return Utility.FormatNumber(modelStatistics.Expectancy, 2);
      }
    }

    public IBrush ExpectationColor
    {
      get
      {
        if (null == modelStatistics) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
        if (modelStatistics.Expectancy > 0.00) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
        return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
      }
    }

    public String GraphTitle
    {
      get
      {
        if (null == sessionParams || null == modelPerformanceSeries) return "";
        StringBuilder sb = new StringBuilder();
        Positions allPositions = sessionParams.GetCombinedPositions();
        DateTime minDate = allPositions.Min(x => x.PurchaseDate);
        DateTime maxDate = PricingDA.GetLatestDate();

        if (modelPerformanceSeries.Count < 2)
        {
          sb.Append(showAsGainLoss ? "$ GainLoss" : "% Return");
          sb.Append(" ");
          sb.Append("(").Append(minDate.ToShortDateString()).Append("-").Append(maxDate.ToShortDateString()).Append(")");
          sb.Append(showAsGainLoss ? Utility.FormatCurrency(modelPerformanceSeries[modelPerformanceSeries.Count - 1].CumulativeGainLoss) : Utility.FormatPercent(modelPerformanceSeries[modelPerformanceSeries.Count - 1].CumProdMinusOne));
          return sb.ToString();
        }
        if (showAsGainLoss)
        {
          double latestGainLoss = modelPerformanceSeries[modelPerformanceSeries.Count - 1].CumulativeGainLoss;
          double change = modelPerformanceSeries[modelPerformanceSeries.Count - 1].GainLossDOD;
          sb.Append("$ GainLoss");
          sb.Append(" ");
          sb.Append("(").Append(minDate.ToShortDateString()).Append("-").Append(maxDate.ToShortDateString()).Append(")");
          sb.Append(" ");
          sb.Append(Utility.FormatCurrency(latestGainLoss));
          sb.Append(",");
          sb.Append(" ");
          sb.Append(change > 0.00 ? "+" : "").Append(Utility.FormatCurrency(change));
        }
        else
        {
          double latestCumGainLoss = modelPerformanceSeries[modelPerformanceSeries.Count - 1].CumProdMinusOne;
          double prevCumGainLoss = modelPerformanceSeries[modelPerformanceSeries.Count - 2].CumProdMinusOne;
          double change = latestCumGainLoss - prevCumGainLoss;
          sb.Append("% Return");
          sb.Append(" ");
          sb.Append("(").Append(minDate.ToShortDateString()).Append("-").Append(maxDate.ToShortDateString()).Append(")");
          sb.Append(" ");
          sb.Append(Utility.FormatPercent(latestCumGainLoss));
          sb.Append(",");
          sb.Append(" ");
          sb.Append(change > 0.00 ? "+" : "").Append(Utility.FormatPercent(change));
        }
        return sb.ToString();
      }
    }

    // *****************************************************************************************************************************************************
    public ObservableCollection<MenuItem> PositionsMenuItems
    {
      get
      {
        ObservableCollection<MenuItem> collection = new ObservableCollection<MenuItem>();
        collection.Add(new MenuItem() { Header = "Bollinger Bands...", Command = BollingerBandsCommand, StaysOpenOnClick = false });
        collection.Add(new MenuItem() { Header = "Close Position...", Command = CloseCommand, StaysOpenOnClick = false });
        collection.Add(new MenuItem() { Header = "Edit Position...", Command = EditCommand, StaysOpenOnClick = false });
        collection.Add(new MenuItem() { Header = "Add To WatchList", Command = AddToWatchListCommand, StaysOpenOnClick = false });
        collection.Add(new MenuItem() { Header = "Remove From WatchList", Command = RemoveFromWatchListCommand, StaysOpenOnClick = false });
        return collection;
      }
    }

    // ********************************************************************* R E L A Y S  *****************************************************************
    [RelayCommand(CanExecute = nameof(CanExecuteBollingerBands))]
    public async Task BollingerBands()
    {
      await ExecuteBollingerBands();
    }

    public bool CanExecuteBollingerBands()
    {
      if (null == selectedPosition || null == selectedPosition.Symbol) return false;
      return true;
    }    

    [RelayCommand(CanExecute = nameof(CanClosePosition))]
    public async Task Close()
    {
      await OpenCloseDialog();
    }

    public bool CanClosePosition()
    {
      if (null == selectedPosition || null == selectedPosition.Symbol) return false;
      return true;
    }

    [RelayCommand(CanExecute = nameof(CanEdit))]
    public async Task Edit()
    {
      await OpenEditDialog();
    }

    public bool CanEdit()
    {
      if (null == selectedPosition || null == selectedPosition.Symbol || !Utility.IsEpoch(selectedPosition.SellDate)) return false;
      return true;
    }

    [RelayCommand]
    public void ToggleReturnOrPercent()
    {
      HandleToggleReturnOrPercent();
    }

    // This is not currently being displayed
    // [RelayCommand]
    // public void Run()
    // {
    //   RunCandidateGenerator();
    // }

    [RelayCommand]
    public async Task LoadFile()
    {
      await LoadTradeFile();
    }

    [RelayCommand]
    public async Task Reload()
    {
      await ReloadTradeFile();
    }

    public async Task ReloadTradeFile()
    {
      LoadSessionFile();
      await Task.FromResult(true);
    }

    [RelayCommand(CanExecute = nameof(CanAddToWatchList))]
    public async Task AddToWatchList()
    {
      WatchListDA.AddToWatchList(selectedPosition.Symbol);
      MxMessageBox.Show(GetTopLevelWindow(), $"Added {selectedPosition.Symbol} to WatchList", "Add To WatchList");
      await Task.FromResult(true);
    }

    public bool CanAddToWatchList()
    {
      if (null == selectedPosition) return false;
      return WatchListDA.IsInWatchList(selectedPosition.Symbol) ? false : true;
    }

    [RelayCommand(CanExecute = nameof(CanRemoveFromWatchList))]
    public async Task RemoveFromWatchList()
    {
      WatchListDA.RemoveFromWatchList(selectedPosition.Symbol);
      MxMessageBox.Show(GetTopLevelWindow(), $"Removed {selectedPosition.Symbol} from WatchList", "Remove From WatchList");
      await Task.FromResult(true);
    }

    public bool CanRemoveFromWatchList()
    {
      if (null == selectedPosition) return false;
      if (!WatchListDA.IsInWatchList(selectedPosition.Symbol)) return false;
      PortfolioTrades portfolioTrades = PortfolioDA.GetOpenTrades();
      if (portfolioTrades.Any(x => x.Symbol.Equals(selectedPosition.Symbol))) return false;
      return true;
    }

    // *********************************************************************************************************************************
    public async Task ExecuteBollingerBands()
    {
      SaveParameters saveParams = SaveParameters.Parse("Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,360");
      saveParams.Referer=this;
      WorkspaceInstantiator.Invoke(saveParams);      
      await Task.FromResult(true);
    }

    private void HandleToggleReturnOrPercent()
    {
      showAsGainLoss = !showAsGainLoss;
      base.OnPropertyChanged("Data");
      base.OnPropertyChanged("PercentButtonText");
      base.OnPropertyChanged("GraphTitle");
    }

    public bool ReloadEnabled
    {
      get
      {
        return !String.IsNullOrEmpty(pathFileName);
      }
    }

    public CMTPositionModelCollection AllPositions
    {
      get { return positions; }
    }

    public override String DisplayName
    {
      get
      {
        if (null == pathFileName) return "CMTrend Model";
        String pureFileName = Utility.GetFileNameNoExtension(pathFileName);
        return "CMTrend Model (" + pureFileName + ")";
      }
    }

    public override String Title
    {
      get
      {
        return DisplayName;
      }
    }



    // ***********************************************************************************************************************************
    #region Operations

    public async Task OpenCloseDialog()
    {
      bool deleteStop = false;
      IPosition clonedPosition = Position.Clone(selectedPosition.Position);
      //      bool hasStopLimit = PortfolioDA.HasStopLimit(clonedPosition.Symbol);

      ClosePositionDialog dialog = new ClosePositionDialog();
      ClosePositionDialogViewModel closePositionViewModel = new ClosePositionDialogViewModel(dialog, clonedPosition);
      dialog.DataContext = closePositionViewModel;
      await dialog.ShowDialog(GetTopLevelWindow());
      if (!closePositionViewModel.IsSuccess) return;
      CMTTrendModel mmTrendModel = new CMTTrendModel();
      if (!mmTrendModel.ClosePosition(clonedPosition.Symbol, clonedPosition.PurchaseDate, clonedPosition.SellDate, clonedPosition.CurrentPrice, pathFileName))
      {
        MxMessageBox.Show(GetTopLevelWindow(), "Failed to close the position, check log for details.", "Close Position");
        return;
      }
      //      if (deleteStop) PortfolioDA.DeleteStopLimit(clonedPosition.Symbol);
      String strMessage = String.Format("Closed position for {0}, Purchase Date:{1}, Sell Date{2}, Current Price:{3}, Delete Stop:{4} to {5}.  A backup was created.",
      clonedPosition.Symbol, clonedPosition.PurchaseDate.ToShortDateString(), clonedPosition.SellDate.ToShortDateString(), Utility.FormatCurrency(clonedPosition.CurrentPrice), deleteStop, pathFileName);
      MxMessageBox.Show(GetTopLevelWindow(), strMessage, "Close Position");
      LoadSessionFile();
    }

    public async Task OpenEditDialog()
    {
      EditPositionDialog dialog = new EditPositionDialog();
      IPosition clonedPosition = Position.Clone(selectedPosition.Position);
      EditPositionDialogViewModel editPositionViewModel = new EditPositionDialogViewModel(dialog, clonedPosition);
      dialog.DataContext = editPositionViewModel;
      await dialog.ShowDialog(GetTopLevelWindow());
      GetTopLevelWindow().BringIntoView();
      if (!editPositionViewModel.IsSuccess) return;
      CMTTrendModel mmTrendModel = new CMTTrendModel();
      if (!mmTrendModel.EditPosition(clonedPosition.Symbol, clonedPosition.PurchaseDate, clonedPosition.PurchasePrice, clonedPosition.InitialStopLimit, clonedPosition.TrailingStopLimit, pathFileName))
      {
        MxMessageBox.Show(GetTopLevelWindow(), "Failed to edit the position, check log for details.", "Edit Position");
        return;
      }
      if (!selectedPosition.TrailingStopLimit.Equals(clonedPosition.TrailingStopLimit))
      {
        selectedPosition.TrailingStopLimit = clonedPosition.TrailingStopLimit;
        selectedPosition.LastStopAdjustment = DateTime.Now.Date;
      }
      selectedPosition.PurchaseDate = clonedPosition.PurchaseDate;
      selectedPosition.PurchasePrice = clonedPosition.PurchasePrice;
      selectedPosition.TrailingStopLimit = clonedPosition.TrailingStopLimit;
      selectedPosition.InitialStopLimit = clonedPosition.InitialStopLimit;

      String strMessage = String.Format("Edited Position for {0} Purchase Date:{1} Purchase Price:{2}, Trailing Stop:{3}. A backup was created.",
      selectedPosition.Symbol, selectedPosition.PurchaseDate.ToShortDateString(), Utility.FormatCurrency(selectedPosition.PurchasePrice), Utility.FormatCurrency(selectedPosition.TrailingStopLimit));
      MxMessageBox.Show(GetTopLevelWindow(), strMessage, "Edit Position");
      LoadSessionFile();
    }

    public async Task LoadTradeFile()
    {
      TopLevel topLevel = TopLevel.GetTopLevel(GetTopLevelWindow());
      IReadOnlyList<IStorageFile> files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
      {
        Title = "Open Trade File",
        AllowMultiple = false,
      });
      IStorageFile storageFile = files.FirstOrDefault();
      if (null == storageFile) return;
      Uri uri = storageFile.Path;
      pathFileName = uri.LocalPath;

      if (null == pathFileName) return;
      if (!CMTSessionManager.IsValidSessionFile(pathFileName))
      {
        pathFileName = null;
      }
      else LoadSessionFile();
    }

    public bool LoadSessionFile()
    {
      IsBusy = true;
      Task workerTask = Task.Factory.StartNew(() =>
      {
        try
        {
          if (!CMTSessionManager.IsValidSessionFile(pathFileName)) return false;
          sessionParams = CMTSessionManager.RestoreSession(pathFileName);
          if (null == sessionParams)
          {
            pathFileName = null;
            return false;
          }
          modelStatistics = CMTTrendModel.GetModelStatistics(sessionParams);
          modelPerformanceSeries = CMTTrendModel.GetModelPerformance(sessionParams);
          configuration = sessionParams.CMTParams;
          NVPCollection nvpCollection = sessionParams.CMTParams.ToNVPCollection();
          nvpDictionary = nvpCollection.ToDictionary();
          List<String> dictionaryKeys = new List<String>(nvpDictionary.Keys);
          dictionaryKeys.Sort();
          nvpDictionaryKeys = new ObservableCollection<String>(dictionaryKeys);
          selectedParameter = nvpDictionaryKeys[0];
          positions = new CMTPositionModelCollection();
          positions.Add(sessionParams.ActivePositions);
          positions.Add(sessionParams.AllPositions);
          UpdatePositionPrices(false);
          RunPerformance();
          return true;
        }
        catch (Exception exception)
        {
          IsBusy = false;
          MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString());
          pathFileName = null;
          return false;
        }
      });
      workerTask.ContinueWith(continuation =>
      {
        IsBusy = false;
        base.OnPropertyChanged("Parameters");
        base.OnPropertyChanged("SelectedParameter");
        base.OnPropertyChanged("ParameterValue");
        base.OnPropertyChanged("Title");
        base.OnPropertyChanged("DisplayName");
        base.OnPropertyChanged("AllPositions");
        base.OnPropertyChanged("CashBalance");
        base.OnPropertyChanged("NonTradeableCash");
        base.OnPropertyChanged("ModelExpectation");
        base.OnPropertyChanged("ExpectationColor");
        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;
      modelPerformanceSeries = CMTTrendModel.GetModelPerformance(sessionParams);
      base.OnPropertyChanged("Data");
      base.OnPropertyChanged("GraphTitle");
    }

    private void UpdatePositionPrices(bool signalChangeEvent = true)
    {
      try
      {
        DateTime today = DateTime.Now;
        if (null == positions || 0 == positions.Count) return;
        List<String> symbols = (from CMTPositionModel position in positions where position.IsActivePosition select position.Symbol).Distinct().ToList();
        foreach (String symbol in symbols)
        {
          var selectedPositions = (from CMTPositionModel position in positions where position.IsActivePosition && position.Symbol.Equals(symbol) select position);
          foreach (CMTPositionModel selectedPosition in selectedPositions)
          {
            Price price = PricingDA.GetPrice(symbol);
            if (null == price) continue;
            selectedPosition.CurrentPrice = price.Close;
            selectedPosition.CurrentPriceLow = price.Low;
            selectedPosition.LastUpdated = price.Date.Date < today.Date ? price.Date : today;
            selectedPosition.EdgeRatio = EdgeRatioGenerator.CalculateEdgeRatio(symbol, selectedPosition.PurchaseDate, selectedPosition.PurchasePrice, price.Date).EdgeRatio;
          }
        }
        if (signalChangeEvent) positions.OnCollectionChanged();
      }
      catch (Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString());
      }
    }

    #endregion

    public CMTPositionModel SelectedPosition
    {
      get
      {
        return selectedPosition;
      }
      set
      {
        selectedPosition = value;
        base.OnPropertyChanged("SelectedPosition");
      }
    }

    // ****************************************************** P E R S I S T E N C E ********************************************************
    public override bool CanPersist()
    {
      return true;
    }

    public override SaveParameters GetSaveParameters()
    {
      SaveParameters saveParams = new SaveParameters();
      if (null == pathFileName) return null;
      saveParams.Add(new KeyValuePair<String, String>("Type", GetType().Namespace + "." + GetType().Name));
      saveParams.Add(new KeyValuePair<String, String>("PathFileName", pathFileName));
      return saveParams;
    }

    public override void SetSaveParameters(SaveParameters saveParameters)
    {
      try
      {
        pathFileName = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("PathFileName") select item).FirstOrDefault().Value;
        if (!LoadSessionFile()) pathFileName = null;
      }
      catch (Exception exception)
      {
        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();
        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("\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;
    }    

  }
}