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

namespace MarketData.Generator.CMTrend
{
  public class CMTTrendModel
  {
    public enum RunType { BacktestResult, Daily };
    private double CashBalance { get; set; }
    private double NonTradeableCash{get;set;}
    public CMTParams Parameters { get; set; }
    private int MaxDailyPositions { get { return Parameters.MaxDailyPositions; } }
    private int MaxOpenPositions { get { return Parameters.MaxOpenPositions; } }
    private int MaxPricingExceptions { get { return Parameters.MaxPricingExceptions; } }
    private ActivePositions ActivePositions { get; set; }
    private Positions AllPositions { get; set; }
    private CMTCandidates Candidates { get; set; }
    private CMTPricingExceptions PricingExceptions { get; set; }
    private StopLimits StopLimits { 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();
      if(null!=paramPathSessionFileName) paramPathSessionFileName=paramPathSessionFileName.Trim();
      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
      {
        CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(paramPathSessionFileName);
        return GetModelPerformance(sessionParams);
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
        return null;
      }
    }

    // Calcualtes the expectation for the model ( Percent of Winning Trades * Average Gain)/(Percent Losing Trades * Average Loss)
    // The expectation should be above zero
    public static ModelStatistics GetModelStatistics(CMTSessionParams 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(CMTSessionParams sessionParams)
    {
      Profiler profiler=new Profiler();
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      DateGenerator dateGenerator=new DateGenerator();
      try
      {
        if(null==sessionParams) return null;
        MarketData.Generator.CMTrend.Positions 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)
        {
          MarketData.Generator.CMTrend.Positions openPositions=new MarketData.Generator.CMTrend.Positions(combinedPositions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList());
          MarketData.Generator.CMTrend.Positions closedPositions=new MarketData.Generator.CMTrend.Positions(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(MarketData.Generator.CMTrend.Position 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()));
              return performanceSeries;
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(MarketData.Generator.CMTrend.Position 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;
      CMTSessionParams sessionParams=null;
      if(null==(sessionParams=RestoreSession()))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",paramPathSessionFileName));
        return;
      }
      Console.WriteLine(String.Format("SessionFile:{0} Last Updated:{1}",paramPathSessionFileName,sessionParams.LastUpdated));
      Parameters.DisplayConfiguration();
      MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L   P O S I T I O N S  *************");
      AllPositions=new Positions((from Position position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
      AllPositions.Display();
      MDTrace.WriteLine(LogLevel.DEBUG,"************** T O P  G A I N E R S  *************");
      AllPositions.DisplayTop(10);
      MDTrace.WriteLine(LogLevel.DEBUG,"************** T O P  L O S E R S   *************");
      AllPositions.DisplayBottom(10);
      MDTrace.WriteLine(LogLevel.DEBUG,"************** A C T I V E   P O S I T I O N S  *************");
      ActivePositions.Display();
      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)));

      double gainLoss=0.00;
      double winners=0.00;
      double losers=0.00;
      double averageWinner=0.00;
      double averageLoser=0.00;
      double totalTrades=0.00;

      winners=ActivePositions.Where(x => x.GainLoss>=0).Count()+AllPositions.Where(x => x.GainLoss>=0).Count();
      losers=ActivePositions.Where(x => x.GainLoss<0).Count()+AllPositions.Where(x => x.GainLoss<0).Count();
      averageWinner=(ActivePositions.Count>0&&ActivePositions.Where(x => x.GainLoss>=0).Count()>0?ActivePositions.Where(x => x.GainLoss>=0).Select(x => x.GainLoss).Average():0+AllPositions.Count>0&&AllPositions.Where(x => x.GainLoss>=0).Count()>0?AllPositions.Where(x => x.GainLoss>=0).Select(x => x.GainLoss).Average():0)/2.00;
      averageLoser=(ActivePositions.Count>0&&ActivePositions.Where(x => x.GainLoss<0).Count()>0?ActivePositions.Where(x => x.GainLoss<0).Select(x => x.GainLoss).Average():0+AllPositions.Count>0&&AllPositions.Where(x => x.GainLoss<0).Count()>0?AllPositions.Where(x => x.GainLoss<0).Select(x => x.GainLoss).Average():0)/2.00;
      totalTrades=AllPositions.Count+ActivePositions.Count;
      double netProfit;

      double totalRWinners=ActivePositions.Count>0?ActivePositions.Where(x => x.GainLoss>=0).Select(x => x.RMultiple).Sum():0+AllPositions.Count>0?AllPositions.Where(x => x.GainLoss>=0).Select(x => x.RMultiple).Sum():0;
      double totalRLosers=ActivePositions.Count>0?ActivePositions.Where(x => x.GainLoss<0).Select(x => x.RMultiple).Sum():0+AllPositions.Count>0?AllPositions.Where(x => x.GainLoss<0).Select(x => x.RMultiple).Sum():0;

      if(0==ActivePositions.Count&&0==AllPositions.Count)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,"There does not appear to be any trade data in this file.");
      }

      if(null!=Candidates&&0!=Candidates.Count)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,"*********************************** C A N D I D A T E S  *********************************");
        MDTrace.WriteLine(LogLevel.DEBUG,CMTCandidate.Header());
        foreach(CMTCandidate candidate in Candidates) MDTrace.WriteLine(LogLevel.DEBUG,candidate.ToString());
      }

      // ********************************************************************************************************************************************************************************
      // *************************************************************************  S T O P  L I M I T S  A N D  P R I C I N G  *********************************************************
      // ********************************************************************************************************************************************************************************
      if(null!=AllPositions&&0!=AllPositions.Count)
      {
        DateGenerator dateGenerator=new DateGenerator();
        foreach(Position position in AllPositions)
        {
          double stopPrice=position.InitialStopLimit;
          List<DateTime> dates=dateGenerator.GenerateHistoricalDates(position.SellDate,position.PurchaseDate);
          StringBuilder sb=new StringBuilder();
          sb.Append("Symbol,Date,StopLimit,High,Low,Close,Gain/Loss,Gain/Loss (%),PriceTrend,Slope,Purchase");
          MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());

          double stopLimitValue=0.00;
          double priceTrendIndicatorSlope=double.NaN;
          for(int dateIndex=dates.Count-1;dateIndex>=0;dateIndex--)
          {
            DateTime date=dates[dateIndex];
            Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,date);
            if(null==price) continue;
            if(dateIndex==dates.Count-1) stopLimitValue=position.InitialStopLimit;
            double gainLossItem=0.00;
            double gainLossPercentItem=0.00;
            if(date.Equals(position.SellDate))
            {
              gainLossItem=(position.CurrentPrice*position.Shares)-(position.PurchasePrice*position.Shares);
              gainLossPercentItem=((position.CurrentPrice-position.PurchasePrice)/position.PurchasePrice)*100.00;
            }
            else
            {
              gainLossItem=(price.Close*position.Shares)-(position.PurchasePrice*position.Shares);
              gainLossPercentItem=((price.Close-position.PurchasePrice)/position.PurchasePrice)*100.00;
            }
            if(null!=StopLimits&&0!=StopLimits.Count)
            {
              StopLimit stopLimit=StopLimits.Where(x => x.Symbol.Equals(price.Symbol)&&x.AnalysisDate.Equals(price.Date)).FirstOrDefault();
              if(null!=stopLimit)
              {
                stopLimitValue=stopLimit.NewStop;
                priceTrendIndicatorSlope=stopLimit.PriceTrendIndicatorSlope;
              }
            }
            sb=new StringBuilder();
            sb.Append(price.Symbol).Append(",");
            sb.Append(price.Date.ToShortDateString()).Append(",");
            sb.Append(Utility.FormatCurrency(stopLimitValue)).Append(",");
            sb.Append(Utility.FormatCurrency(price.High)).Append(",");
            sb.Append(Utility.FormatCurrency(price.Low)).Append(",");
            sb.Append(Utility.FormatCurrency(price.Close)).Append(",");
            sb.Append(Utility.FormatCurrency(gainLossItem)).Append(",");
            sb.Append(Utility.FormatNumber(gainLossPercentItem,2,false)).Append(",");
            sb.Append(Utility.FormatNumber(priceTrendIndicatorSlope,4,false)).Append(",");
            sb.Append(Utility.FormatCurrency(position.PurchasePrice));
            MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
          }
        }
      }
      // ********************************************************************************************************************************************************************************
      // ********************************************************************************************************************************************************************************
      // ********************************************************************************************************************************************************************************
      if(ActivePositions.Count > 0 && AllPositions.Count > 0)
      {
        double realtimeGainLoss=GetRealtimeGainLoss(PricingDA.GetLatestDate());
        gainLoss=AllPositions.Sum(x => x.GainLoss);
        if(!double.IsNaN(realtimeGainLoss)) gainLoss+=realtimeGainLoss;
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Gain/Loss {0}",Utility.FormatCurrency(gainLoss)));
      }
      else if(ActivePositions.Count>0)
      {
        double realtimeGainLoss=GetRealtimeGainLoss(PricingDA.GetLatestDate());
        if(!double.IsNaN(realtimeGainLoss)) gainLoss=realtimeGainLoss;
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Active Gain/Loss {0}",Utility.FormatCurrency(gainLoss)));
      }
      else if(AllPositions.Count>0)
      {
        gainLoss=AllPositions.Sum(x => x.GainLoss);
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Gain/Loss {0}",Utility.FormatCurrency(gainLoss)));
      }
      netProfit=gainLoss/totalTrades;
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Winners to losers {0}:{1}  Hit Ratio:{2}",(int)winners,(int)losers,Utility.FormatPercent((winners/(winners+losers)))));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Average winner {0} Average Loser:{1}",Utility.FormatCurrency(averageWinner),Utility.FormatCurrency(averageLoser)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Trades:{0}",Utility.FormatNumber(totalTrades,0,true)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total RWinners:{0}",Utility.FormatNumber(totalRWinners,0,true)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total RLosers:{0}",Utility.FormatNumber(totalRLosers,0,true)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Average Profit {0}/Trade",Utility.FormatCurrency(netProfit)));
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Average RProfit {0}R per trade (expectancy)",Utility.FormatNumber((totalRWinners+totalRLosers)/totalTrades,2,false)));
      GBPriceCache.GetInstance().Dispose();
    }

    // ******************************************************************************************************************************************************
    // ******************************************************************  E N T R Y  T E S T    *****************************************************************
    // ******************************************************************************************************************************************************
    // This is for standalone testing.
    public void EntryTest(String symbol,DateTime analysisDate)
    {
      try
      {
        CMTParams cmtParams=new CMTParams();
        Parameters=cmtParams;

// DMA200 check : Candidates must remain above the 200 day moving average.
        Prices prices200=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,MovingAverageGenerator.DayCount200+10);
        Price currentPrice=GBPriceCache.GetInstance().GetPrice(symbol,analysisDate);
        if(null==prices200||prices200.Count<MovingAverageGenerator.DayCount200)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to insufficient pricng data",symbol));
				
          return;
        }
        if(null==currentPrice)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to lack of pricng data for {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(analysisDate)));
          return;
        }
        prices200=new Prices(prices200.Take(MovingAverageGenerator.DayCount200).ToList());
        DMAPrices dma200Prices=MovingAverageGenerator.GenerateMovingAverage(prices200,prices200.Count);
        double dma200Close=dma200Prices[0].AVGPrice;
        if(currentPrice.Close<dma200Close)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA200 Violation for candidate {0}.  The closing price is less than the 200 day moving average {1} < {2} on {3}",symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma200Close),analysisDate.ToShortDateString()));
          return;
        }
