ASKing for Stock Quotes
Another skill I needed for my “morning greeter” program was a way to get stock quotes for multiple companies at a time. Similar to getting news, there are many sources on the internet for getting this information. A popular website for getting stock quotes is Yahoo Finance which also happens to provide a webservice the fits my needs exactly. It allows querying multiple ticker symbols in a single request and can return the data in JSON format.
Here’s a simple Python function that takes a list of ticker symbol strings and returns the URL for getting the stock quotes as JSON data.
def get_url(ticker_list):
tickers = ",".join(ticker_list)
return "http://finance.yahoo.com/webservice/v1/symbols/{tickers}/quote?format=json&view=detail".format(tickers=tickers)
For example, you can get the URL for Apple, Facebook, and Netflix using the list:
['AAPL', 'FB', 'NFLX']
Which will return the URL string: http://finance.yahoo.com/webservice/v1/symbols/APPL,FB,NFLX/quote?format=json&view=detail
An HTTP GET request will return a response like this:
{
"list": {
"meta": {
"type": "resource-list",
"start": 0,
"count": 2
},
"resources": [
{
"resource": {
"classname": "Quote",
"fields": {
"change": "1.489998",
"chg_percent": "1.327747",
"day_high": "113.809998",
"day_low": "112.419998",
"issuer_name": "Facebook, Inc.",
"issuer_name_lang": "Facebook, Inc.",
"name": "Facebook, Inc.",
"price": "113.709999",
"symbol": "FB",
"ts": "1459972800",
"type": "equity",
"utctime": "2016-04-06T20:00:00+0000",
"volume": "20814640",
"year_high": "117.590000",
"year_low": "72.000000"
}
}
},
{
"resource": {
"classname": "Quote",
"fields": {
"change": "-0.110001",
"chg_percent": "-0.104822",
"day_high": "106.440002",
"day_low": "104.250000",
"issuer_name": "Netflix, Inc.",
"issuer_name_lang": "Netflix, Inc.",
"name": "Netflix, Inc.",
"price": "104.830002",
"symbol": "NFLX",
"ts": "1459972800",
"type": "equity",
"utctime": "2016-04-06T20:00:00+0000",
"volume": "9605800",
"year_high": "133.270000",
"year_low": "61.184300"
}
}
}
]
}
}
Once we have the data, we need to parse it and convert it into speech-friendly text for Alexa.
First, we need to send an HTTP GET and parse the JSON data.
import urllib2
import json
# Get the URL for the ticker symbols
url = get_url(['AAPL', 'FB', 'NFLX'])
# Send HTTP GET
res = urllib2.urlopen(url)
# Read the response and convert JSON to Python dict
data = json.loads(res.read())
# Loop through the list of resources and parse the data
resources = data['list']['resources']
stocks = []
for r in resources:
# Parse the data here
Inside the for loop, I’ll extract the info that I want:
- Name of the company
- Is the stock up/down?
- What’s the price?
The name of the company can contain some extra text that’s not needed. Some simple string replacement is done to clean it up:
name = r['resource']['fields']['name']
name = name.replace("Common Stock", '')
name = name.replace(' Inc','').replace(',','').replace('.','')
name = name.replace('(NS) O','')
name = name.replace("(The) Commo", "")
name = name.strip()
The change percentage is a positive/negative floating point number, but it’s returned as a string. The code below will translate the +/- into text and do some type conversion, rounding, and cleanup:
# This is a string
change = r['resource']['fields']['chg_percent']
# Check if postive/negative, i.e. up/down
if "-" in change:
up_down = "is down"
else:
up_down = "is up"
# Convert string to decimal number, get the absolute value, and round to one decimal place
change = round(abs(decimal.Decimal(change)), 1)
if change == 0:
spoken_change = "is trading at"
else:
# Avoid saying things like, "two point zero percent"
splits = str(change).split('.')
if splits[1] == '0':
spoken_change = "{up_down} {change}% at".format(
up_down=up_down, change=splits[0])
else:
spoken_change = "{up_down} {change}% at".format(
up_down=up_down, change=change)
And finally, parse the price and put everything together:
price = round(decimal.Decimal(r['resource']['fields']['price']), 2)
price = round(decimal.Decimal(price), 2)
spoken_price = "${}".format(price)
# Put all the pieces together
spoken = "{name} {change} {price}.".format(
name=name, change=spoken_change, price=spoken_price)
You can find the code for the full skill in my Github repository.