Skip to main content

C#: Using WebClient on CSV Formatted Stock Data

In this example I wrote a program to pull historic stock data from the ticker “DIS” over the past 10 days from the current date (it will be less than 10 results because weekends don’t have data). I used the .NET Framework WebClient object to request and return the data as a string that I parse into a generic Dictionary. It pulls the data from Google’s finance website as well as Yahoo’s finance site. Of course, check out their terms of use before doing anything with the code.

Here is the code that I wrote on my GitHub.

I created a class to handle communication with Google’s and Yahoo’s CSV export links. It handles constructing proper links as well as requesting the data and parsing it into a Dictionary that is structured by date. From that point, you could build an application around the use of that data, but for this example we just translate it back into strings for display in text boxes.


public class WebClientForStockFinanceHistory
{
    WebClient webConnector;

    // See Google's terms of use before accessing their services
    // https://www.google.com/finance/historical?q=NYSE%3ADIS&ei=7O4nV9GdJcHomAG02L_wCw
    private const string googleAddress =
        "http://www.google.com/finance/historical?q=[-|ticker|-]&startdate=[-|sdate|-]&enddate=[-|edate|-]&num=30&output=csv";

    // See Yahoo's terms of use before accessing their services
    // http://finance.yahoo.com/q/hp?s=DIS+Historical+Prices
    private const string yahooAddress =
        "http://real-chart.finance.yahoo.com/table.csv?s=[-|ticker|-]&[-|sdate|-]&[-|edate|-]&g=d&ignore=.csv";

    /// <summary>
    /// Request data from Google's historic stock page.
    /// </summary>
    /// <param name="market">e.g. NYSE</param>
    /// <param name="ticker">e.g. DIS</param>
    /// <param name="startDate">Start date</param>
    /// <param name="endDate">End date</param>
    /// <returns></returns>
    public Dictionary<DateTime, StockDataItem> getStockDataFromGoogle(string market, string ticker, DateTime startDate, DateTime endDate)
    {
        return fillDataDictionary(getData(constructGoogleLink(market, ticker, startDate, endDate)));
    }

    /// <summary>
    /// Form a link to request info from Google's historic stock data export.
    /// </summary>
    /// <param name="market">e.g. NYSE</param>
    /// <param name="ticker">e.g. DIS</param>
    /// <param name="startDate">Start date</param>
    /// <param name="endDate">End date</param>
    /// <returns></returns>
    private string constructGoogleLink(string market, string ticker, DateTime startDate, DateTime endDate)
    {
        string constructedUri = googleAddress;
        constructedUri = constructedUri.Replace("[-|ticker|-]", market + "%3A" + ticker);

        string constructedStartDate = startDate.ToString("MMM") + "+" + startDate.Day.ToString() + "%2C+" + startDate.ToString("yyyy");
        string constructedEndDate = endDate.ToString("MMM") + "+" + endDate.Day.ToString() + "%2C+" + endDate.ToString("yyyy");

        constructedUri = constructedUri.Replace("[-|sdate|-]", constructedStartDate);
        constructedUri = constructedUri.Replace("[-|edate|-]", constructedEndDate);

        return constructedUri;
    }

    /// <summary>
    /// Request historic stock information from Yahoo.
    /// </summary>
    /// <param name="ticker">e.g. DIS</param>
    /// <param name="startDate">Start date</param>
    /// <param name="endDate">End date</param>
    /// <returns></returns>
    public Dictionary<DateTime, StockDataItem> getStockDataFromYahoo(string ticker, DateTime startDate, DateTime endDate)
    {
        return fillDataDictionary(getData(constructYahooLink(ticker, startDate, endDate)));
    }

    /// <summary>
    /// Constructs an HTML link to request data from Yahoo.
    /// </summary>
    /// <param name="ticker">e.g. DIS</param>
    /// <param name="startDate">Start date</param>
    /// <param name="endDate">End date</param>
    /// <returns></returns>
    private string constructYahooLink(string ticker, DateTime startDate, DateTime endDate)
    {
        string constructedUri = yahooAddress;
        constructedUri = constructedUri.Replace("[-|ticker|-]", ticker);

        string constructedStartDate = "a=" + (startDate.Month - 1).ToString() +
                                      "&b=" + startDate.Day.ToString() +
                                      "&c=" + startDate.Year.ToString();

        string constructedEndDate = "d=" + (endDate.Month - 1).ToString() +
                                    "&e=" + endDate.Day.ToString() +
                                    "&f=" + endDate.Year.ToString();

        constructedUri = constructedUri.Replace("[-|sdate|-]", constructedStartDate);
        constructedUri = constructedUri.Replace("[-|edate|-]", constructedEndDate);

        return constructedUri;
    }

