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

namespace MarketData.Generator.CMMomentum
{
  public class Position : IPurePosition
  {
    public Position()
    {
      CurrentPrice = double.NaN;
      CNNPrediction=false;
    }

    public Position(Position position)
    {
      Symbol = position.Symbol;
      PurchaseDate = position.PurchaseDate;
      SellDate = position.SellDate;
      Shares = position.Shares;
      PurchasePrice = position.PurchasePrice;
      CurrentPrice = position.CurrentPrice;
      Beta = position.Beta;
      BetaMonths = position.BetaMonths;
      SharpeRatio = position.SharpeRatio;
      Weight = position.Weight;
      RiskAdjustedWeight = position.RiskAdjustedWeight;
      RiskAdjustedAllocation = position.RiskAdjustedAllocation;
      TargetBetaOverBeta = position.TargetBetaOverBeta;
      CNNPrediction=position.CNNPrediction;
    }

    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.Beta = position.Beta;
      clonedPosition.BetaMonths = position.BetaMonths;
      clonedPosition.SharpeRatio = position.SharpeRatio;
      clonedPosition.Weight = position.Weight;
      clonedPosition.RiskAdjustedWeight = position.RiskAdjustedWeight;
      clonedPosition.RiskAdjustedAllocation = position.RiskAdjustedAllocation;
      clonedPosition.TargetBetaOverBeta = position.TargetBetaOverBeta;
      clonedPosition.CNNPrediction = position.CNNPrediction;
      return clonedPosition;
    }

    public String Symbol { get; set; }

    public DateTime PurchaseDate { get; set; }

    public DateTime SellDate { get; set; }

    public double Shares { get; set; }

    public double PurchasePrice { get; set; }

    public double CurrentPrice { get; set; }

    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 GainLossPcnt { get { return (MarketValue - Exposure) / Exposure; } }// Derived

    public double Beta { get; set; }

    public int BetaMonths { get; set; }

    public double SharpeRatio { get; set; }

    public double Weight { get; set; }

    public double RiskAdjustedWeight { get; set; }

    public double RiskAdjustedAllocation { get; set; }

    public double TargetBetaOverBeta { get; set; }

		public double Score{get;set;}

		public bool CNNPrediction{get;set;}

