using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System.Linq;
using MarketData.Generator.Interface;

namespace MarketData.Generator.CMTrend
{
  public class Position : IPosition
  {
    public Position()
    {
      CurrentPrice=double.NaN;
    }
    public Position(Position position)
    {
      Symbol=position.Symbol;
      PurchaseDate=position.PurchaseDate;
      SellDate=position.SellDate;
      Shares=position.Shares;
      PurchasePrice=position.PurchasePrice;
      CurrentPrice=position.CurrentPrice;
      R=position.R;
      C=position.C;
      PositionRiskPercentDecimal=position.PositionRiskPercentDecimal;
      InitialStopLimit=position.InitialStopLimit;
      TrailingStopLimit=position.TrailingStopLimit;
      Volume=position.Volume;
      Volatility=position.Volatility;
      LastStopAdjustment=position.LastStopAdjustment;
      Comment=position.Comment;
    }
    public static Position Clone(Position position)
    {
      Position clonedPosition=new Position();
      clonedPosition.Symbol=position.Symbol;
      clonedPosition.PurchaseDate=position.PurchaseDate;
      clonedPosition.SellDate=position.SellDate;
      clonedPosition.Shares=position.Shares;
      clonedPosition.PurchasePrice=position.PurchasePrice;
      clonedPosition.CurrentPrice=position.CurrentPrice;
      clonedPosition.R=position.R;
      clonedPosition.C=position.C;
      clonedPosition.PositionRiskPercentDecimal=position.PositionRiskPercentDecimal;
      clonedPosition.InitialStopLimit=position.InitialStopLimit;
      clonedPosition.TrailingStopLimit=position.TrailingStopLimit;
      clonedPosition.Volume=position.Volume;
      clonedPosition.Volatility=position.Volatility;
      clonedPosition.LastStopAdjustment=position.LastStopAdjustment;
      clonedPosition.Comment=position.Comment;
      return clonedPosition;
    }
    public String Symbol { get; set; }
    public DateTime PurchaseDate { get; set; }
    public DateTime SellDate { get; set; }
    public double Shares { get; set; }                                                              // This is actual shares (round down P to the nearest whole number)
    public double PurchasePrice { get; set; }
    public double CurrentPrice { get; set; }                                                        // When the position is sold the current price will hold the sell price
    public double Exposure { get { return Shares*PurchasePrice; } }                               // Derived
    public double MarketValue { get { return Shares*CurrentPrice; } }                             // Derived
    public double GainLoss { get { return MarketValue-Exposure; } }                               // Derived
    public double GetGainLoss(Price currentPrice) { return (Shares*currentPrice.Close)-Exposure; }     // get the gain loss based upon the given price
    public double GainLossPcnt { get { return (MarketValue-Exposure)/Exposure; } }              // Derived
    public double PositionRiskPercentDecimal { get; set; }                                          // (i.e.) .06   = 6%
    public double R { get; set; }                                                                   // PositionRiskPercentDecimal*PurchasePrice
    public double C { get; set; }                                                                   // AvailableCash * TotalRiskPercentDecimal
    public double P { get { return C/R; } }                                                               // Derived.  This is the number of shares to buy according to risk limits
    public double TrailingStopLimit { get; set; }                                                       // This is the trailing stop limit.
    public double InitialStopLimit { get; set; }                                                        // This is the initial stop limit.
    public DateTime LastStopAdjustment { get; set; }
    public double TotalRiskExposure { get { return R*Shares; } }                                          // This is the total risk in dollars that we are exposed to
    public double RMultiple { get { return (CurrentPrice-PurchasePrice)/R; } }                      // Derived.
    public String RMultipleAsString
    {
      get
      {
        return Utility.FormatNumber(RMultiple,2,false)+"R";
      }
    } // Derived.
    public double Volatility { get; set; }
    public long Volume { get; set; }
    public String Comment { get; set; }

