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.

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
    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
    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">eg. NYSE</param>
    /// <param name="ticker">eg. 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">eg. NYSE</param>
    /// <param name="ticker">eg. DIS</param>
    /// <param name="startDate">start date</param>
    /// <param name="endDate">end date</param>
    /// <returns></returns>
    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">eg. 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 a html link to request data from Yahoo
    /// </summary>
    /// <param name="ticker">eg. DIS</param>
    /// <param name="startDate">start date</param>
    /// <param name="endDate">end date</param>
    /// <returns></returns>
    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>
    string getData(string webpageUriString)
    {
        string tempStorageString = "";

        if (webpageUriString != "")
        {
            //create a new instance of the class
            //using should properly close and dispose so we don't have to bother
            using (webConnector = new WebClient())
            {
                using (Stream responseStream = webConnector.OpenRead(webpageUriString))
                {
                    using (StreamReader responseStreamReader = new StreamReader(responseStream))
                    {
                        //extract the response we got from the internet server
                        tempStorageString = responseStreamReader.ReadToEnd();

                        //change the unix style line endings so they work here
                        tempStorageString = tempStorageString.Replace("\n", Environment.NewLine);
                    }
                }
            }
        }

        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>
    Dictionary<DateTime, StockDataItem> fillDataDictionary(string csvData)
    {
        Dictionary<DateTime, StockDataItem> parsedStockData = new Dictionary<DateTime, StockDataItem>();

        using (StringReader reader = new StringReader(csvData))
        {
            string csvLine;

            //drop the first row because it is a header we don't need
            reader.ReadLine();

            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, we 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)
    {
        //attempt to start the request to the two websites
        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;
    }
}
Posted in C# | Tagged , , , , , , , , , , , , | Comments

A simple classic VB code counter made in C#

I made this simple application a while back when I needed to quote a project where I was tasked with converting a sizeable amount of classic Visual Basic code into the .NET Framework. This application I wrote allowed me to start my estimation so that I could tie a dollar amount to the work. There is a lot more I could have done to the project, but when you are doing contracted work on a per project basis, it’s important to be efficient. While I did some browsing through the projects that was equally important, this code counter did make a difference in my estimations thereby allowing for a properly researched quote. Now that I’m basically done with the project, I felt that I was relatively close to my estimation.

Here is the code for my VB code counter on my GitHub.

Here is the important code for my counter tool:

public IEnumerable<string> FilterFiles(string path, params string[] exts)
{
    return exts.Select(x => "*." + x).SelectMany(x => Directory.EnumerateFiles(path, x));
}

private void button1_Click(object sender, EventArgs e)
{
    //only parse VB6 frm, cls, and bas files
    var validExtensions = new[] { "frm", "cls", "bas" };

    txtResult.Text = "";
    txtLineCount.Text = "";

    if (folderSelector.ShowDialog() == DialogResult.OK)
    {
        //only parse files that end in our desired extensions
        IEnumerable<string> foundFiles = FilterFiles(folderSelector.SelectedPath, validExtensions);

        //loop through all of the files so we can count lines of code per project folder
        foreach (string value in foundFiles)
        {
            string currentFile = File.ReadAllText(value);

            //clear out any lines that are entirely comments
            currentFile = Regex.Replace(currentFile, @"'(.*)", "", RegexOptions.Multiline);

            //clear out any lines that are blank
            currentFile = Regex.Replace(currentFile, @"^\s+$[\r\n]*", "", RegexOptions.Multiline);

            //remove any blocks of empty spaces so that there are only single spaces
            while (currentFile.Contains("  "))
            {
                currentFile = currentFile.Replace("  ", " ");
            }

            //confirm there are not empty lines
            while (currentFile.Contains("\n\n"))
            {
                currentFile = currentFile.Replace("\n\n", "\n");
            }
                    
            //append the parsed code file text
            txtResult.Text += currentFile;
        }

        //count the project's entire lines of code
        txtLineCount.Text = (txtResult.Lines.Count() - 1).ToString();
    }
}

The application allows you to select a directory where a given VB project’s files are located. It searches the directory for frm, cls, and bas files, which were the key project files for classic Visual Basic. It pulls all of the text in for each file, removing lines that are comments and also clearing out all empty lines. Improvements to the code might be in somehow excluding the form control property sections (though still an important thing you would need to decipher when converting classic VB, especially if you don’t have the VB6 IDE) and counting up instances of special functions or logic that were unique to classic VB.

I’ll likely be writing an article about some of the classic VB functions I had converted to C# to be part of my general code library that I was able to utilize in some of my contract work mentioned before. If I ever get around to converting some of my personal VB code I have from the 90’s, they will be helpful for sure in that. I’m not a fan of using the “using Microsoft.VisualBasic” option, because I don’t trust that Visual Basic will be around for the long term.

Posted in C#, Classic Visual Basic | Tagged , , , , , , , , | Comments

Android: Using the CountDownTimer class

In this article I go over the Android SDK CountDownTimer class. It’s easy to use and gives you the ability to count for projects like a workout timer. In this case we are extending that class so that we can access the methods we need to make it work.

Here is the code on GitHub.

Here is our SimpleCountDownTimer:

public class SimpleCountdownTimer extends CountDownTimer {
    public static int oneSecond = 1000;
    TextView textViewTimeLeftDisplay;

    public SimpleCountdownTimer(long millisInFuture, long countDownInterval,
                                TextView textViewTimeLeftDisplay) {

        super(millisInFuture, countDownInterval);

        this.textViewTimeLeftDisplay = textViewTimeLeftDisplay;
    }

    @Override
    public void onFinish() {
        textViewTimeLeftDisplay.setText("Finished");
    }

    @Override
    public void onTick(long millisUntilFinished) {
        textViewTimeLeftDisplay.setText(String.valueOf(millisUntilFinished / oneSecond));
    }
}

The extended timer class takes in the amount of time we want to count, the counting interval, and a textview that we will send status to. We update the text view every interval as well as when we are finished. There really isn’t much to it.

Here is our main activity:

public class MainActivity extends Activity {
    Spinner timerValueSpinner;
    Button startTimerButton;
    Button stopTimerButton;
    TextView statusTextView;
    SimpleCountdownTimer timer;
    String[] timeValues;
    Resources resourcePointer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        timerValueSpinner = (Spinner)this.findViewById(R.id.secondsSpinner);
        statusTextView = (TextView)this.findViewById(R.id.timerView);

        resourcePointer = getResources();
        timeValues = resourcePointer.getStringArray(R.array.seconds_list);

        //the button that will start the timer
        startTimerButton = (Button)this.findViewById(R.id.startButton);
        startTimerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(timerValueSpinner.getSelectedItemPosition() > -1) {
                    int parsedSpinnerValue = 0;
                    parsedSpinnerValue = Integer.parseInt(
                            timeValues[timerValueSpinner.getSelectedItemPosition()]);

                    if (parsedSpinnerValue > 0) {
                        //if there is a timer already, end that before making a new one
                        if(timer != null) {
                            timer.cancel();
                        }

                        //initialize a new timer instance
                        timer = new SimpleCountdownTimer(parsedSpinnerValue
                                * SimpleCountdownTimer.oneSecond,
                                SimpleCountdownTimer.oneSecond,
                                statusTextView);

                        timer.start();
                    }
                }
            }
        });

        //the button that will stop the timer
        stopTimerButton = (Button)this.findViewById(R.id.stopButton);
        stopTimerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(timer != null) {
                    timer.cancel();
                }
            }
        });
    }
}

In the main activity we do a lot of referencing to our interface. In the start timer button we extract a value from the spinner, convert it into an int, make sure any existing timer is stopped, and instantiate a new timer with the spinner value.

Posted in Android, Java | Tagged , , , | Comments