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;

          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);
      }

      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");
      }
    }
  }
}
