########################################### package CachedQuote; # Cache stock closing prices # Mike Schilli, 2007 (m@perlmeister.com) ########################################### use strict; use warnings; use Cache::Historical; use Log::Log4perl qw(:easy); use Finance::QuoteHist::Yahoo; ########################################### sub new { ########################################### my($class, %options) = @_; my $self = { file => "/tmp/cached-quote.dat", %options, }; $self->{cache} = Cache::Historical->new( sqlite_file => $self->{file}); bless $self, $class; } ########################################### sub quote { ########################################### my($self, $date, $key) = @_; my $quote = $self->{cache}->get( $date, $key); return $quote if defined $quote; $self->quote_refresh( $date, $key ); return $self->{cache}->get_interpolated( $date, $key); } ########################################### sub quote_refresh { ########################################### my($self, $date, $symbol) = @_; my($from, $to) = $self->{cache}->time_range($symbol); my $upd = $self->{cache}-> since_last_update($symbol); # Date available, no refresh if(defined $to and defined $from and $date <= $to and $date >= $from) { DEBUG "Date within, no refresh"; return 1; } if(defined $date and defined $to and defined $upd and $date > $to and $upd->delta_days < 1) { DEBUG "Date ($date) above cached", " range ($from-$to), but cache ", "is up-to-date."; return 1; } my $start = $date->clone->subtract( years => 1 ); if(defined $start and defined $from and $start > $from and $to > $start) { # no need to refresh old data $start = $to; } $self->quotes_fetch( $start, DateTime->today(), $symbol); } ########################################### sub quotes_fetch { ########################################### my($self, $start, $end, $symbol) = @_; DEBUG "Refreshing $symbol ", "($start - $end)"; my $q = Finance::QuoteHist::Yahoo->new( symbols => [$symbol], start_date => date_format($start), end_date => date_format($end), ); foreach my $row ($q->quotes()) { my($symbol, $date, $open, $high, $low, $close, $volume) = @$row; $self->{cache}->set( dt_parse($date), $symbol, $close ); } } ########################################### sub date_format { ########################################### my($dt) = @_; return $dt->strftime("%m/%d/%Y"); } ########################################### sub dt_parse { ########################################### my($string) = @_; my $fmt = DateTime::Format::Strptime->new( pattern => "%Y/%m/%d"); $fmt->parse_datetime($string); } 1;