#!/usr/bin/perl
#
# Fetch, and update the stock prices for a list of ticker symbols using
# Yahoo's Financial/Quote server. Print the time stamp, stock's ticker
# symbol, and price in tsinvest(1) tab delimited format.
#
# Requirements:
#
#     1) Requires Perl 5.
#
#     2) Requires the Finance::YahooQuote package available from:
#
#            http://www.padz.net/~djpadz/YahooQuote/
#
#        Written and maintained by Dj Padzensky, djpadz@padz.net.
#
#        Note: to test YahooQuote with tsticker without installing:
#
#            1) cd ~
#            2) gunzip Finance-YahooQuote-0.17.tar.gz
#            3) tar xvf Finance-YahooQuote-0.17.tar
#            4) mkdir Finance
#            5) cp Finance-YahooQuote-0.17/YahooQuote.pm Finance
#            6) tsticker -i 10 lnux rhat corl cobt msft intc csco lu
#
#     3) Requires getopts.pl.
#
# Installation:
#
#    1) The first line of this file, "#!/usr/bin/perl", should be set
#    appropriately for the perl program.
#
#    2) The $interval variable sets the number of seconds between
#    updating ticker symbol prices, and is the only user configurable
#    variable-this value ABSOLUTELY MUST be sufficiently large to
#    permit all ticker symbol quotes to be fetched in an interval-the
#    fetch mechanism uses fork() to push the fetch process into the
#    background for accurate interval timing. The $interval variable
#    can be set from the command line with the -i option.
#
#    3) The $Finance::YahooQuote::TIMEOUT variable sets the timeout for
#    getting quotes from the Yahoo Financial server.
#
# As an example usage:
#
#     tsticker lnux rhat corl cobt msft intc csco lu | tsinvest -i -t -s -m0 -d5
#
# Options:
#
#    1) The -v option to tsticker is for printing all data fields
#    available from the Yahoo Financial server; the fields are
#    presented in tab delimited format, suitable for manipulation by
#    the Unix text utilities, cut(1), etc., with '#' field comments
#    in the descriptive header.
#
#    2) The -i option to tsticker sets the time interval between
#    updates of stock prices, and defaults to 240 seconds, (4 minutes.)
#
# Copyright (c) 2000, John Conover, All Rights Reserved
#
#     john@email.johncon.com
#
#     http://www.johncon.com/
#     http://www.johncon.com/ntropix/
#     http://www.johncon.com/ndustrix/
#     http://www.johncon.com/nformatix/
#

require "getopts.pl"; # for command line argument processing

use Finance::YahooQuote; # for getting stock price quotes from the Yahoo Financial server