    public virtual NVPCollection ToNVPCollection()
    {
      NVPCollection nvpCollection=new NVPCollection();
      nvpCollection.Add(new NVP("Symbol",Symbol.ToString()));
      nvpCollection.Add(new NVP("PurchaseDate",PurchaseDate.ToString()));
      nvpCollection.Add(new NVP("SellDate",SellDate.ToString()));
      nvpCollection.Add(new NVP("Shares",Shares.ToString()));
      nvpCollection.Add(new NVP("PurchasePrice",PurchasePrice.ToString()));
      if(!double.IsNaN(CurrentPrice)) nvpCollection.Add(new NVP("CurrentPrice",CurrentPrice.ToString()));
      if(!double.IsNaN(Exposure)) nvpCollection.Add(new NVP("Exposure",Exposure.ToString()));
      if(!double.IsNaN(MarketValue)) nvpCollection.Add(new NVP("MarketValue",MarketValue.ToString()));
      if(!double.IsNaN(GainLoss)) nvpCollection.Add(new NVP("GainLoss",GainLoss.ToString()));
      if(!double.IsNaN(GainLossPcnt)) nvpCollection.Add(new NVP("GainLossPcnt",GainLossPcnt.ToString()));
      if(!double.IsNaN(PositionRiskPercentDecimal)) nvpCollection.Add(new NVP("PositionRiskDecimal",PositionRiskPercentDecimal.ToString()));
      if(!double.IsNaN(R)) nvpCollection.Add(new NVP("R",R.ToString()));
      if(!double.IsNaN(C)) nvpCollection.Add(new NVP("C",C.ToString()));
      if(!double.IsNaN(C)) nvpCollection.Add(new NVP("P",P.ToString()));
      if(!double.IsNaN(InitialStopLimit)) nvpCollection.Add(new NVP("InitialStopLimit",InitialStopLimit.ToString()));
      if(!double.IsNaN(TrailingStopLimit)) nvpCollection.Add(new NVP("TrailingStopLimit",TrailingStopLimit.ToString()));
      if(!double.IsNaN(TotalRiskExposure)) nvpCollection.Add(new NVP("TotalRiskExposure",TotalRiskExposure.ToString()));
      if(!double.IsNaN(RMultiple)) nvpCollection.Add(new NVP("RMultiple",RMultipleAsString));
      if(!double.IsNaN(Volatility)) nvpCollection.Add(new NVP("Volatility",Volatility.ToString()));
      nvpCollection.Add(new NVP("Volume",Volume.ToString()));
      nvpCollection.Add(new NVP("LastStopAdjustment",LastStopAdjustment.ToString()));
      if(null!=Comment) nvpCollection.Add(new NVP("Comment",Comment));
      return nvpCollection;
    }
    public static Position FromNVPCollection(NVPCollection nvpCollection)
    {
      Position position=new Position();
      NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
      position.Symbol=nvpDictionary["Symbol"].Get<String>();
      position.PurchaseDate=nvpDictionary["PurchaseDate"].Get<DateTime>();
      position.SellDate=nvpDictionary["SellDate"].Get<DateTime>();
      position.Shares=nvpDictionary["Shares"].Get<double>();
      position.PurchasePrice=nvpDictionary["PurchasePrice"].Get<double>();
      if(nvpDictionary.ContainsKey("CurrentPrice")) position.CurrentPrice=nvpDictionary["CurrentPrice"].Get<double>();
      if(nvpDictionary.ContainsKey("R")) position.R=nvpDictionary["R"].Get<double>();
      if(nvpDictionary.ContainsKey("C")) position.C=nvpDictionary["C"].Get<double>();
      if(nvpDictionary.ContainsKey("PositionRiskDecimal")) position.PositionRiskPercentDecimal=nvpDictionary["PositionRiskDecimal"].Get<double>();
      if(nvpDictionary.ContainsKey("InitialStopLimit")) position.InitialStopLimit=nvpDictionary["InitialStopLimit"].Get<double>();
      if(nvpDictionary.ContainsKey("TrailingStopLimit")) position.TrailingStopLimit=nvpDictionary["TrailingStopLimit"].Get<double>();
      if(nvpDictionary.ContainsKey("Volatility")) position.Volatility=nvpDictionary["Volatility"].Get<double>();
      if(nvpDictionary.ContainsKey("Volume")) position.Volume=nvpDictionary["Volume"].Get<int>();
      if(nvpDictionary.ContainsKey("LastStopAdjustment")) position.LastStopAdjustment=nvpDictionary["LastStopAdjustment"].Get<DateTime>();
      if(nvpDictionary.ContainsKey("Comment")) position.Comment=nvpDictionary["Comment"].Get<String>();
      return position;
    }
    public void Display()
    {
      if(Utility.IsEpoch(SellDate)&&double.IsNaN(CurrentPrice))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21}",
          Symbol,
          Shares,
          Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
          Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
          Utility.AddQuotes(Utility.FormatCurrency(InitialStopLimit)),
          Utility.AddQuotes(Utility.FormatCurrency(TrailingStopLimit)),
          Utility.AddQuotes(Utility.DateTimeToStringMMHDDHYYYY(LastStopAdjustment)),
          Utility.AddQuotes(Utility.FormatNumber(Volatility,2)),
          Utility.AddQuotes(Utility.FormatNumber(Volume,0)),
          Utility.AddQuotes(Utility.FormatCurrency(R)),
          Utility.AddQuotes(Utility.FormatNumber(PositionRiskPercentDecimal,3)),
          Utility.AddQuotes(Utility.FormatCurrency(C)),
          Utility.AddQuotes(Utility.FormatCurrency(TotalRiskExposure)),
          Utility.AddQuotes(Utility.FormatNumber(RMultiple,3)),
          Utility.AddQuotes(RMultipleAsString),
          Utility.IsEpoch(SellDate)?"N/A":Utility.DateTimeToStringMMHDDHYYYY(SellDate),
          "N/A",
          Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
          "N/A",
          "N/A",
          "N/A",
          null==Comment?"":Comment
          ));
      }
      else
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21}",
        Symbol,
        Shares,
        Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
        Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
        Utility.AddQuotes(Utility.FormatCurrency(InitialStopLimit)),
        Utility.AddQuotes(Utility.FormatCurrency(TrailingStopLimit)),
        Utility.IsEpoch(LastStopAdjustment)?"N/A":Utility.DateTimeToStringMMHDDHYYYY(LastStopAdjustment),
        Utility.AddQuotes(Utility.FormatNumber(Volatility,2)),
        Utility.AddQuotes(Utility.FormatNumber(Volume,0)),
        Utility.AddQuotes(Utility.FormatCurrency(R)),
        Utility.AddQuotes(Utility.FormatNumber(PositionRiskPercentDecimal,3)),
        Utility.AddQuotes(Utility.FormatCurrency(C)),
        Utility.AddQuotes(Utility.FormatCurrency(TotalRiskExposure)),
        Utility.AddQuotes(Utility.FormatNumber(RMultiple,3)),
        Utility.AddQuotes(RMultipleAsString),
        Utility.IsEpoch(SellDate)?"N/A":Utility.DateTimeToStringMMHDDHYYYY(SellDate),
        Utility.AddQuotes(Utility.FormatCurrency(CurrentPrice)),
        Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
        Utility.AddQuotes(Utility.FormatCurrency(MarketValue)),
        Utility.AddQuotes(Utility.FormatCurrency(GainLoss)),
        Utility.FormatPercent(GainLossPcnt),
        null==Comment?"":Comment
        ));
      }
    }
    public static void DisplayHeader()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"Symbol,Shares,Purchase Date,Purchase Price,Initial Stop Limit,Trailing Stop Limit,Last Stop Adjustment,Volatility,Volume,R,PositionRiskPercentDecimal,C,TotalRiskExposure,RMultiple,RMultipleString,Sell Date,Sell Price,Exposure,Market Value,Gain Loss,Gain Loss(%),Comment");
    }
  }
  // ****************************************************************************************************************************************************************
  public class Positions:List<Position>
  {
    public Positions()
    {
    }
    public Positions(Positions positions)
    {
      if(null==positions) return;
      foreach(Position position in positions) Add(position);
    }
    public Positions(List<Position> positions)
    {
      if(null==positions) return;
      foreach(Position position in positions) Add(position);
    }
    public Positions(Position position)
    {
      if(null==position) return;
      Add(position);
    }
    public void Add(Positions positions)
    {
      if(null==positions) return;
      foreach(Position position in positions) Add(position);
    }
    public int PositionsOn(DateTime purchaseDate)
    {
      return this.Count(x => x.PurchaseDate.Equals(purchaseDate));
    }
    public double GetExposure()
    {
      Positions openPositions=new Positions(this.Where(x => Utility.IsEpoch(x.SellDate)).ToList());
      return (from Position position in openPositions select position.PurchasePrice*position.Shares).Sum();
    }
    public double GetMarketValue()
    {
      Positions closedPositions=new Positions(this.Where(x => !Utility.IsEpoch(x.SellDate)).ToList());
      if(null==closedPositions||0==closedPositions.Count) return 0.00;
      return closedPositions.Select(x => x.CurrentPrice*x.Shares).Sum();
    }
    public List<String> GetSymbols()
    {
      return (from Position position in this select position.Symbol).Distinct().ToList();
    }
    public double GetGainLoss()
    {
      Positions closedPositions=new Positions(this.Where(x => !Utility.IsEpoch(x.SellDate)).ToList());
      if(null==closedPositions||0==closedPositions.Count) return 0.00;
      double totalMarketValue=closedPositions.Select(x => x.Shares*x.CurrentPrice).Sum();
      double totalExposure=closedPositions.Select(x => x.Shares*x.PurchasePrice).Sum();
      return totalMarketValue-totalExposure;
    }
    public double GetGainLossPercent()
    {
      Positions closedPositions=new Positions(this.Where(x => !Utility.IsEpoch(x.SellDate)).ToList());
      if(null==closedPositions||0==closedPositions.Count) return 0.00;
      double totalMarketValue=closedPositions.Select(x => x.Shares*x.CurrentPrice).Sum();
      double totalExposure=closedPositions.Select(x => x.Shares*x.PurchasePrice).Sum();
      return (totalMarketValue-totalExposure)/totalExposure;
    }
    public NVPCollections ToNVPCollections()
    {
      NVPCollections nvpCollections=new NVPCollections();
      foreach(Position position in this)
      {
        nvpCollections.Add(position.ToNVPCollection());
      }
      return nvpCollections;
    }
    public static Positions FromNVPCollections(NVPCollections nvpCollections)
    {
      Positions positions=new Positions();
      foreach(NVPCollection nvpCollection in nvpCollections)
      {
        positions.Add(Position.FromNVPCollection(nvpCollection));
      }
      return positions;
    }
    public void AddFromNVPCollection(NVPCollection nvpCollection)
    {
      Add(Position.FromNVPCollection(nvpCollection));
    }
    public void DisplayTop(int count=10)
    {
      Positions positions=new Positions(this.OrderByDescending(x => x.GainLossPcnt).Take(count).ToList());
      Position.DisplayHeader();
      for(int index=0;index<positions.Count;index++)
      {
        Position position=positions[index];
        position.Display();
      }
    }
    public void DisplayBottom(int count=10)
    {
      Positions positions=new Positions(this.OrderBy(x => x.GainLossPcnt).Take(count).ToList());
      Position.DisplayHeader();
      for(int index=0;index<positions.Count;index++)
      {
        Position position=positions[index];
        position.Display();
      }
    }
    public void Display()
    {
      Position.DisplayHeader();
      for(int index=0;index<Count;index++)
      {
        Position position=this[index];
        position.Display();
      }
      MDTrace.WriteLine(LogLevel.DEBUG,"****************************************************************************************************************************");
    }
  }
}

