Created
March 10, 2013 15:15
-
-
Save ufizo/5128956 to your computer and use it in GitHub Desktop.
Queries Google Finance to get current stock rates for NSE and NYSE.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=head1 NAME | |
stock4.pl No fancy name yet | |
=head1 DESCRIPTION | |
Queries Google Finance to get current stock rates for NSE and NYSE. | |
=head1 DEPENDENCIES | |
Parallel::ForkManager | |
HTTP::Tiny | |
Time::HiRes | |
=head1 BUGS AND LIMITATIONS | |
We are basically looking at running this 24x7, with parallel processes. | |
Shared hosting servers may not like this. | |
Timeout should be tuned to avoid overlap of jobs. | |
May need to explicitly specify mysql_socket (Needed with XAMPP on ArchLinux) | |
mysql_socket=/opt/lampp/var/mysql/mysql.sock:$dbhost | |
Shared hosting also limits MAX_USER_CONNECTIONS to 15. With forked process, it is very easy to hit that limit very quickly, specially even if the script is closed, the mySQL sometimes might still stay open. Workaround can be killing the perl script externally, or dumping all the data into a matrix, and updating the database with just one mySQL connection, instead of creating separate connections for each forked process. | |
=head1 AUTHOR | |
Arpit Singh `me@arpitsingh.in` | |
=head1 CREDITS | |
Abhay Rana `capt.n3m0@gmail.com` (For his update script in php) | |
=cut | |
use warnings; | |
use lib 'Parallel'; | |
use DBI; | |
use Parallel::ForkManager; | |
use HTTP::Tiny qw(); | |
use Time::HiRes qw(gettimeofday); | |
# Script VARS | |
my $n = 90; # Numbers of symbols to query to google at once. | |
# Google allows upto 100. | |
my $threads = 15; # Number of parallel connections. | |
# More may be faster, but will also use more resources. | |
my $timeout = 55; # Timeout in seconds. Default 55, to allow 1 min CRON jobs. | |
# DB VARS | |
my $dbname = ""; | |
my $dbhost = "localhost"; | |
my $dbuser = ""; | |
my $dbpass = ""; | |
my $gUrl = 'http://www.google.com/finance/info?infotype=infoquoteall&q='; | |
my $market_open = 1; # To terminate the script immediately if markets are closed. | |
sub doItdoIt(){ | |
# Set the exchange | |
my @t = gmtime(time); | |
# Dirty convert to IST | |
my $hr = $t[2]+5; if ($hr > 23) {$hr = $hr - 24;} | |
my $min = $t[1]+30; if ($min > 59) {$min = $min - 60; $hr++;} | |
my $nyse = 0, my $nse = 0; | |
my $exchange, my @links; | |
if ( ($hr > 9)&&($hr < 15)|| | |
($hr == 9)&&($min >= 15)|| | |
($hr == 15)&&($min <= 30) | |
){$nse = 1;} | |
if ( ($hr >= 20)&&($hr<=24)|| | |
($hr >= 0)&&($hr<2)|| | |
($hr == 2)&&($min <= 30) | |
){$nyse = 1;} | |
if($nyse == 0 && $nse == 0){ | |
print "$hr:$min, "; | |
print "MC"; | |
$market_open = 0; | |
} | |
else{ | |
if($nyse){$exchange = 'NYSE';} | |
if($nse){$exchange = 'NSE';} | |
} | |
# Prepare the list to update stocks for | |
if ($exchange){ | |
# Get total symbols, and divide into chunks | |
my $dbh = DBI->connect("DBI:mysql:$dbname;$dbhost","$dbuser","$dbpass") || die "Database connection not made: $DBI::errstr"; | |
my $sth = $dbh->prepare("SELECT COUNT(*) FROM stocks WHERE exchange='$exchange'"); | |
$sth->execute(); | |
my $data = $sth->fetchall_arrayref(); | |
$sth->finish(); | |
my $total_syms = $$data[0][0]; | |
my $n_parts = int($total_syms/$n); | |
# Make the list of google links | |
my $start, my $query, my @data, my $gquery, my $url; | |
for (0..$n_parts){ | |
$start = $_*$n; | |
$query = "SELECT symbol FROM stocks WHERE exchange='$exchange' LIMIT $start,$n"; | |
@data = @{$dbh->selectcol_arrayref($query)}; | |
$gquery = join(',',@data); | |
$url = $gUrl.$gquery; | |
#print $url."\n"; | |
push (@links,$url); | |
#print $_*$n." to ".(($_+1)*$n-1)."\n"; | |
} | |
$dbh->disconnect or warn "Disconnection error: $DBI::errstr\n"; | |
undef($dbh); | |
} | |
# Fetch and update stocks | |
my $pm = Parallel::ForkManager->new($threads); | |
foreach my $link (@links) { | |
$pm->start and next; # do the fork | |
refresh_stocks($link); | |
$pm->finish; # do the exit in the child process | |
} | |
$pm->wait_all_children; | |
} | |
sub refresh_stocks(){ | |
#print "Getting @_\n"; | |
my $newstocks = HTTP::Tiny->new->get(@_); | |
if ($newstocks->{success}) { | |
my $json_text = $newstocks->{content}; $json_text =~ s|\\x26||; | |
my @jsons = ( $json_text =~ m|({.*?})|sig ); | |
for my $js (@jsons){ | |
my $e1, my $t1, my $symbol, my $current, my $change, my $cp, my $mc, my $opening, my $high, my $low; | |
if ($js =~ m|"e"\W\:\W"(.*?)"|){$e1 = $1;} | |
if ($js =~ m|"t"\W\:\W"(.*?)"|){$t1 = $1;} | |
$symbol = $e1.':'.$t1; | |
if ($js =~ m|"l"\W\:\W"(.*?)"|){$current = $1; $current =~ s|\,||;} | |
if ($js =~ m|"c"\W\:\W"(.*?)"|){$change = $1; $change =~ s|\,||;} | |
if ($js =~ m|"cp"\W\:\W"(.*?)"|){$cp = $1; $cp =~ s|\,||;} | |
if ($js =~ m|"mc"\W\:\W"(.*?)"|){$mc = $1; $mc =~ s|\,||;} | |
if ($js =~ m|"op"\W\:\W"(.*?)"|){$opening = $1; $opening =~ s|\,||;} | |
if ($js =~ m|"hi"\W\:\W"(.*?)"|){$high = $1; $high =~ s|\,||;} | |
if ($js =~ m|"lo"\W\:\W"(.*?)"|){$low = $1; $low =~ s|\,||;} | |
#print "$symbol is at $current \n"; | |
my $dbh = DBI->connect("DBI:mysql:$dbname;$dbhost","$dbuser","$dbpass") || die "Database connection not made: $DBI::errstr"; | |
my $sth = $dbh->prepare("UPDATE stocks SET current='$current', `change`='$change', change_percent = '$cp',market_cap='$mc',opening='$opening',high='$high',low='$low' WHERE symbol='$symbol'"); | |
$sth->execute(); | |
$sth->finish(); | |
$dbh->disconnect or warn "Disconnection error: $DBI::errstr\n"; | |
undef($dbh); | |
} | |
} | |
} | |
my $t1, my $t2; | |
$SIG{ALRM} = sub { die "Time out\n" }; | |
eval { | |
alarm($timeout); | |
while($market_open){ | |
$t1 = [Time::HiRes::gettimeofday()]; | |
doItdoIt(); | |
# Current prices of stocks have been updated, Update networths, and run the trigger job. | |
HTTP::Tiny->new->get('http://cognistreet.cognizance.org.in/update/networth'); | |
HTTP::Tiny->new->get('http://cognistreet.cognizance.org.in/update/triggers'); | |
$t2 = Time::HiRes::tv_interval($t1); | |
print "$t2\n"; | |
} | |
alarm(0); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment