diff --git a/MarketDataLib/DataAccess/FundamentalDA.cs b/MarketDataLib/DataAccess/FundamentalDA.cs index 2dc06ab..702c7c0 100644 --- a/MarketDataLib/DataAccess/FundamentalDA.cs +++ b/MarketDataLib/DataAccess/FundamentalDA.cs @@ -459,6 +459,87 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + + public static Fundamentals GetFundamentalMaxDateTop(String symbol, DateTime asof, int top) + { + MySqlConnection sqlConnection = null; + MySqlDataReader sqlDataReader = null; + MySqlCommand sqlCommand=null; + String strQuery = null; + Fundamentals fundamentals = new Fundamentals(); + + try + { + DateTime? maxDate=GetMaxDateFromFundamental(symbol,asof); // get the maximum date on record on or before max date + if(null==maxDate)return null; + StringBuilder sb = new StringBuilder(); + sqlConnection = SqlUtils.CreateMySqlConnection(MainDataSource.Instance.LocateDataSource("market_data")); + sb.Append("select symbol,asof,next_earnings_date,beta,low52,high52,volume,market_cap,pe,eps,peg,return_on_assets,return_on_equity,total_cash,total_debt,shares_outstanding,revenue,revenue_per_share,qtrly_revenue_growth,gross_profit,ebitda,net_income_available_to_common,book_value_per_share,operating_cashflow,leveraged_free_cashflow,book_value_per_share*shares_outstanding as equity,trailing_pe,ebit,enterprise_value,source from fundamentals where symbol="); + sb.Append("'").Append(symbol).Append("'").Append(" "); + sb.Append("and asof<=").Append("'").Append(Utility.DateTimeToStringYYYYHMMHDD(maxDate.Value)).Append("'"); + sb.Append(" order by asof desc "); + sb.Append(" limit ").Append(top); + strQuery = sb.ToString(); + sqlCommand = new MySqlCommand(strQuery, sqlConnection); + sqlCommand.CommandTimeout = SqlUtils.COMMAND_TIMEOUT; + sqlDataReader = sqlCommand.ExecuteReader(); + while(sqlDataReader.Read()) + { + + double totalStockHolderEquity=BalanceSheetDA.GetTotalStockHolderEquityOnOrBefore(symbol,asof,BalanceSheet.PeriodType.Annual); + Fundamental fundamental = new Fundamental(); + fundamental.Symbol = sqlDataReader.GetString(0); + fundamental.AsOf = sqlDataReader.GetDateTime(1); + if (!sqlDataReader.IsDBNull(2)) fundamental.NextEarningsDate = sqlDataReader.GetDateTime(2); + if (!sqlDataReader.IsDBNull(3)) fundamental.Beta = sqlDataReader.GetDouble(3); + if (!sqlDataReader.IsDBNull(4)) fundamental.Low52 = sqlDataReader.GetDouble(4); + if (!sqlDataReader.IsDBNull(5)) fundamental.High52 = sqlDataReader.GetDouble(5); + if (!sqlDataReader.IsDBNull(6)) fundamental.Volume = sqlDataReader.GetInt64(6); + if (!sqlDataReader.IsDBNull(7)) fundamental.MarketCap = sqlDataReader.GetDouble(7); + if (!sqlDataReader.IsDBNull(8)) fundamental.PE = sqlDataReader.GetDouble(8); + if (!sqlDataReader.IsDBNull(9)) fundamental.EPS = sqlDataReader.GetDouble(9); + if (!sqlDataReader.IsDBNull(10)) fundamental.PEG = sqlDataReader.GetDouble(10); + if (!sqlDataReader.IsDBNull(11)) fundamental.ReturnOnAssets = sqlDataReader.GetDouble(11); + if (!sqlDataReader.IsDBNull(12)) fundamental.ReturnOnEquity = sqlDataReader.GetDouble(12); + if (!sqlDataReader.IsDBNull(13)) fundamental.TotalCash = sqlDataReader.GetDouble(13); + if (!sqlDataReader.IsDBNull(14)) fundamental.TotalDebt = sqlDataReader.GetDouble(14); + if (!sqlDataReader.IsDBNull(15)) fundamental.SharesOutstanding = sqlDataReader.GetDouble(15); + if (!sqlDataReader.IsDBNull(16)) fundamental.Revenue = sqlDataReader.GetDouble(16); + if (!sqlDataReader.IsDBNull(17)) fundamental.RevenuePerShare = sqlDataReader.GetDouble(17); + if (!sqlDataReader.IsDBNull(18)) fundamental.QtrlyRevenueGrowth = sqlDataReader.GetDouble(18); + if (!sqlDataReader.IsDBNull(19)) fundamental.GrossProfit = sqlDataReader.GetDouble(19); + if (!sqlDataReader.IsDBNull(20)) fundamental.EBITDA = sqlDataReader.GetDouble(20); + if (!sqlDataReader.IsDBNull(21)) fundamental.NetIncomeAvailableToCommon = sqlDataReader.GetDouble(21); + if (!sqlDataReader.IsDBNull(22)) fundamental.BookValuePerShare = sqlDataReader.GetDouble(22); + if (!sqlDataReader.IsDBNull(23)) fundamental.OperatingCashflow = sqlDataReader.GetDouble(23); + if (!sqlDataReader.IsDBNull(24)) fundamental.LeveragedFreeCashflow = sqlDataReader.GetDouble(24); + if (!sqlDataReader.IsDBNull(25)) fundamental.Equity = sqlDataReader.GetDouble(25); + if (!sqlDataReader.IsDBNull(26)) fundamental.TrailingPE = sqlDataReader.GetDouble(26); + if (!sqlDataReader.IsDBNull(27)) fundamental.EBIT = sqlDataReader.GetDouble(27); + if (!sqlDataReader.IsDBNull(28)) fundamental.EnterpriseValue = sqlDataReader.GetDouble(28); + if (!sqlDataReader.IsDBNull(29)) fundamental.Source = sqlDataReader.GetString(29); + if (!double.IsNaN(totalStockHolderEquity) && !double.IsNaN(fundamental.TotalDebt)) + { + if(0.00==totalStockHolderEquity)fundamental.TotalDebt=0.00; + else fundamental.DebtToEquity=fundamental.TotalDebt/totalStockHolderEquity; + } + fundamentals.Add(fundamental); + } + return fundamentals; + } + catch (Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,exception); + return null; + } + finally + { + if(null!=sqlCommand)sqlCommand.Dispose(); + if (null != sqlDataReader) sqlDataReader.Close(); + if (null != sqlConnection) sqlConnection.Close(); + } + } + public static bool InsertFundamentals(Fundamentals fundamentals) { MySqlConnection sqlConnection = null; diff --git a/MarketDataLib/Generator/CMMomentum/CMBacktest.cs b/MarketDataLib/Generator/CMMomentum/CMBacktest.cs index 4a1dbc2..b6e8c19 100644 --- a/MarketDataLib/Generator/CMMomentum/CMBacktest.cs +++ b/MarketDataLib/Generator/CMMomentum/CMBacktest.cs @@ -386,6 +386,11 @@ namespace MarketData.Generator.CMMomentum Parameters.DisplayConfiguration(); CheckCNNServerStatus(); DisplayBalance(); + if(TradeDate > AnalysisDate) + { + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("*** It looks like the TradeDate {0} is greater than the AnalysisDate {1}. Please check the TradeDate in the session file. It should be the last market date of the previous month. You may have altered the system date during the last run.", TradeDate.ToShortDateString(), AnalysisDate.ToShortDateString())); + if(null!=PathSessionFileName)MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Session file: {0}",PathSessionFileName)); + } while (true) { if (TradeDate > AnalysisDate) break; @@ -409,6 +414,7 @@ namespace MarketData.Generator.CMMomentum { Positions slotPositions = ActivePositions[slotIndex]; SellPositions(slotPositions, TradeDate); + DisplaySales(slotPositions, TradeDate); MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************"); slotPositions.Display(); AllPositions.Add(slotPositions); @@ -417,6 +423,7 @@ namespace MarketData.Generator.CMMomentum DisplayBalance(); double cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / HoldingPeriod); // Even out the cash allocation so that no one slot eats up all the cash Positions positions=BuyPositions(slotIndex,TradeDate,AnalysisDate,cashAllocation,SymbolsHeld()); + DisplayPurchases(positions, TradeDate); MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); positions.Display(); if (CashBalance - positions.Exposure <= 0.00) @@ -439,6 +446,7 @@ namespace MarketData.Generator.CMMomentum { if (!ActivePositions.ContainsKey(slotIndex) || 0 == ActivePositions[slotIndex].Count()) continue; Positions slotPositions = ActivePositions[slotIndex]; + DisplaySales(slotPositions, TradeDate); SellPositions(slotPositions, AnalysisDate); MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L ********************"); slotPositions.Display(); @@ -463,6 +471,25 @@ namespace MarketData.Generator.CMMomentum // *************************************************************************************************************************************************** // **************************************************************** S E L L P O S I T I O N S ***************************************************** // *************************************************************************************************************************************************** +// These make the monthly process a bit easier to read + private 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())); + } + } + + private 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())); + } + } + private void SellPositions(Positions positions, DateTime sellDate) { DateGenerator dateGenerator = new DateGenerator(); diff --git a/MarketDataLib/Generator/CMMomentum/CMCandidate.cs b/MarketDataLib/Generator/CMMomentum/CMCandidate.cs index 26304cf..9d2cf8f 100644 --- a/MarketDataLib/Generator/CMMomentum/CMCandidate.cs +++ b/MarketDataLib/Generator/CMMomentum/CMCandidate.cs @@ -33,6 +33,7 @@ namespace MarketData.Generator.CMMomentum public long Volume { get; set; } public bool Violation { get; set; } public String Reason { get; set; } + public String ReasonCategory { get; set; } public bool CNNPrediction{get;set;} } diff --git a/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs b/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs index 60309d4..567532d 100644 --- a/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs +++ b/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs @@ -22,32 +22,35 @@ namespace MarketData.Generator.CMMomentum { CMCandidate cmCandidate = new CMCandidate(); - // Check MarketCap Fundamental fundamental = FundamentalDA.GetFundamentalMaxDate(symbol, tradeDate); if (null == fundamental) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("No fundamental for {0}.", symbol); + cmCandidate.Reason = String.Format("No fundamental."); + cmCandidate.ReasonCategory = String.Format("No fundamental."); return cmCandidate; } if (!(fundamental.MarketCap >= cmParams.MarketCapLowerLimit)) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("MarketCapLowerLimit constraint violation for {0}.", symbol); + cmCandidate.Reason = String.Format("MarketCapLowerLimit constraint violation."); + cmCandidate.ReasonCategory = String.Format("MarketCapLowerLimit constraint violation."); return cmCandidate; } // Check if the symbol is held in any open positions if (null != symbolsHeld && symbolsHeld.Any(x => x.Equals(symbol))) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("{0} is already held.", symbol); + cmCandidate.Reason = String.Format("Already held."); + cmCandidate.ReasonCategory = String.Format("Already held."); return cmCandidate; } // No trade symbols if (null!=cmParams.NoTradeSymbolsList&&cmParams.NoTradeSymbolsList.Any(x => x.Equals(symbol))) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("{0} is in the No-Trade list.", symbol); + cmCandidate.Reason = String.Format("Candidate in the No-Trade list."); + cmCandidate.ReasonCategory = String.Format("Candidate in the No-Trade list."); return cmCandidate; } // Equity check @@ -55,7 +58,8 @@ namespace MarketData.Generator.CMMomentum if (!companyProfile.IsEquity) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("{0} is not an equity. {1}.", symbol, companyProfile.SecurityType); + cmCandidate.Reason = String.Format("Candidate is not an equity."); + cmCandidate.ReasonCategory = String.Format("Candidate is not an equity."); return cmCandidate; } // Moving Average check @@ -63,21 +67,24 @@ namespace MarketData.Generator.CMMomentum if (null == pricesDMA || (cmParams.MovingAverageConstraintDays + 15) != pricesDMA.Count) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("Insufficient pricing to generate {0} day moving average for {1}. Required {2} days.",cmParams.MovingAverageConstraintDays,symbol,cmParams.MovingAverageConstraintDays + 15); + cmCandidate.Reason = String.Format("Insufficient pricing to generate moving average."); + cmCandidate.ReasonCategory = String.Format("Insufficient pricing to generate moving average."); return cmCandidate; } DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(pricesDMA, cmParams.MovingAverageConstraintDays); if (dmaPrices[0].CurrentPrice < dmaPrices[0].AVGPrice) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("Current price for {0} is less than moving average. {1}<{2} on {3}", symbol, dmaPrices[0].CurrentPrice, dmaPrices[0].AVGPrice, dmaPrices[0].Date.ToShortDateString()); + cmCandidate.Reason = String.Format("Current price is less than moving average."); + cmCandidate.ReasonCategory = String.Format("Current price is less than moving average."); return cmCandidate; } Prices prices = PricingDA.GetPrices(symbol, tradeDate, cmParams.DayCount); // The prices come back with the most recent date in the lowest index. We want the earliest date in the lowest index if (null == prices || cmParams.DayCount != prices.Count) { cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("Insufficient pricing data for {0}. Required {1} days.", symbol, cmParams.DayCount); + cmCandidate.Reason = String.Format("Insufficient pricing data."); + cmCandidate.ReasonCategory = String.Format("Insufficient pricing data."); return cmCandidate; } // Filter penny stocks - don't trade anything less than $1.00 @@ -85,6 +92,7 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Penny stock violation {0} Close price is {1}, Open price is ", symbol, Utility.FormatCurrency(prices[0].Close), Utility.FormatCurrency(prices[0].Open)); + cmCandidate.ReasonCategory = String.Format("Penny stock violation."); return cmCandidate; } // Capture latest Volume - we'll do a min check later on @@ -94,6 +102,7 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Liquidity check violation for {0}. Price volume must be above 10,000 ", symbol); + cmCandidate.ReasonCategory = String.Format("Liquidity check violation."); return cmCandidate; } // OverExtended check @@ -104,24 +113,19 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation=true; cmCandidate.Reason=String.Format("OverExtended violation for {0}. The security is price overextended for TradeDate:{1}.",symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)); + cmCandidate.ReasonCategory=String.Format("OverExtended violation. The security is price overextended."); return cmCandidate; } } // Momentum Check - float[] dailyReturns = prices.GetReturns(); // First we build the returns (before we reverse the pricing direction) - if (HasReturnViolation(dailyReturns,cmParams.DailyReturnLimit)) // Check the return stream. If any daily return exceeds DailyReturnLimit then we discard. + if (HasReturnViolation(prices,cmParams.DailyReturnLimit)) // Check the return stream. If any daily return exceeds DailyReturnLimit then we discard. { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Daily return violation for {0}. A daily return exceeded {1}%.", symbol, cmParams.DailyReturnLimit); + cmCandidate.ReasonCategory = String.Format("Daily return violation. A daily return exceeded expectations."); return cmCandidate; } - // check for outliers in the return stream - if ((from float value in dailyReturns where Math.Abs(value) > cmParams.DailyReturnLimit select value).Count() > 0) - { - cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("Outlier encountered in return stream for {0}. Limit {1}", symbol, cmParams.DailyReturnLimit); - return cmCandidate; - } + prices.Reverse(); // Reverse the series here. double[] logPrices = null; logPrices = new double[prices.Count]; @@ -147,18 +151,21 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Unable to calculate {0} month beta for {1} ", cmParams.BetaMonths, symbol); + cmCandidate.ReasonCategory = String.Format("Beta cannot be calculated."); return cmCandidate; } if (cmCandidate.Beta <= 0.00) { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Beta for {0} is less than or equal to zero {1}", symbol, cmCandidate.Beta); + cmCandidate.ReasonCategory = String.Format("Beta is less than or equal to zero."); return cmCandidate; } if (cmParams.UseMaxBeta && cmCandidate.Beta > cmParams.MaxBeta) { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Beta for {0} exceeds maximum allowed. Candidate beta {1}, Max Beta:{2}", symbol, cmCandidate.Beta,cmParams.MaxBeta); + cmCandidate.ReasonCategory = String.Format("Beta exceeds maximum allowed."); return cmCandidate; } cmCandidate.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(cmCandidate.Symbol, tradeDate); @@ -166,6 +173,7 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Unable to calculate Sharpe Ratio for {0}", symbol); + cmCandidate.ReasonCategory = String.Format("Unable to calculate Sharpe Ratio."); return cmCandidate; } return cmCandidate; @@ -183,12 +191,14 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("No fundamental for {0}.", symbol); + cmCandidate.ReasonCategory = String.Format("No fundamental."); return cmCandidate; } if (!(fundamental.MarketCap >= cmParams.MarketCapLowerLimit)) { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("MarketCapLowerLimit constraint violation for {0}.", symbol); + cmCandidate.ReasonCategory = String.Format("MarketCapLowerLimit constraint violation."); return cmCandidate; } @@ -197,6 +207,7 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("{0} is already held.", symbol); + cmCandidate.ReasonCategory = String.Format("Candidate already held."); return cmCandidate; } Prices prices = PricingDA.GetPrices(symbol, tradeDate, cmParams.DayCount); // The prices come back with the most recent date in the lowest index. We want the earliest date in the lowest index @@ -204,25 +215,20 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Insufficient pricing data for {0}. Required {1} days.", symbol, cmParams.DayCount); + cmCandidate.ReasonCategory = String.Format("Insufficient pricing data."); return cmCandidate; } cmCandidate.Volume = prices[0].Volume; - // Momentum Check - float[] dailyReturns = prices.GetReturns(); // First we build the returns (before we reverse the pricing direction) - // check for outliers in the return stream - if ((from float value in dailyReturns where Math.Abs(value) > cmParams.DailyReturnLimit select value).Count() > 0) - { - cmCandidate.Violation = true; - cmCandidate.Reason = String.Format("Outlier encountered in return stream for {0}. Limit {1}", symbol,cmParams.DailyReturnLimit); - return cmCandidate; - } - if (HasReturnViolation(dailyReturns, cmParams.DailyReturnLimit)) + // Momentum Check + if (HasReturnViolation(prices, cmParams.DailyReturnLimit)) { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Daily return violation for {0}. A daily return exceeded {1}%.", symbol, cmParams.DailyReturnLimit); + cmCandidate.ReasonCategory = String.Format("Daily return violation. A daily return exceeded the limit."); return cmCandidate; } + prices.Reverse(); // Reverse the series here. double[] logPrices = null; logPrices = new double[prices.Count]; @@ -248,18 +254,21 @@ namespace MarketData.Generator.CMMomentum { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Unable to calculate {0} month beta for {1} ", cmParams.BetaMonths, symbol); + cmCandidate.ReasonCategory = String.Format("Beta could not be calculated."); return cmCandidate; } if (cmCandidate.Beta <= 0.00) { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Beta for {0} is less than or equal to zero {1}", symbol, cmCandidate.Beta); + cmCandidate.ReasonCategory = String.Format("Beta is less than or equal to zero."); return cmCandidate; } if (cmParams.UseMaxBeta && cmCandidate.Beta > cmParams.MaxBeta) { cmCandidate.Violation = true; cmCandidate.Reason = String.Format("Beta for {0} exceeds maximum allowed. Candidate beta {1}, Max Beta:{2}", symbol, cmCandidate.Beta, cmParams.MaxBeta); + cmCandidate.ReasonCategory = String.Format("Beta exceeds maximum allowed."); return cmCandidate; } cmCandidate.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(cmCandidate.Symbol, tradeDate); @@ -306,9 +315,25 @@ namespace MarketData.Generator.CMMomentum } return (from CMCandidate cmCandidate in cmCandidates select cmCandidate).OrderByDescending(x => x.Slope).ToList().Take(1).FirstOrDefault(); } - private static bool HasReturnViolation(float[] dailyReturns,double dailyReturnLimit) + //private static bool HasReturnViolation(float[] dailyReturns,double dailyReturnLimit) + //{ + // foreach (float dailyReturn in dailyReturns) if (dailyReturn > dailyReturnLimit) return true; + // return false; + //} + + private static bool HasReturnViolation(Prices prices, double dailyReturnLimit) { - foreach (float dailyReturn in dailyReturns) if (dailyReturn > dailyReturnLimit) return true; + float[] dailyReturns = prices.GetReturns(); + int index=0; + + String symbol = prices.Take(1).First().Symbol; + for(;index dailyReturnLimit) + { + return true; + } + } return false; } } diff --git a/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs b/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs index 48e7e73..a893435 100644 --- a/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs +++ b/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs @@ -56,6 +56,21 @@ namespace MarketData.Generator.CMMomentum cmGeneratorResult.CMCandidates.Add(cmCandidate); } } + + if(null!=cmGeneratorResult.CMCandidatesWithViolation && 0!=cmGeneratorResult.CMCandidatesWithViolation.Count) + { + MDTrace.WriteLine(LogLevel.DEBUG,"**************** C A N D I D A T E S U M M A R Y ************************"); + IEnumerable> groups = cmGeneratorResult.CMCandidatesWithViolation.GroupBy(x => x.ReasonCategory).OrderByDescending(group => group.Count()).Select(group => Tuple.Create(group.Key, group.Count())); + foreach(Tuple group in groups) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Group: {0} Count:{1}",group.Item1, group.Item2)); + } + MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Considered : {cmGeneratorResult.CMCandidates.Count+cmGeneratorResult.CMCandidatesWithViolation.Count}")); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Disqualified : {cmGeneratorResult.CMCandidatesWithViolation.Count}")); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Eligible : {cmGeneratorResult.CMCandidates.Count}")); + MDTrace.WriteLine(LogLevel.DEBUG,"******************************************************************************************************"); + } + if (0 == cmGeneratorResult.CMCandidates.Count) { cmGeneratorResult.CMCandidates = GenerateFallbackCandidates(tradeDate, analysisDate, cmParams,symbolsHeld); @@ -129,7 +144,6 @@ namespace MarketData.Generator.CMMomentum dataProcessor.PenWidth=1; TestCase testCase=new TestCase(cmCandidate.Symbol,cmCandidate.TradeDate,cmParams.UseCNNDayCount,TestCase.CaseType.Test,TestCase.GenerateType.BollingerBand,TestCase.OutputType.OutputStream); dataProcessor.ProcessData(testCase); -// String prediction = cnnClient.Predict(CNNClient.Model.resnet50,testCase.LastStream); String prediction = cnnClient.Predict(CNNClient.Model.resnet50_20241024_270,testCase.LastStream); prediction=prediction.Substring(prediction.IndexOf("-->")); int result=int.Parse(Utility.BetweenString(prediction,"[[","]")); diff --git a/MarketDataLib/Generator/CMMomentum/CMParams.cs b/MarketDataLib/Generator/CMMomentum/CMParams.cs index 57eac15..d5974d7 100644 --- a/MarketDataLib/Generator/CMMomentum/CMParams.cs +++ b/MarketDataLib/Generator/CMMomentum/CMParams.cs @@ -14,7 +14,7 @@ namespace MarketData.Generator.CMMomentum DayCount = 90; // The lookback period AnalysisDate = DateTime.Now.Date; // The analysis date of the run TradeDate = DateTime.Now; // The current trade date - DailyReturnLimit = .15; // Reject candidates that exceed DailyReturnLimit within the lookback period + DailyReturnLimit = .25; // .15 was producing a lot unqualified candidates, this test to .25. Reject candidates that exceed DailyReturnLimit within the lookback period MovingAverageConstraintDays = 100; // If current price of a candidate is below the DMA(MovingAverageConstraintDays) then it is rejected FallbackCandidateBestOf = "SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA"; // if set then the fallback candidate is selected as the best 252 day return in this comma seperated list (i.e.) "SHV,ACWX,AGG" FallbackMaxAlloc = 1000; // Max Allocation for Fallback candidate. diff --git a/MarketDataLib/MarketDataModel/Prices.cs b/MarketDataLib/MarketDataModel/Prices.cs index 927ad76..1637496 100644 --- a/MarketDataLib/MarketDataModel/Prices.cs +++ b/MarketDataLib/MarketDataModel/Prices.cs @@ -281,7 +281,7 @@ namespace MarketData.MarketDataModel DateTime minPricingDate = PricingDA.GetEarliestDate(symbol); Dictionary symbolPricesByDate = new Dictionary(); List historicalDates = new List(); - while (historicalDates.Count < (months + 1)) + while (historicalDates.Count < (months + 5)) // pad the months by 5 { historicalDates.Add(startDate); startDate = dateGenerator.GetPrevMonthStart(startDate); @@ -290,10 +290,11 @@ namespace MarketData.MarketDataModel Prices symbolPrices = PricingDA.GetPrices(symbol, requestStartDate, historicalDates[historicalDates.Count - 1]); foreach (Price price in symbolPrices) symbolPricesByDate.Add(price.Date, price); startDate = dateGenerator.GetCurrMonthStart(asof); + if(startDate>asof)startDate = dateGenerator.GetPrevMonthStart(asof); // if start date winds up > asof on account of a weekend or holiday then fall back a further month while (prices.Count < (months + 1)) { Price price = GetPrice(symbol, startDate, symbolPricesByDate); - if (null == price) return null; + if(null == price)return null; prices.Add(price); startDate = dateGenerator.GetPrevMonthStart(startDate); if (startDate < minPricingDate) break;