using System;
using System.Text;
using System.Collections.Generic;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;

namespace MarketData.Generator
{
  public class SignalTrader
  {
    private SignalTrader()
    {
    }
// stopLossThreshold is a percentage that minimizes the loss on the sell side and sells on the Sell signal if the last
// trade price dips below the offering price.
    public static Portfolio TradeSignals(DateRange tradeDates,Signals signals, Prices prices, double initialCash, double stopLossThreshold, int stopLossDays)
    {
      Dictionary<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
      Dictionary<DateTime, Signal> signalsByDate = new Dictionary<DateTime, Signal>();
      Portfolio portfolio = new Portfolio(initialCash);

      for (int index = 0; index < prices.Count; index++)
      {
        Price price = prices[index];
        pricesByDate.Add(price.Date, price);
      }
      for (int index = 0; index < signals.Count; index++)
      {
        Signal signal = signals[index];
        signalsByDate.Add(signal.SignalDate, signal);
      }
//      portfolio.AvailableCash = initialCash;
      for (int index = 0; index < tradeDates.Count; index++)
      {
        DateTime tradingDate = tradeDates[index];
        if (!pricesByDate.ContainsKey(tradingDate)) continue;
        if (!signalsByDate.ContainsKey(tradingDate)) continue;
        Signal signal = signalsByDate[tradingDate];
        if (signal.IsStrongBuy() && !portfolio.HasOpenPosition)
        {
          if (portfolio.AvailableCash > 0)
          {
            ModelTrade trade = new ModelTrade();
            Price price = pricesByDate[signal.SignalDate];
            trade.Symbol = price.Symbol;
            trade.Price = price.Close;
            trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
            trade.TradeDate = price.Date;
            trade.Exposure = trade.Shares * trade.Price;
            trade.Type = ModelTrade.TradeType.Buy;
            portfolio.AvailableCash -= (trade.Price * trade.Shares);
            portfolio.Add(trade);
            portfolio.HasOpenPosition = true;
          }
        }
        else if (signal.IsStrongSell() && portfolio.HasOpenPosition)
        {
          ModelTrade trade = new ModelTrade();
          ModelTrade lastTrade = portfolio.GetLastTrade();
          Price price = pricesByDate[signal.SignalDate];
          if (0.00 == stopLossThreshold && price.Close < lastTrade.Price) continue;
          int daysHeld = (price.Date - lastTrade.TradeDate).Days;
          double stopLossPrice = lastTrade.Price - (lastTrade.Price * stopLossThreshold);
          bool stopLossIndicator = price.Close < stopLossPrice && daysHeld > stopLossDays ? true : false;
          if (!stopLossIndicator && price.Close < lastTrade.Price) continue;
          else if (stopLossIndicator) trade.Comment = "Stop Loss. price decrease " + String.Format("{0:p}", stopLossThreshold) + ", holding days (" + daysHeld + ") >" + stopLossDays;
          portfolio.AvailableCash += ((lastTrade.Shares * price.Close));
          trade.Symbol = price.Symbol;
          trade.Price = price.Close;
          trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
          trade.Type = ModelTrade.TradeType.Sell;
          trade.TradeDate = price.Date;
          double marketValue = (trade.Price * trade.Shares);
//          trade.GainLoss = (trade.Price * trade.Shares) - (lastTrade.Price * lastTrade.Shares);
          trade.GainLoss = marketValue - lastTrade.Exposure;
          trade.Return = ((marketValue - lastTrade.Exposure) / lastTrade.Exposure);
          trade.DaysHeld = daysHeld;
          portfolio.Add(trade);
          portfolio.HasOpenPosition = false;
        }
      }
      return portfolio;
    }

//    Add the MA(10,50) 
//    if (8,17,9) signals a buy and we are on the + side of the MA(10,50) then seek support from the (12,26,9) which must be a buy signal 
//    if (8,17,9) signals a buy and we are on the - side of the MA(10,50) then no support is necessary. 
//    sell when the (8,17,9) signals a sell

