28 Aug Return Stock Data Using Excel VBA
Howdee! Querying data over the web becomes more common every day. Almost every cloud based program has the ability to serve up its data through some sort of API. Sometimes, the data can be extracted to Excel through some sort of reporting interface within the program. However, I’ve found that this functionality is usually difficult to use and/or incredibly slow or doesn’t exist at all. This is where the ability to make an Excel http get request is so valuable.
In my example today I’ll be building a small excel application that will allow the user to search for a company’s ticker and then return the most recent stock data using an http request. To do this, we will use the site http://dev.markitondemand.com/MODApis/Api/v2/doc. It is a free service that provides the ability to search for tickers and company names and then fetch stock data on that entity.
HTTP Basics
Before diving into the technical aspects of an Excel http get request, let’s first explore what http requests are. HTTP stands for “Hypertext Transfer Protocol”. In laymen’s terms, it is a means of communicating between a host and a client.
Let’s assume you type www.facebook.com into a web browser. You, the client, have just initiated an HTTP GET request to Facebook, the host (for thoroughness, it’s worth mentioning that sites like Facebook that contain sensitive information use an HTTPS request to secure the data being transferred). What you’ve sent is called the “Request” and the HTML page that Facebook sends back is the “Response”.
The request is sent using a URL (Uniform Resource Locator). While you might be familiar with URL’s, you might not be familiar with their structure. They consist of these components:
Protocol is usually HTTP but, as I said earlier, HTTPS is used when the connection needs to be secured (personal data, credit card information, etc.). The host is just that, the domain address a host has chosen for you to reach their server. The port is usually not needed to be explicitly typed. The default port is 80 and the URL implicitly sends that if you exclude it. The resource path and the query are the import parts I want to point out.
The resource path is instructions sent to the host that tell the server where to navigate to so that your information can be found. Lastly, the query section is always kicked off with a question mark. This section provides any parameters you want to include as well the option to explicitly list the precise data you want returned.
This is as dep as I’ll go into HTTP since this is enough to get started querying information. However, if you’re planning to develop an application that will make HTTP calls, I recommend reading up on the subject so you can handle several types of responses. Let’s say a user passes a bad request to the program you build; the host will send back an HTTP response but it could be an error 404 response and not include any data. You’ll need to be able to handle that and other issues that could arise. This article on the Mozilla Developer Network is a great place to start.
Excel HTTP Get Request – Creating the Search
Now that we know a little about HTTP, let’s dive into the Excel http request using VBA. To get started open a new Excel file and press alt + F11 to open the VBA editor window. The first thing we need to do is add some library references so we can access the controls we will need to make HTTP calls. Click on “Tools” and then “References” and add a check box to Microsoft Scripting Runtime, Microsoft Internet Controls, and Microsoft WinHTTP Services, version 5.1 as pictured below.
In this application, we want to allow the user the ability to search for a company’s stock ticker. We will then need to pass that data into our code. When my program needs to interact with data on a spreadsheet, I prefer to name the range or cell that it will be interacting with so that, when I’m writing code, it’s evident exactly what data I’m collecting or where I need to write data to. I’ve named a cell “Search_For_Company” and put instructions on the sheet so the user realizes that he/she needs to enter a search term there.
Now, we need to write the code that will accept this input, and then make an http get request to return potential matches. But, before that, let’s consider the returned results. I user might type in “face” when searching for Facebook. That would return dozens of companies so we need somewhere to store those values temporarily. For this, I’ve created a basic user form with a list box to display the values. It looks like this:
Typically I’ll break my code down in to sections to discuss, but I think this makes sense to look at the code in its entirety and then explain what’s happening step by step.
Sub GetCompanyInformation()
Dim hReq As Object, JSON As Dictionary
Dim i As Long
Dim var As Variant
Dim ws As Worksheet
Set ws = Sheet1
'create our URL string and pass the user entered information to it
Dim strUrl As String
strUrl = "http://dev.markitondemand.com/MODApis/Api/v2/Lookup/json?input=" & ws.[Search_For_Company]
Set hReq = CreateObject("MSXML2.XMLHTTP")
With hReq
.Open "GET", strUrl, False
.Send
End With
'wrap the response in a JSON root tag "data" to count returned objects
Dim response As String
response = "{""data"":" & hReq.ResponseText & "}"
Set JSON = JsonConverter.ParseJson(response)
'set array size to accept all returned objects
ReDim var(JSON("data").Count, 1)
i = 1
'loop through returned data and get Symbol & Name
For Each Item In JSON("data")
var(i, 0) = Item("Symbol")
var(i, 1) = Item("Name")
i = i + 1
Next
ufCompaniesReturned.lbCompaniesReturned.List = var
Erase var
Set var = Nothing
Set hReq = Nothing
Set JSON = Nothing
If ufCompaniesReturned.Visible = False Then
ufCompaniesReturned.Show
End If
End Sub
Let’s skip over the variable declarations as we’ll discuss each as they’re used. The first line simply assigns the “ws” variable to the sheet where I’m working, Sheet1 in this case. Next, I dimension a new string variable (I could have dimensions this variable above with the others but this is just personal preference when I’m creating a variable that represents a URL) and assign the URL I’m using to it. Note here that for the query section of the URL, I’m using a combination of text entered and I’m pulling the value the user typed into the range we named earlier. This is why I like to use that approach in my Excel http get request programs. I can easily see that I’m passing a value from Sheet1 that represents a company to search for.
Next set the Object variable “hReq” equal to a new object in the Microsoft XML Core Services. If you want to read more about what this entails, MSDN has a great write up on it. Suffice it to say this service allows us to send HTTP requests and parse the responses. Now, with that variable we will open a connection and pass it three parameters. We are telling it that we are sending a “GET” request, we pass the URL to use, and lastly, we want to explicitly tell it the call is not asynchronous (also could be said the call is synchronous). This prevents anything further from happening until we get a response from the host. If we left this blank, the default is true. The program could then move on while we are waiting the call and break. There are two more optional parameters in this Open method for username and password, in that order. If that information is required for your request, you’ll need to provide them. They are not required in this example however.
In the next section of code, we create a response variable and store the response from the request as a string. Since this response is returned in JSON, I’m using the VBA-JSON library to parse the JSON. If you’re unfamiliar with this, click here for my blog post on how to use the library. Note that I wrap my response in some additional text. This is a root JSON object that I wrap the data in so I can perform a count on how many objects were returned, which I do in the next step when I’m resizing the array I declared at the beginning of the code.
Lastly we will loop through each object returned by our Excell http get request, and assign the company’s symbol and name to the array. Once the array has been populated, we will pass that array to a listbox, dispose of some resources, and finally check if the form is already visible and, if it isn’t, show it.
Excel HTTP Get Request – Returning the Stock Quote
Now that we’ve successfully written code that will perform our search query, let’s move on to the http get request to return our stock quote. In the form that pops up, our user will select a company for which they want to return a stock quote. Once they click “OK” we will want the result to be returned. To do this, we need to have this process kick off when the event for the button click occurs. To create a sub routine that subscribes to that event, simply double click the “OK” on your user from inside the VBA editor window.
We can certainly embed all the code we want to run inside this event and call it a day. However, I prefer to keep my code separate. I’ve created another module called “Functions” and created a function inside to run my Excel http get request stock quote call when passed the correct parameters. That function looks like this:
Function GetStockData(company As String, companyName As String)
Dim hReq As Object, JSON As Dictionary
Dim sht As Worksheet
Set sht = Sheet1
Dim strUrl As String
strUrl = "http://dev.markitondemand.com/Api/v2/Quote/json?symbol=" & company
Set hReq = CreateObject("MSXML2.XMLHTTP")
With hReq
.Open "GET", strUrl, False
.Send
End With
Dim response As String
response = hReq.ResponseText
Set JSON = JsonConverter.ParseJson(response)
sht.[Last_Price] = JSON("LastPrice")
sht.[Change] = JSON("Change")
sht.[Change_Percent] = JSON("ChangePercent") / 100
sht.[Quote_Time] = JSON("MSDate")
sht.[Market_Cap] = JSON("MarketCap") / 1000000
sht.[Volume] = JSON("Volume")
sht.[Change_YTD] = JSON("ChangeYTD")
sht.[Change_YTD_Percent] = JSON("ChangePercentYTD") / 100
sht.[High] = JSON("High")
sht.[Low] = JSON("Low")
sht.[Open] = JSON("Open")
sht.[Search_For_Company].Value = companyName
End Function
As you can see, this code structure is very like the previous code structure so I won’t go through every detail. The first thing you might notice is that this isn’t a subroutine. I’ve made this a function and given it two parameters to receive. I first pass in the “company” parameter which will represent the stock ticker symbol. This is the parameter I pass to the strUrl variable to complete my Excel http get request string. Next, I pass in the company Name parameter which will be the formal name returned from our previous query.
The next difference you might notice is that I don’t wrap the HTTP response like I did previously. Since I know this response will only return one object, I don’t need to count or loop through the response. I can simply parse that response into a dictionary and then access them through the respective keys. Also, I’ve created several more named ranges to write these values to. Again, this is not necessary but I find it helps tremendously with the readability of my code. Lastly, I just assign the formal name I used in the call back to the cell.
To call this function, I used this code in the button click event handler:
Dim i As Long
For i = 0 To ufCompaniesReturned.lbCompaniesReturned.ListCount - 1
If lbCompaniesReturned.Selected(i) = True Then
Call GetStockData(lbCompaniesReturned.List(i, 0), lbCompaniesReturned.List(i, 1))
Else
'do nothing
End If
Next i
ufCompaniesReturned.Hide
This code is pretty straight forward. I’m first looping through each item in my list box and checking to see if that item has a selected property of true. If it does, I’m passing the two values in the list box to my GetStockData function. From there, the code is handled by the function in my Functions module. All of this together ends up with this result:
I hope this article showed you the power of being able to make http requests from within an Excel application. You can customize the user experience, pull only the data you need, and it’s very fast. As always, the file I worked with today is available for download if you’re a subscriber to my site. It’s free, easy to sign-up, and I won’t be spamming you with dozens of emails each week. If you have questions on anything I went over, please drop them in the comments section below.
Cheers,
R
No Comments