/// <summary> /// Retrieves stock history data from Google for the specified stock. /// </summary> /// <param name="stock">The stock for which data will be retrieved.</param> /// <param name="startDate">The start date of the data set.</param> /// <param name="endDate">The end date of the data set.</param> /// <returns> /// The stock history read from Google. /// </returns> public List<StockDaily> GetDataFromGoogle(Stock stock, DateTime startDate, DateTime endDate) { var history = new List<StockDaily>(); // Read all of the lines from the CSV file. var csvLines = GetCsvData(stock, startDate, endDate).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); // Iterate through each line to populate the price history... // ...skip the first line which contains the column headers. for (var index = 1; index < csvLines.Length; index++) { var fields = csvLines[index].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // Construct the interval data from the fields of the CSV line. var priceData = new StockDaily { Exchange = stock.Exchange, Ticker = stock.Ticker, Date = DateTime.Parse(fields[GoogleCsvDateColumnIndex]), OpenPrice = Convert.ToSingle(fields[GoogleCsvOpenPriceColumnIndex]), HighPrice = Convert.ToSingle(fields[GoogleCsvHighPriceColumnIndex]), LowPrice = Convert.ToSingle(fields[GoogleCsvLowPriceColumnIndex]), ClosePrice = Convert.ToSingle(fields[GoogleCsvClosePriceColumnIndex]), Volume = Convert.ToInt64(fields[GoogleCsvVolumeColumnIndex]) <= int.MaxValue ? Convert.ToInt32(fields[GoogleCsvVolumeColumnIndex]) : int.MaxValue }; // Google sometimes provides data for non-trading days. Weed these out based on volume. if (priceData.Volume > 0) { // Google also sometimes has missing data points...we'll filter those out too. const float Epsilon = 0.000001f; if (Math.Abs(priceData.OpenPrice - 0.0f) < Epsilon || Math.Abs(priceData.ClosePrice - 0.0f) < Epsilon || Math.Abs(priceData.HighPrice - 0.0f) < Epsilon || Math.Abs(priceData.LowPrice - 0.0f) < Epsilon) { Console.WriteLine("WARNING: Filtering out data from " + priceData.Date + " due to missing price values!"); Console.WriteLine(); } else { history.Add(priceData); } } } // Sort the history into chronological order (Google CSV's are in reverse-chronological order). history.Sort(CompareDailyStockIntervals); return history; }
/// <summary> /// Gets CSV data from Google containing 5 years of historical data for the specified stock.. /// </summary> /// <param name="stock">The stock for which data will be retrieved.</param> /// <param name="startDate">The start date of the data set.</param> /// <param name="endDate">The end date of the data set.</param> /// <returns> /// The contents of the retrieved CSV file. /// </returns> private static string GetCsvData(Stock stock, DateTime startDate, DateTime endDate) { // Construct a Google URL with the correct parameters. var url = "http://www.google.com/finance/historical?q=" + stock.Exchange + "%3A" + stock.Ticker + "&startdate= " + startDate.ToShortDateString() + "&enddate=" + endDate.ToShortDateString() + "&output=csv"; StreamReader reader = null; try { // Retrieve and return the raw CSV data served by Google. var webClient = new WebClient(); var stream = webClient.OpenRead(url); Debug.Assert(stream != null, "stream != null"); reader = new StreamReader(stream); return reader.ReadToEnd(); } finally { if (reader != null) { reader.Close(); } } }
/// <summary> /// Deprecated Method for adding a new object to the Stocks EntitySet. Consider using the .Add method of the associated ObjectSet<T> property instead. /// </summary> public void AddToStocks(Stock stock) { base.AddObject("Stocks", stock); }
/// <summary> /// Create a new Stock object. /// </summary> /// <param name="exchange">Initial value of the Exchange property.</param> /// <param name="ticker">Initial value of the Ticker property.</param> /// <param name="companyName">Initial value of the CompanyName property.</param> public static Stock CreateStock(global::System.String exchange, global::System.String ticker, global::System.String companyName) { Stock stock = new Stock(); stock.Exchange = exchange; stock.Ticker = ticker; stock.CompanyName = companyName; return stock; }
/// <summary> /// Reads a list of stocks for the specified stock exchange from the specified file name. /// </summary> /// <param name="exchangeId">The ID of the stock exchange.</param> /// <param name="companyListFile">The file containing the list of stocks for the exchange.</param> /// <returns> /// A list of stocks for the specified stock exchange. /// </returns> private List<Stock> ReadStockListForExchange(string exchangeId, string companyListFile) { var stocks = new List<Stock>(); var csvLines = File.ReadAllLines(companyListFile); var tickerRegEx = new Regex(@"^[A-Z]*$"); const int MaxNameLength = 50; // Loop through each line of the CSV file and create a new Stock object. // Start with index 1 to skip the header line. for (var index = 1; index < csvLines.Length; index++) { var fields = csvLines[index].Replace("\"", string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // Validate the ticker symbol var ticker = fields[0].ToUpper(CultureInfo.InvariantCulture); if (!tickerRegEx.IsMatch(ticker)) { // This isn't a standard ticker with only capital letters. continue; } // Length-limit the company name. var name = fields[1]; if (name.Length > MaxNameLength) { name = name.Substring(0, MaxNameLength); } // Create and add the stock. var stock = new Stock { Exchange = exchangeId, Ticker = ticker, CompanyName = name }; stocks.Add(stock); } return stocks; }