using System; using System.Collections.Generic; using MarketData.MarketDataModel; using MarketData.DataAccess; using MarketData.Utils; using System.Linq; using MarketData.Cache; using MarketData.Generator.Model; namespace MarketData.Generator.Momentum { public class MomentumBacktest { private double NonTradeableCash{get;set;} private double CashBalance{get;set;} private MGConfiguration Configuration{get;set;} private int HoldingPeriod{get{return Configuration.HoldingPeriod;}} private int MaxPositions{get{return Configuration.MaxPositions;}} private ActivePositions ActivePositions{get;set;} private Positions AllPositions{get;set;} private int Cycle{get;set;} private DateTime TradeDate{get;set;} private DateTime StartDate{get;set;} private DateTime AnalysisDate{get;set;} private String PathSessionFileName{get;set;} // ****************************************************************************************************************************************************** //************************************************************** D I S P L A Y G A I N L O S S ***************************************************** // ****************************************************************************************************************************************************** public static void DisplayGainLoss(String paramPathSessionFileName) { Profiler profiler=new Profiler(); ModelPerformanceSeries performanceSeries=GetModelPerformance(paramPathSessionFileName); if(null==performanceSeries)return; MDTrace.WriteLine("Date,Exposure,MarketValue,GainLossDOD,GainLoss,CumulativeGainLoss,R,(1+R),CumProd,CumProd-1,ClosedPositions"); foreach(ModelPerformanceItem modelPerformanceItem in performanceSeries) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\",\"{9}\",\"{10}\"", modelPerformanceItem.Date.ToShortDateString(), Utility.FormatCurrency(modelPerformanceItem.Exposure), Utility.FormatCurrency(modelPerformanceItem.MarketValue), Utility.FormatCurrency(modelPerformanceItem.GainLossDOD), Utility.FormatCurrency(modelPerformanceItem.GainLoss), Utility.FormatCurrency(modelPerformanceItem.CumulativeGainLoss), Utility.FormatNumber(modelPerformanceItem.R,4), Utility.FormatNumber(modelPerformanceItem.OnePlusR,4), Utility.FormatNumber(modelPerformanceItem.CumProd,4), Utility.FormatNumber(modelPerformanceItem.CumProdMinusOne,4), modelPerformanceItem.ClosedPositions)); } MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, took {0}(ms)",profiler.End())); } public static ModelPerformanceSeries GetModelPerformance(String paramPathSessionFileName) { try { MGSessionParams sessionParams=MGSessionManager.RestoreSession(paramPathSessionFileName); return GetModelPerformance(sessionParams); } catch(Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString()); return null; } } // Calculates 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(MGSessionParams 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(MGSessionParams sessionParams) { Profiler profiler=new Profiler(); ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries(); DateGenerator dateGenerator=new DateGenerator(); try { if(null==sessionParams)return null; MarketData.Generator.Momentum.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.Momentum.Positions openPositions=new MarketData.Generator.Momentum.Positions(combinedPositions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList()); MarketData.Generator.Momentum.Positions closedPositions=new MarketData.Generator.Momentum.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.Momentum.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())); } else { gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares)); marketValue+=(price.Close*openPosition.Shares); } } foreach(MarketData.Generator.Momentum.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; MGSessionParams 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)); Configuration.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,"************** A C T I V E P O S I T I O N S *************"); ActivePositions.Display(); MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P G A I N E R S *************"); AllPositions.DisplayTopFive(); MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P L O S S E R S *************"); AllPositions.DisplayBottomFive(); // DisplayBalanceFromPositions(); DisplayLatestModelPerformance(paramPathSessionFileName); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StartDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(StartDate))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle)); } // ****************************************************************************************************************************************************** //******************************************************* L I Q U I D A T E A L L P O S I T I O N S *********************************************** // ****************************************************************************************************************************************************** public void MGLiquididate(String pathSessionFile,DateTime? tradeDate) { if (null == pathSessionFile) return; MGSessionParams sessionParams=null; PathSessionFileName = pathSessionFile; if (null == (sessionParams = RestoreSession())) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile)); return; } MDTrace.WriteLine(LogLevel.DEBUG, "************** L I Q U I D A T E P O S I T I O N S *************"); if (null == ActivePositions || 0 == ActivePositions.Count) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("No active positions in file {0}", pathSessionFile)); return; } if(null==tradeDate)tradeDate = PricingDA.GetLatestDate(ActivePositions.GetSymbols()); MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Trade date:{0}", Utility.DateTimeToStringMMHDDHYYYY(tradeDate.Value))); for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++) { Positions slotPositions = ActivePositions[slotIndex]; SellPositions(slotPositions, tradeDate.Value); MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************"); slotPositions.Display(); AllPositions.Add(slotPositions); DisplaySales(slotPositions, TradeDate); CashBalance += slotPositions.MarketValue; ActivePositions[slotIndex].Clear(); } GBPriceCache.GetInstance().Dispose(); SaveSession(); } // ****************************************************************************************************************************************************** // ****************************************************************** C L O S E ********************************************************************** // ****************************************************************************************************************************************************** public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double sellPrice,String pathSessionFile) { if(null==pathSessionFile) return false; MGSessionParams sessionParams=null; PathSessionFileName=pathSessionFile; if(null==(sessionParams=RestoreSession())) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile)); return false; } if(!BackupSession()) return false; Positions activePositions = ActivePositions.GetPositions(); 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 = sellPrice; CashBalance += position.MarketValue; SaveSession(); return true; } position.SellDate = sellDate; position.CurrentPrice = sellPrice; 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,String pathSessionFile) { if(null==pathSessionFile) return false; PathSessionFileName=pathSessionFile; MGSessionParams sessionParams=null; if(null==(sessionParams=RestoreSession())) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile)); return false; } if(!BackupSession()) return false; Positions activePositions = ActivePositions.GetPositions(); 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.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))); CashBalance+=(position.PurchasePrice-purchasePrice)*position.Shares; 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; } // ****************************************************************************************************************************************************** // ****************************************************************** B A C K T E S T ***************************************************************** // ****************************************************************************************************************************************************** // Ideally, startDate should be November,February,May,August // paramStartDate is startDate {ORIGINAL START DATE} // paramAnalysisDate is endDate {TODAY} public BacktestResult PerformBacktest(DateTime paramStartDate,DateTime paramAnalysisDate,String paramPathSessionFileName,MGConfiguration configuration) { BacktestResult backTestResult=new BacktestResult(); DateGenerator dateGenerator=new DateGenerator(); Configuration=configuration; CashBalance=Configuration.InitialCash; ActivePositions=new ActivePositions(); AllPositions=new Positions(); StartDate=paramStartDate; TradeDate=paramStartDate; AnalysisDate=paramAnalysisDate; PathSessionFileName=paramPathSessionFileName; MGSessionParams sessionParams=null; Cycle=0; if(AnalysisDate.Date>Today().Date)return backTestResult; if(Utility.IsEpoch(AnalysisDate))AnalysisDate=dateGenerator.GetPrevBusinessDay(Today()); // Ensure AnalysisDate is not a weekend or holiday TradeDate =dateGenerator.GetCurrentMonthEnd(StartDate); if(TradeDate>AnalysisDate) { int startMonth=StartDate.Month; TimeSpan timeSpan=new TimeSpan(); if((new int[]{12,3,6,9}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(30,0,0,0); else if((new int[]{1,4,7,10}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(60,0,0,0); else if((new int[]{2,5,8,11}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(90,0,0,0); StartDate=StartDate-timeSpan; TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate); } if(null!=PathSessionFileName)sessionParams=RestoreSession(); if(null!=sessionParams) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using session file {0}, Last updated {1}",paramPathSessionFileName,sessionParams.LastUpdated)); } Configuration.DisplayConfiguration(); DisplayBalance(); while(true) { if(TradeDate>AnalysisDate)break; int slotIndex=(int)(((double)Cycle)%((double)(HoldingPeriod))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TRADE DATE {0} , ANALYSIS DATE {1}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate),Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate))); if(!ActivePositions.ContainsKey(slotIndex)) { Positions positions = null; positions=BuyPositions(TradeDate,AnalysisDate,CashBalance/((double)HoldingPeriod-(double)ActivePositions.Count),SymbolsHeld(TradeDate)); MDTrace.WriteLine(LogLevel.DEBUG, "******************** B U Y ********************"); if(CashBalance-positions.Exposure<0.00) { positions.Clear(); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************")); } positions.Display(); ActivePositions.Add(slotIndex,positions); DisplayPurchases(positions,TradeDate); CashBalance-=positions.Exposure; DisplayBalance(); } else { Positions slotPositions=ActivePositions[slotIndex]; List closedSymbols = slotPositions.ConvertAll(x => x.Symbol); // capture the closed symbols so we don't re-enter the position (avoid wash trades) SellPositions(slotPositions,TradeDate); MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************"); slotPositions.Display(); AllPositions.Add(slotPositions); DisplaySales(slotPositions, TradeDate); CashBalance+=slotPositions.MarketValue; ActivePositions[slotIndex].Clear(); DisplayBalance(); double cashAllocation=CashBalance; cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / (double)HoldingPeriod); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CASH ALLOCATION:{0}",Utility.FormatCurrency(cashAllocation))); Positions positions = null; positions=BuyPositions(TradeDate,AnalysisDate,cashAllocation,new List(SymbolsHeld(TradeDate).Concat(closedSymbols))); MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); positions.Display(); if(CashBalance-positions.Exposure<=0.00) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************")); break; } ActivePositions[slotIndex]=positions; DisplayPurchases(positions, TradeDate); CashBalance-=positions.Exposure; DisplayBalance(); } Cycle++; TradeDate=dateGenerator.GetNextMonthEnd(TradeDate); if(TradeDate>AnalysisDate)break; } // WHILE TRUE MDTrace.WriteLine(LogLevel.DEBUG,"RUN COMPLETE."); if(null!=PathSessionFileName)SaveSession(); for(int slotIndex=0;slotIndex SymbolsHeld(DateTime tradeDate) { List symbolsHeld=ActivePositions.SymbolsHeld(); if(!Configuration.IncludeTradeMasterForSymbolsHeld)return symbolsHeld; if(null == symbolsHeld)symbolsHeld=new List(); PortfolioTrades portfolioTrades=PortfolioDA.GetOpenTradesAsOf(tradeDate); if(null == portfolioTrades || 0==portfolioTrades.Count)return symbolsHeld; symbolsHeld.AddRange(portfolioTrades.Symbols.Distinct()); return symbolsHeld; } // ********************************************************************************************************************************************************** // **************************************************************** G E T E X P O S U R E / M A R K E T V A L U E***************************************** // ********************************************************************************************************************************************************** public RealtimeGainLoss GetRealtimeGainLoss(DateTime tradeDate) { int count=ActivePositions.Count; double marketValue=0.00; double exposure=0.00; RealtimeGainLoss gainLoss=new RealtimeGainLoss(); for(int slotIndex=0;slotIndex positions=ActivePositions[slotIndex]; if(null==positions||0==positions.Count)continue; foreach(Position position in positions) { Price price=GBPriceCache.GetInstance().GetRealtimePrice(position.Symbol); if(null==price){MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot price {0} on {1}",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));continue;} position.CurrentPrice=price.Close; } } for(int slotIndex=0;slotIndex positions=ActivePositions[slotIndex]; if(null==positions||0==positions.Count)continue; exposure+=(from Position position in positions select position.Exposure).Sum(); marketValue+=(from Position position in positions select position.MarketValue).Sum(); } gainLoss.Exposure=exposure; gainLoss.MarketValue=marketValue; return gainLoss; } // ************************************************************************************************************************************************** // **************************************************************** S E L L P O S I T I O N S ***************************************************** // *************************************************************************************************************************************************** private void SellPositions(Positions positions,DateTime sellDate) { foreach(Position position in positions) { Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,sellDate); position.SellDate=sellDate; if(null==price) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate))); position.CurrentPrice=position.PurchasePrice; } else position.CurrentPrice=price.Close; } } private void SellPosition(Position position,DateTime sellDate) { Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,sellDate); position.SellDate=sellDate; if(null==price) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate))); position.CurrentPrice=position.PurchasePrice; } else position.CurrentPrice=price.Close; } // *************************************************************************************************************************************************** // ******************************************************** B U Y P O S I T I O N S ************************************************************* // *************************************************************************************************************************************************** private Positions BuyPositions(DateTime tradeDate, DateTime analysisDate, double cash, List symbolsHeld) { MDTrace.WriteLine(LogLevel.DEBUG,"**BUYPOSITIONS**"); Positions positions = new Positions(); int positionCount = 0; if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash); MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration); for (int index = 0; index < momentumCandidates.Count; index++) { MomentumCandidate momentumCandidate = momentumCandidates[index]; Price price = GBPriceCache.GetInstance().GetPrice(momentumCandidate.Symbol, tradeDate); if (null == price) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", momentumCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate))); continue; } Position position = new Position(); position.Symbol = momentumCandidate.Symbol; position.CumReturn252 = momentumCandidate.CumReturn252; position.IDIndicator = momentumCandidate.IDIndicator; position.Score=momentumCandidate.Score; position.MaxDrawdown = momentumCandidate.MaxDrawdown; position.MaxUpside = momentumCandidate.MaxUpside; position.PE = momentumCandidate.PE; position.Beta = momentumCandidate.Beta; position.ZacksRank = momentumCandidate.ZacksRank; position.Velocity = momentumCandidate.Velocity; position.Volume = momentumCandidate.Volume; position.Return1D = position.Return1D; position.PurchaseDate = tradeDate; position.PurchasePrice = price.Close; position.CurrentPrice = price.Close; position.Shares = (int)Math.Floor((cash / (double)MaxPositions) / position.PurchasePrice); if (0 == position.Shares) continue; // if not able to purchase any shares, for example, if the share price exceeds our purchasing power then move on to the next candidate positions.Add(position); positionCount++; if (positionCount >= MaxPositions) break; } if (0 == positions.Count && Configuration.UseFallbackCandidate) // if we don't get any signals then consider the fallback candidate options as per the configuration file { String fallbackCandidate = Configuration.FallbackCandidate; if (null != Configuration.FallbackCandidateBestOf && !"".Equals(Configuration.FallbackCandidateBestOf)) { QualityIndicator qualityIndicator=new QualityIndicator(Configuration.QualityIndicatorType); fallbackCandidate=CandidateSelector.SelectBestCandidateSymbol(qualityIndicator,Utility.ToList(Configuration.FallbackCandidateBestOf),Configuration.FallbackCandidate,tradeDate); } Price price = GBPriceCache.GetInstance().GetPrice(fallbackCandidate, tradeDate); if (null == price) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", fallbackCandidate, Utility.DateTimeToStringMMHDDHYYYY(tradeDate))); return positions; } Position position = new Position(); position.Symbol = fallbackCandidate; position.PurchaseDate = tradeDate; position.PurchasePrice = price.Close; position.CurrentPrice = price.Close; position.Shares = (int)Math.Floor(cash / position.PurchasePrice); if(0 == position.Shares)return positions; positions.Add(position); } return positions; } private Positions BuyBenchmarkPositions(DateTime tradeDate, double cash) { Positions positions = new Positions(); Price benchmarkPrice = GBPriceCache.GetInstance().GetPrice(Configuration.BenchmarkModeSymbol, tradeDate); if (null == benchmarkPrice) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", Configuration.BenchmarkModeSymbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate))); return positions; } Position position = new Position(); position.Symbol = Configuration.BenchmarkModeSymbol; position.PurchaseDate = tradeDate; position.PurchasePrice = benchmarkPrice.Close; position.Shares = (int)Math.Floor(cash / position.PurchasePrice); if (0 == position.Shares) return positions; positions.Add(position); return positions; } // ********************************************************************************************************************************************************************* // ************************************************************ E N D B U Y P O S I T I O N S *********************************************** // ********************************************************************************************************************************************************************* private void DisplayLatestModelPerformance(String pathSessionFileName) { ModelPerformanceSeries performanceSeries=GetModelPerformance(pathSessionFileName); if(null==performanceSeries || 0==performanceSeries.Count)return; MDTrace.WriteLine("Date,Exposure,MarketValue,GainLossDoD,GainLoss,CumulativeGainLoss,R,(1+R),CumProd,CumProd-1,ClosedPositions"); ModelPerformanceItem modelPerformanceItem = performanceSeries[performanceSeries.Count-1]; // get the last record 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)); } private void DisplayBalance() { MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************"); MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,AVAILABLE CASH,TOTAL ACCOUNT"); 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)))); for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++) { Positions slotPositions = ActivePositions[slotIndex]; MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SLOT:{0} EXPOSURE:{1}",slotIndex,Utility.FormatCurrency(slotPositions.Exposure))); } MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE: {0}",Utility.FormatCurrency(ActivePositions.GetExposure()))); MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************"); } 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 DisplayBalance(RealtimeGainLoss gainLoss) { 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(gainLoss.GainLoss)), Utility.AddQuotes(Utility.FormatPercent(gainLoss.GainLossPercent)), Utility.AddQuotes(Utility.FormatCurrency(CashBalance)), Utility.AddQuotes(Utility.FormatCurrency(gainLoss.MarketValue+CashBalance)))); } // **************************************************************************************************************************************** // ************************************************************* C O N T R O L T O D A Y *********************************************** // **************************************************************************************************************************************** public DateTime Today() { return DateTime.Now; } // **************************************************************************************************************************************** // **************************************************************** S E S S I O N M A N A G E M E N T *********************************** // **************************************************************************************************************************************** public MGSessionParams RestoreSession() { try { MGSessionManager sessionManager=new MGSessionManager(); if(!MGSessionManager.SessionAvailable(PathSessionFileName))return null; MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Restoring session from '{0}'",PathSessionFileName)); MGSessionParams sessionParams=MGSessionManager.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())); } } } }