Saturday, December 20, 2008

Using Perl's Finance::Quote to retrieve stock information from the Web

Using Perl's Finance::Quote to retrieve stock information from the Web

Looking for a way to add stock quotes to your web site? With a little Perl scripting and the Finance::Quote module you can script this up in no time.
Perl's CPAN contains many gems, and one of them is undoubtedly the Finance::Quote module. Designed to ease the task of retrieving stock prices off the Internet, the module makes it simple for developers to integrate stock-tracking mechanisms into their applications and thus create customised reports on financial market movements over differing time periods or investment portfolios.
If you're a Perl developer looking to build a Web-based stock ticker or portfolio tracker for equity markets and/or mutual funds, keep reading, because this article will teach you how to:
● Retrieve stock prices and market data for stocks traded on the major exchanges, such as the NYSE and NASDAQ.
● Retrieve mutual fund NAVs from a variety of online sources.
● Create a simple MySQL-based portfolio tracker to display the current value of an investment basket.InstallationThe Finance::Quote package was originally part of the GnuCash application until it was broken out as a separate project. The package consists of a base module that provides the underlying query methods and a series of supplementary modules that take care of parsing third-party Web sites for the required data. A variety of data sources are supported, and developers can easily add new sources as needed.
The package is licensed under the GPL and is currently maintained by Paul Fenwick. You can download it from the SourceForge site or from the CPAN page. The download archive provides detailed installation instructions, but by far the simplest way to install it is to use the CPAN shell:shell> perl -MCPAN -e shellcpan> install Finance::Quote
Finance::Quote depends on three other packages: LWP::UserAgent, HTTP::Request::Common, and HTML::TableExtractor. If yours is a relatively recent Perl installation, you'll probably already have the first two; you can download the third from this site. If you use the CPAN shell, dependencies will be automatically downloaded for you (unless you've configured your CPAN shell to ignore dependencies, in which case you'll need to download and install these three packages manually).
One important note before you proceed: The data retrieved by the Finance::Quote module is governed by the terms and conditions of the source from whence it was retrieved. So before you go out there and start selling your code to the highest bidder, take a close look at the license terms and data usage restrictions to ensure that you're in compliance. The examples here are purely illustrative. Okay, so let's start retrieving stock data.
Here's a simple example of using the Perl Finance::Quote package to retrieve the price of a fictitious stock (symbol XYZ) from the NYSE:#!/usr/bin/perl# import moduleuse Finance::Quote;# create objectmy $q = Finance::Quote->new();# retrieve stock quotemy %data = $q->fetch('nyse', 'XYZ');# print priceprint "The current price of XYZ on the NYSE is " . $data{'XYZ', 'price'};
Since Finance::Quote provides an object-oriented interface, the first step is to create an object with new(). This object exposes a number of methods, including the fetch() method. This method accepts two arguments: the name of the data source and the stock symbol.
The data returned by fetch() is structured as a two-dimensional hash: The first dimension is the stock symbol, and the second is a label corresponding to the attribute received. The hash returned by fetch() includes a number of different labels, some of which are listed below (a complete list is available in the online documentation):
nameâ€"official namepriceâ€"last tradehighâ€"highest price todaylowâ€"lowest price todayvolumeâ€"number of shares traded today year_rangeâ€"52-week price range peâ€"current P/E ratio navâ€"net asset value for mutual fund exchangeâ€"the source exchange successâ€"Boolean indicating if data retrieval was successful errormsgâ€"error string if data retrieval was unsuccessful
So, to retrieve a particular data fragment from the two-dimensional hash, you'd use the following notation:$hash(symbol, label)
This is clearly shown in the last line of the example above, which uses this notation to retrieve the price of XYZ stock.
Tip: You can have fetch() retrieve more than one quote at a time by specifying a comma-separated list (or an array) of stock symbols as the second input argument.
Okay, so that's stock prices. Now let's look at how to retrieve the NAV for a mutual fund.
The net asset value, or NAV, of a mutual fund is its assets minus its liabilitiesâ€"in essence, the net worth of the mutual fund. To retrieve a mutual fund's NAV using the Perl Finance::Quote package, simply use the fetch() method with an appropriate data source and the mutual fund symbol:#!/usr/bin/perl# import moduleuse Finance::Quote;# create objectmy $q = Finance::Quote->new();# set mutual fund code# each fund has a unique codemy $fcode = '1#761';# retrieve mutual fund NAVmy %quotes = $q->fetch('indiamutual', $fcode);# print detailsif ($quotes{$fcode, 'success'}){print "NAV of " . $quotes{$fcode, 'name'} . " is " . $quotes{$fcode, 'nav'}; } else{die "Data retrieval error";}
If Finance::Quote is unable to retrieve the data successfully, it sets the success key of the return hash to 0 (false) and places an error message in the errormsg key. By checking for this, you can implement basic error-handling routines into your Perl script, as I've done above.
Note that in many countries (for example, Canada and India), mutual funds do not have a unique global symbol. In such cases, you'll need to manually locate the mutual fund code assigned by the country's fund watchdog and use that in your Perl script. Links and instructions on how to accomplish this are included in the specific Finance::Quote submodule's documentation.
Now let's take a look at how your Perl program can retrieve the available data sources.

