using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using System.Linq;
using MarketData.Cache;
using MarketData.Generator.Model;
using MarketData.Generator.Indicators;
using MarketData.Numerical;
using StopLimit=MarketData.Generator.Model.StopLimit;
using StopLimits=MarketData.Generator.Model.StopLimits;
using MarketData.Generator.ModelGenerators;

namespace MarketData.Generator.MGSHMomentum
{
  public class MGSHMomentumBacktest
  {
    private double NonTradeableCash{get;set;}
    private double CashBalance{get;set;}
    private double HedgeCashBalance{get;set;}
    private MGSHConfiguration Configuration{get;set;}
    private int HoldingPeriod{get{return Configuration.HoldingPeriod;}}
    private int MaxPositions{get{return Configuration.MaxPositions;}}
    private int MaxPricingExceptions{get{return Configuration.MaxPricingExceptions;}}
    private MGSHActivePositions ActivePositions{get;set;}
    private StopLimits StopLimits{get;set;}
    private MGSHPositions AllPositions{get;set;}
    private MGSHPositions HedgePositions{get;set;}
    private MGSHPricingExceptions PricingExceptions{get;set;}
    private int Cycle{get;set;}
    private DateTime TradeDate{get;set;}
    private DateTime StartDate{get;set;}
    private DateTime AnalysisDate{get;set;}
    private String PathSessionFileName{get;set;}
// ******************************************************************************************************************************************************
//************************************************************** D I S P L A Y  G A I N  L O S S    *****************************************************
// ******************************************************************************************************************************************************
    public static void DisplayGainLoss(String paramPathSessionFileName)
    {
      Profiler profiler=new Profiler();
      ModelPerformanceSeries performanceSeries=GetModelPerformance(paramPathSessionFileName);
      if(null==performanceSeries)return;
      MDTrace.WriteLine("Date,Exposure,MarketValue,GainLossDOD,GainLoss,CumulativeGainLoss,R,(1+R),CumProd,CumProd-1,ClosedPositions");
      foreach(ModelPerformanceItem modelPerformanceItem in performanceSeries)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\",\"{9}\",\"{10}\"",
          modelPerformanceItem.Date.ToShortDateString(),
          Utility.FormatCurrency(modelPerformanceItem.Exposure),                                                                         
          Utility.FormatCurrency(modelPerformanceItem.MarketValue),                                                                     
          Utility.FormatCurrency(modelPerformanceItem.GainLossDOD),
          Utility.FormatCurrency(modelPerformanceItem.GainLoss), 
          Utility.FormatCurrency(modelPerformanceItem.CumulativeGainLoss),
          Utility.FormatNumber(modelPerformanceItem.R,4),
          Utility.FormatNumber(modelPerformanceItem.OnePlusR,4),
          Utility.FormatNumber(modelPerformanceItem.CumProd,4),
          Utility.FormatNumber(modelPerformanceItem.CumProdMinusOne,4), 
          modelPerformanceItem.ClosedPositions));
      }
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, took {0}(ms)",profiler.End()));
    }

    public static ModelPerformanceSeries GetModelPerformance(String paramPathSessionFileName)
    {
      try
      {
        MGSHSessionParams sessionParams=MGSHSessionManager.RestoreSession(paramPathSessionFileName);
        return GetModelPerformance(sessionParams);
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
        return null;
      }
    }

// ******************************************************************************************************************************************************
// *************************************************************************** E D I T ******************************************************************
// ******************************************************************************************************************************************************
    public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,double initialStop,double trailingStop,String sessionFile)
    {
      if (!MGSHSessionManager.IsValidSessionFile(sessionFile))
      {
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Invalid session file '{0}'.", sessionFile));
        return false;
      }
      PathSessionFileName = sessionFile;
      MGSHSessionParams sessionParams=MGSHSessionManager.RestoreSession(PathSessionFileName);
      Configuration = sessionParams.Configuration;
      TradeDate = sessionParams.TradeDate;
      StartDate = sessionParams.StartDate;
      AnalysisDate = sessionParams.AnalysisDate;
      Cycle=sessionParams.Cycle;
      sessionParams.LastUpdated = DateTime.Now;
      StopLimits=sessionParams.StopLimits;
      ActivePositions=sessionParams.ActivePositions;
      AllPositions=sessionParams.AllPositions;
      HedgePositions=sessionParams.HedgePositions;
      CashBalance=sessionParams.CashBalance;
      NonTradeableCash=sessionParams.NonTradeableCash;
      HedgeCashBalance=sessionParams.HedgeCashBalance;
      PricingExceptions=sessionParams.PricingExceptions;
      if(!BackupSession())return false;
      MGSHPositions activePositions = ActivePositions.GetPositions();
      MGSHPosition position = activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
      if (null == position)
      {
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate position for symbol '{0}' purchased on {1}.", symbol, purchaseDate.ToShortDateString()));
        return false;
      }
      if (!position.PurchaseDate.Equals(purchaseDate)) position.PurchaseDate = purchaseDate;
      if (!position.TrailingStopLimit.Equals(trailingStop)) position.TrailingStopLimit = trailingStop;
      if (!position.InitialStopLimit.Equals(initialStop)) position.InitialStopLimit = initialStop;
      if (!position.PurchasePrice.Equals(purchasePrice))
      {
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Adjusting Cash for Position for symbol '{0}' purchased on {1}.  Original Price: {2}  New Price: {3} Change in Cash: {4}", symbol, purchaseDate.ToShortDateString(), Utility.FormatCurrency(position.PurchasePrice), Utility.FormatCurrency(purchasePrice), Utility.FormatCurrency((position.PurchasePrice - purchasePrice) * position.Shares)));
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Adjusting R for Position for symbol '{0}' purchased on {1}.  Original R: {2}  New R: {3} ", symbol, purchaseDate.ToShortDateString(), Utility.FormatNumber(position.R, 2), Utility.FormatNumber(position.PositionRiskPercentDecimal * purchasePrice)));
        CashBalance += (position.PurchasePrice - purchasePrice) * position.Shares;
        position.Comment = (String.IsNullOrEmpty(position.Comment) ? "" : position.Comment + ".") + String.Format("Price changed on {0} from {1} to {2}", DateTime.Now.ToShortDateString(), Utility.FormatCurrency(position.PurchasePrice), Utility.FormatCurrency(purchasePrice));
        position.PurchasePrice = purchasePrice;
      }
      SaveSession();
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} has been modified and saved.",symbol,purchaseDate.ToShortDateString()));
      return true;
    }