// DMA50 check : The 50 day moving average must remain above the 200 day moving average
        Prices prices50=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,MovingAverageGenerator.DayCount50+10);
        if(null==prices50 || prices50.Count < MovingAverageGenerator.DayCount50)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to insufficient pricng data",symbol));
          return;
        }
        if(null==currentPrice)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to lack of pricng data for {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(analysisDate)));
          return;
        }
        prices50=new Prices(prices50.Take(MovingAverageGenerator.DayCount50).ToList());
        DMAPrices dma50Prices=MovingAverageGenerator.GenerateMovingAverage(prices50,prices50.Count);
        double dma50Close=dma50Prices[0].AVGPrice;
// CHECK THAT THE CURRENT PRICE IS ABOVE THE 50 DAY MOVING AVERAGE
				if(currentPrice.Close <= dma50Close)
				{
					MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}.  The closing price is less than the 50 day moving average {1} < {2} on {3}",symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma50Close),analysisDate.ToShortDateString()));
					return;
				}

// CHECK THAT THE 50 DAY MOVING AVERAGE IS ABOVE THE 200 DAY MOVING AVERAGE
				if(dma50Close <= dma200Close)
				{
					MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}.  The 50 DMA must remain about 200 DMA. {1} < {2} on {3}",symbol,Utility.FormatCurrency(dma50Close),Utility.FormatCurrency(dma200Close),analysisDate.ToShortDateString()));
					return;
				}

// Price trend check.  Ensure that prices are trending higher      UsePriceTrendSlope  UsePriceTrendSlopeDayCount
        if(Parameters.UsePriceSlopeIndicator)
				{
					int dayCount=Parameters.UsePriceSlopeIndicatorDays;
					Prices pricesTrend=PricingDA.GetPrices(symbol,dayCount);
					double[] pricesLow=Numerics.ToDouble(pricesTrend.GetPricesLow());
					LeastSquaresResult leastSquaresResult=LeastSquaresHelper.CalculateLeastSquares(pricesLow);
					if(leastSquaresResult.Slope<=0.00)
					{
						MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Price trend violation {0}. The {1} pricing slope is {2}",symbol,dayCount,Utility.FormatNumber(leastSquaresResult.Slope,6)));
						return;
					}
				}
        
// NarrowRange check
				Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.EntryHorizon+5);
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Checking for NarrowRange entry for symbol {0} on {1}",symbol,analysisDate.ToShortDateString()));
        if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Narrow Range entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
          return;
        }
        if(NarrowRangeIndicator.IsNarrowRangeEntry(analysisDate,prices,Parameters.EntryHorizon)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on NarrowRange",symbol));
// Preparation for SwingEntry check 
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Checking for Swing entry for symbol {0} on {1}",symbol,analysisDate.ToShortDateString()));
        prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.EntryHorizon<40?40:Parameters.EntryHorizon);
        if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Swing entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
          return;
        }
        DMAPrices dmaPrices=MovingAverageGenerator.GenerateMovingAverage(prices,Parameters.EntryHorizon);
        BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);

// SwingTrade check
        SwingTrades swingTrades=SwingTradeHelper.FindSwingTrades(prices,dmaPrices.GetDMAPricesByDate(),bollingerBands.GetBollingerBandElementsByDate());
        if(null!=swingTrades&&swingTrades.HasEntryOn(analysisDate)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on Swing on {1}",symbol,analysisDate.ToShortDateString()));
        // MACD check
        if(!CMTMACDIndicator.IsMACDDowntrend(analysisDate,symbol,Parameters)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on MACD{1}",symbol,Parameters.MACDSetup));

// PriceTrend check
        prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.PriceTrendDays+10);
        if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Price Trend entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
          return;
        }
        if(PriceTrendIndicator.IsUptrend(prices,Parameters.PriceTrendDays).IsUpTrend) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on PriceTrend on {1}",symbol,analysisDate.ToShortDateString()));
