September 15, 2011
11:28pm

Quick Stock Quote on Page Refresh

I thought I would throw a post up, demonstrating a "quick and easy" use case of the Stock API module, and simultaneously, give this site's GeSHi filter a quick test run.

A few days ago I encountered a site requirement asking for a simple informative stock quote block on a Drupal 6.x installation. The format required was something like:
NFLX 208.75 -1.30 Sep 13 7:30 EST
My initial thought was to enable the Stock module and simply configure and theme the built in block. The stock module will allow the management of multiple stocks on a per-user basis, and provides a block that displays this information in a table. There is no doubt that stock module is useful but in this instance it is overkill. All we are after is a simple quote from a single unchanging stock symbol displayed in a single line of text.

So let's take a short step back to the stock API module. There is really one "go to" function when grabbing a quote from the stock api and that is stockapi_load($symbol). This function is defined like so:

function stockapi_load($symbol) {
  if (!empty($symbol)) {
    $stock = db_fetch_array(db_query("SELECT * FROM {stockapi} WHERE symbol = '%s'", $symbol));
    if (!$stock) {
      $stock = stockapi_fetch($symbol);
      if ($stock) {
        if ($stock[8] != 'N/A') {
          // Date is 'N/A' means an invalid stock symbol
          stockapi_save($stock);

          // Load again so it can show for the first time
          $stock = stockapi_load($symbol);
        }
      }
    }
  }
  return $stock;
}

As you can see the stock API module is wanting to store stock information in the database to be updated on cron runs. It does this as a type of database caching. This way if a site is pulling information on many stocks, potentially every page load, compounded by many users the API can retrieve the information with a simple query to the local database. Not only is this method much faster than hitting the third party service (yahoo) every time, it is able to serve saved information (especially handy if that service happens to be down or a connection cannot be established). This feature along with returning a nice keyed array of stock information is reason enough to love this function. However, I think we could go a bit simpler still. We only need information on one stock site wide, and in this instance that information should be as current as possible. Furthermore, I would like to avoid setting up a cron job to run every half hour just to keep stock information current. Rather, we look further into the Stock API and make a more basal call to stockapi_fetch($symbol);. This function is a direct call to the 3rd party service, in this case yahoo:

/**
 * Fetch the stock info for a given ticker symbol from Yahoo!
 */

function stockapi_fetch($symbol) {
  if (empty($symbol)) {
    return FALSE;
  }

  $fields = 'snl1c1ohgvd1t1';
  /*
    "s"  => "Symbol",
    "n"  => "Name",
    "l1" => "Last",
    "c1" => "Change",
    "o"  => "Opening",
    "h"  => "High",
    "g"  => "Low",
    "v"  => "Volume",
    "d1" => "Date",
    "t1" => "Time"
   */


  $host = 'http://download.finance.yahoo.com';
  $url = $host .'/d/quotes.csv?s='. urlencode($symbol) .'&f='.$fields.'&e=.csv';

  $result = drupal_http_request($url);
  ...

This ensures fresh data, without a cron run to update the database. This function is not quite as nice as the stockapi_load() function in that the array it returns is not keyed. The commented section above (from the module) comes to the rescue. It is in the same sequence as the array returned so it can be used as a reference. So to return something like our requirement example above:
NFLX 208.75 -1.30 Sep 13 7:30 EST
We would write something like:

$output = '';
$my_stock = stockapi_fetch('NFLX');
if($my_stock) {
  $output .= '<span class="my-stock-quote">'.$my_stock[0].' '.$my_stock[2].' '.$my_stock[3].' '.$my_stock[8].' '.$my_stock[9].' EST</span>';
}
return $output;

I wound up throwing a variant of the snippet above into a block created in my module, allowing me to then jump to panels and throw the output wherever I wanted.

Now I know... the example is somewhat contrived. There is no "secret" here. Everything outlined in this post would quickly be revealed by only a few minutes of looking through the Stock API module. I wanted to demonstrate that by making a simple call to the Stock API I was able to save significant time in themeing, as well as gain flexibility in function. The alternative would be wrangling the output of the Stock module's block, and making concessions for it's functional behavior. It is easy to fall into the thinking that Drupal site building is just finding a module that is a closest fit and wrestling with what it gives us. Indeed sometimes that may be the case (Organic Groups comes to mind), but to follow that line of thinking blindly is most certainly a mistake.

Ryan Oles theoleschool.com