    public static Portfolio TradeSignals(DateRange tradeDates,Signals signals, Signals supportSignals, Signals crossoverSignals,Prices prices, double initialCash)
    {
      Dictionary<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
      Dictionary<DateTime, Signal> supportSignalsByDate = new Dictionary<DateTime, Signal>();
      Dictionary<DateTime, Signal> signalsByDate = new Dictionary<DateTime, Signal>();
      Dictionary<DateTime, Signal> crossoverSignalsByDate = new Dictionary<DateTime, Signal>();
      Signal prevSignal = null;

      MDTrace.WriteLine(LogLevel.DEBUG,"[SignalTrader::TradeSignals]");
      Portfolio portfolio = new Portfolio(initialCash);
      int holdingDays = 0;
      int tradeCount = 0;
      int averageHoldingDays = 0;
      for (int index = 0; index < prices.Count; index++)
      {
        Price price = prices[index];
        pricesByDate.Add(price.Date, price);
      }
      for (int index = 0; index < supportSignals.Count; index++)
      {
        Signal supportSignal = supportSignals[index];
        supportSignalsByDate.Add(supportSignal.SignalDate, supportSignal);
      }
      for (int index = 0; index < signals.Count; index++)
      {
        Signal signal = signals[index];
        signalsByDate.Add(signal.SignalDate, signal);
      }
      for (int index = 0; index < crossoverSignals.Count; index++)
      {
        Signal signal = crossoverSignals[index];
        crossoverSignalsByDate.Add(signal.SignalDate, signal);
      }
//      portfolio.AvailableCash = initialCash;
      for(int index=0;index<tradeDates.Count;index++)
      {
        DateTime tradingDate = tradeDates[index];
        if (!pricesByDate.ContainsKey(tradingDate)) continue;
        if (!supportSignalsByDate.ContainsKey(tradingDate)) continue;
        if (!signalsByDate.ContainsKey(tradingDate)) continue;
        if(!crossoverSignalsByDate.ContainsKey(tradingDate))continue;
        Signal signal = signalsByDate[tradingDate];
        Signal supportSignal = supportSignalsByDate[tradingDate];
        Signal crossoverSignal=crossoverSignalsByDate[tradingDate];
        Price price = pricesByDate[signal.SignalDate];
        if(signal.IsStrongBuy() && portfolio.AvailableCash>0 &&!portfolio.HasOpenPosition)
        {
          if (null != prevSignal && prevSignal.IsStrongBuy()) continue;
          if (crossoverSignal.IsBuy() && !supportSignal.IsBuy()) continue;
          ModelTrade trade = new ModelTrade();
          trade.Symbol = price.Symbol;
          trade.Price = price.Close;
          trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
          trade.TradeDate = price.Date;
          trade.Type = ModelTrade.TradeType.Buy;
          trade.Exposure = trade.Shares * trade.Price;
          portfolio.AvailableCash -= (trade.Price * trade.Shares);
          portfolio.Add(trade);
          portfolio.HasOpenPosition = true;
        }
        else if (signal.IsStrongSell() && portfolio.HasOpenPosition)
        {
          ModelTrade trade = new ModelTrade();
          ModelTrade lastTrade = portfolio.GetLastTrade();
          if (price.Close < lastTrade.Price) continue;
          portfolio.AvailableCash += ((lastTrade.Shares * price.Close));
          trade.Symbol = price.Symbol;
          trade.Price = price.Close;
          trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
          trade.Type = ModelTrade.TradeType.Sell;
          trade.TradeDate = price.Date;
          double marketValue = trade.Price * trade.Shares;
          trade.GainLoss = marketValue - lastTrade.Exposure;
          trade.Return = (marketValue - lastTrade.Exposure) / lastTrade.Exposure;
          trade.DaysHeld = (price.Date - lastTrade.TradeDate).Days; ;
          portfolio.Add(trade);
          portfolio.HasOpenPosition = false;
          tradeCount++;
          holdingDays += (price.Date - lastTrade.TradeDate).Days;
          averageHoldingDays = holdingDays / tradeCount;
        }
        prevSignal = signal;
      }
      return portfolio;
    }
  }
}