    /// <summary>
    /// Pull data based on a passed URL text.
    /// </summary>
    /// <param name="webpageUriString"></param>
    /// <returns></returns>
    private string getData(string webpageUriString)
    {
        string tempStorageString = "";

        if (!string.IsNullOrEmpty(webpageUriString))
        {
            using (webConnector = new WebClient())
            {
                using (Stream responseStream = webConnector.OpenRead(webpageUriString))
                {
                    using (StreamReader responseStreamReader = new StreamReader(responseStream))
                    {
                        tempStorageString = responseStreamReader.ReadToEnd();
                        tempStorageString = tempStorageString.Replace("\n", Environment.NewLine); // Change Unix-style line endings
                    }
                }
            }
        }

        return tempStorageString;
    }

    /// <summary>
    /// Take CSV data and push it into a dictionary.
    /// </summary>
    /// <param name="csvData">Data from Google or Yahoo in CSV format</param>
    /// <returns></returns>
    private Dictionary<DateTime, StockDataItem> fillDataDictionary(string csvData)
    {
        Dictionary<DateTime, StockDataItem> parsedStockData = new Dictionary<DateTime, StockDataItem>();

        using (StringReader reader = new StringReader(csvData))
        {
            string csvLine;
            reader.ReadLine(); // Drop the first row because it is a header we don't need

            while ((csvLine = reader.ReadLine()) != null)
            {
                string[] splitLine = csvLine.Split(',');

                if (splitLine.Length >= 6)
                {
                    StockDataItem newItem = new StockDataItem();

                    // Parse all values from the downloaded CSV file
                    double tempOpen;
                    if (double.TryParse(splitLine[1], out tempOpen))
                        newItem.open = tempOpen;

                    double tempHigh;
                    if (double.TryParse(splitLine[2], out tempHigh))
                        newItem.high = tempHigh;

                    double tempLow;
                    if (double.TryParse(splitLine[3], out tempLow))
                        newItem.low = tempLow;

                    double tempClose;
                    if (double.TryParse(splitLine[4], out tempClose))
                        newItem.close = tempClose;

                    double tempVolume;
                    if (double.TryParse(splitLine[5], out tempVolume))
                        newItem.volume = tempVolume;

                    // If we parse the date successfully, add the new StockDataItem to our result set
                    DateTime tempDate;
                    if (DateTime.TryParse(splitLine[0], out tempDate))
                        parsedStockData.Add(tempDate, newItem);
                }
            }
        }

        return parsedStockData;
    }
}


The user can request information from Yahoo or Google, which basically results in the same thing because I opted to drop Yahoo’s last row of data that wasn’t included in Google’s. The most important line of code in the entire class is in getData() where I check and replace unix styled line endings with Windows ones. If you don’t do that then the parsing system won’t be able to extract out each day of information.

The main form manages the process with a Background Worker and pushes the result into two text boxes:

public partial class frmWebClientExample : Form
{
    public frmWebClientExample()
    {
        InitializeComponent();
    }

    string googleDataResult = "";
    string yahooDataResult = "";

    private void btnStartExample_Click(object sender, EventArgs e)
    {
        googleDataResult = "";
        yahooDataResult = "";

        if (!bwProcessor.IsBusy)
        {
            btnStartExample.Enabled = false;
            txtGoogleResult.Text = "";
            txtYahooResult.Text = "";
            bwProcessor.RunWorkerAsync();
        }
        else
        {
            MessageBox.Show("The background worker is busy...");
        }
    }