//  VolumeTrend check      
        prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.VolumeTrendDays+10);
        if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Volume Trend entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
          return;
        }
        if(VolumeTrendIndicator.IsUptrend(prices,Parameters.VolumeTrendDays)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on VolumeTrend on {1}",symbol,analysisDate.ToShortDateString()));
// ChannelBreakout
        prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.ChannelBreakoutHorizon);
        if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate ChannelBreakout entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
          return;
        }
        if(ChannelBreakoutIndicator.IsChannelBreakOut(TradeDate,prices,Parameters.ChannelBreakoutHorizon)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on ChannelBreakout on {1}",symbol,analysisDate.ToShortDateString()));
      }
      finally
      {
        GBPriceCache.GetInstance().Dispose();
      }
    }

    // ******************************************************************************************************************************************************
    // ****************************************************************** C L O S E    **********************************************************************
    // ******************************************************************************************************************************************************
    public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double price,String sessionFile)
    {
      if(!CMTSessionManager.IsValidSessionFile(sessionFile))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid session file '{0}'.",sessionFile));
        return false;
      }
      PathSessionFileName=sessionFile;
      CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(PathSessionFileName);
      Parameters=sessionParams.CMTParams;
      TradeDate=sessionParams.TradeDate;
      AnalysisDate=DateTime.Now;
      sessionParams.CMTParams.AnalysisDate=DateTime.Now;
      sessionParams.LastUpdated=DateTime.Now;
      sessionParams.AnalysisDate=DateTime.Now;
      Candidates=sessionParams.Candidates;
      StopLimits=sessionParams.StopLimits;
      PricingExceptions=sessionParams.PricingExceptions;
      ActivePositions=sessionParams.ActivePositions;
      AllPositions=sessionParams.AllPositions;
      CashBalance=sessionParams.CashBalance;
      NonTradeableCash=sessionParams.NonTradeableCash;
      if(!BackupSession()) return false;
      Position 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;
    }

    // ******************************************************************************************************************************************************
    // *************************************************************************** E D I T ******************************************************************
    // ******************************************************************************************************************************************************
    public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,double initialStop,double trailingStop,String sessionFile)
    {
      if(!CMTSessionManager.IsValidSessionFile(sessionFile))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid session file '{0}'.",sessionFile));
        return false;
      }
      PathSessionFileName=sessionFile;
      CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(PathSessionFileName);
      Parameters=sessionParams.CMTParams;
      TradeDate=sessionParams.TradeDate;
      AnalysisDate=DateTime.Now;
      sessionParams.CMTParams.AnalysisDate=DateTime.Now;
      sessionParams.LastUpdated=DateTime.Now;
      sessionParams.AnalysisDate=DateTime.Now;
      Candidates=sessionParams.Candidates;
      StopLimits=sessionParams.StopLimits;
      PricingExceptions=sessionParams.PricingExceptions;
      ActivePositions=sessionParams.ActivePositions;
      AllPositions=sessionParams.AllPositions;
      CashBalance=sessionParams.CashBalance;
      NonTradeableCash=sessionParams.NonTradeableCash;
      if(!BackupSession()) return false;
      Position 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=(null==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;
    }

    // ******************************************************************************************************************************************************
    // ****************************************************************** D A I L Y   *****************************************************************
    // ******************************************************************************************************************************************************
    public CMTTrendModelResult RunDaily(DateTime tradeDate,String paramPathSessionFileName,CMTParams cmtParams)
    {
      CMTTrendModelResult result=new CMTTrendModelResult();

      try
      {
        DateTime now=DateTime.Now;
        Parameters=cmtParams;
        CashBalance=Parameters.InitialCash;
        ActivePositions=new ActivePositions();
        AllPositions=new Positions();
        Candidates=new CMTCandidates();
        PricingExceptions=new CMTPricingExceptions();
        StopLimits=new StopLimits();
        TradeDate=tradeDate;
        PathSessionFileName=paramPathSessionFileName;
        if(CMTSessionManager.IsValidSessionFile(paramPathSessionFileName))
        {
          CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(paramPathSessionFileName);
          result=CheckSequentialTradeDate(tradeDate,sessionParams.TradeDate);
          if(!result.Success)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,result.Message);
            return result;
          }
          Parameters=sessionParams.CMTParams;
          cmtParams=sessionParams.CMTParams;
          sessionParams.TradeDate=TradeDate;
          sessionParams.CMTParams.TradeDate=tradeDate;
          sessionParams.CMTParams.AnalysisDate=now;
          sessionParams.LastUpdated=now;
          sessionParams.AnalysisDate=now;
          AnalysisDate=sessionParams.AnalysisDate;
          Candidates=sessionParams.Candidates;
          StopLimits=sessionParams.StopLimits;
          StartDate=sessionParams.StartDate;
          PricingExceptions=sessionParams.PricingExceptions;
          ActivePositions=sessionParams.ActivePositions;
          AllPositions=sessionParams.AllPositions;
          CashBalance=sessionParams.CashBalance;
          NonTradeableCash=sessionParams.NonTradeableCash;
        }
        else Parameters=cmtParams;
        DisplayRealtimeBlotter(TradeDate);
        if(0==PricingDA.GetPriceCountOn(TradeDate))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No prices on {0}, skipping date.",TradeDate.ToShortDateString()));
          return result;
        }
        if(0!=ActivePositions.Count&&TradeDate<ActivePositions.Select(x => x.PurchaseDate).Max())
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate ({0}) must be greater than or equal to the max position date ({1}) in the trade file.",TradeDate.ToShortDateString(),ActivePositions.Select(x => x.PurchaseDate).Max().ToShortDateString()));
          return result;
        }
        ManageOpenPositions(TradeDate);
        ManageCandidates(TradeDate);
