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 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; 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())); continue; // 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 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 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 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); DisplayPurchases(positions, TradeDate); 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 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 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); DisplayPurchases(positions, TradeDate); 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 symbols=PricingDA.GetSymbols(); CMTParams cmtParams=new CMTParams(); List violations=new List(); List candidates=new List(); // Filter out symbols where we do not have a price on trade date Profiler profiler = new Profiler(); Dictionary latestDates = PricingDA.GetLatestDates(symbols); symbols=symbols.Where(x => latestDates.ContainsKey(x) && latestDates[x].Date>=analysisDate.Value.Date).ToList(); MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded pricing dates in {Utility.FormatNumber(profiler.End(),2)} (ms)"); // Prefetch a subset of fundamentals where each fundamental.asof is no greater than tradeDate profiler.Reset(); FundamentalsV2 fundamentals = FundamentalDA.GetFundamentalsMaxDateV2(analysisDate.Value); MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded fundamentals in {Utility.FormatNumber(profiler.End(),2)} (ms)"); // Prefetch the company profiles profiler.Reset(); Dictionary companyProfiles = CompanyProfileDA.GetCompanyProfiles(symbols); MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded company profiles in {Utility.FormatNumber(profiler.End(),2)} (ms)"); // Prefetch the pricing dates required for 200 day moving average. profiler.Reset(); DateGenerator dateGenerator = new DateGenerator(); DateTime historicalDate=dateGenerator.GenerateHistoricalDate(analysisDate.Value,cmtParams.DMA200Horizon+10); List historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,analysisDate.Value); MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded moving average dates in {Utility.FormatNumber(profiler.End(),2)} (ms)"); // Prefetch the EPS time series profiler.Reset(); Dictionary epsTimeSeriesCollectionDictionary = FundamentalDA.GetEPS(symbols,analysisDate.Value,3); MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded EPS Time Series in {Utility.FormatNumber(profiler.End(),2)} (ms)"); // Prefetch the profit margin time series profiler.Reset(); Dictionary profitMarginTimeSeriesCollectionDictionary = IncomeStatementDA.GetProfitMarginMaxAsOf(symbols,analysisDate.Value,3,IncomeStatement.PeriodType.Quarterly); MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded Profit Margin Time Series in {Utility.FormatNumber(profiler.End(),2)} (ms)"); for(int index=0;index violationTypes=violations.Select(x => x.Reason).Distinct().ToList(); violationTypes.Sort(); foreach(String violationType in violationTypes) { List 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; Positions closedPositions = new Positions(); // List closedPositions=new List(); 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.Lowposition.PurchasePrice) // If the Low price is above our purchase price then re-evaluate the stop { EvaluateStopPrice(tradeDate,price,position); } } if(0!=closedPositions.Count) { DisplaySales(closedPositions, TradeDate); 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 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 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 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 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 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("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 symbolsHeld=new List(); 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 candidatesToRemove=new List(); 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[] 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 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].Close0||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 /// This makes for easier reading of the sales /// /// /// private static void DisplaySales(Positions positions,DateTime tradeDate) { MDTrace.WriteLine(LogLevel.DEBUG,"********* S E L L S *********"); foreach (Position position in positions) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Sell {0} {1} @ {2} on {3}",position.Symbol,Utility.FormatNumber(position.Shares,3),Utility.FormatCurrency(position.CurrentPrice,2),tradeDate.ToShortDateString())); } } /// /// This makes for easier reading of the purchases /// /// /// private static void DisplayPurchases(Positions positions, DateTime tradeDate) { MDTrace.WriteLine(LogLevel.DEBUG,"********* B U Y S *********"); foreach (Position position in positions) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Buy {0} {1} @ {2} on {3}",position.Symbol,Utility.FormatNumber(position.Shares,3),Utility.FormatCurrency(position.PurchasePrice,2),tradeDate.ToShortDateString())); } } } }