Since the Perl Finance::Quote package uses data from a variety of sources, a natural question (especially if you're letting the user choose the exchange or region) is: How do you find out which data sources are supported? The answer: with the sources() method. #!/usr/bin/perl# import moduleuse Finance::Quote;# create objectmy $q = Finance::Quote->new();# retrieve sourcesmy @sources = $q->sources();# print sourcesprint "Available sources are: ";foreach $s (@sources){print "$s ", ;}
Here's an example of the output:Available sources are: vwd nyse fidelity_direct canadamutual asxaustralia usa troweprice amfiindia tdwaterhouse fool nasdaqbmonesbittburns trustnet ftportfolios_direct yahoo_asia dutchuk_unit_trusts canada troweprice_direct tiaacref yahoo ftportfolios fidelity fundlibrary aex dwsfunds yahoo_australia yahoo_europe vanguard indiamutual asia europe
Some of the more important sources in this list are Yahoo! Finance (in all major world regions), the NYSE and NASDAQ, the Australian Stock Exchange, and data from Canadian, European, and Asian markets. In some cases, most notably Asia, you need to suffix the stock symbol with an exchange identifier as well.
You might notice an overlap among the various source names. This is because some are specific to exchanges, while others are more general country databases. For example, use "usa" for U.S. market data, but "nyse" or "nasdaq" to limit the query to the respective exchanges only.
Some of the more important sources in this list are Yahoo! Finance (in all major world regions), the NYSE and NASDAQ, the Australian Stock Exchange, and data from Canadian, European, and Asian markets. In some cases, most notably xxxx, you need to suffix the stock symbol with an exchange identifier as well.
You might notice an overlap among the various source names. This is because some are specific to exchanges, while others are more general country databases. For example, use "usa" for U.S. market data, but "nyse" or "nasdaq" to limit the query to the respective exchanges only.
With a little imagination, you can use the Perl Finance::Quote package for some very interesting financial applications. The one I like the most is an online portfolio tracker, which gives you real-time data on whether your investments are making money or not. Such an application would store information on your past stock purchasesâ€"quantity and purchase priceâ€"and retrieve, on demand or at regular intervals, the latest prices off the Web for quick comparison and evaluation.
To develop this application, I'll assume the existence of a MySQL table containing stock symbols, the quantities purchased of each, and the purchase prices of each. It looks something like this:
This example table holds portfolio information for a single user. Adding multiuser capability is as easy as adding a new owner field to each record and filtering by owner in the SQL query.
The next task is to develop a CGI script to communicate with MySQL, retrieve the portfolio data, and combine it with the latest prices from the Web. The original and current prices can then be compared to offer some insight into whether your portfolio is appreciating or depreciating, in both absolute and percentage terms (see Listing A below)
Code Listing A#!/usr/bin/perl # alter above to reflect the path to your Perl interpreter
# import modules
use Finance::Quote;
use DBI();
# print HTML headers
print "Content-type:text/html\n\n";
print "<head></head>";
# print table
print "<table border="1" cellpadding="5">";
print "<tr><td>Symbol</td><td>Quantity</td><td>Purchase
price</td><td>Current price</td> <td>Purchase value</td><td>Current value</td><td>Absolute gain/loss</td><td>% gain/loss</td></tr>\n";
# create Finance::Quote object
my $q = Finance::Quote->new();
# create DBI object
# connect
my $dbh = DBI->connect("DBI:mysql:database=stocks;host=localhost", "user",
"pass") or die("Error in connection");
# execute query
my $sth = $dbh->prepare("SELECT symbol, qty, pprice FROM portfolio"); $sth->execute();
# get everything as an array of arrays
my $result = $sth->fetchall_arrayref();
# iterate over record set
foreach $record (@$result)
{
# assign fields to variables
$symbol = $record->[0];
$qty = $record->[1];
$pprice = $record->[2];
# retrieve stock quote
my %data = $q->fetch('usa', $symbol);
$cprice = $data{$symbol, 'price'};
# calculate value
$pvalue = $pprice * $qty;
$cvalue = $cprice * $qty;
# calculate gain/loss in value
$diff = $cvalue - $pvalue;
$diff_percent = sprintf("%0.2f", $diff * 100/$pvalue);
# progressively calculate totals
$pvalue_total = $pvalue_total + $pvalue;
$cvalue_total = $cvalue_total + $cvalue;
# print table
print "<tr><td>$symbol</td><td>$qty</td><td>$pprice</td><td>$cprice</td>
<td>$pvalue</td><td>$cvalue</td><td>$diff</td><td>$diff_percent</td></tr>\n";
}
# calculate totals
$diff_total = $cvalue_total - $pvalue_total; $diff_total_percent = sprintf("%.2f", $diff_total * 100/$pvalue_total);
# clean up
$sth->finish();
$dbh->disconnect();
# print final row of totals
print "<tr><td></td><td></td><td></td><td></td>
<td><b>$pvalue_total</b></td><td><b>$cvalue_total</b></td>
<td><b>$diff_total</b></td><td><b>$diff_total_percent</b></td></tr>";
# close table
print "</table>";

After sending the standard HTML headers, the script in Listing A uses the Perl DBI to open a connection to the MySQL database and query it for the list of stocks, quantities, and original prices. Then, as it iterates over the record result returned by the query, the Finance::Quote module is used to retrieve the latest price of each stock. The two prices are then compared, and the change in absolute and percentage terms is recorded. Once all the records have been retrieved, the information generated is displayed in a neat table, together with summary totals.
Of course, this is just one of the myriad applications of Finance::Quote. With power like this at your fingertips, there's literally a whole world at your disposal. So go on...give your creativity full rein!

No comments: