Click here to Skip to main content
Email Password   helpLost your password?
Release

Introduction

The service that Yahoo! Finance provides to download financial data is nice, but you have to know the right tags and symbols and how to build the URL to use it. This library will undertake this annoying task for you.

Background

I was working on a Stock Trader application, and used the Yahoo! Finance service as a database. In order to focus on programming this application, I thought it would be better if the receiving of the data is a closed unit and if I can control it easily. A strong typed handling of the code was also an objective. So I started creating this library just for downloading finance data and chart images.

Using the Code

DownloadBase

The DownloadBase^ class is the base for QuotesDownload^, HistQuotesDownload^, ImageDownload^, ExchangeDownload^, MarketDownload^, and OnlineCheck^ classes. For every Download function of any download class that inherits DownloadBase, there exists an equivalent for multiple async downloading. For that, you have the optional args parameter in every DownloadAsync method, where you can pass an object of your choice. When the download is completed, this object is stored as the property UserArgs in DownloadCompletedEventArgs, e.g., for identifying the explicit download. If an error occurs, the Failure event will be raised with FailureEventArgs. You are able to get the number of running async downloads with the AsyncDownloadsCount property. And also, you can cancel all running async downloads with the CancelAsyncAll method in every DownloadBase class. If you want to stop a particular running download, use the CancelAsync function. You need to pass the UserArgs of the download you want to stop. The function returns the number of stopped downloads. It could be more than one, e.g., if you don't have an object as UserArgs and are stopping all downloads with a UserArgs of null/Nothing. DownloadBase also has the Contains function based on UserArgs. If you're using a proxy, you have the Proxy property to set the credentials.

IID

The IID^ interface is implemented by all classes that can be used for downloading. These classes are StockID^, IndexID^, and CurrencyRelation^. With the ID property of these classes, you're able to download (historic) quotes and images. In each of these classes, the ToString method returns the ID.

The IndexID class stores information about a stock index. Here, you have the DownloadComponents property. Set it to True, and the downloader will download the information for all stocks in this index. If it's False, you will get the information for the index itself. I created the market.xml file as an embedded resource, where I collected some indices information like county, currency, and ID. The WorldMarket class uses this file to create a structure of continents, regions, countries, and indices to reflect the world market situation. With CurrencyRelation, you're able to create an ID for downloading currency quotes and images. For that, you have the Currency enum and the BaseCurrency and DepCurrency properties. When you're downloading quotes of currency exchanges, you will get back a value that represents the format 1 BaseCurrency : X DepCurrency.

Downloading historic quotes of currency exchanges is not possible. I don't know why it's not possible, as Yahoo! must have the data, because you're able to download the (historic) image, and for that, they need the data.

StockID and StockExchange

The StockID class can be used for downloading quotes and information about a stock. This class contains an instance of StockExchange to handle the ID suffix. It's possible that a stock is traded at different stock exchanges. E.g., APPLE has the base ID "AAPL". If you want to get the values of the stock that is traded at NYSE(NYS), the ID is only "AAPL". The XETRA(GER)-stock ID is then "AAPL.DE". You only have to call the SetID method of this class, and it will get the stock exchange information automatically, if it's possible. The BaseID property returns only the base part of the ID. After setting the ID, you can easily change the suffix, but you have to know or find out if this combination of ID and suffix exists. If the suffix has the value Unknown, the BaseID property is the same as the ID property. Setting ID strings without a suffix gets the exchange ID Unknown, because there is more than one stock exchange without a suffix.

StockExchange^ is a class for storing and managing information about a stock exchange. For this, there exists the static DefaultStockExchanges list with the Yahoo! supported stock exchanges. By setting the properties ExchangeID or Suffix, the class will get the information automatically, except it's Unknown. A StockExchange also stores information about the trading time. You have some properties like the opening and closing time or trading days you have to fill. Then, you can use the IsActive function to prove if trading at this stock exchange is active at a specific time, by using the UTC or the machine's local time zone.

Servers

