Tuesday, May 23, 2017

Integration API: charts with live data

charte.ca integration API comes handy when you want your published chart to display up-to-date information and you do not have the luxury of logging in to the editor, tweaking the data and re-publishing the chart manually every time the data changes. Consider the following P/E ratio vs. Price to book bubble chart that we want to be updated every hour:




This chart was created and published using charte.ca online editor once, and it is updated and re-published by a script that runs every hour and performs the following tasks:
  • obtain stock quotes for specific symbols from Yahoo;
  • compare them with the data currently displayed by the chart, and, if the data is different:
    • upload new data to the chart;
    • update chart subtitle so it reflects the latest update
    • re-publish the chart
This example uses the following API calls:
  • Download data
  • Put config
  • Upload data
  • Publish chart
For full list of available API calls, see charte.ca API guide.

This particular example uses Python as scripting language, but it could be any language or framework capable of performing HTTP requests. The script is scheduled to run every hour on a system integrator's infrastructure. Full source code is below.

import urllib2
import json
import sys

charteca_root = "https://api.charte.ca"
chart_id = "g7h9GRRPqk23GRZW"
api_key = "..."
# Stock exchange symbols we are interested in. Order matters.
symbols = ["TD","BMO","BNS","CM","RY"]
symbol_name_map ={"TD":"Toronto Dominion","RY":"Royal Bank of Canada", "BMO":"Bank of Montreal", "BNS":"Scotia Bank","CM":"Canadian Imperial Bank of Commerce"}
symbol_short_name_map ={"TD":"TD","RY":"RBC", "BMO":"BMO", "BNS":"Scotia Bank","CM":"CIBC"}
# Data fields we are interested in
fields = "symbol,PriceBook,PERatio,ChangeinPercent,MarketCapitalization"
# Call YQL API
print "Retrieving data from Yahoo..."
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request('https://query.yahooapis.com/v1/public/yql?q=select%20' + fields + '%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(' + ",".join(["\""+symbol+"\"" for symbol in symbols]) + ')&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=')
request.add_header('Cache-Control', "no-cache")
request.get_method = lambda: 'GET'
response = opener.open(request)

# Deserialize JSON response
response_data = json.loads(response.read())
# Start building data spreadsheet for charte.ca: populate first column
#print response_data

data = [["Symbol"],["P/E Ratio"],["Price to book"],["Market cap (billions)"],["Performance today"],["Name"],["Symbol"]]
updated_on = response_data["query"]["created"].replace("T"," ").replace("Z", " UTC")
# Populate series columns. Order matters, follow the order in symbols array
for symbol in symbols:
  # Find correspondent quote
  quote = next(quote for quote in response_data["query"]["results"]["quote"] if quote["symbol"] == symbol)
  data[0].append(symbol_short_name_map[quote["symbol"]] + ": " + quote["ChangeinPercent"])
  data[1].append(quote["PERatio"])
  data[2].append(quote["PriceBook"])
  data[3].append(quote["MarketCapitalization"])
  data[4].append(quote["ChangeinPercent"])
  data[5].append(symbol_name_map[quote["symbol"]])
  data[6].append(quote["symbol"])
# Print out tab-separated values
tsv_data = ""
for data_row in data:
  tsv_data += "\t".join(data_row) + "\n"

# Download existing chart data
print "Downloading existing chart data..."
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(charteca_root + '/chart_integration/data/' + chart_id)
request.add_header('api-key', api_key)
request.get_method = lambda: 'GET'
download_response = opener.open(request)
download_response_data = download_response.read()
download_response_headers = download_response.info().dict
if "content-type" not in download_response_headers:
  print "Error: no content-type in the response"
  sys.exit(-1);
if "application/json" in download_response_headers["content-type"]:
  # We got an error
  print download_response_data
  sys.exit(-1);
if "text/tab-separated-values" not in download_response_headers["content-type"]:
  # We expected TSV data
  print " Error: expected TSV data in the response"
  sys.exit(-1);

# Compare existing data with what we got from Yahoo
old_lines = filter(None, [line.replace("\r", "") for line in download_response_data.split("\n")])
new_lines = filter(None, [line.replace("\r", "") for line in tsv_data.split("\n")])
# Compare first few lines: performance, P/E, PriceBook, Capitalization
if old_lines[0] == new_lines[0] and old_lines[1] == new_lines[1] and old_lines[2] == new_lines[2] and old_lines[3] == new_lines[3] and old_lines[4] == new_lines[4]:
  print "Nothing to update, the data is the same"
  sys.exit(0)

print old_lines
print new_lines

# Update chart config: change the subtitle to reflect the date
print "Updating chart config..."
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(charteca_root + '/chart_integration/config/' + chart_id, data='{"config": {"subtitle": {"text1": "Data from Yahoo Finance: %s"}}}' % updated_on)
request.add_header('api-key', api_key)
request.add_header('Content-Type', "application/json")
request.get_method = lambda: 'PUT'
update_response = opener.open(request)
update_respose_object = json.loads(update_response.read())
if update_respose_object["code"] != "SUCCESS":
  print json.dumps(update_respose_object)
  sys.exit(-1);

# Upload chart data
print "Uploading data..."
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(charteca_root + '/chart_integration/data/' + chart_id, data=tsv_data)
request.add_header('api-key', api_key)
request.get_method = lambda: 'PUT'
upload_response = opener.open(request)
upload_respose_object = json.loads(upload_response.read())
if upload_respose_object["code"] != "SUCCESS":
  print json.dumps(upload_respose_object)
  sys.exit(-1);

# Re-publish the chart
print "Publishing chart..."
request = urllib2.Request(charteca_root + '/chart_integration/publication/' + chart_id)
request.add_header('api-key', api_key)
request.get_method = lambda: 'POST'
publish_response = opener.open(request)
publish_respose_object = json.loads(publish_response.read())
if publish_respose_object["code"] != "SUCCESS":
  print json.dumps(publish_respose_object)
  sys.exit(-1);

sys.exit(0)

No comments:

Post a Comment