Putting Google Finance to REST with Groovy
by Justin Spradlin | July 16, 2008 – 11:07 pmFor a while now I've been interested in learning more about building and consuming REST based web services. Fortunately, many tech giants including Google and Yahoo expose much of their data and functionality through REST based APIs. These powerful APIs, combined with Groovy's concise, readable syntax make it very easy to learn about the REST software architecture approach.
This particular blog entry makes use of Google's Finance Data API. I'll explain how it is possible to programmatically authenticate with Google, create a new stock portfolio, and create positions (buy and sell stocks) within this portfolio by making REST based service calls. I won't dive too deep into the details of the REST architecture, but if you have a general understanding of XML and the HTTP protocol it won't be too difficult to follow along. For more information on REST, please see the links in the Reference section below.
Authenticating with Google
In order to use Google's service we must first authenticate by POSTing a query string containing our username, password, service identifier, and source (application name) to the following URL: https://www.google.com/accounts/ClientLogin. This code snippet illustrates how a POST request can be made in Groovy:
static createAuthToken(String username, String password){
def url = new URL("https://www.google.com/accounts/ClientLogin")
def connection = url.openConnection()
def queryString = "Email=${username}&Passwd=${password}" +
"&service=finance&source=company-groovyfinance-1.0"
def returnMessage = processRequest(connection, queryString)
if(returnMessage != "Error"){
//the authtentication token
return returnMessage.split(/Auth=/)[1].trim()
}
}
static String processRequest(connection, dataString){
connection.setRequestMethod("POST")
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(dataString)
writer.flush()
writer.close()
connection.connect()
if (connection.responseCode == 200 || connection.responseCode == 201)
return connection.content.text
return "Error"
}
In the code above we create a connection to Google's authentication URL and POST the queryString by calling the reusable processRequest() method. Within processRequest() we set the connection's request method type to "POST", create a Writer, and write the data to the supplied connection.
If our credentials are valid, Google will respond to our request with a string of name value pairs similar to this:
SID=DQAAAIEAAAIgccs7qKgmwXGagt...lu6fg
LSID=DQAAAIIAAACsegj9oEm0Ob8pz...abyi5
Auth=DQAAAIMAAACgadj9oEm0Ob8pz...AGasG
The only piece of information we are interested in is the authorization token value that follows the "Auth" label. We will need to use this token in the rest of our requests in order to authenticate with Google.
Creating a Portfolio
When users log into Google Finance for the first time they will be presented by default with an empty portfolio called "My Portfolio". Note: a portfolio is simply a collection of assets (stocks, bonds, mutual funds, cash, etc.).
Let's pretend however that we do not wish to use the default portfolio, but instead want to create our own. To create a new portfolio we simply need to send a well formed Atom XML feed that contains the name of the new portfolio (and optionally a currency code) to this URL: http://finance.google.com/finance/feeds/default/portfolios
static createPortfolio(String authorizationToken, String portfolioName){
def url =
new URL("http://finance.google.com/finance/feeds/default/portfolios")
def connection = url.openConnection()
connection.setRequestProperty("Content-Type", "application/atom+xml")
connection.setRequestProperty("Authorization",
"GoogleLogin auth=${authorizationToken}")
def atomString = """<?xml version='1.0'?>
<entry xmlns='http://www.w3.org/2005/Atom'
xmlns:gf='http://schemas.google.com/finance/2007'
xmlns:gd='http://schemas.google.com/g/2005'>
<title type='text'>${portfolioName}</title>
<gf:portfolioData currencyCode='USD'/>
</entry>"""
def returnMessage = processRequest(connection, atomString)
//Get the id of the newly created portfolio
if(returnMessage != "Error"){
def entry = new XmlSlurper().parseText(returnMessage)
def entryId = "${entry.id}"
return entryId[entryId.lastIndexOf('/')+1..-1]
}
}
Once again we create a connection, but this time we also need to set two request header parameters: Content-Type and Authorization. We set these parameters to "application/atom+xml" and "GoogleLogin auth=${authorizationToken}" respectively. Note that the authorizationToken variable stores the authentication token that was returned by the createAuthToken() method.
After the connection is created we again call the processRequest() method that was discussed in the Authenticating with Google section above. If all goes well, Google will create the new portfolio and return an Atom representation of that new portfolio to the client. If we are logged into our web based Google Finance account we can refresh the screen and will notice our newly created portfolio.
Two code features really shine through in the example above: Groovy's Triple Quotes and Groovy's XmlSlurper. The triple quotes surrounding the Atom XML allow us to easily write multi-line XML strings without having to worry about escaping special characters. The XmlSluper meanwhile allows us to easily parse and locate a specific attribute (the portfolio id in this case) within the returned XML string. Imagine how many lines of Java code it would take to recreate this functionality.
Placing an Order
So far our portfolio is pretty boring. Let's create some stock transactions to add some assets into our portfolio.
static createStockTransaction(authorizationToken, tickerSymbol, numberOfShares,
transactionType, portfolioId){
StringBuffer transactionURL = new StringBuffer()
transactionURL.append("http://finance.google.com/finance/feeds/")
.append("default/portfolios/${portfolioId}/positions/")
.append("${URLEncoder.encode(tickerSymbol)}/transactions")
def url = new URL(transactionURL.toString())
def connection = url.openConnection()
connection.setRequestProperty("Content-Type", "application/atom+xml")
connection.setRequestProperty("Authorization",
"GoogleLogin auth=${authorizationToken}")
String timeFormat = "yyyy-MM-dd'T'HH:mm:ss.S"
SimpleDateFormat dateFormatter = new SimpleDateFormat(timeFormat)
String transactionDate = dateFormatter.format(new Date())
def atomString = """<?xml version='1.0'?>
<entry xmlns='http://www.w3.org/2005/Atom'
xmlns:gf='http://schemas.google.com/finance/2007'
xmlns:gd='http://schemas.google.com/g/2005'>
<gf:transactionData date='${transactionDate}'
shares='${numberOfShares}' type='${transactionType}' />
</entry>"""
processRequest(connection, atomString)
}
The concept with this code is basically the same as the portfolio creation example above. We create a connection with the correct Content-Type and Authorization headers, define some Atom XML with attributes about the transaction we would like to make, and then POST the XML to a specific URL. The interesting thing to note about this code is that unlike the other two examples, the URL for creating transactions is not static. Instead, this URL needs to be parameterized with the portfolio id and ticker symbol of the stock you wish to trade: http://finance.google.com/finance/feeds/default/portfolios/<portfolio id>/
positions/<ticker symbol>/transactions.
Method calls to createStockTransaction() might look something like this:
createStockTransaction(authorizationToken, "NASDAQ:JAVA", 10000.00, "Buy", pId)
sleep(2000)
createStockTransaction(authorizationToken, "NYSE:F", 10000.00, "Buy", pId)
This would place an order for 10,000 shares of Sun Microsystems and Ford Motor Company stock. Notice that the ticker symbol must be preceded by the name of the market on which it trades. Therefore the ticker symbol we sent to Google to purchase Sun stock is: NASDAQ:JAVA because Sun trades on the NASDAQ stock exchange. Stocks that trade on the New York Stock Exchange would be preceded by the acronym: NYSE (i.e. Ford = NYSE:F). Also notice that it is a good idea to "sleep" between service calls because sending too many requests to Google at once will result in errors.
When we refresh our Google Finance page we will notice these shares placed into our portfolio.
Conclusion
This blog post only skims the surface of what is possible using Google's Finance Data API. The API also allows for RESTful calls to GET, UPDATE and DELETE specific portfolio and transaction feeds. Overall, I've been incredibly impressed by all of the data and functionality made available through Google's Data APIs.
The code provided in this example should give you a solid foundation to start playing around with Google's Finance API, but it is far from bulletproof. For example, I don't really do much in the way of error handling or input validation. So you shouldn't be surprised if things bomb out if you try to sell shares of a stock that you don't own or create a portfolio with the same name as one of your existing portfolios.
The examples I've shown here are very low level because I wanted to learn the basics of REST. A much easier way to implement the same functionality would be to use Google's Data APIs Client Libraries (which by the way work perfectly well with Groovy).
References
Download the Code
Google Data API Developers Guide
Google Finance Developers Guide
Google Finance Reference
Building Web Services the REST Way
REST Wikipedia Entry
Disclaimer
I am in no way endorsing any of the companies mentioned in this blog entry and all company references are for example purposes only.
2 Responses to “Putting Google Finance to REST with Groovy”
I found your blog on google and read a few of your other posts. I just added you to my Google News Reader. Keep up the good work. Look forward to reading more from you in the future.
By Susan Kishner on Jul 16, 2008
*** Update ***
I changed the code in the createAuthToken() method to split the “returnMessage” using a regular expression.
Old code:
returnMessage.split(”\n”)[2].split(”=”)[1]
New code:
returnMessage.split(/Auth=/)[1].trim()
I think we can all agree that this should make the code much more readable.
By Justin on Jul 23, 2008