You're able to choose between different servers where you can download quote-info, etc. For this, you have the Servers^ enum with the different locations of servers. You can't download all data from every server, because not every server has all data or can get them. In my experience, the European and US servers have been relatively more reliable. Also, you have the option to choose a YQL server. This server is different from all other servers in the list. YQL is the latest Web Service of Yahoo!, that concentrates all Yahoo! Web Services in one API, the "Yahoo Query Language". It's similar to SQL syntax, and you receive XML data instead of CSV. The server has the same location: USA. In my experience, the query server is slower in response than the others. But the benefit is more data safety, because the query server can return data from most servers. So, you have to decide what you prefer. Image download is not possible with YQL. Sector, industry, and company download is only possible with YQL/one CSV server.

QuotesDownload

The first download class I want to show is the QuotesDownload class. With this class, you're able to download quotes data of stocks or stock indices (with or without components). It's also possible to download currency exchanges with this class, but I recommend using the ExchangeDownload, or even better, the ExchangeCalculator class for this. At first, I will show an example with different stocks. Declare the IDs of the stocks you want to download:

Dim symbols() As String = {"ABBN.VX", "AA", "BAS.DE", "MMM"}

Declare the quote properties^ you want to get:

Dim properties() As Enums.QuoteProperty = {Enums.QuoteProperty.Name, _
                                           Enums.QuoteProperty.Symbol, _
                                           Enums.QuoteProperty.LastTradePriceOnly, _
                                           Enums.QuoteProperty.Open, _
                                           Enums.QuoteProperty.DaysHigh, _
                                           Enums.QuoteProperty.DaysLow}

If you pass null/Nothing for the properties parameter, LastTradePrizeOnly will be set automatically. Then, create a new QuotesDownload class and start the async download:

Dim qdl As New Download.QuotesDownload
AddHandler qdl.AsyncDownloadCompleted, AddressOf QuotesDownloadCompleted
qdl.DownloadAsync(symbols, properties, Enums.Servers.USA)

If the download is completed, the qdl instance raises the AsyncDownloadCompleted event. The received data is then stored in the QuotesDownloadCompletedEventArgs class.

Private Sub QuotesDownloadCompleted(ByVal sender As Download.DownloadBase, _
                                    ByVal ea As Data.QuotesDownloadCompletedEventArgs)
    Dim quotes() As Data.QuoteData = ea.Quotes
    Debug.WriteLine(quotes(0).Values(Enums.QuoteProperty.LastTrade_PriceOnly))
End Sub

Downloading with stock index information is nearly the same. You just have to declare the index/indices, instead of the stock symbol strings. You can use every IID instance or list for downloading quotes.

Dim index As Data.IndexID = Data.WorldMarket.AllIndices(0)
'...
qDl.DownloadAsync(index, properties, Enums.Servers.USA)

You can also use the QuotesDownloadCompleted method (from the stocks download) for getting the data. The result is the same.

ImageDownload

Now, we will have a look at the ImageDownload class. First, declare and set ImageDownloadOptions^.

Dim options As New Data.ImageDownloadOptions("BAS.DE")
With options
    .ImageSize = Enums.ChartImageSize.Middle
    .TimeSpan = Enums.ChartTimeSpan._3M
    .Type = Enums.ChartType.Line
    .MovingAverages.Add(Enums.MovingAverageInterval._5)
    .Server = Enums.Servers.USA
End With

In this case, it's the image of BASF in middle size with a 3 month period, and the indicator moving average for a 5 days average period. If you choose, e.g., a 1 day period, the moving average has a period of 5 minutes. So it's always a much smaller unit. Now, start the download like before:

Dim imageDl As New Download.ImageDownload
AddHandler imageDl.AsyncDownloadCompleted, AddressOf ImageDownloadCompleted
imageDl.DownloadAsync(options)

ImageDownloadCompleted could look like this:

Private Sub ImageDownloadCompleted(ByVal sender As Download.DownloadBase, _
            			   ByVal ea As Data.ImageDownloadCompletedEventArgs)
    PictureBox1.Image = Image.FromStream(ea.Image)
End Sub

HistQuotesDownload

With this class, you can download the historic quotes of a specific stock or stock index. As a result, you will get an array of HistQuoteData that stores the values of the trading periods. Each instance is one trading period. You can get data from 1990 (I think so) until today. Just create a new HistQuotesDownload instance, and start the download with the required parameters.