// ******************************************************************************************************************************************************
// ************************************************************************ C L O S E  ******************************************************************
// ******************************************************************************************************************************************************
    public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double price,String sessionFile)
    {
      if (!MGSHSessionManager.IsValidSessionFile(sessionFile))
      {
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Invalid session file '{0}'.", sessionFile));
        return false;
      }

      PathSessionFileName = sessionFile;
      MGSHSessionParams sessionParams=MGSHSessionManager.RestoreSession(PathSessionFileName);
      Configuration = sessionParams.Configuration;
      TradeDate = sessionParams.TradeDate;
      StartDate = sessionParams.StartDate;
      AnalysisDate = sessionParams.AnalysisDate;
      Cycle=sessionParams.Cycle;
      sessionParams.LastUpdated = DateTime.Now;
      StopLimits=sessionParams.StopLimits;
      ActivePositions=sessionParams.ActivePositions;
      AllPositions=sessionParams.AllPositions;
      HedgePositions=sessionParams.HedgePositions;
      CashBalance=sessionParams.CashBalance;
      NonTradeableCash=sessionParams.NonTradeableCash;
      HedgeCashBalance=sessionParams.HedgeCashBalance;
      PricingExceptions=sessionParams.PricingExceptions;
      if(!BackupSession())return false;

      MGSHPositions activePositions = ActivePositions.GetPositions();
      MGSHPosition position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
      if(null==position)  // if it is not in the active positions then the position is already closed and we are modifying either the sell date or the sell price
      {
        position=AllPositions.Where(x => x.Symbol.Equals(symbol)&&x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
        if(null==position)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot locate position for symbol '{0}' purchased on {1}.",symbol,purchaseDate.ToShortDateString()));
          return false;
        }
        position.SellDate=sellDate;
        CashBalance-=position.MarketValue;
        position.CurrentPrice=price;
        CashBalance+=position.MarketValue;
        SaveSession();
        return true;
      }
      position.SellDate=sellDate;
      position.CurrentPrice=price;
      position.Comment="Manual close.";
      CashBalance+=position.MarketValue;
      ActivePositions.Remove(position);
      AllPositions.Add(position);
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} is now closed.",symbol,purchaseDate.ToShortDateString()));
      SaveSession();
      return true;
    }

  // ******************************************************************************************************************************************************
  // **************************************************************** S T A T I S T I C S ******************************************************************
  // ******************************************************************************************************************************************************

// Calculates the expectation for the model ( Percent of Winning Trades * Average Gain)/(Percent Losing Trades * Average Loss)
// The expectation should be above zero
// Using the AllPositions collection ignored open positions and active hedge positions
    public static ModelStatistics GetModelStatistics(MGSHSessionParams sessionParams)
    {
      ModelStatistics modelStatistics=new ModelStatistics();
      try
      {
        if(null==sessionParams||null==sessionParams.AllPositions||0==sessionParams.AllPositions.Count) return modelStatistics;
        double totalTrades=sessionParams.AllPositions.Count;
        double winningTrades=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Count();
        double losingTrades=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Count();
        double averageWinningTrade=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Average(x => x.GainLossPcnt)*100.00;
        double averageLosingTrade=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Average(x => x.GainLossPcnt)*100.00;
        double percentWinningTrades=(winningTrades/(double)sessionParams.AllPositions.Count)*100.00;
        double percentLosingTrades=(losingTrades/(double)sessionParams.AllPositions.Count)*100.00;
        double expectation=(percentWinningTrades*averageWinningTrade)/(percentLosingTrades*Math.Abs(averageLosingTrade));

        modelStatistics.TotalTrades=(long)totalTrades;
        modelStatistics.WinningTrades=(long)winningTrades;
        modelStatistics.LosingTrades=(long)losingTrades;
        modelStatistics.AverageWinningTradePercentGain=averageWinningTrade;
        modelStatistics.AverageLosingTradePercentLoss=averageLosingTrade;
        modelStatistics.WinningTradesPercent=percentWinningTrades;
        modelStatistics.LosingTradesPercent=percentLosingTrades;
        modelStatistics.Expectancy=expectation;
        return modelStatistics;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
        return modelStatistics;
      }
    }

    public static ModelPerformanceSeries GetModelPerformance(MGSHSessionParams sessionParams)
    {
      Profiler profiler=new Profiler();
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      DateGenerator dateGenerator=new DateGenerator();
      try
      {
        if(null==sessionParams)return null;
        MGSHPositions combinedPositions=sessionParams.GetCombinedPositions();
        DateTime minDate=combinedPositions.Min(x => x.PurchaseDate);
        DateTime maxDate=PricingDA.GetLatestDate();
        double prevGainLoss=double.NaN;

        LocalPriceCache.GetInstance().RemoveDate(maxDate);
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
        foreach(DateTime currentDate in historicalDates)
        {
          MGSHPositions openPositions=new MGSHPositions(combinedPositions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList());
          MGSHPositions closedPositions=new MGSHPositions(combinedPositions.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(MGSHPosition openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.PurchasePrice;
            if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
            {
              Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
              LocalPriceCache.GetInstance().Add(prices);
            }
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
            }
            else
            {
              gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
              marketValue+=(price.Close*openPosition.Shares);
            }
          }
          foreach(MGSHPosition closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          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;
        }
        performanceSeries.CalculatePerformance();
        return performanceSeries;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
        return null;
      }
      finally
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, total took {0}(ms)",profiler.End()));
      }
    }

// ******************************************************************************************************************************************************
//************************************************************** D I S P L A Y  S E S S I O N   *****************************************************
// ******************************************************************************************************************************************************
    public void DisplaySession(String paramPathSessionFileName)
    {
      if(null==paramPathSessionFileName)return;
      PathSessionFileName=paramPathSessionFileName;
      MGSHSessionParams sessionParams=null;
      if(null==(sessionParams=RestoreSession()))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",paramPathSessionFileName));
        return;
      }
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SessionFile:{0} Last Updated:{1}",paramPathSessionFileName,sessionParams.LastUpdated));
      Configuration.DisplayConfiguration();
      MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L   P O S I T I O N S  *************");
      AllPositions=new MGSHPositions((from MGSHPosition position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
      AllPositions.Display();
      MDTrace.WriteLine(LogLevel.DEBUG,"************** A C T I V E   P O S I T I O N S  *************");
      ActivePositions.Display();
      MDTrace.WriteLine(LogLevel.DEBUG,"************** H E D G E   P O S I T I O N S  *************");
      HedgePositions.Display();
      MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P  G A I N E R S  *************");
      AllPositions.DisplayTopFive();
      MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P  L O S E R S   *************");
      AllPositions.DisplayBottomFive();
      DisplayStatistics(sessionParams);
      DisplayBalanceFromPositions();
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StartDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(StartDate)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle));
    }
