Fix GetMonthlyPrices in Prices. It was not correctly returning monthly prices.

This was causing an issue in the SharpeRatioGenerator whereby the SharpeRatio was not being calculated correctly where the asof date would
fall on a weekend of holiday.
Also, added a ReasonCategory to the CMCanidate as well as a Violation summary line in the model output to show where violations occur.
Had I implemented this previously I might have detected the SharpeRatio issue sooner.
Also added GetFundamentalMaxDateTop in the FundamentalDA which I used during debugging but is not currently being used anywhere.
This commit is contained in:
2025-02-02 14:59:14 -05:00
parent 4b9be6bc12
commit 2882559651
7 changed files with 183 additions and 34 deletions

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;}
}

View File

@@ -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<dailyReturns.Length;index++)
{
if (Math.Abs(dailyReturns[index]) > dailyReturnLimit)
{
return true;
}
}
return false;
}
}

View File

@@ -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<Tuple<string, int>> groups = cmGeneratorResult.CMCandidatesWithViolation.GroupBy(x => x.ReasonCategory).OrderByDescending(group => group.Count()).Select(group => Tuple.Create(group.Key, group.Count()));
foreach(Tuple<string, int> 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,"[[","]"));

View File

@@ -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.

View File

@@ -281,7 +281,7 @@ namespace MarketData.MarketDataModel
DateTime minPricingDate = PricingDA.GetEarliestDate(symbol);
Dictionary<DateTime, Price> symbolPricesByDate = new Dictionary<DateTime, Price>();
List<DateTime> historicalDates = new List<DateTime>();
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;