// ************************************************************************************************************************************************************************
// **************************************************************************** N E W   P O S I T I O N S *****************************************************************
// ************************************************************************************************************************************************************************
        if(null!=PathSessionFileName) SaveSession();    // SAVE THE SESSION FILE POST POSITION AND CANDIDATE MANAGEMENT
        if(Parameters.SuspendTrading)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Trading has been suspended for {0}.  SuspendTrading flag={1} ",TradeDate.ToShortDateString(),Parameters.SuspendTrading));
          result.Success=true;
          return result;
        }
        if(ActivePositions.PositionsOn(TradeDate)>=Parameters.MaxDailyPositions)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Number of trades for {0} is already at maximum {1}",TradeDate.ToShortDateString(),Parameters.MaxDailyPositions));
          result.Success=true;
          return result;
        }
        Positions positions=BuyCandidates(TradeDate,CashBalance,ActivePositions.GetSymbols());
        if(null != positions && 0!=positions.Count)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************");
          positions.Display();
          if(CashBalance-positions.GetExposure()<0.00)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases (1).  Available cash: {0}.  Requested Exposure: {1}",Utility.FormatCurrency(CashBalance),Utility.FormatCurrency(positions.GetExposure())));
            result.Success=false;
            return result;
          }
          ActivePositions.Add(positions);
          CashBalance-=positions.GetExposure();
        }
        DisplayRealtimeBlotter(TradeDate);
        result.Success=true;
        result.CashBalance=CashBalance;
        if(null!=PathSessionFileName) SaveSession();
        return result;
      }
      catch(Exception exception)
      {
        result.Success=false;
        result.Message=exception.ToString();
        return result;
      }
      finally
      {
        GBPriceCache.GetInstance().Dispose();
      }
    }

    // ******************************************************************************************************************************************************
    // ****************************************************************** B A C K T E S T   *****************************************************************
    // ******************************************************************************************************************************************************
    public CMTTrendModelResult RunBacktestMode(DateTime startDate,DateTime endDate,bool sellAtEndOfSimulation,String paramPathSessionFileName,CMTParams cmtParams)
    {
      CMTTrendModelResult result=new CMTTrendModelResult();

      try
      {
        Parameters=cmtParams;
        CashBalance=Parameters.InitialCash;
        ActivePositions=new ActivePositions();
        AllPositions=new Positions();
        Candidates=new CMTCandidates();
        PricingExceptions=new CMTPricingExceptions();
        StopLimits=new StopLimits();
        DateGenerator dateGenerator=new DateGenerator();
        List<DateTime> runDates;

        runDates=dateGenerator.GenerateHistoricalDates(startDate,endDate);
        PathSessionFileName=paramPathSessionFileName;
        if(CMTSessionManager.IsValidSessionFile(paramPathSessionFileName))
        {
          CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(paramPathSessionFileName);
          Parameters=sessionParams.CMTParams;
          cmtParams=sessionParams.CMTParams;
          sessionParams.TradeDate=startDate;
          sessionParams.LastUpdated=DateTime.Now;
          sessionParams.AnalysisDate=DateTime.Now;
          ActivePositions=sessionParams.ActivePositions;
          AllPositions=sessionParams.AllPositions;
          CashBalance=sessionParams.CashBalance;
        }
        else Parameters=cmtParams;
        Parameters.DisplayConfiguration();
        StartDate=startDate;
        AnalysisDate=Today();
        int currentYear=-1;

        foreach(DateTime tradeDate in runDates)
        {
          TradeDate=tradeDate;
          AnalysisDate=TradeDate;
          if(-1==currentYear) currentYear=TradeDate.Year;
          if(TradeDate.Year!=currentYear)
          {
            GBPriceCache.GetInstance().ClearCacheOnOrBefore(TradeDate,true);
            currentYear=TradeDate.Year;
          }
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("PROCESSING TRADE DATE {0} WITH AVAILABLE CASH OF {1}",tradeDate.ToShortDateString(),Utility.FormatCurrency(CashBalance)));
          DisplayRealtimeBlotter(TradeDate);
          if(0==PricingDA.GetPriceCountOn(TradeDate))
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No prices on {0}, skipping date.",TradeDate.ToShortDateString()));
            continue;
          }
          if(0!=ActivePositions.Count&&TradeDate<ActivePositions.Select(x => x.PurchaseDate).Max())
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate ({0}) must be greater than or equal to the max position date ({1}) in the trade file.",TradeDate.ToShortDateString(),ActivePositions.Select(x => x.PurchaseDate).Max().ToShortDateString()));
            return result;
          }
          ManageOpenPositions(TradeDate);
          ManageCandidates(TradeDate);
          if(ActivePositions.PositionsOn(TradeDate)>=Parameters.MaxDailyPositions)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Number of trades for {0} is already at maximum {1}",TradeDate.ToShortDateString(),Parameters.MaxDailyPositions));
            result.Success=true;
            continue;
          }
          Positions positions=BuyCandidates(TradeDate,CashBalance,ActivePositions.GetSymbols());
          if(null!=positions&&0!=positions.Count)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************");
            positions.Display();
            if(CashBalance-positions.GetExposure()<0.00)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases (1).  Available cash: {0}.  Requested Exposure: {1}",Utility.FormatCurrency(CashBalance),Utility.FormatCurrency(positions.GetExposure())));
              result.Success=false;
              return result;
            }
            ActivePositions.Add(positions);
            CashBalance-=positions.GetExposure();
          }
          MDTrace.WriteLine(LogLevel.DEBUG,"\n");
        }
        // ***********************************************************************************************************************************************************************
        // *********************************************************  S E L L   A L L   A T   E N D  O F  S I M U L A T I O N  ***************************************************
				if(sellAtEndOfSimulation)
				{
					SellPositions(ActivePositions,TradeDate,"Closed due to end of simulation.");
					CashBalance+=ActivePositions.GetMarketValue();
					AllPositions.Add(ActivePositions);
					ActivePositions.Clear();
				}
        // ***********************************************************************************************************************************************************************
        MDTrace.WriteLine(LogLevel.DEBUG,"******************************************** C L O S E D   P O S I T I O N S  ***********************************************");
        AllPositions.Display();
        DisplayBalanceFromAllPositions();     //  Aggregate the balance from the closed positions
        result.Success=true;
        result.CashBalance=CashBalance;
        GBPriceCache.GetInstance().Dispose();
        if(null!=PathSessionFileName) SaveSession();
        return result;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
        result.Success=false;
        result.Message=exception.ToString();
        return result;
      }
      finally
      {
        GBPriceCache.GetInstance().Dispose();
      }
    }

    public void RunTrendTemplate(DateTime? analysisDate=null)
    {
      try
      {
        List<String> symbols=PricingDA.GetSymbols();
        CMTParams cmtParams=new CMTParams();
        List<CMTCandidate> violations=new List<CMTCandidate>();
        List<CMTCandidate> candidates=new List<CMTCandidate>();

        for(int index=0;index<symbols.Count;index++)
        {
          String symbol=symbols[index];
          if(0==(index%500)) Console.WriteLine("GenerateCMTCandidates processing item {0} of {1}",index+1,symbols.Count);
          CMTCandidate cmtCandidate=CMTCandidateGenerator.GenerateCandidate(symbol,analysisDate.Value,cmtParams);
          if(cmtCandidate.Violation) violations.Add(cmtCandidate);
          else candidates.Add(cmtCandidate);
        }
        List<String> violationTypes=violations.Select(x => x.Reason).Distinct().ToList();
        violationTypes.Sort();
        foreach(String violationType in violationTypes)
        {
          List<CMTCandidate> candidatesInViolation=violations.Where(x => x.Reason.Equals(violationType)).ToList();
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Violation:{0} Count:{1} Percent:{2}",violationType,candidatesInViolation.Count,Utility.FormatPercent((double)candidatesInViolation.Count/(double)violations.Count)));
        }
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Found {0} qualifying candidates",candidates.Count));
        foreach(CMTCandidate candidate in candidates)
        {
          Console.WriteLine("_____________________________________________");
          String companyName=PricingDA.GetNameForSymbol(candidate.Symbol);
          Console.WriteLine(String.Format("Symbol:{0} Company Name:{1}",candidate.Symbol,companyName));
        }
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
      }
      finally
      {
        GBPriceCache.GetInstance().Dispose();
      }
    }

    // ***********************************************************************************************************************************************************************
    // *********************************************************************** M A N A G E   O P E N  P O S I T I O N S  *****************************************************
    // ***********************************************************************************************************************************************************************
    private void ManageOpenPositions(DateTime tradeDate)
    {
      if(0==ActivePositions.Count) return;
      List<Position> closedPositions=new List<Position>();
      foreach(Position position in ActivePositions)
      {
        Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,tradeDate);
        if(null==price)
        {
          int exceptionCount=AddPricingException(position.Symbol);
          if(exceptionCount>MaxPricingExceptions)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[ManageOpenPositions] Selling {0} on {1} because price exceptions exceeds maximum of {2}",position.Symbol,tradeDate.ToShortDateString(),MaxPricingExceptions));
            price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,tradeDate);
            position.SellDate=tradeDate;
            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("[ManageOpenPositions] Cannot determine price for {0} on {1}",position.Symbol,tradeDate.ToShortDateString()));
          continue;
        }
        position.CurrentPrice=price.Close;                  // Intraday the Close will be the current price
        RemovePricingException(position.Symbol);
        if(Parameters.SidewaysDetection&&Utility.IsEpoch(position.LastStopAdjustment)&&price.Close>position.PurchasePrice)
        {
          DateGenerator dateGenerator=new DateGenerator();
          int daysHeld=Math.Abs(dateGenerator.DaysBetweenActual(position.PurchaseDate,tradeDate));
          if(daysHeld>Parameters.SidewaysAfterDays)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[ManageOpenPositions] Selling {0} on {1} due to sideways motion for {2} days",position.Symbol,tradeDate.ToShortDateString(),daysHeld));
            position.SellDate=tradeDate;
            position.CurrentPrice=position.TrailingStopLimit;
            position.Comment="Closed due to sideways motion.";
            CashBalance+=position.MarketValue;
            AllPositions.Add(position);
            closedPositions.Add(position);
            continue;
          }
        }
        if(price.Low<position.TrailingStopLimit&&!position.PurchaseDate.Equals(tradeDate))   // This will miss StopLimit violations that occur on the date of purchase.  Needs to be watched manually because we don't have tick data                             
        {
          position.SellDate=tradeDate;
          position.CurrentPrice=position.TrailingStopLimit;
          position.Comment="Closed due to trailing stop.";
          CashBalance+=position.MarketValue;
          AllPositions.Add(position);
          closedPositions.Add(position);
        }
        else if(HasDMABreak(position,tradeDate,price))
        {
          position.SellDate=tradeDate;
          position.CurrentPrice=price.Close;
          position.Comment="Closed due to DMA break";
          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(tradeDate,price,position);
        }
      }
      if(0!=closedPositions.Count)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L  *********************");
        foreach(Position closedPosition in closedPositions)
        {
          closedPosition.Display();
          ActivePositions.Remove(closedPosition);
        }
      }
    }

    // **********************************************************************************************************************************************************
    // ***************************************************** M O V I N G  A V E R A G E  B R E A K   C H E C K  *************************************************
    // **********************************************************************************************************************************************************
    private bool HasDMABreak(Position position,DateTime tradeDate,Price currentPrice)
    {
      if(!Parameters.SellOnDMABreak) return false;
      List<String> dmaBreakValues=Parameters.DMABreakValuesCollection;
      foreach(String strDMABreakValue in dmaBreakValues)
      {
        int dmaBreakValue=int.Parse(strDMABreakValue);
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Evaluating DMA({0}) for {1} on {2}",dmaBreakValue,position.Symbol,tradeDate.ToShortDateString()));
        Prices prices=GBPriceCache.GetInstance().GetPrices(position.Symbol,tradeDate,dmaBreakValue+5);
        if(null==prices||prices.Count<dmaBreakValue)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA({0}) for {1} on {2}",dmaBreakValue,position.Symbol,tradeDate.ToShortDateString()));
          return true;
        }
        prices=new Prices(prices.Take(dmaBreakValue).ToList());
        DMAPrices dmaPrices=MovingAverageGenerator.GenerateMovingAverage(prices,prices.Count);
        if(currentPrice.Close<dmaPrices[0].AVGPrice)
        {
          bool isLoss=position.GetGainLoss(currentPrice)<0.00;
          if(isLoss)
          {
            if(Parameters.DMABreakForceBreak)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Moving average violation detected for {0} on {1}.  Price:{2} DMA({3}):{4} : Sell triggered.",position.Symbol,tradeDate.ToShortDateString(),Utility.FormatCurrency(currentPrice.Close),dmaBreakValue,Utility.FormatCurrency(dmaPrices[0].AVGPrice)));
              return true;
            }
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Moving average violation detected for {0} on {1}.  Price:{2} DMA({3}):{4} : DMABreakForceBreak=False, holding security.",position.Symbol,tradeDate.ToShortDateString(),Utility.FormatCurrency(currentPrice.Close),dmaBreakValue,Utility.FormatCurrency(dmaPrices[0].AVGPrice)));
            return false;
          }
          else
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Moving average violation detected for {0} on {1}.  Price:{2} DMA({3}):{4} : Sell triggered.",position.Symbol,tradeDate.ToShortDateString(),Utility.FormatCurrency(currentPrice.Close),dmaBreakValue,Utility.FormatCurrency(dmaPrices[0].AVGPrice)));
            return true;
          }
        }
      }
      return false;
    }

    // **********************************************************************************************************************************************************
    // ***************************************************** 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)
    {
      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)
    {
      CMTPricingException pricingException=PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
      if(null==pricingException) { pricingException=new CMTPricingException(symbol,1); PricingExceptions.Add(pricingException); PricingExceptions.Add(pricingException); }
      else pricingException.ExceptionCount++;
      return pricingException.ExceptionCount;
    }

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

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

    // ***************************************************************************************************************************************************
    // **************************************************************** S E L L   P O S I T I O N S  *****************************************************
    // ***************************************************************************************************************************************************
    private void SellPositions(Positions positions,DateTime sellDate,String comment)
    {
      DateGenerator dateGenerator=new DateGenerator();
      foreach(Position position in positions)
      {
        SellPosition(position,sellDate,comment);
      }
    }

    private void SellPosition(Position position,DateTime sellDate,String comment)
    {
      position.SellDate=sellDate;
      position.Comment=comment;
      Price price=GetPrice(position.Symbol,sellDate);
      if(null==price)
      {
        DateTime latestPricingDate=PricingDA.GetLatestDate(position.Symbol);
        price=PricingDA.GetPrice(position.Symbol,latestPricingDate);
        if(null==price)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[SellPosition] **********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
          position.CurrentPrice=position.PurchasePrice;
        }
        else
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Database does not contain a price for {0} on {1}.  Latest pricing date for this security is {2}",position.Symbol,sellDate.ToShortDateString(),latestPricingDate.ToShortDateString()));
          position.CurrentPrice=price.Close;
        }
      }
      else position.CurrentPrice=price.Close;
    }

    // ***************************************************************************************************************************************************
    // **************************************************************** B U Y   C A N D I D A T E S  *****************************************************
    // ***************************************************************************************************************************************************
    private Positions BuyCandidates(DateTime tradeDate,double cash,List<String> symbolsHeld)
    {
      try
      {
        DateGenerator dateGenerator=new DateGenerator();
        Positions positions=new Positions();
        int positionCount=0;

        if(ActivePositions.Count==MaxOpenPositions)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The maximum number of active positions has been reached.  MaxOpenPositions={0}",MaxOpenPositions));
          return positions;
        }
        if(null==Candidates||0==Candidates.Count)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,"There are no candidates in the candidate pool to search for entry points.");
          return positions;
        }
        if(!IsTradeableMarket(tradeDate,Parameters))
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The market is not tradeable on {0} due to one or more violations.",tradeDate));
          return positions;
        }
        Candidates.Sort();
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format(""));
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SEARCHING FOR ENTRY POINTS (TradeDate:{0} Available Cash:{1})",tradeDate.ToShortDateString(),Utility.FormatCurrency(cash)));
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",CMTCandidate.Header()));
        for(int index=0;index<Candidates.Count;index++)
        {
          bool skipChecks=false;
          CMTCandidate mmCandidate=Candidates[index];
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",mmCandidate.ToString()));

// Check NoTradeSymbol
          if(Parameters.NoTradeSymbolsList.Any(x => x.Equals(mmCandidate.Symbol)))
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Candidate {0} is in the NoTrade list",mmCandidate.Symbol));
            RemoveCandidate(mmCandidate);
            continue;
          }