// *************************************************************************************************************************************************************************************
// *************************************************************************************************************************************************************************************
// *************************************************************************************************************************************************************************************
// *************************************************************************************************************************************************************************************
// ******************************************************************************************************************************************************
// ****************************************************************** B A C K T E S T   *****************************************************************
// ******************************************************************************************************************************************************
    /// <summary>
    /// Ideally, startDate should be November,February,May,August 
    /// Use this endpoint to first launch the model on the last trading day of a given month supplying the configuration with initial settings
    /// Subsequent daily runs are accomplished by calling UpdateDaily(startDate,endDate,pathSessionFileName) where the startDate=endDate=the trade date 
    /// after the market closes
    /// </summary>
    /// <param name="paramStartDate">{ORIGINAL START DATE}</param>
    /// <param name="paramAnalysisDate">{TODAY}</param>
    /// <param name="paramPathSessionFileName">The session file name</param>
    /// <param name="configuration">The configuration</param>
    /// <returns></returns>
    public MGSHBacktestResult PerformBacktest(DateTime paramStartDate,DateTime paramAnalysisDate,String paramPathSessionFileName,MGSHConfiguration configuration)
    {
      MGSHBacktestResult backTestResult=new MGSHBacktestResult();
      DateGenerator dateGenerator=new DateGenerator();
      Configuration=configuration;
      CashBalance=Configuration.InitialCash;
      HedgeCashBalance=Configuration.HedgeInitialCash;
      ActivePositions=new MGSHActivePositions();
      AllPositions=new MGSHPositions();
      HedgePositions=new MGSHPositions();
      PricingExceptions=new MGSHPricingExceptions();
      StartDate=paramStartDate;
      TradeDate=paramStartDate;
      AnalysisDate=paramAnalysisDate;
      PathSessionFileName=paramPathSessionFileName;
      MGSHSessionParams sessionParams=null;

      Cycle=0;
      if(AnalysisDate.Date>Today().Date)return backTestResult;
      if(Utility.IsEpoch(AnalysisDate))AnalysisDate=Today();  
      TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate);
      if(TradeDate>AnalysisDate)
      {
        int startMonth=StartDate.Month;
        TimeSpan timeSpan=new TimeSpan();
        if((new int[]{12,3,6,9}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(30,0,0,0);     
        else if((new int[]{1,4,7,10}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(60,0,0,0);
        else if((new int[]{2,5,8,11}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(90,0,0,0);
        StartDate=StartDate-timeSpan;
        TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate);
      }
      if(null!=PathSessionFileName)
      {
        sessionParams=RestoreSession();
        DateTime currentMonthEndDate = dateGenerator.GetCurrentMonthEnd(TradeDate);
        if(TradeDate!=currentMonthEndDate)
        {
          TradeDate=currentMonthEndDate;
          AnalysisDate=TradeDate;
        }
      }
      if(null!=sessionParams)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using session file {0}, Last updated {1}, Current Trade Date: {2}",paramPathSessionFileName,sessionParams.LastUpdated.ToShortDateString(),TradeDate.ToShortDateString()));
      }
      Configuration.DisplayConfiguration();
      DisplayBalance();

      while(true)
      {
        if(TradeDate > AnalysisDate)break;
        int slotIndex=(int)(((double)Cycle)%((double)(HoldingPeriod)));
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TRADE DATE {0} , ANALYSIS DATE {1}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate),Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
        if(!ActivePositions.ContainsKey(slotIndex))
        {
          BuySlotPositions(slotIndex);
        }
        else
        {
          SellAndBuySlotPositions(slotIndex);
        }
        ManageHedgedPositions(TradeDate);
        DateTime nextBusinessDay = dateGenerator.FindNextBusinessDay(TradeDate);
        DateTime nextMonthEndDate = dateGenerator.GetNextMonthEnd(TradeDate);

// if the TradeDate and AnalysisDate are equal then set the trade date to the next business day and exit the cycle
        if(TradeDate.Equals(AnalysisDate))
        {
          TradeDate = nextBusinessDay;
          break;
        }

// This will enter the daily cycle which we need for backtesting
        if(Configuration.UseStopLimits || Configuration.UseHedging)
        {
          if(nextMonthEndDate >= AnalysisDate)
          {
            nextMonthEndDate=dateGenerator.FindNextBusinessDay(AnalysisDate);
          }
          TradeDate = nextBusinessDay;
          SaveSession();             
          UpdateDaily(nextBusinessDay,nextMonthEndDate,AnalysisDate,paramPathSessionFileName);   // This will save the session file
          sessionParams = RestoreSession();
        }
        Cycle++;
      }   // WHILE TRUE

      MDTrace.WriteLine(LogLevel.DEBUG,$"RUN COMPLETE FOR ANALYSIS DATE {AnalysisDate.ToShortDateString()}.");
      if(null!=PathSessionFileName)SaveSession();
      for(int slotIndex=0;slotIndex<HoldingPeriod;slotIndex++)
      {
        if(!ActivePositions.ContainsKey(slotIndex)||0==ActivePositions[slotIndex].Count())continue;
        MGSHPositions slotPositions=ActivePositions[slotIndex];
        SellPositions(slotPositions,AnalysisDate);
        MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L  ********************");
        slotPositions.Display();
        AllPositions.Add(slotPositions);
        CashBalance+=slotPositions.MarketValue;
        ActivePositions[slotIndex].Clear();
      }
      DisplayStatistics(sessionParams);
      MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L   P O S I T I O N S  *************");
      AllPositions=new MGSHPositions((from MGSHPosition position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
      AllPositions.Display();
      DisplayBalance();
      backTestResult.Success=true;
      backTestResult.CashBalance=CashBalance;
      GBPriceCache.GetInstance().Dispose();
      return backTestResult;
    }     
///  *******************************************************************************************************************************************************
///  ************************************************** S L O T  S A L E S  A N D  P U R C H A S E S  *****************************************************
///  *******************************************************************************************************************************************************
    public void BuySlotPositions(int slotIndex)
    {
      MGSHPositions positions = null;
      positions=BuyPositions(slotIndex, TradeDate, AnalysisDate, CashBalance/((double)HoldingPeriod-(double)ActivePositions.Count), SymbolsHeld(TradeDate));
      MDTrace.WriteLine(LogLevel.DEBUG, "******************** B U Y ********************");
      MDTrace.WriteLine(LogLevel.DEBUG, $"SLOT:{slotIndex}");
      if(CashBalance-positions.Exposure<0.00)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************"));
        return;
      }
      ActivePositions.Add(slotIndex,positions);
      CashBalance-=positions.Exposure;
      SetInitialStopLimitsForNewPositions(TradeDate, positions);
      positions.Display();
      UpdateStopLimitsForActivePositions(TradeDate);
      DisplayBalance();
    }

    public void SellAndBuySlotPositions(int slotIndex)
    {
      MGSHPositions slotPositions=ActivePositions[slotIndex];
      if(!Configuration.KeepSlotPositions)  // if we are not configured to KeepSlotPositions then don't sell anything just buy to the max positions allowed for the slot
      {
        SellPositions(slotPositions,TradeDate);
        MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L  *********************");
        slotPositions.Display();
        AllPositions.Add(slotPositions);
        CashBalance+=slotPositions.MarketValue;
        ActivePositions[slotIndex].Clear();
      }
      DisplayBalance();

      int positionsToFill =  MaxPositions-slotPositions.Count;
      double cashAllocation = (CashBalance / ((HoldingPeriod * MaxPositions) - ActivePositions.GetCount()))*positionsToFill;  // split the cash between the total positions we can own less the number of positions we have
      MGSHPositions positions = null;
      positions=BuyPositions(slotIndex, TradeDate,AnalysisDate,cashAllocation,SymbolsHeld(TradeDate), positionsToFill);
      if(CashBalance-positions.Exposure<=0.00)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************"));
      }
      else
      {
        MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************");
        if(!ActivePositions.ContainsKey(slotIndex))ActivePositions.Add(slotIndex, positions);
        else  ActivePositions[slotIndex].AddRange(positions);
        CashBalance-=positions.Exposure;
        SetInitialStopLimitsForNewPositions(TradeDate, positions);
        ActivePositions[slotIndex].Display();
      }
      UpdateStopLimitsForActivePositions(TradeDate);
      DisplayBalance();
    }

///  *******************************************************************************************************************************************************
///  *******************************************************************************************************************************************************
///  *******************************************************************************************************************************************************


// ********************************************************************************************************************************************************
// ********************************************************** P O S I T I O N  M A N A G E M E N T  *******************************************************
// ********************************************************************************************************************************************************
    /// <summary>
    /// Entry point for daily runs.  Make sure for daily runs that startDate=endDate=Trading Date after market close 
    /// The system does not process endDate.  (i.e.) startDate < endDate
    /// </summary>
    /// <param name="startDate">{ORIGINAL START DATE}</param>
    /// <param name="endDate">{TODAY}</param>
    /// <param name="paramPathSessionFileName">The session file name</param>
    /// <returns></returns>
    public MGSHBacktestResult UpdateDaily(DateTime startDate, DateTime endDate, DateTime analysisDate, String pathSessionFileName)
    {
      DateGenerator dateGenerator = new DateGenerator();
      PathSessionFileName = pathSessionFileName;

      RestoreSession();
      AnalysisDate=analysisDate;

      if(startDate != TradeDate)
      {      
        MDTrace.WriteLine(LogLevel.DEBUG,ConsoleColor.Red,$"Unexpected StartDate.  Start Date:{startDate.ToShortDateString()} Trade Date:{TradeDate.ToShortDateString()}");
        return new MGSHBacktestResult(false, CashBalance);
      }

      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("************** U P D A T E  D A I L Y **************"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Trade Date:{TradeDate.ToShortDateString()} Analysis Date:{AnalysisDate.ToShortDateString()}"));

      while(startDate < endDate)
      {
        TradeDate=startDate;
        if(Configuration.UseStopLimits)
        {
          UpdateStopLimitsForActivePositions(startDate);
        }

        if(Configuration.UseHedging)
        {
          ManageHedgedPositions(startDate);
        }

        DisplayBalance();
        startDate = dateGenerator.FindNextBusinessDay(startDate);
        TradeDate = startDate;
      }
      SaveSession();
      return new MGSHBacktestResult(true, CashBalance);
    }
// *******************************************************************************************************************************************************
// ************************************************************ H E D G E   M A N A G E M E N T    *******************************************************
// *******************************************************************************************************************************************************
    /// <summary>
    /// ManageHedgedPositions 
    /// </summary>
    /// <param name="analysisDate"></param>
    /// <returns>True if any changed were made otherwise false</returns>
    public bool ManageHedgedPositions(DateTime analysisDate)
    {
      HedgeManager hedgeManager = new HedgeManager(){HedgeShortSymbol=Configuration.HedgeShortSymbol,Verbose=false};
      bool changed = false;
      bool openHedgeIndicator = hedgeManager.IsOpenHedgeIndicator(analysisDate,Configuration.HedgeCloseAboveSMANDays, Configuration.HedgeBandBreakCheckDays);
      bool haveHedgedPositions = HedgePositions.Where(x => x.Symbol.Equals(Configuration.HedgeShortSymbol)).Count()>0?true:false;
      int numHedgePositions =  HedgePositions.Where(x => x.Symbol.Equals(Configuration.HedgeShortSymbol)).Count();

// There are no hedged positions and the indicator is off
      if(!openHedgeIndicator && !haveHedgedPositions)
      {
        return changed;
      }

// The indicator is on and we have no hedged positions 
      else if(openHedgeIndicator && !haveHedgedPositions)
      {
        MGSHPosition shortPosition =  BuyHedgePosition(analysisDate, HedgeCashBalance);
        if(null != shortPosition)
        {
          changed = true;
          MDTrace.WriteLine(LogLevel.DEBUG, "******************** B U Y ********************");
          MGSHPosition.DisplayHeader();
          shortPosition.Display();
          HedgePositions.Add(shortPosition);
          HedgeCashBalance-=shortPosition.Exposure;
        }
      }
      else if(openHedgeIndicator && haveHedgedPositions)
      {
        changed = ManageActiveHedgePositions(analysisDate);
      }
      else if(!openHedgeIndicator && haveHedgedPositions)
      {
        changed = ManageActiveHedgePositions(analysisDate);
      }
      return changed;
    }

    /// <summary>
    /// Attempts to purchase the hedge with the supplied cash allocation
    /// </summary>
    /// <param name="analysisDate"></param>
    /// <param name="cashAllocation"></param>
    /// <returns>Position on success otherwise null</returns>
    public MGSHPosition BuyHedgePosition(DateTime analysisDate, double cashAllocation)
    {
      Price shortPositionPrice = GBPriceCache.GetInstance().GetPrice(Configuration.HedgeShortSymbol,analysisDate);
      if(null == shortPositionPrice)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Cannot locate a price for {Configuration.HedgeShortSymbol} on {analysisDate.ToShortDateString()}"));
        return null;
      }
      MGSHPosition shortPosition = new MGSHPosition();
      shortPosition.Symbol = Configuration.HedgeShortSymbol;
      shortPosition.PurchaseDate = analysisDate;
      shortPosition.PurchasePrice = shortPositionPrice.Close;
      shortPosition.CurrentPrice = shortPositionPrice.Close;
      shortPosition.InitialStopLimit = shortPosition.PurchasePrice - ( shortPosition.PurchasePrice * Configuration.HedgeRiskPercentDecimal);
      shortPosition.PositionRiskPercentDecimal = Configuration.HedgeRiskPercentDecimal;             // The risk percent
      shortPosition.R = shortPosition.PositionRiskPercentDecimal * shortPosition.PurchasePrice;     // PositionRiskPercentDecimal*PurchasePrice
      shortPosition.TrailingStopLimit = shortPosition.InitialStopLimit;
      shortPosition.LastStopAdjustment = Utility.Epoch;
      shortPosition.Shares = (int)Math.Floor(cashAllocation / shortPositionPrice.Close);

      if(0 == shortPosition.Shares)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Insufficient Cash Allocation to purchase shares of {Configuration.HedgeShortSymbol} on {analysisDate.ToShortDateString()}.  Cash Allocation:{Utility.FormatCurrency(cashAllocation)}"));
        return null;
      }

      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Initial stop limit for {0} on {1} with PurchsePrice:{2} is {3}, Shares:{4} Risk:{5}%",
        shortPosition.Symbol,
        shortPosition.PurchaseDate.ToShortDateString(),
        Utility.FormatCurrency(shortPosition.PurchasePrice,2),
        Utility.FormatCurrency(shortPosition.InitialStopLimit,2),
        Utility.FormatNumber(shortPosition.Shares,2),
        Utility.FormatPercent(Configuration.HedgeRiskPercentDecimal)
       ));

      return shortPosition;
    }

    /// <summary>
    /// Manages open hedge position by checking and adjusting stop price and determining if the hedge should exit
    /// </summary>
    /// <param name="analysisDate"></param>
    /// <returns></returns>
    public bool ManageActiveHedgePositions(DateTime analysisDate)
    {
      HedgeManager hedgeManager = new HedgeManager(){HedgeShortSymbol=Configuration.HedgeShortSymbol,Verbose=false};
      List<MGSHPosition> closedPositions = new List<MGSHPosition>();

      foreach(MGSHPosition position in HedgePositions)
      {
        Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,analysisDate);

        if(null==price)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[ManageActiveHedgePositions] No price for {0} on {1}.  Cannot evaluate stop limit.",position.Symbol,analysisDate.ToShortDateString()));
          continue;
        }

        position.CurrentPrice = price.Close;
        if(price.Low < position.TrailingStopLimit && !position.PurchaseDate.Equals(analysisDate))
        {
          position.SellDate = analysisDate;
          position.CurrentPrice = position.TrailingStopLimit;
          position.Comment = "Closed due to trailing stop.";
          HedgeCashBalance+=position.MarketValue;

// This tries to maintain a hedgeCashMarginPercentDecimal margin in the hedge cash. In other words some % of the hedge gain -> Cash and some -> hedge cash, not allowing hedge cash to grow some margin beyond it's initial setting
          double hedgeCashMarginPercentDecimal=.10;
          double hedgeCashTreshholdAmount=Configuration.HedgeInitialCash*(1.00+hedgeCashMarginPercentDecimal);
          if(HedgeCashBalance > hedgeCashTreshholdAmount)
          {
            DisplayBalance();
            MDTrace.WriteLine(LogLevel.DEBUG,$"The hedge cash balance would be {Utility.FormatCurrency(HedgeCashBalance)}, setting HedgeCashBalance to {Utility.FormatCurrency(hedgeCashTreshholdAmount)} and adding {Utility.FormatCurrency((HedgeCashBalance-hedgeCashTreshholdAmount))} to CashBalance.");
            CashBalance += (HedgeCashBalance-hedgeCashTreshholdAmount);
            HedgeCashBalance = hedgeCashTreshholdAmount;
            DisplayBalance();
          }
          if(HedgeCashBalance<0.00)
          {
            HedgeCashBalance=0.00;
          }

          // Retain this original code for a while until happy with the hedge cash margin approach
          //if(HedgeCashBalance>Configuration.HedgeInitialCash)
          //{
          //  CashBalance+=HedgeCashBalance-Configuration.HedgeInitialCash;  // if we made gains on the hedge and the hedge cash would grow then put proceeds above the initial hedge cash in regualr cash
          //  HedgeCashBalance=Configuration.HedgeInitialCash;               // For example, let hedge proceeeds help the portfolio invest
          //}

          AllPositions.Add(position);
          closedPositions.Add(position);
        }
        else if(hedgeManager.IsLowerBandBreakIndicator(analysisDate, position))
        {
          StopLimit stopLimit = hedgeManager.EvaluateStopPriceHedge(analysisDate, position,Configuration);
          if(null != stopLimit)
          {
            StopLimits.Add(stopLimit);
          }
        }
      }

      if(0!=closedPositions.Count)
      {
        foreach(MGSHPosition position in closedPositions)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,"");
          MDTrace.WriteLine(LogLevel.DEBUG,"******************************************** S E L L  S T O P  L I M I T ********************************************");
          position.Display();
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Gain/Loss:{Utility.FormatCurrency(position.GainLoss)} Gain/Loss(%):{Utility.FormatPercent(position.GainLossPcnt)}"));
          HedgePositions.Remove(position);
        }
      }
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*************************************************************************************************",analysisDate.ToShortDateString()));
      if(closedPositions.Count>0)return true;
      return false;
    }

    // *************************************************************************************************************************************************
    // ************************************************************ E N D  M A N A G E  H E D G I N G   ************************************************
    // *************************************************************************************************************************************************

    // **********************************************************************************************************************************************************
    // ********************************************************************** S T O P  L I M I T S ***************************************************************
    // **********************************************************************************************************************************************************
    public bool SetInitialStopLimitsForNewPositions(DateTime tradeDate,MGSHPositions positions)
    {
      if(!Configuration.UseStopLimits)return true;

      if(null == positions || 0==positions.Count)
      {
        return true;
      }

      foreach(MGSHPosition position in positions)
      {
        position.InitialStopLimit = position.PurchasePrice - (position.PurchasePrice * Configuration.StopLimitRiskPercentDecimal);
        position.PositionRiskPercentDecimal = Configuration.StopLimitRiskPercentDecimal; //    = position.PurchasePrice - position.InitialStopLimit;
        position.R = position.PositionRiskPercentDecimal * position.PurchasePrice;     // PositionRiskPercentDecimal*PurchasePrice
        position.TrailingStopLimit = position.InitialStopLimit;
        position.LastStopAdjustment = Utility.Epoch;
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Initial stop limit for {0} on {1} with PurchsePrice:{2} is {3}, Shares:{4} Risk:{5}%",
          position.Symbol,
          position.PurchaseDate.ToShortDateString(),
          Utility.FormatCurrency(position.PurchasePrice,2),
          Utility.FormatCurrency(position.InitialStopLimit,2),
          Utility.FormatNumber(position.Shares,2),
          Utility.FormatPercent(Configuration.StopLimitRiskPercentDecimal)
          ));
      }
      return true;
    }

    /// <summary>
    /// Updates stop limits for active positions and closes positions that have stop limit violations
    /// </summary>
    /// <returns>true if any positions were closed, otherwise returns false</returns>
    public bool UpdateStopLimitsForActivePositions(DateTime analysisDate)
    {
      if(!Configuration.UseStopLimits)return true;
      MGSHPositions closedPositions = new MGSHPositions();

      MDTrace.WriteLine(LogLevel.DEBUG,"************************** U P D A T E  S T O P   L I M I T S   F O R   A C T I V E   P O S I T I O N S  ************");
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("ANALYSIS DATE:{0}",analysisDate.ToShortDateString()));
      List<int> slotKeys = new List<int>(ActivePositions.Keys);
      for(int slotIndex = 0; slotIndex< slotKeys.Count;slotIndex++)
      {
        MGSHPositions positions = ActivePositions[slotKeys[slotIndex]];
        foreach(MGSHPosition position in positions)
        {
          Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,analysisDate);
          if(null==price)
          {
            int exceptionCount=AddPricingException(position.Symbol);
            if(exceptionCount>MaxPricingExceptions)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[UpdateStopLimitsForActivePositions] Selling {0} on {1} because price exceptions exceeds maximum of {2}",position.Symbol,analysisDate.ToShortDateString(),MaxPricingExceptions));
              price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,analysisDate);
              position.SellDate=analysisDate;
              position.CurrentPrice=price.Close;
              position.Comment="Close due to pricing exceptions.";
              CashBalance+=position.MarketValue;
              AllPositions.Add(position);
              closedPositions.Add(position);
            }
            else MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[UpdateStopLimitsForActivePositions] Cannot determine price for {0} on {1}",position.Symbol,analysisDate.ToShortDateString()));
            continue;
          }
          // This is an older position for which we never set an initial stop.  We will not adjust these
          if (double.IsNaN(position.R) || double.IsNaN(position.TrailingStopLimit) || double.IsNaN(position.InitialStopLimit))
          {
            MDTrace.WriteLine(LogLevel.DEBUG, String.Format("[UpdateStopLimitsForActivePositions] Position {0} on {1} is legacy will not adjust stop limit.", position.Symbol, analysisDate.ToShortDateString()));
            continue;
          }
          position.CurrentPrice = price.Close;
          RemovePricingException(position.Symbol);
          if (price.Low < position.TrailingStopLimit && !position.PurchaseDate.Equals(analysisDate))
          {
            position.SellDate = analysisDate;
            position.CurrentPrice = position.TrailingStopLimit;
            position.Comment = "Closed due to trailing stop.";
            CashBalance += position.MarketValue;
            AllPositions.Add(position);
            closedPositions.Add(position);
          } 
          else if (price.Low > position.PurchasePrice) // If the Low price is above our purchase price then re-evaluate the stop
          {
            EvaluateStopPrice(analysisDate, price, position);
          }
          else
          {
            MDTrace.WriteLine(LogLevel.DEBUG, String.Format("[UpdateStopLimitsForActivePositions] No stop adjustment for {0} on {1} because low price {2} is less then or equal to the purchase price {3}.", 
              position.Symbol, 
              analysisDate.ToShortDateString(),
              Utility.FormatCurrency(price.Low),
              Utility.FormatCurrency(position.PurchasePrice)));
          }
        }
      }
      if(0!=closedPositions.Count)
      {
        foreach(MGSHPosition position in closedPositions)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,"");
          MDTrace.WriteLine(LogLevel.DEBUG,"******************************************** S E L L  S T O P  L I M I T ********************************************");
          position.Display();
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Gain/Loss:{Utility.FormatCurrency(position.GainLoss)} Gain/Loss(%):{Utility.FormatPercent(position.GainLossPcnt)}"));
          ActivePositions.Remove(position);
        }
      }
      if(closedPositions.Count>0)return true;
      return false;
    }

    /// <summary>
    /// Evaluates stop prices for slot positons to see if we need to perform updates
    /// </summary>
    /// <returns>true if changes were made otherwise false</returns>
    private bool EvaluateStopPrice(DateTime analysisDate,Price currentPrice,MGSHPosition position)
    {
      bool changed = false;

	    MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EvaluateStopPrice: {0} on {1}",position.Symbol,analysisDate.ToShortDateString()));
      DateGenerator dateGenerator=new DateGenerator();

      // only adjust stops if we are trending up
      Prices prices=GBPriceCache.GetInstance().GetPrices(position.Symbol,analysisDate,Configuration.StopLimitPriceTrendDays);   
      PriceTrendIndicatorResult priceTrendIndicatorResult=PriceTrendIndicator.IsUptrend(prices,Configuration.StopLimitPriceTrendDays);
      if (!priceTrendIndicatorResult.IsUpTrend)
      {
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0} does not have upward price trend (higher highs and higher lows for {1} consecutive days), will not adjust stop price", position.Symbol, Configuration.StopLimitPriceTrendDays));
        return changed;
      }

      // where R = Risk Per Share in $
      double trailingStop=position.InitialStopLimit+Math.Floor((currentPrice.Low-position.PurchasePrice)/position.R)*position.R; 
      double trailingStopScaled=trailingStop;
      double daysHeld=Math.Abs(dateGenerator.DaysBetweenActual(position.PurchaseDate,analysisDate));
      if(Utility.IsEpoch(position.LastStopAdjustment))          // we've never adjusted the stop price
      {
        if(daysHeld>=Configuration.MinDaysBetweenInitialStopAdjustment)
        {
          trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(analysisDate,currentPrice,position,trailingStop);
          trailingStop=Math.Max(trailingStop,trailingStopScaled);

          if(trailingStop>=currentPrice.Low || trailingStop>=currentPrice.Close)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(currentPrice.Low)));
            return changed;
          }

          if(Numerics.Round(trailingStop)<=(Numerics.Round(position.TrailingStopLimit)))
					{
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be less than or equal to the existing stop limit of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(position.TrailingStopLimit)));
            return changed;
					}

          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T   S T O P  L I M I T ************************"));
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit after {0} days for {1} from {2} to {3}.  Purchase Price: {4} Current Price:{5} Spread:{6} Shares:{7}",
            daysHeld,
            position.Symbol,Utility.FormatCurrency(position.TrailingStopLimit),
            Utility.FormatCurrency(trailingStop),
            Utility.FormatCurrency(position.PurchasePrice),
            Utility.FormatCurrency(currentPrice.Close),
            Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),
            Utility.FormatNumber(position.Shares,2)));
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*****************************************************************************************"));

          StopLimit newStopLimit=new StopLimit
          {
            StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate),
            Symbol=position.Symbol,
            AnalysisDate=analysisDate,
            PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit,
            NewStop=trailingStop,
            CurrentPriceLow=currentPrice.Low,
            CurrentPriceClose=currentPrice.Close,
            PriceTrendIndicatorSlope=priceTrendIndicatorResult.LowPriceSlope
          };
          AddStopLimit(newStopLimit);
          position.TrailingStopLimit=trailingStop;
          position.LastStopAdjustment=analysisDate;
          changed=true;
        }
        else 
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The initial stop for {0} will not be modified until the grace period is reached.  Days Held:{1} MinDaysBetweenInitialStopAdjustment:{2} ",
          position.Symbol,daysHeld,Configuration.MinDaysBetweenInitialStopAdjustment));
        }
      }
      else  // we have already made prior stop adjustments
      {
        int daysSinceLastStopAdjustment=Math.Abs(dateGenerator.DaysBetweenActual(position.LastStopAdjustment,analysisDate));
        if(daysSinceLastStopAdjustment>=Configuration.MinDaysBetweenStopAdjustments)
        {
          trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(analysisDate,currentPrice,position,trailingStop);
          trailingStop=Math.Max(trailingStop,trailingStopScaled);
          if(trailingStop>=currentPrice.Low||trailingStop>=currentPrice.Close)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(currentPrice.Low)));
            return changed;
          }
          if(Numerics.Round(position.TrailingStopLimit) < Numerics.Round(trailingStop))  // round the stop limits to fractionals don't look like differences
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T   S T O P  L I M I T ************************"));
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit after {0} days for {1} from {2} to {3}.  Purchase Price: {4} Current Price:{5} Spread:{6} Shares:{7}",daysHeld,position.Symbol,Utility.FormatCurrency(position.TrailingStopLimit),Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(currentPrice.Close),Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),Utility.FormatNumber(position.Shares,2)));
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************************************************************************"));
            StopLimit newStopLimit=new StopLimit
            {
              StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate),
              Symbol=position.Symbol,
              AnalysisDate=analysisDate,
              PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit,
              NewStop=trailingStop,
              CurrentPriceLow=currentPrice.Low,
              CurrentPriceClose=currentPrice.Close,
              PriceTrendIndicatorSlope=priceTrendIndicatorResult.LowPriceSlope
            };
            AddStopLimit(newStopLimit);
            position.TrailingStopLimit=trailingStop;
            position.LastStopAdjustment=analysisDate;
            changed = true;
          }
          else
          {
            double currentTrailingStopLimit=Numerics.Round(position.TrailingStopLimit);
            double suggestedTrailingStopLimit=Numerics.Round(trailingStop);
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Not Adjusting Stop Limit for {0} because the new trailing stop limit would be less than or equal to the current trailing stop limit. Current Trailing Stop Limit:{1}, Calculated Trailing Stop Limit:{2}.",position.Symbol,Utility.FormatCurrency(currentTrailingStopLimit),Utility.FormatCurrency(suggestedTrailingStopLimit)));
          }
        }
        else
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The trailing stop for {0} was adjusted {1} days ago.  MinDaysBetweenStopAdjustments is {2}",
            position.Symbol,
            daysSinceLastStopAdjustment,
            Configuration.MinDaysBetweenStopAdjustments));
        }
      }
      return changed;
    }

    private double GetStopLimitWithScalingAverageTrueRange(DateTime tradeDate,Price currentPrice,MGSHPosition position,double stopLimitNonScaled)
    {
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetStopLimitWithScalingAverageTrueRange: Symbol:{0}",position.Symbol));      
      double volatility=double.NaN;
      volatility=VolatilityGenerator.CalculateVolatility(position.Symbol,tradeDate,Configuration.StopLimitScalingVolatilityDays, Configuration.StopLimitATRMultiplier);
      if(double.IsNaN(volatility))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Unable to calculate AverageTrueRange for {0} on {1}.  Using non-scaled stop limit.",position.Symbol,tradeDate.ToShortDateString()));
        return stopLimitNonScaled;
      }
      double stopLimit = currentPrice.Low - volatility;  // We base the stop off of the low in order to give a bit more breathing room in the stop in the event that we have a wide spread between the close and the low.  Backtested currentPrice.Close vs currentPrice.Low and basing off the low yields better results.
      return stopLimit;
    }