Dim qdl As New Download.HistQuotesDownload
AddHandler qdl.AsyncDownloadCompleted, AddressOf HistQuotesDownloadCompleted
qdl.DownloadAsync("BAS.DE", #1/1/2000#, Date.Today, _
  		  Enums.HistQuotesInterval.Weekly)

Also, in this class, the AsyncDownloadCompleted event is raised when the download completes. You can easily bind the data, e.g., to a DataGridView.

Private Sub HistQuotesDownloadCompleted(ByVal sender As Download.DownloadBase, _
                           		ByVal ea As Data.HistQuotesDownloadCompletedEventArgs)
    DataGridView1.DataSource = Data.HistQuoteData.ToTable(ea.Data)
End Sub

ExchangeDownload

For downloading exchange data, use the ExchangeDownload class. For managing this information, you have the ExchangeCalculator^ class. The ExchangeCalculator class already has an update functionality. Just call the Update or UpdateAsync method, and this class will download all ratios of all passed currencies to the passed base currency. It's not important if the base currency is Euro, or US Dollar, or something else, because it's only the needed database for the ConvertCurrency function. With this function, you're able to convert every value of any available currency to another currency.

If you're not using the ExchangeCalculator class, you can download the data "manually". At first, declare the properties. It's like a quotes-download, because the ExchangeItem class inherits from QuoteData.

Dim properties() As Enums.QuoteProperty = {Enums.QuoteProperty.Name, _
                                           Enums.QuoteProperty.Symbol, _
                                           Enums.QuoteProperty.LastTradePriceOnly}

After that, you have to create a list of CurrencyRelations^. A CurrencyRelation only stores information about the base currency and the dependent currency.

Dim lst As New List(Of Data.CurrencyRelation)
For Each cur As Enums.Currency In Data.ExchangeCalculator.MainCurrencies
   lst.Add(New Data.CurrencyRelation With _
           {.BaseCurrency = Enums.Currency.EUR, .DepCurrency = cur})
Next

Then, declare an instance of the ExchangeDownload class. Now, you're able to start the download.

Dim exchangeDl As New Download.ExchangeDownload()
AddHandler exchangeDl.AsyncDownloadCompleted, AddressOf ExchangeCompleted
  AddHandler exchangeDl.Failure, AddressOf Failure
exchangeDl.DownloadAsync(lst, properties, Enums.Servers.USA)

As a result, you will get a list of ExchangeItems^, where the downloaded information is saved.

Private Sub ExchangeCompleted(ByVal sender As Download.DownloadBase, _
                              ByVal ea As Data.ExchangeDownloadCompletedEventArgs)
    Dim dt As New DataTable
    dt.Columns.Add("BaseValue", GetType(Double))
    dt.Columns.Add("BaseCurrency", GetType(Enums.Currency))
    dt.Columns.Add("DepValue", GetType(Double))
    dt.Columns.Add("DepCurrency", GetType(Enums.Currency))

    For Each ei As Data.ExchangeItem In ea.Items
        Dim row As DataRow = dt.NewRow
        row("BaseValue") = 1
        row("BaseCurrency") = ei.CurrencyRelation.BaseCurrency
        row("DepValue") = ei.DependencyValue
        row("DepCurrency") = ei.CurrencyRelation.DepCurrency
        dt.Rows.Add(row)
    Next
    DataGridView1.DataSource = dt
End Sub

The table now has the schema: 1 EUR : x Currency, and can be used, e.g., for calculating, or for quotes, etc.

MarketDownload

With the MarketDownload class, you're able to download information about Sectors^, Industrys^, or Companys^. A sector has only a list of industries, and an industry has a list of companies. A company stores information about a specific stock, like company name or number of employees. Here is an example for downloading all the sectors, industries, and also the company names, in two steps. Declare the downloader and download all the sectors with industry names and IDs:

Dim mdl As New Download.MarketDownload

Dim sectors() As Data.Sector = mdl.DownloadSectors
If sectors IsNot Nothing Then

    Dim lstInd As New List(Of String)
    For Each sect As Data.Sector In sectors
        For Each ind As Data.Industry In sect.Industries
            lstInd.Add(ind.ID)
        Next
    Next

