From acee222089a1984d75c83163980030aad5887c9f Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 10 Mar 2026 12:40:41 -0400 Subject: [PATCH] Ensure new symbols are added to watchlist. Ensure that a missing price does not cause GetModelPerformance to end abruptly. --- .../MarketDataLib/DataAccess/WatchListDA.cs | 32 +- .../Generator/CMMomentum/CMBacktest.cs | 25 +- .../Generator/CMTrend/CMTTrendModel.cs | 25 +- .../Generator/MGSHMomentum/MGSHBacktest.cs | 21 ++ .../Generator/Momentum/Backtest.cs | 24 +- .../Generator/Momentum/MomentumGenerator.cs | 299 ------------------ 6 files changed, 120 insertions(+), 306 deletions(-) diff --git a/MarketData/MarketDataLib/DataAccess/WatchListDA.cs b/MarketData/MarketDataLib/DataAccess/WatchListDA.cs index f98440a..25ed7d6 100755 --- a/MarketData/MarketDataLib/DataAccess/WatchListDA.cs +++ b/MarketData/MarketDataLib/DataAccess/WatchListDA.cs @@ -10,6 +10,29 @@ namespace MarketData.DataAccess private WatchListDA() { } + + /// + /// AddToWatchList - Add list of symbols to specified watch list + /// + /// + /// + /// + public static bool AddToWatchList(List symbols,String watchListName = "Valuations") + { + if(null == symbols || 0==symbols.Count || String.IsNullOrEmpty(watchListName))return false; + foreach(string symbol in symbols) + { + AddToWatchList(symbol, watchListName); + } + return true; + } + + /// + /// AddToWatchList - This will ignore the insert if the record already exists + /// + /// + /// + /// public static bool AddToWatchList(String symbol, String watchListName = "Valuations") { MySqlConnection sqlConnection = null; @@ -23,11 +46,11 @@ namespace MarketData.DataAccess symbol = symbol.ToUpper(); WatchListItem watchListItem = GetWatchListItem(watchListName); if (null == watchListItem) return false; - if (IsInWatchList(symbol, watchListName)) return true; +// if (IsInWatchList(symbol, watchListName)) return true; sqlConnection = SqlUtils.CreateMySqlConnection(MainDataSource.Instance.LocateDataSource("portfolio_data")); sqlTransaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted); StringBuilder sb = new StringBuilder(); - sb.Append("insert into watchlist(watch_list_id,symbol)values("); + sb.Append("insert ignore into watchlist(watch_list_id,symbol)values("); sb.Append(watchListItem.WatchListId).Append(","); sb.Append(SqlUtils.AddQuotes(symbol)); sb.Append(")"); @@ -50,6 +73,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static bool RemoveFromWatchList(String symbol, String watchListName = "Valuations") { MySqlConnection sqlConnection = null; @@ -88,6 +112,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static bool IsInWatchList(String symbol, String watchListName = "Valuations") { MySqlConnection sqlConnection = null; @@ -123,6 +148,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static List GetWatchList(String watchListName) { MySqlConnection sqlConnection = null; @@ -160,6 +186,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static WatchListItem GetWatchListItem(String watchListName) { MySqlConnection sqlConnection = null; @@ -197,6 +224,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static List GetWatchLists() { MySqlConnection sqlConnection = null; diff --git a/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs b/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs index 3e439bc..7a0d55b 100755 --- a/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs +++ b/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs @@ -143,8 +143,8 @@ namespace MarketData.Generator.CMMomentum price=PricingDA.GetPrice(openPosition.Symbol,currentDate); if(null==price) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString())); - return null; + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("******************* No price for {0} on {1} *****************",openPosition.Symbol,currentDate.ToShortDateString())); + continue; // Log a message and continue otherwise upstream fails entirely. } LocalPriceCache.GetInstance().Add(price); } @@ -399,6 +399,7 @@ namespace MarketData.Generator.CMMomentum 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); + AddToWatchList(positions); MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); positions.Display(); if (CashBalance - positions.Exposure <= 0.00) @@ -475,6 +476,26 @@ namespace MarketData.Generator.CMMomentum } } + /// + /// AddToWatchList - Add purchased positions to watch list for price tracking + /// + /// + /// + private static void AddToWatchList(Positions positions) + { + try + { + if(null==positions || 0==positions.Count)return; + List symbols = positions.Select(x=>x.Symbol).ToList(); + MDTrace.WriteLine(LogLevel.DEBUG,$"Adding {string.Join(",",symbols)} to WatchList"); + WatchListDA.AddToWatchList(symbols); + } + catch(Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"{exception.ToString()}"); + } + } + private void SellPositions(Positions positions, DateTime sellDate) { DateGenerator dateGenerator = new DateGenerator(); diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs index fbecd13..29966c4 100755 --- a/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs @@ -150,7 +150,7 @@ namespace MarketData.Generator.CMTrend if(null==price) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("******************* No price for {0} on {1} *****************",openPosition.Symbol,currentDate.ToShortDateString())); - return performanceSeries; + continue; // Log a message and continue. } gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares)); marketValue+=(price.Close*openPosition.Shares); @@ -653,6 +653,7 @@ namespace MarketData.Generator.CMTrend } ActivePositions.Add(positions); DisplayPurchases(positions, TradeDate); + AddToWatchList(positions); CashBalance-=positions.GetExposure(); } DisplayRealtimeBlotter(TradeDate); @@ -755,6 +756,7 @@ namespace MarketData.Generator.CMTrend } ActivePositions.Add(positions); DisplayPurchases(positions, TradeDate); + AddToWatchList(positions); CashBalance-=positions.GetExposure(); } MDTrace.WriteLine(LogLevel.DEBUG,"\n"); @@ -1818,6 +1820,25 @@ namespace MarketData.Generator.CMTrend { 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())); } - } + } + /// + /// AddToWatchList - Add purchased positions to watch list for price tracking + /// + /// + /// + private static void AddToWatchList(Positions positions) + { + try + { + if(null==positions || 0==positions.Count)return; + List symbols = positions.Select(x=>x.Symbol).ToList(); + MDTrace.WriteLine(LogLevel.DEBUG,$"Adding {string.Join(",",symbols)} to WatchList"); + WatchListDA.AddToWatchList(symbols); + } + catch(Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"{exception.ToString()}"); + } + } } } diff --git a/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs b/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs index c38422b..5b270a1 100755 --- a/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs +++ b/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs @@ -508,6 +508,7 @@ namespace MarketData.Generator.MGSHMomentum return; } DisplayPurchases(positions,TradeDate); + AddToWatchList(positions); ActivePositions.Add(slotIndex,positions); CashBalance-=positions.Exposure; SetInitialStopLimitsForNewPositions(TradeDate, positions); @@ -545,6 +546,7 @@ namespace MarketData.Generator.MGSHMomentum if(!ActivePositions.ContainsKey(slotIndex))ActivePositions.Add(slotIndex, positions); else ActivePositions[slotIndex].AddRange(positions); DisplayPurchases(positions, TradeDate); + AddToWatchList(positions); CashBalance-=positions.Exposure; SetInitialStopLimitsForNewPositions(TradeDate, positions); ActivePositions[slotIndex].Display(); @@ -1442,5 +1444,24 @@ namespace MarketData.Generator.MGSHMomentum } } + /// + /// AddToWatchList - Add purchased positions to watch list for price tracking + /// + /// + /// + private static void AddToWatchList(MGSHPositions positions) + { + try + { + if(null==positions || 0==positions.Count)return; + List symbols = positions.Select(x=>x.Symbol).ToList(); + MDTrace.WriteLine(LogLevel.DEBUG,$"Adding {string.Join(",",symbols)} to WatchList"); + WatchListDA.AddToWatchList(symbols); + } + catch(Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"{exception.ToString()}"); + } + } } } diff --git a/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs b/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs index f2d3cd5..cd71406 100755 --- a/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs +++ b/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs @@ -369,6 +369,7 @@ namespace MarketData.Generator.Momentum MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************")); } DisplayPurchases(positions,TradeDate); + AddToWatchList(positions); positions.Display(); ActivePositions.Add(slotIndex,positions); CashBalance-=positions.Exposure; @@ -398,6 +399,7 @@ namespace MarketData.Generator.Momentum break; } DisplayPurchases(positions, TradeDate); + AddToWatchList(positions); ActivePositions[slotIndex]=positions; CashBalance-=positions.Exposure; DisplayBalance(); @@ -749,6 +751,26 @@ namespace MarketData.Generator.Momentum { 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())); } - } + } + + /// + /// AddToWatchList - Add purchased positions to watch list for price tracking + /// + /// + /// + private static void AddToWatchList(Positions positions) + { + try + { + if(null==positions || 0==positions.Count)return; + List symbols = positions.Select(x=>x.Symbol).ToList(); + MDTrace.WriteLine(LogLevel.DEBUG,$"Adding {string.Join(",",symbols)} to WatchList"); + WatchListDA.AddToWatchList(symbols); + } + catch(Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"{exception.ToString()}"); + } + } } } diff --git a/MarketData/MarketDataLib/Generator/Momentum/MomentumGenerator.cs b/MarketData/MarketDataLib/Generator/Momentum/MomentumGenerator.cs index 17a2606..a608098 100755 --- a/MarketData/MarketDataLib/Generator/Momentum/MomentumGenerator.cs +++ b/MarketData/MarketDataLib/Generator/Momentum/MomentumGenerator.cs @@ -393,304 +393,5 @@ namespace MarketData.Generator.Momentum MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MomentumGenertor.GenerateMomentum:{0} candidates",momentumCandidates.Count())); return momentumCandidates; } - -/* -// This interface is called by the Backtest - public static MomentumCandidates GenerateMomentum(DateTime tradeDate,List symbolsHeld,MGConfiguration config) - { - DateGenerator dateGenerator=new DateGenerator(); - List symbols=PricingDA.GetSymbols(); - MomentumCandidates momentumCandidates=new MomentumCandidates(); - MomentumCandidates highPECandidates=new MomentumCandidates(); - DateTime startDateOfReturns=dateGenerator.GetPrevMonthEnd(tradeDate,2); - List noTradeSymbols=Utility.ToList(config.NoTradeSymbols); - List noTradeFinancialSymbols=Utility.ToList(config.NoTradeFinancialSymbols); - CandidateViolations candidateViolations = new CandidateViolations(); - - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Generate momentum.. examining candidates")); -// Go through the universe of stocks - for(int index=0;indexx.Equals(symbol, StringComparison.CurrentCultureIgnoreCase))) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate already held.")); - continue; - } - -// Check if the symbol is in the no trade list (i.e.) Bitcoin etc., - if(noTradeSymbols.Any(x=>x.Equals(symbol, StringComparison.CurrentCultureIgnoreCase))) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate in NoTradeSymbol.")); - continue; - } - -// Check MarketCap, EBITDA, PE, and Revenue Per Share - Fundamental fundamental=FundamentalDA.GetFundamentalMaxDate(symbol,tradeDate); - if(null==fundamental) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate no fundamental.")); - continue; - } - - if(!(fundamental.MarketCap>=config.MarketCapLowerLimit)) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate MarketCapLimit.")); - continue; - } - - if(config.UseEBITDAScreen && (double.IsNaN(fundamental.EBITDA)||fundamental.EBITDA<=0)) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate EBITDA violation.")); - continue; - } - - if(config.UseRevenuePerShareScreen && (double.IsNaN(fundamental.RevenuePerShare)||fundamental.RevenuePerShare<0.00)) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate RevenuePerShare violation.")); - continue; - } - -// Initial PE screening. This screen checks for existance of PE and if it is availabe it must be >0.00 . There is another PE based on limits further below - if(config.UsePEScreen && (double.IsNaN(fundamental.PE)||fundamental.PE<=0.00)) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate PE violation.")); - continue; - } - -// Exclude any company in the "Financial" sector - CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(symbol); - if(null!=companyProfile&&null!=companyProfile.Sector&&noTradeFinancialSymbols.Any(x=>x.Equals(companyProfile.Sector))) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate Financial Sector violation.")); - continue; - } - -// Fetch single day price - Price price=GBPriceCache.GetInstance().GetPrice(symbol,tradeDate); - if(null==price) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate missing price on trade date.")); - continue; - } -// Filter penny stocks - don't trade anything less than $1.00 - if(price.Close<1.00||price.Open<1.00) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate penny stock violation.")); - continue; - } - -// Retrieve prices - Prices prices=null; - prices=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,(int)MomentumGeneratorConstants.DayCount); - if(null==prices||prices.Count!=(int)MomentumGeneratorConstants.DayCount) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate missing price history.")); - continue; - } - -// calculate the one day return - double return1D=prices.GetReturn1D(); - -// Liquidity check - if any day has volume < 10,000 then we reject it - if(((from Price xPrice in prices where xPrice.Volume<10000 select xPrice).Count())>1) - { - candidateViolations.Add(new CandidateViolation(symbol,"Liquidity violation.")); - continue; - } - -// Calculate velocity as a percentage range of the open price within the 252+20 day range of prices - This is used for display purposes - double velocity; - Prices velocityPrices=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,(int)MomentumGenerator.MomentumGeneratorConstants.DayCount+20); - double priceHigh=(from Price selectPrice in velocityPrices select selectPrice.Open).Max(); - double priceLow=(from Price selectPrice in velocityPrices select selectPrice.Open).Min(); - if(0.00==priceHigh-priceLow)velocity=0.00; - else velocity=((price.Open-priceLow)*(100/(priceHigh-priceLow)))/100.00; - -// Price slopes - These are used for display purposes - double[] pricesArray=null; - LeastSquaresResult leastSquaresResult; - -// Get the benchmark pricing low pricing data and check the slope of previous lows; only if Beta of candidate is >= LowSlopeBetaThreshhold -// The idea behind this check is that a high beta stock will track to the benchmark. So if the benchmark lows are forming a downward pattern then we -// assume that this is a somewhat bearish condition. The config has the setting at a 15 day check and the threshold beta set to 1.00 - if(config.UseLowSlopeBetaCheck && fundamental.Beta>=config.LowSlopeBetaThreshhold) - { - Prices benchmarkPrices=GBPriceCache.GetInstance().GetPrices(config.Benchmark,tradeDate,config.LowSlopeBetaDays); - pricesArray=Numerics.ToDouble(benchmarkPrices.GetPricesLow()); - leastSquaresResult=Numerics.LeastSquares(pricesArray); - double slopeBmk=leastSquaresResult.Slope; - if(slopeBmk<0) - { - candidateViolations.Add(new CandidateViolation(symbol,"Beta threshhold violation.")); - continue; - } - } - -// *** MACDSignal detection - if(config.UseMACD) - { - MACDSetup macdSetup=new MACDSetup(config.MACDSetup); - MACDSignals macdSignals=MACDGenerator.GenerateMACD(prices,macdSetup); - Signals signalsMACD = SignalGenerator.GenerateSignals(macdSignals); - signalsMACD=new Signals(signalsMACD.Take(config.MACDSignalDays).ToList()); - int weakSellSignals=(from Signal signal in signalsMACD where signal.IsWeakSell() select signal).Count(); - int strongSellSignals=(from Signal signal in signalsMACD where signal.IsStrongSell() select signal).Count(); - if(config.MACDRejectWeakSellSignals && weakSellSignals>0) - { - candidateViolations.Add(new CandidateViolation(symbol,"MACD Reject Weak Sell violation.")); - continue; - } - if(config.MACDRejectStrongSellSignals && strongSellSignals>0) - { - candidateViolations.Add(new CandidateViolation(symbol,"MACD Reject Strong Sell violation.")); - continue; - } - } - -// *** Stochastics oscillator - if(config.UseStochastics) - { - Stochastics stochastics=StochasticsGenerator.GenerateStochastics(prices); - Signals signalsStochastics=new Signals(SignalGenerator.GenerateSignals(stochastics).OrderByDescending(x => x.SignalDate).ToList()); - signalsStochastics=new Signals(signalsStochastics.Take(config.StochasticsSignalDays).ToList()); - int weakSellCount=(from Signal signal in signalsStochastics where signal.IsWeakSell() select signal).Count(); - int strongSellCount=(from Signal signal in signalsStochastics where signal.IsStrongSell() select signal).Count(); - if(config.StochasticsRejectStrongSells&&strongSellCount>0) - { - candidateViolations.Add(new CandidateViolation(symbol,"Stochastics Oscillator Reject Strong Sell violation.")); - continue; - } - if(config.StochasticsRejectWeakSells&&weakSellCount>0) - { - candidateViolations.Add(new CandidateViolation(symbol,"Stochastics Oscillator Reject Weak Sell violation.")); - continue; - } - } - -// Analyst Ratings - "Downgrades" that are more than a year old (252 days) are not considered. Mean reversion.... bad companies improve, good companies decline. - DateTime minRatingDate=dateGenerator.GenerateHistoricalDate(startDateOfReturns,(int)MomentumGeneratorConstants.DayCount); - AnalystRatings analystRatings=AnalystRatingsDA.GetAnalystRatingsMaxDateNoZacks(symbol,tradeDate); - analystRatings.RemoveAll(x => x.Date.50 select value).Count()>0) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate pricing contains outliers in the returns.")); - continue; - } -// Cumulative return - double cumulativeReturn=prices.GetCumulativeReturn(); - if(cumulativeReturn<.10) - { - candidateViolations.Add(new CandidateViolation(symbol,"Candidate cumulative returns below threshhold.")); - continue; - } - -// Zacks Rank. This is for informational purposes for now but may further it's use in the future. - ZacksRank zacksRank=ZacksRankDA.GetZacksRankOnOrBefore(symbol,tradeDate); - -// Apply the PEScreening last because there an option to permit the inclusion of the high PE candidates if we have no other available candidates. -// The idea is to try to avoid high PE stocks as they are more likey to introduce drawdowns as backtests have shown. - if(config.UseMaxPEScreen && !double.IsNaN(fundamental.PE) && fundamental.PE>config.MaxPE) - { - candidateViolations.Add(new CandidateViolation(symbol,"PE violation.")); - MomentumCandidate highPECandidate=new MomentumCandidate(); - highPECandidate.AnalysisDate=tradeDate; - highPECandidate.Symbol=symbol; - highPECandidate.CumReturn252=prices.GetCumulativeReturn(); - highPECandidate.DayCount=(int)MomentumGeneratorConstants.DayCount; - highPECandidate.IDIndicator=IDIndicator.Calculate(prices); - highPECandidate.Score=ScoreIndicator.Calculate(prices); - highPECandidate.MaxDrawdown=prices.MaxDrawdown(); - highPECandidate.MaxUpside=prices.MaxUpside(); - highPECandidate.PE=fundamental.PE; - highPECandidate.Beta=fundamental.Beta; - highPECandidate.Velocity=velocity; - highPECandidate.Volume=price.Volume; - highPECandidate.Return1D=return1D; - if(null!=zacksRank)highPECandidate.ZacksRank=zacksRank.Rank; - highPECandidates.Add(highPECandidate); - continue; - } -// *********************************************************************** C A N D I D A T E A C C E P T A N C E ******************************************************* -// At this point whatever remains is taken so initialize the candidate and add to list - MomentumCandidate momentumCandidate=new MomentumCandidate(); - momentumCandidate.AnalysisDate=tradeDate; - momentumCandidate.Symbol=symbol; - momentumCandidate.CumReturn252=prices.GetCumulativeReturn(); - momentumCandidate.DayCount=(int)MomentumGeneratorConstants.DayCount; - momentumCandidate.IDIndicator=IDIndicator.Calculate(prices); - momentumCandidate.Score=ScoreIndicator.Calculate(prices); - momentumCandidate.MaxDrawdown=prices.MaxDrawdown(); - momentumCandidate.MaxUpside=prices.MaxUpside(); - momentumCandidate.PE=fundamental.PE; - momentumCandidate.Beta=fundamental.Beta; - momentumCandidate.Velocity=velocity; - momentumCandidate.Volume=price.Volume; - momentumCandidate.Return1D=return1D; - if(null!=zacksRank)momentumCandidate.ZacksRank=zacksRank.Rank; - momentumCandidates.Add(momentumCandidate); - - } // for all symbols - - if(0!=candidateViolations.Count) - { - MDTrace.WriteLine(LogLevel.DEBUG,"**************** C A N D I D A T E S U M M A R Y ************************"); - IEnumerable> groups = candidateViolations.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 : {momentumCandidates.Count+candidateViolations.Count}")); - MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Disqualified : {candidateViolations.Count}")); - MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Eligible : {momentumCandidates.Count}")); - MDTrace.WriteLine(LogLevel.DEBUG,"******************************************************************************************************"); - -// ********************************************************* E N D C A N D I D A T E S E L E C T I O N C R I T E R I A **************************************** -// If we wind up with less than the number of required candidates then check the StrictMaxPE -// flag and, if allowed, add the highPECandidate (that we've accumulated but skipped) to the momentumCandidates ordering them by the Lowest PE - if(!config.StrictMaxPE && momentumCandidates.Count0) - { - int takeCandidates=config.MaxPositions-momentumCandidates.Count; - highPECandidates=new MomentumCandidates(highPECandidates.OrderBy(x=>x.PE).Take(takeCandidates).ToList()); - momentumCandidates.AddRange(highPECandidates); - if(config.Verbose)MDTrace.WriteLine(LogLevel.DEBUG,String.Format("High PE Candidates,{0}",Utility.FromList((from MomentumCandidate momentumCandidate in highPECandidates select momentumCandidate.Symbol).ToList()))); - } - - QualityIndicator qualityIndicator=new QualityIndicator(config.QualityIndicatorType); - if(qualityIndicator.Quality.Equals(QualityIndicator.QualityType.IDIndicator)) - { - momentumCandidates=new MomentumCandidates((from MomentumCandidate momentumCandidate in momentumCandidates orderby momentumCandidate.IDIndicator ascending, momentumCandidate.CumReturn252 descending, momentumCandidate.Return1D descending, momentumCandidate.Volume descending select momentumCandidate).ToList()); - } - else - { - momentumCandidates=new MomentumCandidates((from MomentumCandidate momentumCandidate in momentumCandidates orderby momentumCandidate.Score descending,momentumCandidate.CumReturn252 descending,momentumCandidate.Return1D descending,momentumCandidate.Volume descending select momentumCandidate).ToList()); - } - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MomentumGenertor.GenerateMomentum:{0} candidates",momentumCandidates.Count())); - return momentumCandidates; - } -*/ } }