// **************************************************************************************************************************************
// **************************************************  S T O P  L I M I T  C O L L E C T I O N   M A I N T E N A N C E ******************
// **************************************************************************************************************************************
    private void AddStopLimit(StopLimit stopLimit)
    {
      if(null == StopLimits)StopLimits = new StopLimits();
      StopLimits.Add(stopLimit);
    }

// ************************************************************************************************************************************************
// *********************************************  P R I C I N G  E X C E P T I O N   C O L L E C T I O N   M A I N T E N A N C E ******************
// ************************************************************************************************************************************************

    private int AddPricingException(String symbol)
    {
      MGSHPricingException pricingException = PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
      if (null == pricingException) 
      { 
        pricingException = new MGSHPricingException(symbol, 1);
        PricingExceptions.Add(pricingException); 
        PricingExceptions.Add(pricingException); 
      } 
      else pricingException.ExceptionCount++;
      return pricingException.ExceptionCount;
    }

    private void RemovePricingException(String symbol)
    {
      MGSHPricingException pricingException = PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
      if (null == pricingException) return;
      PricingExceptions.Remove(pricingException);
    }

    private bool HasPricingException(String symbol)
    {
      MGSHPricingException pricingException = PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
      if (null == pricingException) return false;
      return true;
    }

// ******************************************************************************************************************************************************
// ******************************************************************************************************************************************************
// ******************************************************************************************************************************************************
    public List<String> SymbolsHeld(DateTime tradeDate)
    {
      List<String> symbolsHeld=ActivePositions.SymbolsHeld();
      if(!Configuration.IncludeTradeMasterForSymbolsHeld)return symbolsHeld;
      if(null == symbolsHeld)symbolsHeld=new List<String>();
      PortfolioTrades portfolioTrades=PortfolioDA.GetOpenTradesAsOf(tradeDate);
      if(null == portfolioTrades || 0==portfolioTrades.Count)return symbolsHeld;
      symbolsHeld.AddRange(portfolioTrades.Symbols.Distinct());
      return symbolsHeld;
    }  

