[
Advertise | Submit Code | About us | Contact us | Link us
]
Go!
Membership Services
Login
Register

Home
C# General

General

C# Language

Design & Architecture

Algorithms

Database

Security

Active Directory

COM Interop

Remoting
C# Windows Forms

General

Combo and List boxes

Miscellaneous Controls

Button Controls

Edit Controls
Cutting Edge

ASP.NET 2.0

Visual Studio 2005

Windows Longhorn

SQL Server 2005
C# Multimedia and GDI+

General

DirectX

GDI+

Audio
Internet & Web

General

Images and multimedia

Database

Utilities

Security

ASP.NET Controls

Design and Architecture

Webservices
.NET

General

Design & Architecture

Algorithms

Database

Security

Active Directory

COM Interop

Remoting

ADO.NET

XML.NET

Tools

Enterprise

IDE
Visual Basic .NET

VB.NET General

VB.NET Controls
General Reading

.NET Books Review

Product Showcase

Book Chapters

Business Design & Strategy
Community

Discuss

Job Board

Discussion

CodeXchange
DeveloperLand

Advertise

Submit Code

About us

Contact us

Link us
Miscellaneous

Favorite Links

Downloads

Programming Sites

Top Stories
Regular Expressions

E-Mail

Date/Time
Home > Internet & Web > Webservices
Build a Web Service to provide Real-Time Stock Quotes
Posted by on Wednesday, September 22, 2004 (EST)

This article provides an example of a useful web service and how it is consumed. I will go into detail about how the web service operates and describe the code.

This article has been viewed: 6,698 times
Technology: Webservices.

code.zip (3.55 KB)

Contents

Introduction

The web service I have written returns real-time stock quotes. There are two functions. The first takes a string representing the stock ticker and returns a string representing the current price on the ECNs, which are electronic marketplaces for stocks. As will be described below, the prices are scraped from the Yahoo! Finance website. Please note, that not all stocks are traded on ECNs, so some tickers will return ‘N/A’. The second function takes a space-delimited string of tickers and returns a dataset of current prices. The information returned from these web services is freely available on the Internet.

There are many benefits to using a web service. One is that the versatility of the client that consumes the web service. Later in the article, I will provide an example of how to use a web page to consume it, but the client could also be a Windows Form or even another web service. Also, because of the nature of XML, the client is not restricted to the Windows platform. Another is the reusability of the code. In essence, the web service is like a class library. As long as a reference is made to it, there is little distinction between calling a function from a local class versus a web service.

Top Go to Table of Contents

Let's look at the code

There are two files that contain the main code for the web service. The first is a class library that contains a general purpose function I use to screen scrape. .Net has made it very simple to screen scrape, a process where a web page is returned as a string. Depending on the web page, the string can be parsed to extract valuable information. The only function in this library, GetPageContent(url).

public static string GetPageContent(string url)
{
    WebRequest wreq;
    WebResponse wres;
    StreamReader sr;
    String content;
    wreq = HttpWebRequest.Create(url);
    wres = wreq.GetResponse();
    sr = new StreamReader(wres.GetResponseStream());
    content = sr.ReadToEnd();
    sr.Close();
    return content;
}

This function is declared static, so no object needs to be instantiated in my web service in order to use it. GetPageContent performs the screen scraping. First, we create a WebRequest to the url that is passed to the function. Then we assign the response to a WebResponse object and use a StreamReader to assign the response stream to a string. At this point, if we were to use Reponse.Write to write this string to the browser window, we would see an exact replica of the web page. Also, if we were to write the string to a TextBox, we would see the HTML source just as if we would have selected View -> Source in the browser. This is the string we will use to extract the real-time quote.

The trick to extracting data from a web page is knowing where it is in the source. If I were requesting the quote for Microsoft, my URL would be http://finance.yahoo.com/q/ecn?s=MSFT [^]. On this page, you will notice that the quote is just after the text ‘Last Trade:’. In the source, the actual price is wrapped in <B> tags. Our next function will use this information to extract our real-time quote from the HTML source.