// Check if already held
          if(ActivePositions.Any(x => x.Symbol.Equals(mmCandidate.Symbol)))
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Candidate {0} is already held in active positions",mmCandidate.Symbol));
            RemoveCandidate(mmCandidate);
            continue;
          }
// Check to make sure the candidate is still above the 200DMA.  Candidates must remain above the 200 day moving average. (MMarc Minervini & Paul Tudor Jones)
          Prices prices200=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,MovingAverageGenerator.DayCount200+10);
          Price currentPrice=GBPriceCache.GetInstance().GetPrice(mmCandidate.Symbol,tradeDate);
          if(null==prices200||prices200.Count<MovingAverageGenerator.DayCount200)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to insufficient pricing data",mmCandidate.Symbol));
            continue;
          }
          if(null==currentPrice)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to lack of pricng data for {1}",mmCandidate.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
            continue;
          }
          prices200=new Prices(prices200.Take(MovingAverageGenerator.DayCount200).ToList());
          DMAPrices dma200Prices=MovingAverageGenerator.GenerateMovingAverage(prices200,prices200.Count);
          double dma200Close=dma200Prices[0].AVGPrice;
          if(currentPrice.Close<dma200Close)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA200 Violation for candidate {0}.  The closing price is less than the 200 day moving average {1} < {2} on {3}",mmCandidate.Symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma200Close),tradeDate.ToShortDateString()));
            continue;
          }
// DMA50 check : The 50 day moving average must remain above the 200 day moving average
        Prices prices50=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,MovingAverageGenerator.DayCount50+10);
        if(null==prices50||prices50.Count<MovingAverageGenerator.DayCount50)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to insufficient pricng data",mmCandidate.Symbol));
				
          continue;
        }
        if(null==currentPrice)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to lack of pricng data for {1}",mmCandidate.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
          continue;
        }
        prices50=new Prices(prices50.Take(MovingAverageGenerator.DayCount50).ToList());
        DMAPrices dma50Prices=MovingAverageGenerator.GenerateMovingAverage(prices50,prices50.Count);
        double dma50Close=dma50Prices[0].AVGPrice;

// CHECK THAT THE CURRENT PRICE IS ABOVE THE 50 DAY MOVING AVERAGE
				if(currentPrice.Close <= dma50Close)
				{
					MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}.  The closing price is less than the 50 day moving average {1} < {2} on {3}",mmCandidate.Symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma50Close),tradeDate.ToShortDateString()));
					continue;
				}
// CHECK THAT THE 50 DAY MOVING AVERAGE IS ABOVE THE 200 DAY MOVING AVERAGE
				if(dma50Close <= dma200Close)
				{
					MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}.  The 50 DMA must remain about 200 DMA. {1} < {2} on {3}",mmCandidate.Symbol,Utility.FormatCurrency(dma50Close),Utility.FormatCurrency(dma200Close),tradeDate.ToShortDateString()));
					continue;
				}

// Trend check ensure that prices are trending higher
        if(Parameters.UsePriceSlopeIndicator)
				{
					int dayCount=Parameters.UsePriceSlopeIndicatorDays;
					Prices pricesTrend=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,dayCount);
					double[] pricesLow=Numerics.ToDouble(pricesTrend.GetPricesLow());
					LeastSquaresResult leastSquaresResult=LeastSquaresHelper.CalculateLeastSquares(pricesLow);
					if(leastSquaresResult.Slope<=0.00)
					{
						MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Price trend violation {0}. The {1} pricing slope is {2}",mmCandidate.Symbol,dayCount,Utility.FormatNumber(leastSquaresResult.Slope,6)));
						continue;
					}
				}