// **********************************************************************************************************************************************************
// **************************************************************** G E T  E X P O S U R E  / M A R K E T  V A L U E *****************************************
// **********************************************************************************************************************************************************
    public RealtimeGainLoss GetRealtimeGainLoss(DateTime tradeDate)
    {
      int count=ActivePositions.Count;
      double marketValue=0.00;
      double exposure=0.00;
      RealtimeGainLoss gainLoss=new RealtimeGainLoss();

      for(int slotIndex=0;slotIndex<count;slotIndex++)
      {
        List<MGSHPosition> positions=ActivePositions[slotIndex];
        if(null==positions||0==positions.Count)continue;
        foreach(MGSHPosition position in positions)
        {
          Price price=GBPriceCache.GetInstance().GetRealtimePrice(position.Symbol);
          if(null==price){MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot price {0} on {1}",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));continue;}
          position.CurrentPrice=price.Close;
        }
      }
      for(int slotIndex=0;slotIndex<count;slotIndex++)
      {
        List<MGSHPosition> positions=ActivePositions[slotIndex];
        if(null==positions||0==positions.Count)continue;
        exposure+=(from MGSHPosition position in positions select position.Exposure).Sum();
        marketValue+=(from MGSHPosition position in positions select position.MarketValue).Sum();
      }
      gainLoss.Exposure=exposure;
      gainLoss.MarketValue=marketValue;
      return gainLoss;
    }

// **************************************************************************************************************************************************
// **************************************************************** S E L L   P O S I T I O N S  *****************************************************
// ***************************************************************************************************************************************************
    private void SellPositions(MGSHPositions positions,DateTime sellDate)
    {
      foreach(MGSHPosition position in positions)
      {
        SellPosition(position, sellDate);
      }
    }

    private void SellPosition(MGSHPosition position,DateTime sellDate)
    {
      Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,sellDate);
      position.SellDate=sellDate;
      if(null==price)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
        position.CurrentPrice=position.PurchasePrice;
      }
      else position.CurrentPrice=price.Close;
    }