Then, download all industries and their company names and IDs. After that, you can sort the list and the companies.

    Dim industries() As Data.Industry = mdl.DownloadIndustries(lstInd.ToArray)

    For Each sect As Data.Sector In sectors
        For Each ind As Data.Industry In sect.Industries
            For Each indNew As Data.Industry In industries
                If ind.ID = indNew.ID Then
                    For Each comp As Data.Company In indNew.Companies
                        comp.IndustryName = ind.Name
                        comp.SectorName = sect.Name
                        ind.Companies.Add(comp)
                    Next
                    Exit For
                End If
            Next
        Next
    Next
End If

Data import/export

It's possible to export or import all QuoteData from and to XML, CSV, or data tables. For that, use the static methods FromXml/ToXml, FromCSV/ToCSV, or FromTable/ToTable in nearly every quote class (not completed yet). Personally, I don't like methods that can't be used dynamically. That's the reason why these methods don't write files directly, because I don't know if you would want to write a file at all. With the XML method, you have to pass an XmlTextWriter where the information will be written. Consequently, you're free to build your own XML structure. Also, with reading, XML is similar. You pass the node or nodelist of the quotes, and will get the quotes or null/Nothing if it's not possible to read the node. Each node of QuoteData has to have the non case-sensitive name "quote" and an ExchangeItem with node name "exchangeitem". From the CSV method, you will get back and also pass back the CSV formatted string. For what you are using the string, is your decision. Importing CSV with a CSV formatted string needs a header with the names of the properties. You're able to pass an array of QuoteProperty to define the exported properties. If it's null/Nothing, it will use all available properties where the values are not null/Nothing. Also, it's possible to pass a culture that's used for formatting the dates and numbers. If it's null/Nothing, it will use the current culture of the machine. The historic quote datatable import needs some requirements. It must be seven columns with the names (types): Date (Date), Open (Double), High (Double), Low (Double), Close (Double), Volume (Long/Integer), AdjClose (Double).

market.xml

The market.xml is not yet complete. It would be nice if you could help in updating and completing market.xml. If you have a newer version, please contact me. It doesn't have to have hundreds of indices or stock exchanges that you have added. Even one is enough to update the file.

Thanks to

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAlso see...
Ravi Bhavnani
8:11 14 Jan '10  
...this[^] MFC app (based on this[^] CP article). Smile

/ravi

My new year resolution: 2048 x 1536
Home | Articles | My .NET bits | Freeware
ravib(at)ravib(dot)com

GeneralRe: Also see...
MaasOne
6:35 15 Jan '10  
Thanks for the links!
Is the symbol/ID finder you can use in the App a service of yahoo?

Maas
GeneralRe: Also see...
Ravi Bhavnani
6:58 15 Jan '10  
Hi Mass,

No, I built my own set of providers.

/ravi

My new year resolution: 2048 x 1536
Home | Articles | My .NET bits | Freeware
ravib(at)ravib(dot)com

GeneralHallo aus Deutschland
EDVBS
1:00 7 Jan '10  
Das sieht für mich sehr interessant aus, da ich schon längere Zeit etwas ähnliches programmiere (nicht auf Chartanalyse ausgelegt sondern eher auf Verwaltung von Gesamtvermögen). Ich lese meine Kurse auch bei Yahoo aus und die ändern ja auch oft die Kürzel. Deswegen überlege ich ein Programm mit Deinen Klassen zu optimieren.
Vieleicht kann man sich ja mal austauschen.
Wollte gerade noch einen Screenshot meiner Anwendung hier rein pasten, aber das funktioniert leider nicht.
Da ich gerade unter Zeitdurck stehe poste ich kurz in deutsch.

Gruß Micha
GeneralRe: Hallo aus Deutschland
MaasOne
3:07 7 Jan '10  
Hallo Micha,
Ich wusste gar nicht, dass Yahoo die Symbole verändert. Aber du kannst dafür die IndexID benutzen und alle Aktiennotierungen des Index herunterladen, inkl. aktueller Symbole.
Edit: Du meinst wohl die Tags ... Roll eyes Das muss dann eben immer angepasst werden. Da führt kein Weg dran vorbei.
Wenn du irgendwelche Fragen oder Anregungen hast, ist das kein Problem. Einfach eine Mail schreiben.