    private void bwProcessor_DoWork(object sender, DoWorkEventArgs e)
    {
        DateTime tenDaysPast = DateTime.Now.Subtract(TimeSpan.FromDays(10));
        StringBuilder textString = new StringBuilder();
        WebClientForStockFinanceHistory wc1 = new WebClientForStockFinanceHistory();
        Dictionary<DateTime, StockDataItem> downloadedStockData;

        // Request data from Google for the past 10 days
        downloadedStockData = wc1.getStockDataFromGoogle("NYSE", "DIS", tenDaysPast, DateTime.Now);
        textString.AppendLine("Data on DIS from Google:");
        foreach (KeyValuePair<DateTime, StockDataItem> singleItem in downloadedStockData)
        {
            textString.AppendLine($"{singleItem.Key.ToShortDateString()} " +
                                  $"[{singleItem.Value.open.ToString("0.0")}; " +
                                  $"{singleItem.Value.close.ToString("0.0")}; " +
                                  $"{singleItem.Value.high.ToString("0.0")}; " +
                                  $"{singleItem.Value.low.ToString("0.0")}; " +
                                  $"{singleItem.Value.volume}]");
        }
        // Save the Google result to a string on the UI thread
        googleDataResult = textString.ToString();
        textString.Clear();

        textString.AppendLine("Data on DIS from Yahoo:");
        // Request data from Yahoo for the past 10 days
        downloadedStockData = wc1.getStockDataFromYahoo("DIS", tenDaysPast, DateTime.Now);
        foreach (KeyValuePair<DateTime, StockDataItem> singleItem in downloadedStockData)
        {
            textString.AppendLine($"{singleItem.Key.ToShortDateString()} " +
                                  $"[{singleItem.Value.open.ToString("0.0")}; " +
                                  $"{singleItem.Value.close.ToString("0.0")}; " +
                                  $"{singleItem.Value.high.ToString("0.0")}; " +
                                  $"{singleItem.Value.low.ToString("0.0")}; " +
                                  $"{singleItem.Value.volume}]");
        }
        // Save the Yahoo result to a string on the UI thread
        yahooDataResult = textString.ToString();
    }

    private void bwProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        btnStartExample.Enabled = true;
        txtGoogleResult.Text = googleDataResult;
        txtYahooResult.Text = yahooDataResult;
    }
}



Popular posts from this blog

ChatGPT is a new, and faster, way to do programming!

Currently ChatGPT is in a free “initial research preview” . One of its well known use cases at this point is generating software code. I’ve also just used it to write most of this article… Well, actually a future article about cleaning up SRT subtitle files of their metadata faster than I have been by hand with Notepad++ and its replace functionality. Update: I recorded a screencast of writing the SRT subtitle cleaner application loading and processing portion. I relied heavily on ChatGPT for code. It was a fun process! https://youtu.be/TkEW39OloUA ChatGPT, developed by OpenAI, is a powerful language model that can assist developers in a variety of tasks, including natural language processing and text generation. One such task that ChatGPT can help with is creating an SRT cleaner program. SRT, or SubRip Subtitle, files are commonly used to add subtitles to video files. However, these files can become cluttered with unnecessary information, such as timing lines or blank spaces. To clean...

Theme error in 2010s Android App after AppCompat Migration

I plan on releasing a lot of my old work as GPL open source, but most of it has aged to the point that it no longer functions, or if it does work it’s running in compatibility mode. Basically it’s no longer best practices. Not a good way to start off any new public GPL projects, in my opinion. The current project I’m working on is an Android app that calculates star trails meant to help photographers get or avoid that in their night time photos. For now I’m going to skip some of the import process because I didn’t document it exactly. It’s been mostly trial and error as I poke around Android Studio post import. The Android Studio import process… Removing Admob Google Play code before the project would run at all. After removing dependencies, it kind of worked, but when running it in the emulator it shows a pop-up message saying that the app was developed for an old version of Android. Going through the process of updating code to match current best practices… I had the IDE convert the ...

Printing to file in Linux WINE

I noticed that this post has been sitting as a draft since 2011. At this point I have no idea if it’s useful or what I was even doing, but I might as well make it public in case someone can find it helpful! So I’ve been trying to get one of those PDF print drivers working in WINE without success. I then came upon a process that might work. When printing you need to select the checkbox “Print to file” that creates a .prn file. Just Linux things... I was using a program that only has printing facilities, but I want to export around 100 pages of text and images. Once you have the .prn (postscript) file, you can do any number of things to it. In my case I want the postscript file to be converted to HTML. I am also considering PDF format because that has more conversion options to eventually get me to HTML or plain text. sudo apt-get install cups-pdf Or it looks like that package might have changed to this… sudo apt-get install printer-driver-cups-pdf Where PDFs would be generated in /home/...