// ************************************************************************************************************************************************************
// ***********************************************************  B U Y  S L O T  P O S I T I O N S  ************************************************************
// ************************************************************************************************************************************************************
    private MGSHPositions BuyPositions(int slotIndex, DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld, double maxPositions=double.NaN)
    {
      MGSHPositions positions = new MGSHPositions();
      if(double.IsNaN(maxPositions))maxPositions=MaxPositions;
      if(0==maxPositions)return positions;
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"BUY POSITIONS: SLOT:{slotIndex} TRADE DATE:{TradeDate.ToShortDateString()} CASH:{Utility.FormatCurrency(cash)} POSITIONS TO FILL:{maxPositions}"));
      int positionCount = 0;
      if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash);
      MGSHMomentumCandidates momentumCandidates = MGSHMomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration);
      for (int index = 0; index < momentumCandidates.Count; index++)
      {
        MGSHMomentumCandidate momentumCandidate = momentumCandidates[index];
        Price price = GBPriceCache.GetInstance().GetPrice(momentumCandidate.Symbol, tradeDate);
        if (null == price)
        {
          MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", momentumCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
          continue;
        }
        MGSHPosition position = new MGSHPosition();
        position.Symbol = momentumCandidate.Symbol;
        position.CumReturn252 = momentumCandidate.CumReturn252;
        position.IDIndicator = momentumCandidate.IDIndicator;
        position.Score=momentumCandidate.Score;
        position.PE = momentumCandidate.PE;
        position.Beta = momentumCandidate.Beta;
        position.Velocity = momentumCandidate.Velocity;
        position.Volume = momentumCandidate.Volume;
        position.Return1D = position.Return1D;
        position.PurchaseDate = tradeDate;
        position.PurchasePrice = price.Close;
        position.CurrentPrice = price.Close;
        position.Shares = (int)Math.Floor((cash / (double)MaxPositions) / position.PurchasePrice);   // make sure the lot is evenly divided by the MaxPositions allowble for the slot
        if (0 == position.Shares) continue;    // if not able to purchase any shares, for example, if the share price exceeds our purchasing power then move on to the next candidate
        positions.Add(position);
        positionCount++;
        if (positionCount >= maxPositions) break;
      }
      if (0 == positions.Count && Configuration.UseFallbackCandidate)    // if we don't get any signals then consider the fallback candidate options as per the configuration file
      {
        String fallbackCandidate = Configuration.FallbackCandidate;
        if (null != Configuration.FallbackCandidateBestOf && !"".Equals(Configuration.FallbackCandidateBestOf))
        {
          MGSHQualityIndicator qualityIndicator = new MGSHQualityIndicator(Configuration.QualityIndicatorType);
          fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(qualityIndicator,Utility.ToList(Configuration.FallbackCandidateBestOf),Configuration.FallbackCandidate,tradeDate);
        }
        Price price = GBPriceCache.GetInstance().GetPrice(fallbackCandidate, tradeDate);
        if (null == price)
        {
          MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", fallbackCandidate, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
          return positions;
        }
        MGSHPosition position = new MGSHPosition();
        position.Symbol = fallbackCandidate;
        position.PurchaseDate = tradeDate;
        position.PurchasePrice = price.Close;
        position.CurrentPrice = price.Close;
        position.Shares = (int)Math.Floor(cash / position.PurchasePrice);

        if(0==position.Shares)
        {
          MDTrace.WriteLine(LogLevel.DEBUG, String.Format("************** Insufficient cash to purchase shares of {0} on {1}.  Available Cash:{2} Price:{3} ************", 
            fallbackCandidate, 
            Utility.DateTimeToStringMMHDDHYYYY(tradeDate),
            Utility.FormatCurrency(cash),
            Utility.FormatCurrency(price.Close)));
          return positions;
        }

        positions.Add(position);
      }
      return positions;
    }

    /// <summary>
    /// Buys the benchmark mode symbol (BenchmarModeSymbol) if the configuration is set to BenchmarkMode (BenchmarkMode)
    /// </summary>
    /// <returns>Positions</returns>
    private MGSHPositions BuyBenchmarkPositions(DateTime tradeDate, double cash)
    {
      MGSHPositions positions = new MGSHPositions();
      Price benchmarkPrice = GBPriceCache.GetInstance().GetPrice(Configuration.BenchmarkModeSymbol, tradeDate);
      if (null == benchmarkPrice)
      {
        MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", Configuration.BenchmarkModeSymbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
        return positions;
      }
      MGSHPosition position = new MGSHPosition();
      position.Symbol = Configuration.BenchmarkModeSymbol;
      position.PurchaseDate = tradeDate;
      position.PurchasePrice = benchmarkPrice.Close;
      position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
      if (0 == position.Shares) return positions;
      positions.Add(position);
      return positions;
    }