// check for entries
          if(Parameters.EntryTypesCollection.Any(x => x.Equals("OverExtended",StringComparison.InvariantCultureIgnoreCase)))
          {
            bool? result=OverExtendedIndicator.IsOverextended(mmCandidate.Symbol,tradeDate,Parameters.UseOverExtendedIndicatorDays,Parameters.UseOverExtendedIndicatorViolationThreshhold,Parameters.UseOverExtendedIndicatorMarginPercent);
            if(null==result || result.Value)continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on OverExtended",mmCandidate.Symbol));
          }
          if(Parameters.EntryTypesCollection.Any(x => x.Equals("MVP",StringComparison.InvariantCultureIgnoreCase)))
          {
            if(MVPIndicator.IsTrue(mmCandidate.Symbol,tradeDate))
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on MVP",mmCandidate.Symbol));
              skipChecks=true;          // skip the remaining checks.  Immediate buy
            }
          }
          if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("NarrowRange",StringComparison.InvariantCultureIgnoreCase)))
          {
            Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.EntryHorizon+5);
            if(null==prices||0==prices.Count||!prices[0].Date.Date.Equals(tradeDate.Date))
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Narrow Range entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
              continue;
            }
            if(!NarrowRangeIndicator.IsNarrowRangeEntry(tradeDate,prices,Parameters.EntryHorizon)) continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on NarrowRange",mmCandidate.Symbol));
          }
          if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("Swing",StringComparison.InvariantCultureIgnoreCase)))
          {
            Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.EntryHorizon<40?40:Parameters.EntryHorizon);
            if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Swing entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
              continue;
            }
            DMAPrices dmaPrices=MovingAverageGenerator.GenerateMovingAverage(prices,Parameters.EntryHorizon);
            BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
            SwingTrades swingTrades=SwingTradeHelper.FindSwingTrades(prices,dmaPrices.GetDMAPricesByDate(),bollingerBands.GetBollingerBandElementsByDate());
            if(null==swingTrades||!swingTrades.HasEntryOn(tradeDate)) continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on Swing",mmCandidate.Symbol));
          }
// MACD
          if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("MACD",StringComparison.InvariantCultureIgnoreCase)))
          {
            if(CMTMACDIndicator.IsMACDDowntrend(tradeDate,mmCandidate.Symbol,Parameters)) continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on MACD{1}",mmCandidate.Symbol,Parameters.MACDSetup));
          }
// PRICETREND
          if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("PriceTrend",StringComparison.InvariantCultureIgnoreCase)))
          {
            Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.PriceTrendDays+10);
            if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Price Trend entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
              continue;
            }
            if(!PriceTrendIndicator.IsUptrend(prices,Parameters.PriceTrendDays).IsUpTrend) continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on PriceTrend",mmCandidate.Symbol));
          }
// VOLUMETREND
          if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("VolumeTrend",StringComparison.InvariantCultureIgnoreCase)))
          {
            Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.VolumeTrendDays+10);
            if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Volume Trend entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
              continue;
            }
            if(!VolumeTrendIndicator.IsUptrend(prices,Parameters.VolumeTrendDays)) continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on VolumeTrend",mmCandidate.Symbol));
          }
// CHANNELBREAKOUT
          if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("ChannelBreakout",StringComparison.InvariantCultureIgnoreCase)))
          {
            Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.ChannelBreakoutHorizon);
            if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate ChannelBreakout entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
              continue;
            }
            if(!ChannelBreakoutIndicator.IsChannelBreakOut(tradeDate,prices,Parameters.ChannelBreakoutHorizon)) continue;
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on ChannelBreakout",mmCandidate.Symbol));
          }
