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.
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.
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.
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.
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.
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.
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.
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
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
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.
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
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).
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.
StockExchange added IID interface added MarketDownload added StockID added StockExchanges enum added Servers completed QuoteData modified Currencies completed Enum description DownloadBase added | You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||