Building a stock market ticker in VastPark
Update: Revised IMML here: http://theparkisvast.com/2010/02/17/updated-imml-for-stock-market-ticker/ | |
![]() |
As I’m sure you’ve probably noticed by now, the stock markets around the world have crashed and burned with lots of dollars drained off the portfolio’s of many an investor, myself included.
To avoid future surprises, I’ve decided to pay a little more attention to what’s happening with the stock market and what better way to do so than via VastPark 🙂 |
The first step was to work out a reliable data source for the stock prices that would let me retrieve at least the following info:
- Opening price
- Last trade
- Volume
As an Aussie, I’m primarily interested in stocks from the asx – but to allow others to re-use the code, something that would work worldwide was desirable.
After a search lead me to the blog of Mads Kristensen and this post I realised Yahoo Finance would work perfectly using the request csv method:
http://finance.yahoo.com/d/quotes.csv?s=<StockName>&f=sl1c1ohgv
For the first version of the ticker, I wanted to keep things simple. It would provide the following visual for each stock:
- A bar indicating the open price
- A bar indicating the last price
- Some text showing the name of the stock
- Some text showing the last trade price of the stock
- Some text showing the percentage change
I also wanted to avoid writing a plugin to do the work and instead make use of the scene.web:loadstring method in VastScript, which would return a string containing all of the data to process.
Step 1: A Loaded trigger and target script
Use a document level Trigger and an inline script, get things rolling:
<Trigger Event="Loaded" Target="GenerateStocks" /> <Script Name="GenerateStocks"> function main(obj, args) --todo: retrieve the data --todo: parse the response data --todo: generate the visuals end </Script>
Step 2: Retrieve data from Yahoo!
To make life easier when changing stocks, I’m going to store a scene variable called stocks containing all of the stock codes I’m interested in.
--store a scene variable for the stocks to retrieve scene:set('stocks', 'IPL.AX, CFE.AX,FMG.AX,RIO.AX,BHP.AX,BBI.AX,BBP.AX,BOQ.AX,BRM.AX,FGL.AX,NAB.AX,TAM.AX,TLS.AX,WOW.AX') --create the http request request = httprequest('http://finance.yahoo.com/d/quotes.csv?s='..scene:get('stocks')..'&f=sl1c1ohgv') --load the response response = scene.web:loadstring(request)
Step 3: Parse the data into something useful
The response of the above turns out to be a large string containing all of the data for each stock separated by a comma and all stocks separated by a new line.
--reponse is returned as each stock on a new line, break into a table entry for each stock stocks = explode('\r\n', response) count = 1 --stocks is a dictionary of all listed stocks, need to explode each listing to generate a representation --format: stock code, last trade,change, open, high, low, volume for key,value in pairs(stocks) do if(string.len(value) > 0) then scene.ui:writeline(value) stock = explode(',', value) --todo: generate the visual for this stock end end
Step 4: Generate the visual
We get a little tricky here, generating a red primitive if the stock is worth less than the open, green otherwise.
element = _generateStock(string.sub(stock[0],2,-2), stock[1], stock[2], stock[3], stock[4], stock[5], stock[6]) element.position = vector3(-20 + count*4.5, -9, 30) scene:add(element) function _generateStock(code, lastTrade, change, open, high, low, volume) scene.ui:writeline('generating representation for: '..code) emissive = rgb("#00ff00") --default to green changeFromOpen = string.sub(change, 1,1) if(changeFromOpen == "-") then emissive = rgb("#ff0000") --red scene.ui:writeline('stock trading lower than open!') end --representation for the last trade lastTradePrim = primitive() lastTradePrim.type = primitivetype.cylinder lastTradePrim.complexity = primitivecomplexity.high lastTradePrim.name = code.."_last" lastTradePrim.material.emissive = emissive lastTradePrim.size = vector3(0.5, tonumber(lastTrade), 0.5) lastTradePrim.position = vector3(0.5,0.5,0) --representation for the opening price openPricePrim = primitive() openPricePrim.type = primitivetype.cylinder openPricePrim.complexity = primitivecomplexity.high openPricePrim.name = code.."_open" openPricePrim.material.emissive = rgb("#ffffff") openPricePrim.size = vector3(0.5, tonumber(open), 0.5) openPricePrim.position = vector3(-0.5,0.5,0) --stock code text stockCodeText = text() stockCodeText.name = code.."_text" stockCodeText.value = code stockCodeText.billboard = true stockCodeText.position = vector3(0,-1,-3) --1 unit lower than the parent and 3 units fwd --parent prim for the platform the 2 colums will sit on basePrim = primitive() basePrim.name = code.."_parent" basePrim.material.emissive = rgb("#000000") basePrim.size = vector3(3,0.5,3) basePrim:add(lastTradePrim) basePrim:add(openPricePrim) basePrim:add(stockCodeText) return basePrim end
Once the stocks have been generated, they now need to be updated with the new values. Yahoo Finance appears to operate on a slight delay but the data changes real-time, offset by that delay, so we can query for new data quite often. The best way to do this is with a looping timeline that runs a script every n seconds. I’ve chosen to do this every 2 seconds:
<Timeline Name="Timeline" Enabled="True" Speed="1" Loop="True" AutoTween="False"> <ExecuteEvent Time="00:00:02" Element="UpdateStocks" /> </Timeline>
My UpdateStocks script looks something like this:
<Script Name="UpdateStocks">function main(obj, args) request = httprequest('http://finance.yahoo.com/d/quotes.csv?s='..scene:get('stocks')..'&f=sl1c1ohgv') response = scene.web:loadstring(request) --reponse is returned as each stock on a new line, break into a table entry for each stock stocks = explode('\r\n', response) count = 1 --stocks is a dictionary of all listed stocks, need to explode each listing to generate a representation --format: stock code, last trade,change, open, high, low, volume for key,value in pairs(stocks) do if(string.len(value) > 0) then stock = explode(',', value) _updateStock(string.sub(stock[0],2,-2), stock[1], stock[2], stock[3], stock[4], stock[5], stock[6]) end end end function _updateStock(code, lastTrade, change, open, high, low, volume) emissive = rgb("#00ff00") --default to green changeFromOpen = string.sub(change, 1,1) if(changeFromOpen == "-") then emissive = rgb("#ff0000") --red end --update the last trade visual lastTradeVisual = scene:getelement(code..'_last') lastTradeVisual.material.emissive = emissive lastTradeVisual.size = vector3(0.5, tonumber(lastTrade), 0.5) --update the % change percentageChange = (tonumber(change) / tonumber(open)) * 100 stockText = scene:getelement(code..'_text') stockText.value = code.."\r\n$"..lastTrade.."\r\n".._round(percentageChange,2).."%" end function _round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end</Script>
Putting all of the above together does the trick and we now have a good basis to make something more advanced.
Download the IMML document here (right-click, save as): Stock ticker.park
Comments
One Response to “Building a stock market ticker in VastPark”
[…] made some adjustments to the stock ticker built as part of my building a stock ticker in vastpark post to take advantage of the new Tooltip Plugin and the Define element in IMML. Rather than going all […]