// end entry checks
          Position prevPosition=AllPositions.Where(x => x.Symbol.Equals(mmCandidate.Symbol)).OrderByDescending(x => x.SellDate).FirstOrDefault();
          if(null!=prevPosition)
          {
            int daysBetween=dateGenerator.DaysBetween(tradeDate,prevPosition.SellDate);
            if(daysBetween<30)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Candidate was sold less than 30 days ago. Symbol: {0} Date Sold:{1}",mmCandidate.Symbol,prevPosition.SellDate));
              continue;
            }
          }
          Price price=GetPrice(mmCandidate.Symbol,tradeDate);
          if(null==price)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[BuyCandidates] Cannot locate a price for {0} on {1}, skipping purchase.",mmCandidate.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
            continue;
          }
          Position position=new Position();
          position.Symbol=mmCandidate.Symbol;
          position.PurchaseDate=tradeDate;
          position.PurchasePrice=price.Close;
          position.CurrentPrice=double.NaN;
          position.Volume=mmCandidate.Volume;
          position.Volatility=mmCandidate.Volatility;
          if(ActivePositions.Count+positions.Count>=MaxOpenPositions) break;
          RemoveCandidate(mmCandidate);
          positions.Add(position);
          positionCount++;
          if(positionCount>=MaxDailyPositions) break;
        }
        positions=PerformPositionSizing(positions,cash,tradeDate);
        if(null==positions||0==positions.Count)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**** NO ENTRY POINTS FOUND ON TRADE DATE:{0} ****",tradeDate.ToShortDateString()));
          return positions;
        }
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BOUGHT {0} POSITIONS, TRADE DATE:{1}, EXPOSURE:{2}",positions.Count,tradeDate.ToShortDateString(),Utility.FormatCurrency(positions.Sum(x => x.Exposure))));
        return positions;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BuyPositions: Exception:{0}",exception.ToString()));
        return null;
      }
    }

    // ***************************************************************************************************************************************************
    // ***************************************************************** P O S I T I O N   S I Z I N G  **************************************************
    // ***************************************************************************************************************************************************
		private Positions PerformPositionSizing(Positions positions,double availableCash,DateTime tradeDate)
		{
		  return PerformPositionSizingTotalRisk(positions, availableCash, tradeDate);
		}

    private Positions PerformPositionSizingTotalRisk(Positions positions,double availableCash,DateTime tradeDate)
    {
      Positions acceptedPositions=new Positions();
      if(null==positions||0==positions.Count) return acceptedPositions;
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("PERFORM POSITION SIZING TOTAL RISK : Positions:{0} Cash:{1}",positions.Count,Utility.FormatCurrency(availableCash)));
      foreach(Position position in positions)
      {
        position.PositionRiskPercentDecimal=Parameters.PositionRiskPercentDecimal;
        position.R=position.PositionRiskPercentDecimal*position.PurchasePrice;
        position.C=Parameters.TotalRiskPercentDecimal*availableCash;
        position.Shares=Math.Floor(position.P);                         // P=SHARES; P=C/R
        position.InitialStopLimit=position.PurchasePrice-position.R;
        position.TrailingStopLimit=position.InitialStopLimit;
        if(position.Shares<=0)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Number of shares is zero for {0}",position.Symbol));
          continue;
        }
        if(position.Exposure>availableCash)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Insufficient funds to purchase {0} shares of {1} at {2} per share with total exposure {3}.  Available cash is {4}",
          position.Shares,position.Symbol,Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(position.Exposure),Utility.FormatCurrency(availableCash)));
          continue;
        }
        availableCash-=position.Exposure;
        acceptedPositions.Add(position);
      }
      return acceptedPositions;
    }

    // ***************************************************************************************************************************************************
    // ************************************************************** S T O P  L I M I T S   *************************************************************
    // ***************************************************************************************************************************************************
    private void EvaluateStopPrice(DateTime tradeDate,Price currentPrice,Position position)
    {
	    MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EvaluateStopPrice: {0} on {1}",position.Symbol,tradeDate.ToShortDateString()));
      DateGenerator dateGenerator=new DateGenerator();

      Prices prices = GBPriceCache.GetInstance().GetPrices(position.Symbol, tradeDate, Parameters.PriceTrendDays);                      // only adjust stops if we are trending up
      PriceTrendIndicatorResult priceTrendIndicatorResult = PriceTrendIndicator.IsUptrend(prices, Parameters.PriceTrendDays);
      if(Parameters.EvaluateStopOnUpTrend && !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, Parameters.PriceTrendDays));
        return;
      }
      double trailingStop=position.InitialStopLimit+Math.Floor((currentPrice.Low-position.PurchasePrice)/position.R)*position.R;     // where R = Risk Per Share in $
      double trailingStopScaled=trailingStop;
      double daysHeld=Math.Abs(dateGenerator.DaysBetweenActual(position.PurchaseDate,tradeDate));
      if(Utility.IsEpoch(position.LastStopAdjustment))          // we've never adjusted the stop price
      {
        if(daysHeld>=Parameters.MinDaysBetweenInitialStopAdjustment)
        {
          if(Parameters.UseStopLimitScaling)
          {
            if(Parameters.StopLimitScalingType.Equals("AverageTrueRange")) trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(tradeDate,currentPrice,position,trailingStop);
            else
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid StopLimitScalingType({0}).  No scaling will be applied",Parameters.StopLimitScalingType));
              trailingStopScaled=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;
          }
          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;
					}
          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)));
          StopLimit newStopLimit=new StopLimit
          {
            Symbol=position.Symbol,
            AnalysisDate=tradeDate,
            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=tradeDate;
        }
        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,Parameters.MinDaysBetweenInitialStopAdjustment));
      }
      else                                                      // we have already made prior stop adjustments
      {
        int daysSinceLastStopAdjustment=Math.Abs(dateGenerator.DaysBetweenActual(position.LastStopAdjustment,tradeDate));
        if(daysSinceLastStopAdjustment>=Parameters.MinDaysBetweenStopAdjustments)
        {
          if(Parameters.UseStopLimitScaling)
          {
            if(Parameters.StopLimitScalingType.Equals("AverageTrueRange")) trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(tradeDate,currentPrice,position,trailingStop);
            else
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid StopLimitScalingType({0}).  No scaling will be applied",Parameters.StopLimitScalingType));
              trailingStopScaled=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;
          }
          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)));
            StopLimit newStopLimit=new StopLimit
            {
              Symbol=position.Symbol,
              AnalysisDate=tradeDate,
              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=tradeDate;
          }
          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,Parameters.MinDaysBetweenStopAdjustments));
        }
      }
    }

    private double GetStopLimitWithScalingAverageTrueRange(DateTime tradeDate,Price currentPrice,Position position,double stopLimitNonScaled)
    {
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetStopLimitWithScalingAverageTrueRange: Symbol:{0} RMultiple={1}",position.Symbol,position.RMultiple));      
      double volatility=double.NaN;
      if(Parameters.UseProfitMaximization)
      {
        CodeRunner codeRunner=new CodeRunner();
        SymbolTable symbolTable=codeRunner.SymbolTable;
        symbolTable.AddObjects(new Object[]{position}.ToList());
        codeRunner.Execute(Parameters.UseProfitMaximizationExpression);
        double multiplier=codeRunner.GetValue<double>("MULTIPLIER");
        volatility=VolatilityGenerator.CalculateVolatility(position.Symbol,tradeDate,Parameters.StopLimitScalingVolatilityDays,multiplier);
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using profit maximization strategy for {0} which has RMultiple={1}. Multiplier:{2}",position.Symbol,position.RMultiple,Utility.FormatNumber(multiplier,3)));
      }
      else
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using standard ATR multiplier for {0} which has RMultiple={1}.",position.Symbol,position.RMultiple));
        volatility=VolatilityGenerator.CalculateVolatility(position.Symbol,tradeDate,Parameters.StopLimitScalingVolatilityDays);
      }
      if(double.IsNaN(volatility))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Unable to calculate StopLimit for 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;
    }

    // ***************************************************************************************************************************************************
    // ************************************************************* M A N A G E  S E T U P S  ***********************************************************
    // ***************************************************************************************************************************************************
    private void ManageCandidates(DateTime tradeDate)
    {
      List<String> symbolsHeld=new List<String>();
      ExpireCandidates(tradeDate);
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}.  There are {1} candidates in the candidate pool.",tradeDate.ToShortDateString(),Candidates.Count));
      CMTGeneratorResult cmtGeneratorResult=CMTTrendGenerator.GenerateCMTCandidates(tradeDate,Parameters,symbolsHeld);
      if(!cmtGeneratorResult.Success)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GenerateCMTCandidates failed with message {0}",cmtGeneratorResult.LastMessage));
        return;
      }
      for(int index=0;index<cmtGeneratorResult.CMTCandidates.Count;index++)
      {
        CMTCandidate mmCandidate=cmtGeneratorResult.CMTCandidates[index];
        AddCandidate(mmCandidate);
      }
    }

    private void ExpireCandidates(DateTime tradeDate)
    {
      List<CMTCandidate> candidatesToRemove=new List<CMTCandidate>();
      DateGenerator dateGenerator=new DateGenerator();
      if(null==Candidates||0==Candidates.Count) return;
      foreach(CMTCandidate candidate in Candidates)
      {
        if(Math.Abs(dateGenerator.DaysBetweenActual(tradeDate,candidate.AnalysisDate))>Parameters.CandidateExpiryDays) candidatesToRemove.Add(candidate);
      }
      foreach(CMTCandidate candidate in candidatesToRemove) Candidates.Remove(candidate);
    }

    private void AddCandidate(CMTCandidate candidate)
    {
      if(null==Candidates) Candidates=new CMTCandidates();
      if(Candidates.Any(x => x.Symbol.Equals(candidate.Symbol))) return;
      Candidates.Add(candidate);
    }

    private void RemoveCandidate(CMTCandidate candidate)
    {
      if(null==Candidates) Candidates=new CMTCandidates();
      if(!Candidates.Any(x => x.Symbol.Equals(candidate.Symbol))) return;
      Candidates.Remove(Candidates.Where(x => x.Symbol.Equals(candidate.Symbol)).FirstOrDefault());
    }

    // ***************************************************************************************************************************************************
    // ************************************************************************ M A R K E T  C O N D I T I O N S   ***************************************
    // ***************************************************************************************************************************************************
    private static bool IsTradeableMarket(DateTime tradeDate,CMTParams cmtParams)
    {
      Func<DateTime,CMTParams,bool>[] indicators = {IsTradeableMarketIndicator, IsTradeableVolatilityEnvironment};
      return indicators.All(x => x.Invoke(tradeDate, cmtParams));
    }

    private static bool IsTradeableMarketIndicator(DateTime tradeDate,CMTParams cmtParams)
    {
		  MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableMarketIndicator(setting):{0}",cmtParams.UseMarketIndicator?"Active":"Disabled"));
      if(!cmtParams.UseMarketIndicator) return true;
      bool result=true;
      DateGenerator dateGenerator=new DateGenerator();
      Prices benchmarkPrices=null;
      double benchmarkDMA=double.NaN;
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableMarketIndicator: Benchmark:{0} Horizon:{1} Moving Average Days:{2}",cmtParams.Benchmark,cmtParams.BenchmarkMovingAverageHorizon,cmtParams.BenchmarkMovingAverageDays));
      List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(tradeDate,cmtParams.BenchmarkMovingAverageHorizon);
      foreach(DateTime historicalDate in historicalDates)
      {
        benchmarkPrices=GBPriceCache.GetInstance().GetPrices(cmtParams.Benchmark,historicalDate,cmtParams.BenchmarkMovingAverageDays+20);
        benchmarkPrices=new Prices(benchmarkPrices.Take(cmtParams.BenchmarkMovingAverageDays).ToList());
        DMAPrices benchmarkDMAPrices=MovingAverageGenerator.GenerateMovingAverage(benchmarkPrices,benchmarkPrices.Count);
        benchmarkDMA=benchmarkDMAPrices[0].AVGPrice;
        if(benchmarkPrices[0].Close<benchmarkDMA)
        {
           result=false;
           break;
        }
      }
      if(!result)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableMarketIndicator:{0}  Benchmark:{1} BenchmarkMovingAverageHorizon:{2} BenchmarkMovingAverageDays:{3} **",result,cmtParams.Benchmark,cmtParams.BenchmarkMovingAverageHorizon,cmtParams.BenchmarkMovingAverageDays));
      }
      return result;
    }