private string ParsePage(string page)
{
    Int32 i;
    i = page.IndexOf("Last Trade:");
    page = page.Substring(i);
    i = page.IndexOf("");
    page = page.Substring(i);
    i = page.IndexOf("");
    page = page.Substring(0,i);
    page = Regex.Replace(page, "","");
    return page;
}

The entire page source is passed into the function and the price is returned. So far, we have seen two functions that enable us to extract data from a website. We were able to leverage these functions because the URL for the page is constant with the exception of the stock ticker. In order to extract other types of data, the GetURL (not discussed) and ParsePage functions would need to be modified to suit the scenario. For example, I use a different page on Yahoo! Finance to create dynamic stock charts. The ParsePage function is more complicated since I am extracting more information, but the general concept is the same.

Top Go to Table of Contents

Pulling it all together

The helper functions have been covered, so let’s look at the actual web services. First of all, you will notice that the code behind looks just like a regular class with the exception of the [WebService] attribute. That is the first requirement. The other is to expose the functions you want as web services by adding the [WebMethod] attribute to them. In our case, we have two web services, GetQuote and GetQuotes.

[WebMethod]
public string GetQuote(string ticker)
{
    string stockURL, page, retval;
    try
    {
       stockURL = GetURL(ticker);
       page = ScrapeUtility.GetPageContent(stockURL);
       retval = ParsePage(page);
    }
    catch (ArgumentOutOfRangeException)
    {
       retval = "Invalid Ticker";
    }
    catch (Exception)
    {
       retval = "Unknown Error";
    }
    return retval;
}

Most of this code we have already covered. The string that is passed into the function is used to create our URL. Then, this page is retrieved and parsed to extract the price. I wrap the entire process in a try block. If the ticker is not actually a publically traded company, the ArgumentOutOrRangeException is caught. This occurs when we are attempting to parse the page and can not find the ‘Last Trade:’ string. Notice the price is returned as a string. We could also have returned the price as a decimal, but in the case of an invalid ticker, we would need to send something conforming to that datatype. 0.0 may be a logical value, but I choose to return a string. The client could always convert this to a decimal assuming a valid ticker.

The second web service actually uses the first to extract the data. A point of interest is that in this case, GetQuote is not being called as a web service.

[WebMethod]
public string GetQuotes(string tickers)
{
    char[] splitter = {' '};
    string [] _tickers = tickers.Trim().Split(splitter);
    Int32 i, ticks;
    ticks = _tickers.Length;
    DataSet ds = new DataSet();
    DataTable dt = new DataTable("StockData");
    DataColumn dc;
    dc = dt.Columns.Add("Ticker", System.Type.GetType("System.String"));
    dc = dt.Columns.Add("Price", System.Type.GetType("System.String"));
    for (i = 0; i < ticks; i++)
    {
       DataRow dr = dt.NewRow();
       dr["Ticker"] = _tickers[i].ToUpper();
       dr["Price"] = GetQuote(_tickers[i]));
       dt.Rows.Add(dr);     }
    ds.Tables.Add(dt);
    return ds;
}

The GetQuotes function accepts a string as input. This string is a space-delimited string of stock tickers. The first thing to do is split this string into an array. This function returns a dataset which we will generate dynamically. The dataset contains just one table with two fields, Ticker and Price. After creating the dataset, we create a datatable a call it "StockData." The datatable will have two columns each of which are strings. As discussed above, we could have made the price column a decimal, but we would need to return a decimal value in the case of invalid tickers. For each of the items in the _tickers array, we call the GetQuote function to retrieve the price. As I mentioned above, in this case, GetQuote is being called locally, so no web reference needs to be added. After we have retrieved all of the quotes for the requested tickers, we return the results as a dataset.

Top Go to Table of Contents

Consuming the services

The easiest way to add a web reference to your project is within the Visual Studio IDE. In Solution Explorer, right-click on References and select "Add Web Reference". In the address box of the window that opens, enter the URL for the web service. On my development machine, I entered http://localhost/services/stockservices.asmx to add the web service I created for this article. Next, give the reference a name (or accept the default) and click "Add Reference". This process adds several files to your project. There is a .disco file that contains the discovery information for the added web reference. There is also a .wsdl file that contains information about the web service's interface. Also, a proxy class is created that you will use when interacting with the web service. If you open this file, you will notice that Visual Studio builds asynchronous functionality into this proxy class, so nothing special needs to be done on the client side to call the web service asynchronously.

