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 System.Threading; using MarketData.Integration; namespace MarketData.Generator.Momentum { public class MomentumBacktest { 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 List NoTradeSymbols{get{return Utility.ToList(Configuration.NoTradeSymbols);}} private ActivePositions ActivePositions{get;set;} private SMSNotifications SMSNotifications{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;} // ****************************************************************************************************************************************************** //********************************************************** U P D A T E S E S S I O N P R I C E ***************************************************** // ****************************************************************************************************************************************************** public void UpdateSessionPrice(String symbol,DateTime tradeDate,double price,String pathSessionFileName) { MGSessionParams sessionParams=null; if(null==symbol||Utility.IsEpoch(tradeDate)||null==pathSessionFileName) { MDTrace.WriteLine(LogLevel.DEBUG,"UpdateSessionPrice. One or more parameters are invalid."); return; } PathSessionFileName=pathSessionFileName; if(null==(sessionParams=RestoreSession())) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFileName)); return; } List keys=new List(ActivePositions.Keys); bool hasChanges=false; foreach(int key in keys) { Positions positions=ActivePositions[key]; foreach(Position position in positions) { if(!position.Symbol.Equals(symbol)||!position.PurchaseDate.Date.Equals(tradeDate.Date))continue; MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Changing purchase price for {0} on {1} from {2} to {3}", position.Symbol, Utility.DateTimeToStringMMHDDHYYYY(position.PurchaseDate), Utility.FormatCurrency(position.PurchasePrice), Utility.FormatCurrency(price))); position.PurchasePrice=price; if(!hasChanges)hasChanges=true; } } if(hasChanges)SaveSession(); 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))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle)); } // ****************************************************************************************************************************************************** //************************************************************** 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(); DisplayBalanceFromPositions(); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StartDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(StartDate))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle)); } // ****************************************************************************************************************************************************** //******************************************************* 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))); if(null==(sessionParams=RestoreSession())) { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile)); return; } 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); CashBalance += slotPositions.MarketValue; ActivePositions[slotIndex].Clear(); } MGPriceCache.GetInstance().Dispose(); SaveSession(); } // ****************************************************************************************************************************************************** // ****************************************************************** 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; SMSNotifications=new SMSNotifications(); MGSessionParams sessionParams=null; Cycle=0; if(AnalysisDate.Date>Today().Date)return backTestResult; if(Utility.IsEpoch(AnalysisDate))AnalysisDate=Today(); TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate); if(TradeDate>AnalysisDate) { int startMonth=StartDate.Month; TimeSpan timeSpan=new TimeSpan(); if((new int[]{12,3,6,9}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(30,0,0,0); else if((new int[]{1,4,7,10}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(60,0,0,0); else if((new int[]{2,5,8,11}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(90,0,0,0); StartDate=StartDate-timeSpan; TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate); } if(null!=PathSessionFileName)sessionParams=RestoreSession(); if(null!=sessionParams) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using session file {0}, Last updated {1}",paramPathSessionFileName,sessionParams.LastUpdated)); } 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=BuyPositions(TradeDate,AnalysisDate,CashBalance/((double)HoldingPeriod-(double)ActivePositions.Count),ActivePositions.SymbolsHeld()); 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.Add(slotIndex,positions); CashBalance-=positions.Exposure; DisplayBalance(); } else { Positions slotPositions=ActivePositions[slotIndex]; SellPositions(slotPositions,TradeDate); MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************"); slotPositions.Display(); AllPositions.Add(slotPositions); 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 = BuyPositions(TradeDate, AnalysisDate, cashAllocation, ActivePositions.SymbolsHeld()); 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; 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 positions=ActivePositions[slotIndex]; if(null==positions||0==positions.Count)continue; foreach(Position position in positions) { Price price=MGPriceCache.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=MGPriceCache.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=MGPriceCache.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) //{ // 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 = MGPriceCache.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.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 // WatchListDA.AddToWatchList(position.Symbol, "Momentum"); // 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)) // { // fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(Utility.ToList(Configuration.FallbackCandidateBestOf), Configuration.FallbackCandidate, tradeDate); // } // Price price = MGPriceCache.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); // positions.Add(position); // } // return positions; //} private Positions BuyPositions(DateTime tradeDate, DateTime analysisDate, double cash, List symbolsHeld) { // return BuyPositionsWithSharpeRatioRiskAllocation(tradeDate, analysisDate, cash, symbolsHeld); return BuyPositionsWithBetaRiskAllocation(tradeDate, analysisDate, cash, symbolsHeld); } // ****************************************************************************************************************************************************************** // ********************************************** U S E S H A R P E R A T I O F O R P O S I T I O N S I Z I N G *********************************************** // ****************************************************************************************************************************************************************** public class SRRiskAllocation { public String Symbol { get; set; } public double SharpeRatioBaseOne { get; set; } // This applies an offset to the Sharpe Ratios so that we are working with zero based SharpeRatios for allocation purposes. public double Weight { get; set; } public double Allocation { get; set; } public int Shares { get; set; } } private Positions BuyPositionsWithSharpeRatioRiskAllocation(DateTime tradeDate, DateTime analysisDate, double cash, List symbolsHeld) { Positions positions = new Positions(); if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash); MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration); positions = PositonSizingWithSharpeRatioRiskAllocation(momentumCandidates, tradeDate, cash); 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)) { fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(Utility.ToList(Configuration.FallbackCandidateBestOf), Configuration.FallbackCandidate, tradeDate); } Price price = MGPriceCache.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); positions.Add(position); } return positions; } private Positions PositonSizingWithSharpeRatioRiskAllocation(MomentumCandidates momentumCandidates,DateTime tradeDate,double cash) { Positions positions = new Positions(); List riskAllocations = new List(); for (int index = 0; index < momentumCandidates.Count; index++) { MomentumCandidate momentumCandidate = momentumCandidates[index]; Price price = MGPriceCache.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.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.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(position.Symbol, tradeDate); position.Shares = 0.00; if (double.IsNaN(position.SharpeRatio)||0.00==position.SharpeRatio) continue; WatchListDA.AddToWatchList(position.Symbol, "Momentum"); positions.Add(position); } if (null == positions || 0 == positions.Count) return positions; positions = new Positions(positions.Take(MaxPositions).ToList()); double minSharpeRatio = positions.Min(x => x.SharpeRatio); for (int index = 0; index < positions.Count; index++) { Position position = positions[index]; SRRiskAllocation riskAllocation = new SRRiskAllocation(); riskAllocation.Symbol = position.Symbol; riskAllocation.SharpeRatioBaseOne = minSharpeRatio < 0.00 ? position.SharpeRatio + Math.Abs(minSharpeRatio) + 1.00 : position.SharpeRatio; riskAllocations.Insert(index, riskAllocation); } double sumSharpeRatioBaseOne = riskAllocations.Sum(x => x.SharpeRatioBaseOne); riskAllocations.Select(t => t.Weight = t.SharpeRatioBaseOne/sumSharpeRatioBaseOne).ToList(); riskAllocations.Select(t => t.Allocation = t.Weight*cash).ToList(); for (int index = 0; index < positions.Count; index++) { Position position = positions[index]; SRRiskAllocation riskAllocation = riskAllocations[index]; if (!position.Symbol.Equals(riskAllocation.Symbol)) throw new Exception("RiskAllocation items are not in sequence with positions."); position.Shares = Math.Floor(riskAllocation.Allocation/position.PurchasePrice); } positions =new Positions(positions.Where(x => x.Shares > 0).ToList()); return positions; } // ********************************************************************************************************************************************************************* // ********************************************************* E N D S H A R P E R A T I O R I S K A L L O C A T I O N ***************************************** // ********************************************************************************************************************************************************************* // ****************************************************************************************************************************************************************** // ********************************************** U S E B E T A F O R P O S I T I O N S I Z I N G *********************************************** // ****************************************************************************************************************************************************************** public class BetaRiskAllocation { public String Symbol { get; set; } public double TargetBetaOverBeta { get; set; } public double Weight { get; set; } public double Allocation { get; set; } public int Shares { get; set; } } private Positions BuyPositionsWithBetaRiskAllocation(DateTime tradeDate, DateTime analysisDate, double cash, List symbolsHeld) { Positions positions = new Positions(); if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash); MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration); positions = PositonSizingWithBetaRiskAllocation(momentumCandidates, tradeDate, cash); 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)) { fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(Utility.ToList(Configuration.FallbackCandidateBestOf), Configuration.FallbackCandidate, tradeDate); } Price price = MGPriceCache.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); positions.Add(position); } return positions; } private Positions PositonSizingWithBetaRiskAllocation(MomentumCandidates momentumCandidates, DateTime tradeDate, double cash) { Positions positions = new Positions(); List riskAllocations = new List(); for (int index = 0; index < momentumCandidates.Count; index++) { MomentumCandidate momentumCandidate = momentumCandidates[index]; Price price = MGPriceCache.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.MaxDrawdown = momentumCandidate.MaxDrawdown; position.MaxUpside = momentumCandidate.MaxUpside; position.PE = momentumCandidate.PE; position.Beta = BetaGenerator.Beta(position.Symbol, tradeDate, 6); // Use the beta generator to get a 6 month beta. if (double.IsNaN(position.Beta) || position.Beta <= 0.00) continue; 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.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(position.Symbol, tradeDate); // This is just for reference. position.Shares = 0.00; WatchListDA.AddToWatchList(position.Symbol, "Momentum"); positions.Add(position); } if (null == positions || 0 == positions.Count) return positions; positions = new Positions(positions.Take(MaxPositions).ToList()); double targetBeta = 1.00; double positionCount = positions.Count; for (int index = 0; index < positions.Count; index++) { Position position = positions[index]; BetaRiskAllocation riskAllocation = new BetaRiskAllocation(); riskAllocation.Symbol = position.Symbol; riskAllocation.TargetBetaOverBeta = targetBeta / position.Beta; riskAllocations.Insert(index, riskAllocation); } double sumTargetBetaOverBeta = riskAllocations.Sum(x => x.TargetBetaOverBeta); riskAllocations.Select(t => t.Weight = t.TargetBetaOverBeta / sumTargetBetaOverBeta).ToList(); riskAllocations.Select(t => t.Allocation = t.Weight *cash).ToList(); for (int index = 0; index < positions.Count; index++) { Position position = positions[index]; BetaRiskAllocation riskAllocation = riskAllocations[index]; if (!position.Symbol.Equals(riskAllocation.Symbol)) throw new Exception("RiskAllocation items are not in sequence with positions."); position.Shares = Math.Floor(riskAllocation.Allocation / position.PurchasePrice); } positions = new Positions(positions.Where(x => x.Shares > 0).ToList()); return positions; } // ********************************************************************************************************************************************************************* // ************************************************************* E N D B E T A R I S K A L L O C A T I O N ***************************************************** // ********************************************************************************************************************************************************************* // *************************************************************************************************************************************************************************** private Positions BuyBenchmarkPositions(DateTime tradeDate, double cash) { Positions positions = new Positions(); Price benchmarkPrice = MGPriceCache.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; WatchListDA.AddToWatchList(position.Symbol, "Momentum"); positions.Add(position); return positions; } // *********************************************************************************************************************************************************** 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