// Determine volatility based on ^VIX bollinger L band break on the close within 60 days prior
		private static bool IsTradeableVolatilityEnvironment(DateTime tradeDate,CMTParams cmtParams)
		{
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableVolatilityEnvironment(setting):{0}",cmtParams.UseMarketIndicatorVolatility?"Active":"Disabled"));
      if(!cmtParams.UseMarketIndicatorVolatility) return true;
			bool result=true;
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableVolatilityEnvironment: Volatility Benchmark:{0} Volatility Horizon:{1}",cmtParams.UseMarketIndicatorVolatilityBenchmark,cmtParams.UseMarketIndicatorVolatilityHorizon));
      DateTime bandBreakDate=BandBreakIndicator.IsLowerBandBreakClose(cmtParams.UseMarketIndicatorVolatilityBenchmark,tradeDate,cmtParams.UseMarketIndicatorVolatilityHorizon);
      if(!(Utility.IsEpoch(bandBreakDate)))
			{
        DateGenerator dateGenerator=new DateGenerator();
        int elapsedDays = dateGenerator.TradingDaysBetween(tradeDate,bandBreakDate);
        int daysRemaining=cmtParams.UseMarketIndicatorVolatilityHorizon-elapsedDays;
				result=false;
				MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableVolatilityEnvironment:{0}, VolatilityBenchmark:{1}, Violation/Band Break Date:{2}, Elapsed Days:{3}, Days Remaining:{4}, TradeDate:{5}",
          result,cmtParams.UseMarketIndicatorVolatilityBenchmark,bandBreakDate.ToShortDateString(),elapsedDays,daysRemaining,tradeDate.ToShortDateString()));
			}  
			return result;
		}

    // ***************************************************************************************************************************************************
    // ************************************************************************ G E T  P R I C E   ******************************************************
    // ***************************************************************************************************************************************************
    private Price GetPrice(String symbol,DateTime priceDate)
    {
      DateGenerator dateGenerator=new DateGenerator();
      priceDate=dateGenerator.GetPrevBusinessDay(priceDate);
      Price price=GBPriceCache.GetInstance().GetPrice(symbol,priceDate);
      if(null==price) price=GBPriceCache.GetInstance().GetPrice(symbol,dateGenerator.FindPrevBusinessDay(priceDate));
      return price;
    }

    // *********************************************************************************************************************************************************************
    // *********************************************************************************************************************************************************************
    // *********************************************************************************************************************************************************************
    public void DisplayRealtimeBlotter(DateTime tradeDate)
    {
      if(AllPositions.Count>0||ActivePositions.Count>0)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,"***************************************************************************************************************************");
      }
      if(AllPositions.Count>0)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CLOSED POSITIONS ({0})...",AllPositions.Count));
        Position.DisplayHeader();
        foreach(Position position in AllPositions) position.Display();
      }
      if(ActivePositions.Count>0)
      {
        foreach(Position position in ActivePositions)
        {
          Price price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,tradeDate);
          if(null==price) continue;
          position.CurrentPrice=price.Close;
        }
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("OPEN POSITIONS ({0})...",ActivePositions.Count));
        Position.DisplayHeader();
        foreach(Position position in ActivePositions) position.Display();
      }
      if(AllPositions.Count>0||ActivePositions.Count>0)
      {
        double totalGainLoss=GetRealtimeGainLoss(tradeDate);
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE {0}",Utility.FormatCurrency(ActivePositions.GetExposure())));
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL GAIN LOSS {0}",Utility.FormatCurrency(totalGainLoss)));
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CASH BALANCE {0}",Utility.FormatCurrency(CashBalance)));
        MDTrace.WriteLine(LogLevel.DEBUG,"***************************************************************************************************************************");
      }
    }

    public double GetRealtimeGainLoss(DateTime tradeDate)
    {
      int count=ActivePositions.Count;
      double gainLoss=0.00;

      foreach(Position position in AllPositions) gainLoss+=position.GainLoss;
      foreach(Position position in ActivePositions)
      {
        Price price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,tradeDate);
        gainLoss+=(price.Close*position.Shares)-(position.PurchasePrice*position.Shares);
      }
      return gainLoss;
    }

    private void DisplayBalance()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,AVAILABLE CASH,TOTAL ACCOUNT");
      if(!double.IsNaN(ActivePositions.GetMarketValue()))
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2}",
          Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
          Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
          Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance))));
      }
      else
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2}",
          Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
          Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
          Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure()+CashBalance))));
      }
    }

    private void DisplayBalanceFromPositions()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT");
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
        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))));
    }

    private void DisplayBalanceFromAllPositions()
    {
      MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT");
      MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
        Utility.AddQuotes(Utility.FormatCurrency(AllPositions.GetExposure())),
        Utility.AddQuotes(Utility.FormatCurrency(AllPositions.GetGainLoss())),
        Utility.AddQuotes(Utility.FormatPercent(AllPositions.GetGainLossPercent())),
        Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
        Utility.AddQuotes(Utility.FormatCurrency(CashBalance))));
    }
    // ****************************************************************************************************************************************
    // ************************************************************* C O N T R O L  T O D A Y ***********************************************
    // ****************************************************************************************************************************************
    public DateTime Today()
    {
      return DateTime.Now;
    }

    // Ensure that the given trade date is the next anticipated trade date given in the session file. (i.e.) TradeDate=SessionFile.TradeDate+1 
    private CMTTrendModelResult CheckSequentialTradeDate(DateTime tradeDate,DateTime sessionParamsTradeDate)
    {
      CMTTrendModelResult result=new CMTTrendModelResult();
      DateGenerator dateGenerator=new DateGenerator();
      DateTime nextBusinessDay=dateGenerator.FindNextBusinessDay(sessionParamsTradeDate);
      if(!nextBusinessDay.Date.Equals(tradeDate.Date))
      {
        String strMessage=String.Format("****** The supplied trade date does not match the next business date in the trade file. Given:{0}, Expected:{1} ******",tradeDate.Date.ToShortDateString(),nextBusinessDay.ToShortDateString());
        result.Message=strMessage;
        result.Success=false;
        return result;
      }
      result.Success=true;
      return result;
    }
    // ****************************************************************************************************************************************
    // **************************************************************** S E S S I O N  M A N A G E M E N T  ***********************************
    // ****************************************************************************************************************************************
    public CMTSessionParams RestoreSession()
    {
      try
      {
        if(!CMTSessionManager.SessionAvailable(PathSessionFileName)) return null;
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Restoring session from '{0}'",PathSessionFileName));
        CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(PathSessionFileName);
        TradeDate=sessionParams.TradeDate;
        if(TradeDate.Date<AnalysisDate.Date) TradeDate=AnalysisDate;
        StartDate=sessionParams.StartDate;
        Parameters=sessionParams.CMTParams;
        PricingExceptions=sessionParams.PricingExceptions;
        ActivePositions=sessionParams.ActivePositions;
        AllPositions=sessionParams.AllPositions;
        Candidates=sessionParams.Candidates;
        StopLimits=sessionParams.StopLimits;
        CashBalance=sessionParams.CashBalance;
        NonTradeableCash=sessionParams.NonTradeableCash;
        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));
      CMTSessionParams sessionParams=new CMTSessionParams();
      sessionParams.LastUpdated=Today();
      sessionParams.TradeDate=TradeDate;
      sessionParams.StartDate=StartDate;
      sessionParams.AnalysisDate=AnalysisDate;
      sessionParams.CMTParams=Parameters;
      sessionParams.PricingExceptions=PricingExceptions;
      sessionParams.StopLimits=StopLimits;
      sessionParams.ActivePositions=ActivePositions;
      sessionParams.AllPositions=AllPositions;
      sessionParams.Candidates=Candidates;
      sessionParams.CashBalance=CashBalance;
      sessionParams.NonTradeableCash=NonTradeableCash;
      CMTSessionManager.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));
      CMTSessionParams sessionParams=new CMTSessionParams();
      sessionParams.LastUpdated=Today();
      sessionParams.TradeDate=TradeDate;
      sessionParams.StartDate=StartDate;
      sessionParams.AnalysisDate=AnalysisDate;
      sessionParams.CMTParams=Parameters;
      sessionParams.PricingExceptions=PricingExceptions;
      sessionParams.StopLimits=StopLimits;
      sessionParams.ActivePositions=ActivePositions;
      sessionParams.AllPositions=AllPositions;
      sessionParams.Candidates=Candidates;
      sessionParams.CashBalance=CashBalance;
      sessionParams.NonTradeableCash=NonTradeableCash;
      return CMTSessionManager.SaveSession(sessionParams,backupFileName);
    }
  }
}
