Introduction
This simple Windows Forms Application shows how to use the Google Finance APIs to fetch the stock quotes and charts and update them at
a fixed interval using the System.Windows.Forms.Timer
class.
Using the code
In order to fetch XML data from Google Finance, there is no sign up or Log in required. The following URL is used to fetch data from
Google: https://www.google.com/ig/api?stock=GOOG (here, GOOG stands for Google's
stock and this symbol can be replaced by another stock symbol to fetch different stock)
Here is a simple screenshot of the XML that is returned
by Google APIs for Google's stock (GOOG).
Now, we can combine two or more stocks into one URL and fetch a big XML file that can be parsed locally and hence reduce the number of requests made
to Google Finance.
For example, to fetch Google (GOOG) and Apple's (AAPL) stock in one XML file, the following URL can be used:
https://www.google.com/ig/api?stock=GOOG&stock=AAPL
Back to Coding, here is the Class diagram for the Solution:
The following method in Stock_quotes
class is used to fetch the XML data:
public static XDocument FetchQuote(string symbol)
{
symbol = symbol.Trim();
symbol = symbol.Replace(" ", "&stock=");
symbol = symbol.Replace(",", "&stock=");
string url = "https://www.google.com/ig/api?stock=" + (symbol);
return XDocument.Load(url);
}
To validate a quote entered by the user, we look at the "last" element (trading value) in the XML file and if it is = 0.00 or doesn't exist, then the stock quote
entered by the user is in-valid. Here is the method in Stock_quotes
class that validates stock quotes:
public static List<Stock> getValidStocks(XDocument doc)
{
List<Stock> stocks = new List<Stock>();
foreach (var root in doc.Root.Elements("finance"))
{
try
{
if (root.Element("last") != null && root.Element("last").Attribute(
"data").Value != null && root.Element("last").Attribute(
"data").Value.Equals("0.00") == false)
{
stocks.Add(Stock_quotes.createNewStock(root));
}
else
{
System.Windows.Forms.MessageBox.Show(root.Element("symbol").Attribute(
"data").Value + " is not a valid stock symbol");
}
}
catch (Exception er)
{
}
}
return stocks;
}
Stock_quotes.cs
class: This is a static class that handles all the parsing of XML data.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Xml.Linq;
using System.Xml;
namespace Quotes
{
static class Stock_quotes
{
public static XDocument FetchQuote(string symbol)
{
symbol = symbol.Trim();
symbol = symbol.Replace(" ", "&stock=");
symbol = symbol.Replace(",", "&stock=");
string url = "https://www.google.com/ig/api?stock=" + (symbol);
return XDocument.Load(url);
}
public static List<Stock> getValidStocks(XDocument doc)
{
List<Stock> stocks = new List<Stock>();
foreach (var root in doc.Root.Elements("finance"))
{
try
{
if (root.Element("last") != null && root.Element(
"last").Attribute("data").Value != null && root.Element(
"last").Attribute("data").Value.Equals("0.00") == false)
{
stocks.Add(Stock_quotes.createNewStock(root));
}
else
{
System.Windows.Forms.MessageBox.Show(root.Element("symbol").Attribute(
"data").Value + " is not a valid stock symbol");
}
}
catch (Exception er)
{
}
}
return stocks;
}
public static Stock getThisStock(XDocument doc, string symbol, string lookUpField)
{
Stock stock = null;
foreach (var root in doc.Root.Elements("finance"))
{
if (root.Element(lookUpField).Attribute("data").Value.Equals(symbol))
{
return Stock_quotes.createNewStock(root);
}
}
return stock;
}
public static Stock createNewStock(XElement root)
{
Stock stock = new Stock();
stock.Symbol = root.Element("symbol").Attribute("data").Value;
DateTime eastern = Stock_quotes.UTCtoEastern(root.Element("current_date_utc").Attribute(
"data").Value, root.Element("current_time_utc").Attribute("data").Value);
stock.Date = eastern.ToShortDateString();
stock.Time = eastern.ToLongTimeString();
stock.Trade = root.Element("last").Attribute("data").Value;
stock.Chg = root.Element("change").Attribute("data").Value;
stock.Perc_chg = root.Element("perc_change").Attribute("data").Value;
stock.Volume = root.Element("volume").Attribute("data").Value;
stock.High = root.Element("high").Attribute("data").Value;
stock.Low = root.Element("low").Attribute("data").Value;
stock.Chart_url = "https://www.google.com" +
root.Element("chart_url").Attribute("data").Value;
stock.Market_cap = root.Element("market_cap").Attribute("data").Value;
stock.Exchange = root.Element("exchange").Attribute("data").Value;
stock.Currency = root.Element("currency").Attribute("data").Value;
stock.Company = root.Element("company").Attribute("data").Value;
stock.Y_close = root.Element("y_close").Attribute("data").Value;
return stock;
}
public static DateTime UTCtoEastern(string date, string time)
{
int year = Convert.ToInt32(date.Substring(0, 4));
int month = Convert.ToInt32(date.Substring(4, 2));
int day = Convert.ToInt32(date.Substring(6, 2));
int hours = Convert.ToInt32(time.Substring(0, 2));
int mins = Convert.ToInt32(time.Substring(2, 2));
int sec = Convert.ToInt32(time.Substring(4, 2));
DateTime utcTime = new DateTime(year, month, day, hours, mins, sec, DateTimeKind.Utc);
TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, easternZone);
}
}
}
Database.cs class: This class handles the internal data-structures that holds the individual stock and market data.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Windows.Forms;
using System.Xml.Linq;
namespace Quotes
{
class Database
{
private DataTable data_table;
private DataTable market_data_table;
public DataTable Data_table
{
get { return data_table; }
set { data_table = value; }
}
public DataTable Market_data_table
{
get { return market_data_table; }
set { market_data_table = value; }
}
private Timer updateTimer;
public Database(string tableName, string[] colNames)
{
data_table = new DataTable(tableName);
market_data_table = new DataTable("Market Table");
foreach (string s in colNames)
{
data_table.Columns.Add(s);
}
foreach (string s in colNames)
{
market_data_table.Columns.Add(s);
}
updateTimer = new Timer();
updateTimer.Interval = 5000;
updateTimer.Tick += new EventHandler(updateTimer_Tick);
updateTimer.Enabled = true;
}
void updateTimer_Tick(object sender, EventArgs e)
{
XDocument doc = Stock_quotes.FetchQuote(
this.getAllSymbolsFromTable(data_table) + Main_view.market_symbol_string);
this.addValuesToTheTable(data_table, doc);
this.addValuesToTheTable(market_data_table, doc);
}
public void addStockSymbolToTheTable(string symbol, DataTable table)
{
if (symbol != null && symbol.Length > 0)
{
XDocument xDoc = Stock_quotes.FetchQuote(symbol);
List<Stock> list = Stock_quotes.getValidStocks(xDoc);
foreach (Stock stock in list)
{
table.Rows.Add(stock.Symbol, stock.Company ,stock.Date, stock.Time,
stock.Y_close, stock.Trade, stock.Chg, stock.Perc_chg, stock.Volume,
stock.High, stock.Low, stock.Chart_url, stock.Market_cap,
stock.Exchange, stock.Currency);
}
}
else
{
throw new ArgumentException("Added symbol is not accepted as a valid input");
}
}
public string getAllSymbolsFromTable(DataTable table)
{
StringBuilder result = new StringBuilder();
foreach (DataRow row in table.Rows)
{
result.Append(row["Symbol"] + " ");
}
return result.ToString();
}
public void addValuesToTheTable(DataTable table, XDocument doc)
{
foreach (DataRow row in table.Rows)
{
Stock stock = Stock_quotes.getThisStock(doc, (string)row["Symbol"], "symbol");
row["Symbol"] = stock.Symbol;
row["Company"] = stock.Company;
row["Date"] = stock.Date;
row["Time"] = stock.Time;
row["Closed Yesterday"] = stock.Y_close;
row["Trade"] = stock.Trade;
row["Chg"] = stock.Chg;
row["%Chg"] = stock.Perc_chg;
row["Volume"] = stock.Volume;
row["High"] = stock.High;
row["Low"] = stock.Low;
row["Chart"] = stock.Chart_url;
row["Market Cap"] = stock.Market_cap;
row["Exchange"] = stock.Exchange;
row["Currency"] = stock.Currency;
}
}
public string getChartURL(string symbol, DataTable table)
{
string result = string.Empty;
if (table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
if (((string)row["Symbol"]).Equals(symbol))
{
result = (string)row["Chart"];
break;
}
}
return result;
}
else
{
return result;
}
}
public void saveSymbols()
{
Properties.Settings.Default.symbols = new System.Collections.Specialized.StringCollection();
foreach (DataRow row in data_table.Rows)
{
Properties.Settings.Default.symbols.Add((string)row["Symbol"]);
}
Properties.Settings.Default.Save();
}
public void loadSavedSymbols()
{
var list = Properties.Settings.Default.symbols;
if (list !=null && list.Count != 0)
{
StringBuilder symbols = new StringBuilder();
foreach (string s in list)
{
symbols.Append(s + " ");
}
try
{
this.addStockSymbolToTheTable(symbols.ToString(), data_table);
}
catch (ArgumentException ar)
{
MessageBox.Show(ar.Message);
}
}
}
}
}
Stock.cs: Simple Stock
object class that is used to transfer the information between XDocument
and DataTables.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Quotes
{
class Stock
{
private string symbol;
public string Symbol
{
get { return symbol; }
set { symbol = value; }
}
private string date;
public string Date
{
get { return date; }
set { date = value; }
}
private string time;
public string Time
{
get { return time; }
set { time = value; }
}
private string trade;
public string Trade
{
get { return trade; }
set { trade = value; }
}
private string chg;
public string Chg
{
get { return chg; }
set { chg = value; }
}
private string perc_chg;
public string Perc_chg
{
get { return perc_chg; }
set { perc_chg = value; }
}
private string volume;
public string Volume
{
get { return volume; }
set { volume = value; }
}
private string high;
public string High
{
get { return high; }
set { high = value; }
}
private string low;
public string Low
{
get { return low; }
set { low = value; }
}
private string chart_url;
public string Chart_url
{
get { return chart_url; }
set { chart_url = value; }
}
private string market_cap;
public string Market_cap
{
get { return market_cap; }
set { market_cap = value; }
}
private string exchange;
public string Exchange
{
get { return exchange; }
set { exchange = value; }
}
private string currency;
public string Currency
{
get { return currency; }
set { currency = value; }
}
private string company;
public string Company
{
get { return company; }
set { company = value; }
}
private string y_close;
public string Y_close
{
get { return y_close; }
set { y_close = value; }
}
public Stock()
{
symbol = string.Empty;
date = string.Empty;
time = string.Empty;
trade = string.Empty;
chg = string.Empty;
perc_chg = string.Empty;
volume = string.Empty;
high = string.Empty;
low = string.Empty;
chart_url = string.Empty;
market_cap = string.Empty;
exchange = string.Empty;
currency = string.Empty;
company = string.Empty;
y_close = string.Empty;
}
}
}
Points of Interest
I was able to reduce the number of requests that are being made to the Google APIs by combining the more stock quotes into a single URL and then parse the document locally. As, you can imagine, with more stock quotes entered and to fetch the data every 5 seconds, the number of queries can increase dramatically.
To-do
- Analyze the data coming from the stock market and perform some prediction of stock prices.
History