#!/usr/local/bin/perl # # Name: # quote -- a quick and dirty way to get stock quotes # from the command line # # Synopsis # quote [[symbol] ... ] # # Options # run it, or don't # # Requirements # perl (tested on 5.005_03; but should run with anything) # wget # web connectivity to finance.yahoo.com # # License # Don't worry, be happy. Now go away. # http://xdroop.dhs.com/code/license.html # # History: # 0005.20 by xdroop # - initial version used to track stock options # inspired when JWZ's script fails to work for me. # 0101.17 by xdroop # - removed option tracking, made quote grabber more # generic, improved currency exchange, changed # name, added comments # 0104.10 by xdroop # - decimalization -- removed fractional support. The # script should work properly with all US stocks -- # but I have not done exaustive testing. # Values you might want to change: # I live in Canada, and Yahoo! quotes in US Dollars. The script can # convert to other currencies. If you just want US Dollars, comment # out these two settings. Poke around on finance.yahoo.com to figure # out what your currency symbol is -- I only looked up UK pounds as # a test case, and it only took about two minutes. # This is the YaHoo! symbol for your currency. $CURRENCY='^XCD'; # This is the symbol you use when talking about your currency. $EXCHANGE_SYMBOL='$'; # Example settings for UK Pounds #$CURRENCY='^XBP'; #$EXCHANGE_SYMBOL='#'; # This is the number of digits to display after the decimal point. # For dollars, I only display two digits (ie, cents). Note that this # applies to BOTH the $USD quote AND the local currency quote. $DIGITS=2; # This next bit figures out what the name of the script is. $BASENAME = $0; $BASENAME =~ s|\\|/|g; if ($BASENAME =~ m|(.*/)(.*)|) { $BASENAME = $2; } # Grab the list of symbols to quote. while($#ARGV > -1) { $a=shift @ARGV; push(@TICKER,$a); } # Conveniece -- if nothing supplied, use harmless defaults @TICKER=( "RHAT", "CORL") if (!@TICKER); # figure out what time it is and clean up some of the numbers. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year=$year+1900; $mon="0$mon" if ($mon++ < 10); $mday="0$mday" if ($mday < 10); $min="0$min" if ($min < 10); # get the quotes and display them foreach $ticker (@TICKER) { # quote the stock $stock = "e("$ticker"); # do the exchange if necessary $local = "($EXCHANGE_SYMBOL".&trim(&xchng($stock)).")" if ($CURRENCY); # trim off any excess digits on the stock price $stock = &trim($stock); # tell the masses print "$year/$mon/$mday $hour:$min $ticker \$$stock$local\n"; } # Done! # this subroutine trims of excess digits from the passed number. # I find that carrying around more than two decimal places is # a waste of screenspace -- your milage may differ. sub trim { local ($val); $val = shift @_; return (int($val * 10**$DIGITS)/10**$DIGITS); } # this subroutine queries YaHoo! for the information, extracts it from # the web page, converts it from fractions to a decimalized value, and # returns it. sub quote { # $CMD is the string we'll use to build the command to shell out # $store is used as a holding area for the returned web page local ($CMD,$store); # $symbol is the ticker symbol in the subroutine call # $price is the quote # $sub is the bottom part of any fraction attached to the price # $sup is the top part of any fraction attached to the price local ($symbol,$price,$sub,$sup); $symbol=shift @_; $store=""; # $Q is a holder for the " symbol -- makes things a bit easier $Q="\""; # build the command $CMD="wget -q -O - $Q"."http://finance.yahoo.com/q?s=$symbol"."&d=t$Q"; # shell the command and store the result open(IN,"$CMD |") or &die("can't shell:$!"); while () { # warning: cheap-ass way to remove \n from each line follows chop; $store="$store$_"; # why do this? because yahoo fiddles with the returned html periodically # by changing the location of the \n characters in the file. the easiest # way to deal with this is just to strip out all \n characters. before I # did this, the script needed fiddling every couple of weeks -- I have been # running it like this now for six months. } close IN; # find the quote $store=~m/Last Trade(.*)<\/td/; $store=$1; $store=~m/\(.*)\<\/b\>/; $price=$1; # # deal with any fraction # $price=~m/\([0-9]*)/; # $major=$1; # $sub=$1 if($price=~m/\([0-9]*)\<\/sub\>/); # $sup=$1 if($price=~m/\([0-9]*)\<\/sup\>/); # $sub=1 if (!$sub); # $price=$major+($sup/$sub); # return it return $price; } # This subroutine is pretty much the same as quote, except that # since it is extracting an exchange value (always a decimal value) # it lacks the fractional-handling cruft. sub rate { local ($CMD,$store); local ($price,$sub,$sup); $CMD="wget -q -O - $Q"."http://finance.yahoo.com/q?s=$CURRENCY"."&d=t$Q"; open(IN,"$CMD |") or &die("can't shell:$!"); while () { chop; $store="$store$_"; } close IN; $store=~m/Last Trade(.*)<\/td/; $price=$1; $price=~m/\(.*)\<\/b\>/; $price=$1; return $price; } # take the supplied number, fetch (if neccessary) the exchange rate, # and return the supplied number in the new currency. sub xchng { local ($in); $in=shift @_; if (!$EXCHANGE_RATE) { $EXCHANGE_RATE=&rate(); # looks stupid, but protects from div/0 errors when rate() failes. $EXCHANGE_RATE=1/$EXCHANGE_RATE if($EXCHANGE_RATE); } return $in*$EXCHANGE_RATE; } # the rest of these subroutines are part of a template I use for all # my perl scripts so that I don't constantly have to reinvent the # wheel. &die and &warn are useful, and logging is useful enough # often enough to carry it around here. these are pretty simple # subroutines, so comments effectively end here. :) # # Open our log file. sub openLog { # $LOGPATH = " some log path like c:/tmp "; if (!$LOGPATH) { &die("Set logpath in script"); } if (! open (LOG,">$LOGPATH$BASENAME.$$.log")) { &die("log $LOGPATH$BASENAME.$$.log"); } } # # Add an item to the open log sub logItem { local ($gripe); $gripe = pop(@_); print LOG "$gripe\n" if ($LOGPATH); } # # A (braindead) undertaker. sub die { local ($gripe); $gripe = pop(@_); print STDERR "$BASENAME:fatal:$gripe\n"; &logItem($gripe); exit 1; } # # A (braindead) friend for our undertaker. sub warn { local ($gripe); $gripe = pop(@_); print STDERR "$BASENAME:$gripe\n"; &logItem($gripe); }