// *********************************************************************************************************************************************
// ************************************************************ E N D   B U Y  P O S I T I O N S ***********************************************
// *********************************************************************************************************************************************

//******************************************************************************************************************************************************
//************************************************************* S T A T I S T I C S ********************************************************************
//******************************************************************************************************************************************************
    private void DisplayStatistics(MGSHSessionParams sessionParams)
    {
      ModelStatistics modelStatistics = GetModelStatistics(sessionParams);
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("************** M O D E L  S T A T I S T I C S **************"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Trades:{modelStatistics.TotalTrades}"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Winning Trades:{modelStatistics.WinningTrades}"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Losing Trades:{modelStatistics.LosingTrades}"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Average Winning Trade Percent Gain:{Utility.FormatNumber(modelStatistics.AverageWinningTradePercentGain,2)}%"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Average Losing Trade Percent Loss:{Utility.FormatNumber(modelStatistics.AverageLosingTradePercentLoss,2)}%"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Winning Trades Percent:{Utility.FormatNumber(modelStatistics.WinningTradesPercent,2)}%"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Losing Trades Percent:{Utility.FormatNumber(modelStatistics.LosingTradesPercent,2)}%"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Expectancy:{Utility.FormatNumber(modelStatistics.Expectancy,2)}"));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**************************************************************"));
    }

