using MarketData;
using MarketData.Cache;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TradeBlotter.Model
{
  public class ModelPerformanceAggregator
  {
    private ModelPerformanceAggregator()
    {
    }
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** M A R C  M I N E R V I N I   M O M E N T U M  *************************************************************************
// ***********************************************************************************************************************************************************************************************************
// 2 versions : one handles MMTrend the other handes the CMTrend.  
    public static double CalculateCumulativeReturn(IEnumerable<CMTPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
      if(null==performanceSeries) return double.NaN;
      return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
    }
    public static ModelPerformanceSeries GetModelPerformance(IEnumerable<CMTPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      try
      {
        DateTime minDate=positions.Min(x => x.PurchaseDate);
        DateTime maxDate=PricingDA.GetLatestDate();
        DateGenerator dateGenerator=new DateGenerator();
        double cumulativeReturn=double.NaN;
        double prevGainLoss=double.NaN;

        LocalPriceCache.GetInstance().RemoveDate(maxDate);
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);


        // Fix purchase date/sell date fall on weekend
        //foreach(CMTPositionModel position in positions)
        //{
        //  if(dateGenerator.IsWeekend(position.PurchaseDate))
        //  {
        //    while(true)
        //    {
        //      position.PurchaseDate=dateGenerator.GetNextBusinessDay(position.PurchaseDate);
        //      if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break;
        //    }
        //  }
        //  if(dateGenerator.IsWeekend(position.SellDate))
        //  {
        //    while(true)
        //    {
        //      position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate);
        //      if(!HolidayDA.IsMarketHoliday(position.SellDate)) break;
        //    }
        //  }
        //}

        foreach(DateTime currentDate in historicalDates)
        {
          IEnumerable<CMTPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
          IEnumerable<CMTPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
          if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
          double gainLoss=0.00;
          double gainLossClosedPositions=0.00;
          double exposure=0.00;
          double marketValue=0.00;
          if(HolidayDA.IsMarketHoliday(currentDate)) continue;
          ModelPerformanceItem performanceItem=new ModelPerformanceItem();
          foreach(CMTPositionModel openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.PurchasePrice;
            if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
            {
              Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,90);
              LocalPriceCache.GetInstance().Add(prices);
            }
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
              continue;
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(CMTPositionModel closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          double dailyReturn=0.00;
          if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
          else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
          if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
          else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);

          performanceItem.Date=currentDate;
          performanceItem.Exposure=exposure;
          performanceItem.MarketValue=marketValue;
          performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
          performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
          performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
          performanceSeries.Add(performanceItem);
          prevGainLoss=gainLoss;
        }


        for(int index=0;index<performanceSeries.Count;index++)
        {
          ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
          ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
          if(null==prevModelPerformanceItem)
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
            currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
          else
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
            currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
        }
        return performanceSeries;
      }
      catch(Exception)
      {
        return performanceSeries;
      }
    }
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** C L E N O W   M O M E N T U M  (C M M M O M E N T U M )**************************************************************************************
// ***********************************************************************************************************************************************************************************************************
    public static double CalculateCumulativeReturn(IEnumerable<CMPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
      if(null==performanceSeries)return double.NaN;
      return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
    }

    public static double CalculateCumulativeReturn(IEnumerable<MGPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
      if(null==performanceSeries) return double.NaN;
      return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
    }

    public static double CalculateCumulativeReturn(IEnumerable<MGSHPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
      if(null==performanceSeries) return double.NaN;
      return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
    }

    public static ModelPerformanceSeries GetModelPerformance(IEnumerable<CMPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      try
      {
        DateTime minDate=positions.Min(x => x.PurchaseDate);
        DateTime maxDate=PricingDA.GetLatestDate();
        DateGenerator dateGenerator=new DateGenerator();
        double cumulativeReturn=double.NaN;
        double prevGainLoss=double.NaN;

        LocalPriceCache.GetInstance().RemoveDate(maxDate);
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);

        // Fix purchase date/sell date fall on weekend
        //foreach(CMPositionModel position in positions)
        //{
        //  if(dateGenerator.IsWeekend(position.PurchaseDate))
        //  {
        //    while(true)
        //    {
        //      position.PurchaseDate=dateGenerator.GetNextBusinessDay(position.PurchaseDate);
        //      if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break;
        //    }
        //  }
        //  if(dateGenerator.IsWeekend(position.SellDate))
        //  {
        //    while(true)
        //    {
        //      position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate);
        //      if(!HolidayDA.IsMarketHoliday(position.SellDate)) break;
        //    }
        //  }
        //}

        foreach(DateTime currentDate in historicalDates)
        {
          IEnumerable<CMPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
          IEnumerable<CMPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
          if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
          double gainLoss=0.00;
          double gainLossClosedPositions=0.00;
          double exposure=0.00;
          double marketValue=0.00;
          if(HolidayDA.IsMarketHoliday(currentDate)) continue;
          ModelPerformanceItem performanceItem=new ModelPerformanceItem();
          foreach(CMPositionModel openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.PurchasePrice;
            if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
            {
              Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,90);
              LocalPriceCache.GetInstance().Add(prices);
            }
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
              continue;
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(CMPositionModel closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          double dailyReturn=0.00;
          if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
          else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
          if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
          else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);

          performanceItem.Date=currentDate;
          performanceItem.Exposure=exposure;
          performanceItem.MarketValue=marketValue;
          performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
          performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
          performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
          performanceSeries.Add(performanceItem);
          prevGainLoss=gainLoss;
        }


        for(int index=0;index<performanceSeries.Count;index++)
        {
          ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
          ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
          if(null==prevModelPerformanceItem)
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
            currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
          else
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
            currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
        }
        return performanceSeries;
      }
      catch(Exception)
      {
        return performanceSeries;
      }
    }
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** Q U A N T U M   M O M E N T U M  **************************************************************************************
// ***********************************************************************************************************************************************************************************************************
    public static ModelPerformanceSeries GetModelPerformance(IEnumerable<MGPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      try
      {
        DateTime minDate=positions.Min(x => x.PurchaseDate);
        DateTime maxDate=PricingDA.GetLatestDate();
        DateGenerator dateGenerator=new DateGenerator();
        double cumulativeReturn=double.NaN;
        double prevGainLoss=double.NaN;

        LocalPriceCache.GetInstance().RemoveDate(maxDate);
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);

        // Fix purchase date/sell date fall on weekend
        //foreach(MGPositionModel position in positions)
        //{
        //  if(dateGenerator.IsWeekend(position.PurchaseDate))
        //  {
        //    while(true)
        //    {
        //      position.PurchaseDate=dateGenerator.GetNextBusinessDay(position.PurchaseDate);
        //      if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break;
        //    }
        //  }
        //  if(dateGenerator.IsWeekend(position.SellDate))
        //  {
        //    while(true)
        //    {
        //      position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate);
        //      if(!HolidayDA.IsMarketHoliday(position.SellDate)) break;
        //    }
        //  }
        //}

        foreach(DateTime currentDate in historicalDates)
        {
          IEnumerable<MGPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
          IEnumerable<MGPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
          if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
          double gainLoss=0.00;
          double gainLossClosedPositions=0.00;
          double exposure=0.00;
          double marketValue=0.00;
          if(HolidayDA.IsMarketHoliday(currentDate)) continue;
          ModelPerformanceItem performanceItem=new ModelPerformanceItem();
          foreach(MGPositionModel openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.PurchasePrice;
            if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
            {
              Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,90);
              LocalPriceCache.GetInstance().Add(prices);
            }
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
              continue;
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(MGPositionModel closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          double dailyReturn=0.00;
          if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
          else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
          if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
          else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);
          performanceItem.Date=currentDate;
          performanceItem.Exposure=exposure;
          performanceItem.MarketValue=marketValue;
          performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
          performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
          performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
          performanceSeries.Add(performanceItem);
          prevGainLoss=gainLoss;
        }
        for(int index=0;index<performanceSeries.Count;index++)
        {
          ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
          ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
          if(null==prevModelPerformanceItem)
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
            currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
          else
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
            currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
        }
        return performanceSeries;
      }
      catch(Exception)
      {
        return null;
      }
    }

// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** M G S H   Q U A N T U M   M O M E N T U M  **************************************************************************************
// ***********************************************************************************************************************************************************************************************************
    public static ModelPerformanceSeries GetModelPerformance(IEnumerable<MGSHPositionModel> positions)
    {
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      try
      {
        DateTime minDate=positions.Min(x => x.PurchaseDate);
        DateTime maxDate=PricingDA.GetLatestDate();
        DateGenerator dateGenerator=new DateGenerator();
        double cumulativeReturn=double.NaN;
        double prevGainLoss=double.NaN;

        LocalPriceCache.GetInstance().RemoveDate(maxDate);
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);

        // Fix purchase date/sell date fall on weekend
        //foreach(MGSHPositionModel position in positions)
        //{
        //  if(dateGenerator.IsWeekend(position.PurchaseDate))
        //  {
        //    while(true)
        //    {
        //      position.PurchaseDate=dateGenerator.GetNextBusinessDay(position.PurchaseDate);
        //      if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break;
        //    }
        //  }
        //  if(dateGenerator.IsWeekend(position.SellDate))
        //  {
        //    while(true)
        //    {
        //      position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate);
        //      if(!HolidayDA.IsMarketHoliday(position.SellDate)) break;
        //    }
        //  }
        //}

        foreach(DateTime currentDate in historicalDates)
        {
          IEnumerable<MGSHPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
          IEnumerable<MGSHPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
          if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
          double gainLoss=0.00;
          double gainLossClosedPositions=0.00;
          double exposure=0.00;
          double marketValue=0.00;
          if(HolidayDA.IsMarketHoliday(currentDate)) continue;
          ModelPerformanceItem performanceItem=new ModelPerformanceItem();
          foreach(MGSHPositionModel openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.PurchasePrice;
            if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
            {
              Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,90);
              LocalPriceCache.GetInstance().Add(prices);
            }
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
              continue;
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(MGSHPositionModel closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          double dailyReturn=0.00;
          if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
          else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
          if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
          else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);
          performanceItem.Date=currentDate;
          performanceItem.Exposure=exposure;
          performanceItem.MarketValue=marketValue;
          performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
          performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
          performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
          performanceSeries.Add(performanceItem);
          prevGainLoss=gainLoss;
        }
        for(int index=0;index<performanceSeries.Count;index++)
        {
          ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
          ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
          if(null==prevModelPerformanceItem)
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
            currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
          else
          {
            currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
            currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
            currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
            currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
            currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
          }
        }
        return performanceSeries;
      }
      catch(Exception)
      {
        return null;
      }
    }
  }
}