Viele Grüße

Maas
GeneralRe: Hallo aus Deutschland
EDVBS
5:41 7 Jan '10  
Ja, meinte die Tags. Sorry.
Ich werde die Klassen mal durchtesten. Momentan habe ich aber 3 große Projekte gleichzeitig am Laufen. Kann also etwas dauern. Aber ich melde mich.
Aus welchem Teil Deutschlands bist Du denn? Ich bin im wilden Süden angesiedelt.
GeneralI wish I could write an article like this
Marcelo Ricardo de Oliveira
11:28 6 Jan '10  
Hi, MaasOne, that's an impressive work. Although I haven't downloaded the code yet, but I like your article, so clean, well formatted, well written, and well illustrated. Such a pleasant reading. I vote 5.

marcelo

Take a look at full source code C# Snooker game here in Code Project.

GeneralRe: I wish I could write an article like this
MaasOne
14:38 6 Jan '10  
Hi Marcelo,
Thank you so much!!! Big Grin
I hope you will also like the library itself.

Maas
News1.4 Changes
MaasOne
3:22 14 Dec '09  
There are some Enum changes in version 1.4 that are not only increments.
Some names of QuoteProperty Enum changed. The names are now the same like the YQL property names you'll get in xml files. The changes are "minor". The most "_" chars are deleted and properties with numbers are replaced like "50" to "fifty".
There are some new currencies. These are commodities and special country-independent currencies. Every of these currencies starts with "X".
"IDSuffix" Enum changed to "StockExchanges".
I'm sorry if sb. has now a lot of replacing work to do!

Maas
Generalnon async not working
Zvonimir78
8:24 21 Nov '09  
when I use Download method i get null as a result (C#)!



QuotesDownloadCompletedEventArgs ea;
ea= qdl.Download(.....
GeneralRe: non async not working
MaasOne
15:07 21 Nov '09  
Hi Zvonimir78,
thanks for your message. I figured out the failure and will send the updated files soon to codeproject. I'm sorry for this (really simple) malfunction.

Maas
GeneralRe: non async not working
Zvonimir78
0:10 25 Nov '09  
Otherwise, I voted excellent. Smile
GeneralRe: non async not working
MaasOne
4:21 25 Nov '09  
Thx for voting!!! Big Grin

Maas
GeneralNice work
Jon_Boy
6:38 7 Oct '09  
Nice posting with a good selection of data that can be processed through Yahoo Finance.

Thanks for sharing your work and ideas.

"There's no such thing as a stupid question, only stupid people." - Mr. Garrison

GeneralRe: Nice work
MaasOne
13:37 7 Oct '09  
Thanks a lot. Smile
GeneralSource for downloader [modified]
Hitesh97
1:10 24 Sep '09  
Hi There,
the application is good, wanted to study a bit more on downloader and see if i can help on xml.

I beleive the code for downloader is missing from the source.

is it intentional or mistake? would you be able to publish to code for downloader?

modified on Thursday, September 24, 2009 6:20 AM

GeneralRe: Source for downloader
MaasOne
4:12 24 Sep '09  
Oh sorry, this was of course a mistake.
I sent ths source now per mail for updating the file.
GeneralRe: Source for downloader
Hitesh97
5:13 24 Sep '09  
Hi There,

thanks for prompt reply , I downloaded the source files attached to the article again and could not find any new code.

did you say you have updated the code? or you will do it soon?

let me know.
GeneralRe: Source for downloader
MaasOne
6:06 24 Sep '09  
Hi,
I think i cant update the files directly by myself. So I sent a mail to codeproject like its described in the "Update Guidelines". I dont know how long time it will take until they update the file.
GeneralMy vote of 2
sam.hill
19:38 23 Sep '09  
Source for DLL not released (only test app).
GeneralRe: My vote of 2
sam.hill
6:38 22 Nov '09  
Thanks for the re-release.
I changed my vote.


Last Updated 14 Jan 2010 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010