    public virtual NVPCollection ToNVPCollection()
    {
      NVPCollection nvpCollection = new NVPCollection();
      nvpCollection.Add(new NVP("Symbol", Symbol.ToString()));
      nvpCollection.Add(new NVP("PurchaseDate", PurchaseDate.Date.ToString()));
      nvpCollection.Add(new NVP("SellDate", SellDate.Date.ToString()));
      nvpCollection.Add(new NVP("Shares", Shares.ToString()));
      nvpCollection.Add(new NVP("PurchasePrice", PurchasePrice.ToString()));
      nvpCollection.Add(new NVP("Beta", Beta.ToString()));
      nvpCollection.Add(new NVP("BetaMonths", BetaMonths.ToString()));
      nvpCollection.Add(new NVP("SharpeRatio", SharpeRatio.ToString()));
      nvpCollection.Add(new NVP("RiskAdjustedWeight", RiskAdjustedWeight.ToString()));
      nvpCollection.Add(new NVP("RiskAdjustedAllocation", RiskAdjustedAllocation.ToString()));
      nvpCollection.Add(new NVP("TargetBetaOverBeta", TargetBetaOverBeta.ToString()));
      if(!double.IsNaN(CurrentPrice))nvpCollection.Add(new NVP("CurrentPrice", CurrentPrice.ToString()));
      nvpCollection.Add(new NVP("Score", Score.ToString()));
			nvpCollection.Add(new NVP("CNNPrediction", CNNPrediction.ToString()));
      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>().Date;
      position.SellDate = nvpDictionary["SellDate"].Get<DateTime>().Date;
      position.Shares = nvpDictionary["Shares"].Get<double>();
      position.PurchasePrice = nvpDictionary["PurchasePrice"].Get<double>();
      if(nvpDictionary.ContainsKey("CurrentPrice"))position.CurrentPrice = nvpDictionary["CurrentPrice"].Get<double>();
      position.Beta = nvpDictionary["Beta"].Get<double>();
      position.BetaMonths = nvpDictionary["BetaMonths"].Get<int>();
      position.SharpeRatio = nvpDictionary["SharpeRatio"].Get<double>();
      position.RiskAdjustedWeight = nvpDictionary["RiskAdjustedWeight"].Get<double>();
      position.RiskAdjustedAllocation = nvpDictionary["RiskAdjustedAllocation"].Get<double>();
      position.TargetBetaOverBeta = nvpDictionary["TargetBetaOverBeta"].Get<double>();
		  if(nvpDictionary.ContainsKey("Score"))position.Score = nvpDictionary["Score"].Get<double>();
		  if(nvpDictionary.ContainsKey("CNNPrediction"))position.CNNPrediction = nvpDictionary["CNNPrediction"].Get<bool>();
      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}",
          Symbol,
          Shares,
          Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
          Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
          Utility.IsEpoch(SellDate) ? "N/A" : Utility.DateTimeToStringMMHDDHYYYY(SellDate),
          "N/A",
          Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
          Utility.AddQuotes(Utility.FormatNumber(Beta, 4)),
          Utility.AddQuotes(Utility.FormatNumber(BetaMonths)),
          Utility.AddQuotes(Utility.FormatNumber(SharpeRatio, 4)),
          Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedWeight)),
          Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedAllocation)),
          Utility.AddQuotes(Utility.FormatNumber(TargetBetaOverBeta)),
          Utility.AddQuotes(Utility.FormatNumber(Score,3)),
					Utility.AddQuotes(CNNPrediction.ToString()),
          "N/A",
          "N/A",
          "N/A"
          ));
      }
      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}",
        Symbol,
        Shares,
        Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
        Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
        Utility.IsEpoch(SellDate) ? "N/A" : Utility.DateTimeToStringMMHDDHYYYY(SellDate),
        Utility.AddQuotes(Utility.FormatCurrency(CurrentPrice)),
        Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
        Utility.AddQuotes(Utility.FormatNumber(Beta, 4)),
        Utility.AddQuotes(Utility.FormatNumber(BetaMonths)),
        Utility.AddQuotes(Utility.FormatNumber(SharpeRatio, 4)),
        Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedWeight)),
        Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedAllocation)),
        Utility.AddQuotes(Utility.FormatNumber(TargetBetaOverBeta)),
        Utility.AddQuotes(Utility.FormatNumber(Score,3)),
				Utility.AddQuotes(CNNPrediction.ToString()),
        Utility.AddQuotes(Utility.FormatCurrency(MarketValue)),
        Utility.AddQuotes(Utility.FormatCurrency(GainLoss)),
        Utility.FormatPercent(GainLossPcnt)
        ));
      }
    }

    public static void DisplayHeader()
    {
      MDTrace.WriteLine(LogLevel.DEBUG, "Symbol,Shares,Purchase Date,Purchase Price,Sell Date,Sell Price,Exposure,Beta,BetaMonths,SharpeRatio,RiskAdjustedWeight,RiskAdjustedAllocation,TargetBetaOverBeta,Score,CNNPrediction,Market Value,Gain Loss,Gain Loss(%)");
    }
  }
  // ****************************************************************************************************************************************************************
  public class Positions : List<Position>
  {
    public Positions()                                
    {
    }

    public Positions(Positions positions)
    {
      foreach (Position position in positions) Add(position);
    }

    public Positions(List<Position> positions)
    {
      foreach (Position position in positions) Add(position);
    }

    public Positions(Position position)
    {
      Add(position);
    }

    public void Add(Positions positions)
    {
      foreach (Position position in positions) Add(position);
    }

    public double Exposure
    {
      get
      {
        return (from Position position in this select position.PurchasePrice * position.Shares).Sum();
      }
    }

    public double MarketValue
    {
      get
      {
        return (from Position position in this select position.CurrentPrice * position.Shares).Sum();
      }
    }

    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 DisplayTopFive()
    {
      Positions positions = new Positions(this.OrderByDescending(x => x.GainLossPcnt).Take(5).ToList());
      Position.DisplayHeader();
      for (int index = 0; index < positions.Count; index++)
      {
        Position position = positions[index];
        position.Display();
      }
    }

    public void DisplayBottomFive()
    {
      Positions positions = new Positions(this.OrderBy(x => x.GainLossPcnt).Take(5).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, "****************************************************************************************************************************");
    }
  }
}