select((select(STDOUT), $| = 1)[$[]); # the output of the tsticker program will probably be used as to pipe data to tsinvest, flush buffers immediately

$Finance::YahooQuote::TIMEOUT = 30; # timeout for response from Yahoo Financial server

$verbose_output = 0; # verbose output, 0 = output time stamp, ticker symbol, and price, 1 = output all data in @h
$interval = 240; # the number of seconds between updating ticker symbol prices, ie., the sampling interval, in seconds, defaults to four minutes
$retval = 1; # return/exit value, assume error command line error of no arguments

&Getopts('i:v'); # set any command line switches

if ($opt_i != "") # request for setting the number of seconds between updating ticker symbol prices?
{
    $interval = $opt_i; # the number of seconds between updating ticker symbol prices, ie., the sampling interval, in seconds
}

if ($opt_v != "") # request for verbose output?
{
    $verbose_output = 1; # verbose output, 0 = output time stamp, ticker symbol, and price, 1 = output all data in @h
}

#
# The array, h, is the description of the data that the Yahoo
# Financial server maintains:
#
#    0 Symbol
#    1 Company Name
#    2 Last Price
#    3 Last Trade Date
#    4 Last Trade Time
#    5 Change
#    6 Percent Change
#    7 Volume
#    8 Average Daily Vol
#    9 Bid
#    10 Ask
#    11 Previous Close
#    12 Today's Open
#    13 Day's Range
#    14 52-Week Range
#    15 Earnings per Share
#    16 P/E Ratio
#    17 Dividend Pay Date
#    18 Dividend per Share
#    19 Dividend Yield
#    20 Market Capitalization
#    21 Stock Exchange
#

@h =
(
    "Symbol","Name","Last","Trade Date","Trade Time","Change","% Change",
    "Volume","Avg. Daily Volume","Bid","Ask","Prev. Close","Open",
    "Day's Range","52-Week Range","EPS","P/E Ratio","Div. Pay Rate",
    "Div/Share","Div. Yield","Mkt. Cap","Exchange"
);

if($ARGV[0]) # at least one ticker symbol supplied?
{
    $retval = 0; # return/exit value, assume no errors

    if ($verbose_output == 1) # verbose output, 0 = output time stamp, ticker symbol, and price, 1 = output all data in @h
    {
        print("#yr:mo:day:hr:min:sec\t#$h[0]\t#$h[1]\t#$h[2]\t#$h[3]\t#$h[4]\t#$h[5]\t#$h[6]\t#$h[7]\t#$h[8]\t#$h[9]\t#$h[10]\t#$h[11]\t#$h[12]\t#$h[13]\t#$h[14]\t#$h[15]\t#$h[16]\t#$h[017]\t#$h[18]\t#$h[19]\t#$h[20]\t#$h[21]\n"); # print the header as a comment
    }

    while(1) # do forever; this is the loop that continually fetches the stock ticker symbol's prices every $interval seconds
    {

        unless (fork) # fork to get first child
        {

            unless (fork) # fork again to avoid zombies-there will be no parent process for this second child, which will fetch the stock ticker symbol's prices
            {
                sleep 1 until getppid == 1; # wait for the second child process, which will fetch the ticker symbol's prices, to start

                ($sec,$min,$hour,$mday,$mon,$year,$wday,$yyday,$isdst) = localtime(time); # get the time stamp of the interval
                $year = $year + 1900; # for Y2K compliance; $year is the same as struct tm in time.h
                $mon = $mon + 1; # months are numbered 0 - 11, make it 1 - 12

                @q = getquote(@ARGV); # get the ticker symbol's prices for this interval

                foreach $a (@q) # for each ticker symbol record
                {

                    if ($verbose_output == 0) # verbose output, 0 = output time stamp, ticker symbol, and price, 1 = output all data in @h
                    {
                        print ("$year:$mon:$mday:$hour:$min:$sec\t$$a[0]\t$$a[2]\n"); # print the ticker symbol's data
                    }

                    else
                    {
                        print("$year:$mon:$mday:$hour:$min:$sec\t$$a[0]\t$$a[1]\t$$a[2]\t$$a[3]\t$$a[4]\t$$a[5]\t$$a[6]\t$$a[7]\t$$a[8]\t$$a[9]\t$$a[10]\t$$a[11]\t$$a[12]\t$$a[13]\t$$a[14]\t$$a[15]\t$$a[16]\t$$a[017]\t$$a[18]\t$$a[19]\t$$a[20]\t$$a[21]\n"); # print the ticker symbol's data
                    }

                }

                exit (0); # exit the second child with no errors
            }

            exit (0); # exit the first child with no errors
        }

        wait; # fork to first child arrives here quickly
        sleep($interval); # wait for the next sampling interval
    }

}

if ($retval == 1) # command line error of no ticker symbols supplied?
{
    print("Usage: tsticker [-i int] [-v] symbol_1 [symbol_2 ... symbol_n]\n"); # yes, print command line help
    print("        -i int, set the interval between updates to int many seconds\n");
    print("        -v, verbose output, Time Stamp, $h[0], $h[1], $h[2], $h[3],\n                            $h[4], $h[5], $h[6], $h[7],\n                            $h[8], $h[9], $h[10], $h[11], $h[12],\n                            $h[13], $h[14], $h[15], $h[16], $h[017],\n                            $h[18], $h[19], $h[20], $h[21]\n");
}

exit($retval); # exit with any error, 0 means no error