Once we have the web reference, the rest is quite simple. On my sample page, I have two sections used to test each of the web service. Each section has a textbox to enter the ticker or tickers and a button to call the web service. In the case of GetQuote, I display the result on a label, with GetQuotes, I use a datagrid.

private void btnGetQuote_Click(object sender, System.EventArgs e)
{
    localhost.StockServices ss = new localhost.StockServices();
    lblQuote.Text = ss.GetQuote(txtTicker.Text);
}
private void btnGetQuotes_Click(object sender, System.EventArgs e)
    localhost.StockServices ss = new localhost.StockServices();
    dgQuotes.DataSource = ss.GetQuotes(txtTickers.Text);
    dgQuotes.DataBind();
}

As you can see, there is no difference in the code between calling a local function and calling a web service. Create the object and call the function.

Top Go to Table of Contents

Final thoughts

All of the data returned from the web services is freely available on the Internet. All we are doing is scraping the data from a public website. Our example is fairly simple, but there is much data available to us using this technique. For instance, I have also created a console app that uses Yahoo! Finance to extract end-of-day stock data. I run the app every night during off hours to insert this information into a database I use to track price history.

Here [^] is a link to a working example.

Top Go to Table of Contents

About Gavin Donoho

Click here if you want to know more about .

Other articles that may interest you

  • Write a Word Add-In – Part 0
  • Write a Word Add-In – Part I
  • Lengthy Operations on Single Thread in .NET Application
  • Learning Draughts
  • Exceptions and Performance
  • Average Rating :

    Discussion Forums
    Got a programming related question? Hopefully someone has the answer... Want to help out other developers? Visit our discussion forums.

    Sponsored by:

    New Articles

  • Exceptions and Performance
    Almost every time exceptions are mentioned in mailing lists and newsgroups, people say they're really expensive.Let's examine that claim, shall we?

  • Creating multilingual websites - Part 1
    Extend the existing globalization capabilities of .NET to create flexible and powerful multilingual web sites. First, create a custom ResourceManager, and then create custom localized-capable server controls to easily deploy multilingual functionality.

  • Parameter passing in C#
    Many people have become fairly confused about how parameters are passed in C#, particularly with regard to reference types. This page should help to clear up some of that confusion

  • Most Popular Articles

  • LDAP, IIS and WinNT Directory Services
    This article explains how to use .NET Directory Services to retrieve and search directory objects, create new directory objects and edit or delete existing directory objects. Describes Active Directory Application Mode (ADAM) and how to use the IIS, WinNT and LDAP directory (ADSI) provider.

  • An in-depth look at WMI and instrumentation, Part II
    WMI stands for Windows Management Instrumentation and, as the name indicates, is about managing your IT infrastructure this article is the second part of a two-part series.

  • An in-depth look at WMI and instrumentation, Part I
    WMI stands for Windows Management Instrumentation and, as the name indicates, is about managing your IT infrastructure this article provides an in-depth look at WMI and MOM 2005

  • New Books

  • Murach's ASP.NET 2.0 Upgrader's Guide: VB Edition
    What’s new and how to use it! That’s what this book delivers if you’re a VB developer who’s interested in upgrading from ASP.NET 1.x to ASP.NET 2.0.

  • C# in easy steps
    Learn to program with Microsoft’s premier programming language. No previous programming knowledge is assumed. With numerous easy-to-follow examples, this title explains the essentials of object-oriented programming with C#.

  • Murach's ASP.NET web programming with VB.NET
    Murach's ASP.NET web programming with VB.NET by Doug Lowe and Anne Prince is a in depth training and reference book for ASP.NET programming using VB.NET. The book builds upon Murach's previous books and covers more advanced concepts for programming ASP.NET pages.

  • Got Code?

    if you have any article , source code , or anything else you'd like to share with this community that you think others might find useful, please submit it here and we will gladly make it available on this site. submit@developerland.com.
    Partners

    All articles are copyrighted by their individual authors unless otherwise specified , everything else Copyright ©2004-2006 DeveloperLand