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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,"[[","]"));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user