From 14bd7651ca7b97d96d88ae5649f79bc19542bac8 Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 21 Apr 2025 18:03:07 -0400 Subject: [PATCH] Optimizations for CMTrend --- .../DataAccess/CompanyProfileDA.cs | 48 ++++++++++ .../MarketDataLib/DataAccess/FundamentalDA.cs | 87 +++++++++++++++++-- .../DataAccess/IncomeStatementDA.cs | 36 +++++--- .../CMTrend/CMTCandidateGenerator.cs | 27 +++--- .../Generator/CMTrend/CMTTrendGenerator.cs | 35 +++++++- .../Generator/CMTrend/CMTTrendModel..cs | 36 +++++++- .../MarketDataModel/FundamentalV2.cs | 58 +++++++++++++ MarketData/MarketDataLib/Utility/SQLUtils.cs | 4 + README.md | 5 +- 9 files changed, 302 insertions(+), 34 deletions(-) create mode 100644 MarketData/MarketDataLib/MarketDataModel/FundamentalV2.cs diff --git a/MarketData/MarketDataLib/DataAccess/CompanyProfileDA.cs b/MarketData/MarketDataLib/DataAccess/CompanyProfileDA.cs index d502cce..574dd87 100755 --- a/MarketData/MarketDataLib/DataAccess/CompanyProfileDA.cs +++ b/MarketData/MarketDataLib/DataAccess/CompanyProfileDA.cs @@ -69,6 +69,54 @@ namespace MarketData.DataAccess } } + public static Dictionary GetCompanyProfiles(List symbols) + { + Dictionary companyProfiles=new Dictionary(); + MySqlConnection sqlConnection = null; + MySqlDataReader sqlDataReader = null; + MySqlCommand sqlCommand=null; + String strQuery = null; + + try + { + StringBuilder sb = new StringBuilder(); + sqlConnection = SqlUtils.CreateMySqlConnection(MainDataSource.Instance.LocateDataSource("market_data")); + sb.Append("select sm.symbol,sm.sector,sm.industry,sm.security_type,sm.company,cp.description,cp.pricing_source,cp.can_roll_previous,cp.freeze_pricing from securitymaster sm left outer join companyprofile cp on sm.symbol=cp.symbol").Append(" "); + sb.Append("where sm.symbol in ").Append(SqlUtils.CreateInClause(symbols)); + strQuery = sb.ToString(); + sqlCommand = new MySqlCommand(strQuery, sqlConnection); + sqlCommand.CommandTimeout = SqlUtils.COMMAND_TIMEOUT; + sqlDataReader = sqlCommand.ExecuteReader(); + while(sqlDataReader.Read()) + { + CompanyProfile companyProfile = new CompanyProfile(); + companyProfile.Symbol=sqlDataReader.GetString(0); + if (!sqlDataReader.IsDBNull(1)) companyProfile.Sector = sqlDataReader.GetString(1); + if (!sqlDataReader.IsDBNull(2)) companyProfile.Industry = sqlDataReader.GetString(2); + if (!sqlDataReader.IsDBNull(3)) companyProfile.SecurityType = sqlDataReader.GetString(3); + if (!sqlDataReader.IsDBNull(4)) companyProfile.CompanyName = sqlDataReader.GetString(4); + if (!sqlDataReader.IsDBNull(5)) companyProfile.Description = sqlDataReader.GetString(5); + if (!sqlDataReader.IsDBNull(6)) companyProfile.PricingSource = sqlDataReader.GetString(6).ToUpper(); + if (!sqlDataReader.IsDBNull(7)) companyProfile.CanRollPrevious = sqlDataReader.GetBoolean(7); + if (!sqlDataReader.IsDBNull(8)) companyProfile.FreezePricing = sqlDataReader.GetBoolean(8); + if(!companyProfiles.ContainsKey(companyProfile.Symbol))companyProfiles.Add(companyProfile.Symbol, companyProfile); + } + return companyProfiles; + } + catch (Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,exception); + return null; + } + finally + { + if(null!=sqlCommand)sqlCommand.Dispose(); + if (null != sqlDataReader) {sqlDataReader.Close();sqlDataReader.Dispose();} + if (null != sqlConnection) sqlConnection.Close(); + } + } + + public static CompanyProfiles GetCompanyProfiles() { CompanyProfiles companyProfiles=new CompanyProfiles(); diff --git a/MarketData/MarketDataLib/DataAccess/FundamentalDA.cs b/MarketData/MarketDataLib/DataAccess/FundamentalDA.cs index 1f04051..bf90fee 100755 --- a/MarketData/MarketDataLib/DataAccess/FundamentalDA.cs +++ b/MarketData/MarketDataLib/DataAccess/FundamentalDA.cs @@ -167,20 +167,31 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } - public static TimeSeriesCollection GetEPS(String symbol,DateTime? maxDate=null) + + /// + /// Retrieves a collection of timeseries for the given symbols with each symbol having the specified max asof and no more than maxSeries elements in the series + /// + /// + /// + /// + /// + public static Dictionary GetEPS(List symbols, DateTime maxDate,int maxSeries) { MySqlConnection sqlConnection = null; MySqlDataReader sqlDataReader = null; MySqlCommand sqlCommand = null; - TimeSeriesCollection timeSeriesCollection = new TimeSeriesCollection(); + Dictionary timeSeriesCollection = new Dictionary(); String strQuery = null; try { StringBuilder sb = new StringBuilder(); sqlConnection = SqlUtils.CreateMySqlConnection(MainDataSource.Instance.LocateDataSource("market_data")); - if(null==maxDate)sb.Append("select symbol,asof,eps from fundamentals where symbol='").Append(symbol).Append("' order by asof desc"); - else sb.Append("select symbol,asof,eps from fundamentals where symbol='").Append(symbol).Append("'").Append(" and asof<=").Append(SqlUtils.AddQuotes(SqlUtils.ToSqlDateTime(maxDate.Value))).Append(" order by asof desc"); + + sb.Append("SELECT B.symbol, B.asof, B.eps FROM "); + sb.Append("(SELECT symbol, asof, eps, ROW_NUMBER() OVER(PARTITION BY symbol ORDER BY asof desc) AS rownum FROM fundamentals "); + sb.Append($"WHERE symbol IN {SqlUtils.CreateInClause(symbols)} AND asof<={SqlUtils.ToSqlDate(maxDate,true)} )B "); + sb.Append($"WHERE B.rownum<={maxSeries}"); strQuery = sb.ToString(); ; sqlCommand = new MySqlCommand(strQuery, sqlConnection); sqlCommand.CommandTimeout = SqlUtils.COMMAND_TIMEOUT; @@ -195,7 +206,11 @@ namespace MarketData.DataAccess timeSeriesElement.Type = TimeSeriesElement.ElementType.OTHER; timeSeriesElement.OtherType = "EPS"; if (double.IsNaN(timeSeriesElement.Value)) continue; - timeSeriesCollection.Add(timeSeriesElement); + if(!timeSeriesCollection.ContainsKey(timeSeriesElement.Symbol)) + { + timeSeriesCollection.Add(timeSeriesElement.Symbol,new TimeSeriesCollection()); + } + timeSeriesCollection[timeSeriesElement.Symbol].Add(timeSeriesElement); } return timeSeriesCollection; } @@ -210,7 +225,8 @@ namespace MarketData.DataAccess if (null != sqlDataReader) {sqlDataReader.Close();sqlDataReader.Dispose();} if (null != sqlConnection) sqlConnection.Close(); } - } + } + public static Fundamental GetFundamental(String symbol) { MySqlConnection sqlConnection = null; @@ -384,6 +400,65 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + + + /// + /// Retrieve latest MarketCap, PE, EBITDA, RevenuePerShare for all symbols with aasof being no more recent than the provided date + /// Given a tradeDate of 04/18/2025 this method might return a collection similar to below. The model returned is a subset of the fundamental + /// 07/15/2018 ^FTSE + /// 03/13/2019 ^GSPC + /// 04/17/2025 AA + /// + /// The as of date + /// + public static FundamentalsV2 GetFundamentalsMaxDateV2(DateTime tradeDate) + { + MySqlConnection sqlConnection = null; + MySqlDataReader sqlDataReader = null; + MySqlCommand sqlCommand=null; + String strQuery = null; + FundamentalsV2 fundamentals = new FundamentalsV2(); + + try + { + StringBuilder sb = new StringBuilder(); + sqlConnection = SqlUtils.CreateMySqlConnection(MainDataSource.Instance.LocateDataSource("market_data")); + sb.Append("SELECT A.asof,A.symbol, A.market_cap,A.ebitda,A.pe,A.revenue_per_share FROM fundamentals A JOIN "); + sb.Append("(SELECT MAX(asof) asof,symbol FROM fundamentals WHERE asof<=").Append("'"); + sb.Append(Utility.DateTimeToStringYYYYHMMHDD(tradeDate.Date)); + sb.Append("'"); + sb.Append(" GROUP BY symbol ORDER BY symbol ASC)B "); + sb.Append(" ON A.asof=B.asof AND A.symbol=B.symbol "); + strQuery = sb.ToString(); ; + sqlCommand = new MySqlCommand(strQuery, sqlConnection); + sqlCommand.CommandTimeout = SqlUtils.COMMAND_TIMEOUT; + sqlDataReader = sqlCommand.ExecuteReader(); + while(sqlDataReader.Read()) + { + FundamentalV2 fundamental = new FundamentalV2(); + fundamental.AsOf = sqlDataReader.GetDateTime(0); + fundamental.Symbol = sqlDataReader.GetString(1); + if(!sqlDataReader.IsDBNull(2)) fundamental.MarketCap = sqlDataReader.GetDouble(2); + if(!sqlDataReader.IsDBNull(3)) fundamental.EBITDA = sqlDataReader.GetDouble(3); + if(!sqlDataReader.IsDBNull(4)) fundamental.PE = sqlDataReader.GetDouble(4); + if(!sqlDataReader.IsDBNull(5)) fundamental.RevenuePerShare = sqlDataReader.GetDouble(5); + if(!fundamentals.ContainsKey(fundamental.Symbol))fundamentals.Add(fundamental.Symbol,fundamental); + } + return fundamentals; + } + catch (Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,exception); + return null; + } + finally + { + if(null!=sqlCommand)sqlCommand.Dispose(); + if (null != sqlDataReader) {sqlDataReader.Close();sqlDataReader.Dispose();} + if (null != sqlConnection) sqlConnection.Close(); + } + } + public static Fundamental GetFundamentalMaxDate(String symbol, DateTime asof) { MySqlConnection sqlConnection = null; diff --git a/MarketData/MarketDataLib/DataAccess/IncomeStatementDA.cs b/MarketData/MarketDataLib/DataAccess/IncomeStatementDA.cs index 98b6a36..af35479 100755 --- a/MarketData/MarketDataLib/DataAccess/IncomeStatementDA.cs +++ b/MarketData/MarketDataLib/DataAccess/IncomeStatementDA.cs @@ -44,6 +44,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static List GetIncomeStatementDates(String symbol,IncomeStatement.PeriodType periodType) { List incomeStatementDates = new List(); @@ -82,6 +83,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static DateTime? GetLatestIncomeStatementDate(String symbol,IncomeStatement.PeriodType periodType) { MySqlConnection sqlConnection = null; @@ -120,6 +122,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static IncomeStatement GetIncomeStatement(String symbol,IncomeStatement.PeriodType periodType) { MySqlConnection sqlConnection = null; @@ -172,6 +175,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static TimeSeriesCollection GetRevenue(String symbol,IncomeStatement.PeriodType period) { Profiler profiler = new Profiler(); @@ -215,6 +219,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + // Profit Margin is calculated as a percentage. public static TimeSeriesCollection GetProfitMargin(String symbol,IncomeStatement.PeriodType period=IncomeStatement.PeriodType.Annual) { @@ -263,10 +268,10 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } - // Profit Margin is calculated as a percentage. - public static TimeSeriesCollection GetProfitMarginMaxAsOf(String symbol,DateTime maxDate, IncomeStatement.PeriodType period = IncomeStatement.PeriodType.Annual) + + public static Dictionary GetProfitMarginMaxAsOf(List symbols, DateTime maxDate,int maxSeries,IncomeStatement.PeriodType period = IncomeStatement.PeriodType.Annual) { - TimeSeriesCollection timeSeriesCollection = new TimeSeriesCollection(); + Dictionary timeSeriesCollection = new Dictionary(); MySqlConnection sqlConnection = null; MySqlDataReader sqlDataReader = null; MySqlCommand sqlCommand = null; @@ -276,9 +281,11 @@ namespace MarketData.DataAccess { StringBuilder sb = new StringBuilder(); sqlConnection = SqlUtils.CreateMySqlConnection(MainDataSource.Instance.LocateDataSource("market_data")); - sb.Append("select symbol,asof,total_revenue,gross_profit from incomestatement where symbol='").Append(symbol).Append("'").Append(" "); - sb.Append(" and asof<='").Append(SqlUtils.SqlDate(maxDate)).Append("' "); - sb.Append(" and period=").Append(period.Equals(IncomeStatement.PeriodType.Annual) ? 0 : 1).Append(" order by asof desc;"); + sb.Append("SELECT B.symbol, B.asof, B.total_revenue, B.gross_profit FROM "); + sb.Append("(SELECT symbol, asof, total_revenue, gross_profit, period , ROW_NUMBER() OVER(PARTITION BY symbol ORDER BY asof desc) AS rownum FROM incomestatement "); + sb.Append($" WHERE symbol IN {SqlUtils.CreateInClause(symbols)} AND asof<={SqlUtils.ToSqlDate(maxDate.Date,true)}"); + sb.Append($" AND period={(period.Equals(IncomeStatement.PeriodType.Annual) ? 0 : 1)} )B "); + sb.Append($" WHERE B.rownum<={maxSeries} "); strQuery = sb.ToString(); sqlCommand = new MySqlCommand(strQuery, sqlConnection); sqlCommand.CommandTimeout = SqlUtils.COMMAND_TIMEOUT; @@ -288,15 +295,19 @@ namespace MarketData.DataAccess TimeSeriesElement timeSeriesElement = new TimeSeriesElement(); timeSeriesElement.Symbol = sqlDataReader.GetString(0); timeSeriesElement.AsOf = sqlDataReader.GetDateTime(1); - timeSeriesElement.Type = period.Equals(IncomeStatement.PeriodType.Quarterly) ? TimeSeriesElement.ElementType.QuarterlyRevenue : TimeSeriesElement.ElementType.Revenue; - + timeSeriesElement.Type = TimeSeriesElement.ElementType.OTHER; + timeSeriesElement.OtherType = "Profit Margin %"; if (sqlDataReader.IsDBNull(2)) continue; double total_revenue = sqlDataReader.GetDouble(2); if (sqlDataReader.IsDBNull(3)) continue; double gross_profit = sqlDataReader.GetDouble(3); if (0 == gross_profit) continue; timeSeriesElement.Value = (gross_profit / total_revenue) * 100.00; - timeSeriesCollection.Add(timeSeriesElement); + if(!timeSeriesCollection.ContainsKey(timeSeriesElement.Symbol)) + { + timeSeriesCollection.Add(timeSeriesElement.Symbol, new TimeSeriesCollection()); + } + timeSeriesCollection[timeSeriesElement.Symbol].Add(timeSeriesElement); } return timeSeriesCollection; } @@ -311,7 +322,8 @@ namespace MarketData.DataAccess if (null != sqlDataReader) {sqlDataReader.Close();sqlDataReader.Dispose();} if (null != sqlConnection) sqlConnection.Close(); } - } + } + public static IncomeStatement GetIncomeStatement(String symbol,DateTime asof,IncomeStatement.PeriodType periodType) { MySqlConnection sqlConnection = null; @@ -362,6 +374,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static IncomeStatement GetIncomeStatementMaxAsOf(String symbol,DateTime asof,IncomeStatement.PeriodType periodType) { MySqlConnection sqlConnection = null; @@ -413,6 +426,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + public static bool InsertIncomeStatements(List incomeStatements) { MySqlConnection sqlConnection = null; @@ -479,6 +493,7 @@ namespace MarketData.DataAccess if (null != sqlConnection) sqlConnection.Close(); } } + private static bool DeleteIncomeStatements(List incomeStatements, MySqlConnection sqlConnection, MySqlTransaction sqlTransaction) { for (int index = 0; index < incomeStatements.Count; index++) @@ -487,6 +502,7 @@ namespace MarketData.DataAccess } return true; } + private static bool DeleteIncomeStatement(IncomeStatement incomeStatement, MySqlConnection sqlConnection, MySqlTransaction sqlTransaction) { StringBuilder sb = new StringBuilder(); diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs index 56b3ccd..a2db393 100755 --- a/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs @@ -16,17 +16,26 @@ namespace MarketData.Generator.CMTrend public CMTCandidateGenerator() { } + // ******************************************************************************************************************************************************************************* // ******************************************************************* G E N E R A T E C A N D I D A T E - M A R C M I N E R V I N I **************************************** // ******************************************************************************************************************************************************************************* - public static CMTCandidate GenerateCandidate(String symbol,DateTime tradeDate,CMTParams cmtParams,List symbolsHeld=null) + public static CMTCandidate GenerateCandidate( + String symbol, + DateTime tradeDate, + CMTParams cmtParams, + FundamentalV2 fundamental, + CompanyProfile companyProfile, + List historicalDates, + TimeSeriesCollection epsTimeSeries, + TimeSeriesCollection profitMarginTimeSeries, + List symbolsHeld=null) { CMTCandidate cmtCandidate=new CMTCandidate(); try { // Check MarketCap - Fundamental fundamental=FundamentalDA.GetFundamentalMaxDate(symbol,tradeDate); if(null==fundamental) { cmtCandidate.Violation=true; @@ -54,7 +63,6 @@ namespace MarketData.Generator.CMTrend return cmtCandidate; } // Equity check - CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(symbol); if(null==companyProfile) { cmtCandidate.Violation=true; @@ -93,7 +101,7 @@ namespace MarketData.Generator.CMTrend cmtCandidate.Reason=String.Format("Insufficient pricing history, {0} days required.",PRICING_DAYS); return cmtCandidate; } -// Current Price Check +// Current Price Check. The current price must be equal to the trade date Price currentPrice=prices[0]; if(currentPrice.Date.Date!=tradeDate.Date) { @@ -205,10 +213,7 @@ namespace MarketData.Generator.CMTrend // generate a 14 day standard RSI with 30 days of pricing data Prices rsiPrices=GBPriceCache.GetInstance().GetPrices(symbol,currentPrice.Date,30); RSICollection rsiCollection=RSIGenerator.GenerateRSI(rsiPrices); - double rsi=rsiCollection[rsiCollection.Count-1].RSI; - - // RSICollection rsiCollection=RSIGenerator.GenerateRSI(symbol,currentPrice.Date,30); - // double rsi=rsiCollection[rsiCollection.Count-1].RSI; + double rsi=rsiCollection[rsiCollection.Count-1].RSI; if(null==rsiCollection||0==rsiCollection.Count||rsi dma200List=new List(); - DateTime historicalDate=dateGenerator.GenerateHistoricalDate(currentPrice.Date,cmtParams.DMA200Horizon+10); - List historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,currentPrice.Date); historicalDates=historicalDates.Take(cmtParams.DMA200Horizon).ToList(); if(historicalDates.Count x.AsOf); maxDate=profitMarginTimeSeries.Max(x => x.AsOf); values=profitMarginTimeSeries.ToFloat(); - values=Numerics.Reverse(ref values); + values=Numerics.Reverse(ref values); // because most recent date is in the lowest valued index bucket. profitMarginSlope=Numerics.Slope(values); if(profitMarginSlope<=0) { diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendGenerator.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendGenerator.cs index a1eaf2c..b1fb47a 100755 --- a/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendGenerator.cs +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendGenerator.cs @@ -21,13 +21,46 @@ namespace MarketData.Generator.CMTrend try { List symbols=PricingDA.GetSymbols(); + +// Filter out symbols where we do not have a price on trade date Dictionary latestDates = PricingDA.GetLatestDates(symbols); symbols=symbols.Where(x => latestDates.ContainsKey(x) && latestDates[x].Date>=tradeDate.Date).ToList(); + +// Prefetch a subset of fundamentals where each fundamental.asof is no greater than tradeDate + FundamentalsV2 fundamentals = FundamentalDA.GetFundamentalsMaxDateV2(tradeDate); + +// Prefetch the Company Profiles + Dictionary companyProfiles = CompanyProfileDA.GetCompanyProfiles(symbols); + +// Prefetch the pricing dates requires for 200 day moving average. + DateGenerator dateGenerator = new DateGenerator(); + DateTime historicalDate=dateGenerator.GenerateHistoricalDate(tradeDate,cmtParams.DMA200Horizon+10); + List historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,tradeDate); + +// Prefetch the EPS time series + Dictionary epsTimeSeriesCollectionDictionary = FundamentalDA.GetEPS(symbols,tradeDate,3); + +// Prefetch the profit margin time series + Dictionary profitMarginTimeSeriesCollectionDictionary = IncomeStatementDA.GetProfitMarginMaxAsOf(symbols,tradeDate,3,IncomeStatement.PeriodType.Quarterly); + for(int index=0;index violations=new List(); List candidates=new List(); +// Filter out symbols where we do not have a price on trade date + Dictionary latestDates = PricingDA.GetLatestDates(symbols); + symbols=symbols.Where(x => latestDates.ContainsKey(x) && latestDates[x].Date>=analysisDate.Value.Date).ToList(); + +// Prefetch a subset of fundamentals where each fundamental.asof is no greater than tradeDate + FundamentalsV2 fundamentals = FundamentalDA.GetFundamentalsMaxDateV2(analysisDate.Value); + +// Prefetch the company profiles + Dictionary companyProfiles = CompanyProfileDA.GetCompanyProfiles(symbols); + +// Prefetch the pricing dates requires for 200 day moving average. + DateGenerator dateGenerator = new DateGenerator(); + DateTime historicalDate=dateGenerator.GenerateHistoricalDate(analysisDate.Value,cmtParams.DMA200Horizon+10); + List historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,analysisDate.Value); + +// Prefetch the EPS time series + Dictionary epsTimeSeriesCollectionDictionary = FundamentalDA.GetEPS(symbols,analysisDate.Value,3); + +// Prefetch the profit margin time series + Dictionary profitMarginTimeSeriesCollectionDictionary = IncomeStatementDA.GetProfitMarginMaxAsOf(symbols,analysisDate.Value,3,IncomeStatement.PeriodType.Quarterly); + for(int index=0;index + { + public FundamentalsV2() + { + } + } + public class FundamentalV2 + { + private String symbol; + private DateTime asOf; + private double marketCap; + private double pe; + private double ebitda; + private double revenuePerShare; + + public FundamentalV2() + { + } + public String Symbol + { + get { return symbol; } + set { symbol = value; } + } + public DateTime AsOf + { + get { return asOf; } + set { asOf = value; } + } + public double MarketCap + { + get { return marketCap; } + set { marketCap = value; } + } + public double PE + { + get { return pe; } + set { pe = value; } + } + public double RevenuePerShare + { + get { return revenuePerShare; } + set { revenuePerShare = value; } + } + public double EBITDA + { + get { return ebitda; } + set { ebitda = value; } + } + } +} diff --git a/MarketData/MarketDataLib/Utility/SQLUtils.cs b/MarketData/MarketDataLib/Utility/SQLUtils.cs index 4359dbc..6a0f16c 100755 --- a/MarketData/MarketDataLib/Utility/SQLUtils.cs +++ b/MarketData/MarketDataLib/Utility/SQLUtils.cs @@ -220,6 +220,10 @@ namespace MarketData.Utils if (Utility.IsEpoch(dateTime)) return null; return addQuotes?AddQuotes(Utility.DateTimeToStringYYYYHMMHDD(dateTime)):Utility.DateTimeToStringYYYYHMMHDD(dateTime); } + public static String ToSqlDate(DateTime dateTime, bool addQuotes=false) + { + return SqlDate(dateTime, addQuotes); + } public static String ToSqlDateTime(DateTime dateTime, bool addQuotes=false) { return addQuotes?AddQuotes(Utility.DateTimeToStringYYYYHMMHDDHHMMSS(dateTime)):Utility.DateTimeToStringYYYYHMMHDDHHMMSS(dateTime); diff --git a/README.md b/README.md index cdef60f..0861ece 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,9 @@ git push origin --delete git branch -r git branch -l -Please use the Microsoft.AspNet.WebApi.OwinSelfHost package for new projects. -Microsoft.AspNet.WebApi.OwinSelfHost +You can use the following commands to update the list of local branches from remote: +git fetch --prune - dotnet add package Microsoft.AspNet.WebApi.OwinSelfHost 3) IPMonitor 4) Models