//******************************************************************************************************************************************************
//****************************************************************** D I S P L A Y   B A L A N C E *****************************************************
//****************************************************************************************************************************************************** 
    private void DisplayBalance()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************");
      MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE, AVAILABLE CASH, TOTAL PORTFOLIO ACCOUNT (MV+CASH), AVAILABLE HEDGE CASH, TOTAL PORTFOLIO ACCOUNT + HEDGES");
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure()+HedgePositions.Exposure)),
        Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(HedgeCashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+HedgePositions.MarketValue+CashBalance))
        ));

      List<int> keys = new List<int>(ActivePositions.Keys);
      keys.Sort();
      for (int keyIndex = 0; keyIndex < keys.Count; keyIndex++)
      {
        MGSHPositions slotPositions = ActivePositions[keys[keyIndex]];     
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SLOT:{0} POSITIONS:{1} EXPOSURE:{2} SYMBOLS:{3}",
          keys[keyIndex],slotPositions.Count,
          Utility.FormatCurrency(slotPositions.Exposure),
          Utility.ListToString(slotPositions.Symbols(),',')));
      }

      if(HedgePositions.Count>0)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("HEDGES:{0} EXPOSURE:{1} SYMBOLS:{2}",
          HedgePositions.Count,
          Utility.FormatCurrency(HedgePositions.Exposure),
          Configuration.HedgeShortSymbol));
      }

      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE: {0}",Utility.FormatCurrency(ActivePositions.GetExposure()+HedgePositions.Exposure)));
      MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************");
    }

    private void DisplayBalanceFromPositions()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE, GAIN/LOSS, GAIN/LOSS(%), AVAILABLE CASH, TOTAL PORTFOLIO ACCOUNT (MV+CASH), AVAILABLE HEDGE CASH, TOTAL PORTFOLIO ACCOUNT + HEDGES");
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4},{5}",
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetGainLoss())),
        Utility.AddQuotes(Utility.FormatPercent(ActivePositions.GetGainLossPercent())),
        Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(HedgeCashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+HedgePositions.MarketValue+CashBalance))
        ));
    }

    private void DisplayBalance(RealtimeGainLoss gainLoss)
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE, GAIN/LOSS, GAIN/LOSS(%), AVAILABLE CASH, TOTAL PORTFOLIO ACCOUNT (MV+CASH), AVAILABLE HEDGE CASH, TOTAL PORTFOLIO ACCOUNT + HEDGES");
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4},{5}",
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
        Utility.AddQuotes(Utility.FormatCurrency(gainLoss.GainLoss)),
        Utility.AddQuotes(Utility.FormatPercent(gainLoss.GainLossPercent)),
        Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(HedgeCashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+HedgePositions.MarketValue+CashBalance))
        ));
    }
// ****************************************************************************************************************************************
// ************************************************************* C O N T R O L  T O D A Y ***********************************************
// ****************************************************************************************************************************************
    public DateTime Today()
    {
      return DateTime.Now;
    }

// ****************************************************************************************************************************************
// **************************************************************** S E S S I O N  M A N A G E M E N T  ***********************************
// ****************************************************************************************************************************************
    public MGSHSessionParams RestoreSession()
    {
      try
      {
        if(!MGSHSessionManager.SessionAvailable(PathSessionFileName))return null;
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Restoring session from '{0}'",PathSessionFileName));
        MGSHSessionParams sessionParams=MGSHSessionManager.RestoreSession(PathSessionFileName);
        TradeDate=sessionParams.TradeDate;
        StartDate=sessionParams.StartDate;
        Configuration=sessionParams.Configuration;
        ActivePositions=sessionParams.ActivePositions;
        AllPositions=sessionParams.AllPositions;
        HedgePositions=sessionParams.HedgePositions;
        StopLimits=sessionParams.StopLimits;
        PricingExceptions = sessionParams.PricingExceptions;
        Cycle=sessionParams.Cycle;
        CashBalance=sessionParams.CashBalance;
        NonTradeableCash=sessionParams.NonTradeableCash;
        HedgeCashBalance=sessionParams.HedgeCashBalance;
        return sessionParams;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
        return null;
      }
    }
    public void SaveSession()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",PathSessionFileName));
      MGSHSessionParams sessionParams=new MGSHSessionParams();
      sessionParams.LastUpdated=Today();
      sessionParams.TradeDate=TradeDate;
      sessionParams.StartDate=StartDate;
      sessionParams.AnalysisDate=AnalysisDate;
      sessionParams.Configuration=Configuration;
      sessionParams.ActivePositions=ActivePositions;
      sessionParams.AllPositions=AllPositions;
      sessionParams.HedgePositions=HedgePositions;
      sessionParams.Cycle=Cycle;
      sessionParams.StopLimits=StopLimits;
      sessionParams.PricingExceptions=PricingExceptions;
      sessionParams.CashBalance=CashBalance;
      sessionParams.NonTradeableCash=NonTradeableCash;
      sessionParams.HedgeCashBalance=HedgeCashBalance;
      MGSHSessionManager.SaveSession(sessionParams,PathSessionFileName);
    }

    public bool BackupSession()
    {
      String[] parts=PathSessionFileName.Split('.');
      String backupFileName=parts[0]+"_"+Utility.DateTimeToStringYYYYMMDDMMSSTT(DateTime.Now)+".bak";
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",backupFileName));
      MGSHSessionParams sessionParams=new MGSHSessionParams();
      sessionParams.LastUpdated=Today();
      sessionParams.TradeDate=TradeDate;
      sessionParams.StartDate=StartDate;
      sessionParams.AnalysisDate=AnalysisDate;
      sessionParams.Configuration=Configuration;
      sessionParams.ActivePositions=ActivePositions;
      sessionParams.AllPositions=AllPositions;
      sessionParams.HedgePositions=HedgePositions;
      sessionParams.Cycle=Cycle;
      sessionParams.StopLimits=StopLimits;
      sessionParams.PricingExceptions=PricingExceptions;
      sessionParams.CashBalance=CashBalance;
      sessionParams.NonTradeableCash=NonTradeableCash;
      sessionParams.HedgeCashBalance=HedgeCashBalance;
      return MGSHSessionManager.SaveSession(sessionParams,backupFileName);
    }
  }
}
