App-Chart

 view release on metacpan or  search on metacpan

lib/App/Chart/Suffix/TSP.pm  view on Meta::CPAN

  my $lo_tdate = App::Chart::Download::start_tdate_for_update (@$symbol_list);

  if ($lo_tdate > $avail_tdate) {
    App::Chart::Download::verbose_message
        (__('TSP nothing further expected yet'));
    return;
  }

  # ask for extra in case available_tdate() is a bit short
  $avail_tdate += 2;

  my $resp = get_chunk ($symbol_list, $lo_tdate, $avail_tdate);
  my $h = parse ($resp);
  $h->{'last_download'} = 1;
  App::Chart::Download::write_daily_group ($h);
}

sub backto {
  my ($symbol_list, $backto_tdate) = @_;
  my $hi_tdate = App::Chart::Download::start_tdate_for_backto (@$symbol_list);
  my $resp = get_chunk ($backto_tdate, $hi_tdate);
  my $h = parse ($resp);
  App::Chart::Download::write_daily_group ($h);
}

# return a HTTP::Response for data $lo_tdate to $hi_tdate, inclusive
sub get_chunk {
  my ($symbol_list, $lo_tdate, $hi_tdate) = @_;

  App::Chart::Download::status
      (__x('TSP data {date_range}',
           date_range =>
           App::Chart::Download::tdate_range_string ($lo_tdate, $hi_tdate)));

  my ($lo_year, $lo_month, $lo_day) = App::Chart::tdate_to_ymd ($lo_tdate);
  my ($hi_year, $hi_month, $hi_day) = App::Chart::tdate_to_ymd ($hi_tdate);
  my $startdate = sprintf "%04d-%02d-%02d", $lo_year, $lo_month, $lo_day;
  my $enddate   = sprintf "%04d-%02d-%02d", $hi_year, $hi_month, $hi_day;

  return App::Chart::Download->get
    (TSP_SHARE_PRICES_URL
     . "?startdate=$startdate&enddate=$enddate&Lfunds=1&InvFunds=1&download=1",
     user_agent => TSP_USER_AGENT);
}

# $resp is a HTTP::Response object, return a hashref of data
sub parse {
  my ($resp, $symbol_list) = @_;

  # From 1 Jul 2008 prices are 4 decimal places, and old data is padded with
  # zeros.  Could prefer_decimals to strip them, but may as well leave at
  # current precision.
  #
  my @data = ();
  my $h = { source      => __PACKAGE__,
            currency    => 'USD',
            date_format => 'ymd',
            suffix      => '.TSP',
            data        => \@data };

  my $content = $resp->decoded_content (raise_error=>1);
  ### $content

  my @lines = App::Chart::Download::split_lines ($content);
  my ($date, @symbols) = split /,/, $lines[0];
  lc($date) eq 'date'
    or die 'TSP csv doesn\'t start with "date": ', $lines[0];
  shift @lines;
  ### @symbols

  # "L Income" becomes uppercase "LINCOME.TSP" (documented in chart.texi
  # that way)
  @symbols = map { my $symbol = $_;
                   $symbol =~ s/ Fund//i;
                   $symbol =~  s/ //g;
                   "\U$symbol.TSP" }
    @symbols;
  ### @symbols
  $symbol_list //= \@symbols;
  my %symbol_list = map {$_ => 1} @$symbol_list;

  foreach my $line (@lines) {
    my ($date, @prices) = split /,/, $line;
    foreach my $c (0 .. $#prices) {
      my $symbol = $symbols[$c];
      next unless exists $symbol_list{$symbol};
      push @data, { symbol => $symbol,
                    date   => $date,
                    close  => $prices[$c],
                  };
    }
  }
  if (DEBUG) {
    require List::Util;
    print "min date ",List::Util::minstr(map {$_->{'date'}} @data),"\n";
    print "max date ",List::Util::maxstr(map {$_->{'date'}} @data),"\n";
  }
  return $h;
}

1;
__END__



# Finance::Quote::TSP no longer has a %TSP_FUND_NAMES table.
#
# # $symbol is like "L2050.TSP", return a name from the table in
# # Finance::Quote::TSP, or undef if unknown.  The names table there is not a
# # documented feature, so guard with an eval.
# #
# sub symbol_to_name {
#   my ($symbol) = @_;
#   # eg. "TSPL2050" or "TSPGFUND"
#   $symbol = 'TSP'.App::Chart::symbol_sans_suffix($symbol);
#   return eval {
#     require Finance::Quote::TSP;
#     $Finance::Quote::TSP::TSP_FUND_NAMES{$symbol}
#       || $Finance::Quote::TSP::TSP_FUND_NAMES{$symbol.'FUND'}
#   };
# }




# Old code parsing HTML table.
# {
#   my ($resp) = @_;
# 
#   # From 1 Jul 2008 prices are 4 decimal places, and old data is padded with
#   # zeros.  Could prefer_decimals to strip them, but may as well leave at
#   # current precision.
#   #
#   my @data = ();
#   my $h = { source      => __PACKAGE__,
#             currency    => 'USD',
#             date_format => 'mdy',
#             suffix      => '.TSP',
#             data        => \@data };
# 
#   my $content = $resp->decoded_content (raise_error=>1);
#   ### $content
# 
#   require HTML::TableExtract;
#   my $te = HTML::TableExtract->new (headers => [qr/^Date$/i],
#                                     keep_headers => 1,
#                                     slice_columns => 0);
#   $te->parse($content);
#   my $ts = $te->first_table_found();
#   #### $ts
#   if (! $ts) { die 'TSP price table not found'; }
# 
#   my $rows = $ts->rows();
#   my $lastrow = $#$rows;
#   my $lastcol = $#{$rows->[0]};
# 
#   my @symbol = map { my $symbol = $_;
#                      $symbol =~ s/ Fund//i;
#                      $symbol =~  s/ //g;
#                      # upper case for "LINCOME.TSP" (documented in
#                      # chart.texi that way)
#                      $symbol eq 'Date' ? undef : "\U$symbol.TSP" } @{$rows->[0]};
# 
#   require Finance::Quote::TSP;
#   my @name = map { my $key = $_;
#                    $key =~  s/ //g;
#                    $Finance::Quote::TSP::TSP_FUND_NAMES{"TSP$key"} }
#     @{$rows->[0]};
# 
#   foreach my $r (1 .. $lastrow) {
#     my $row = $rows->[$r];
#     if (DEBUG) { require Data::Dumper;
#                  print Data::Dumper::Dumper($row); }
#     my $date = $row->[0];
# 
#     foreach my $c (1 .. $lastcol) {
#       my $price = $row->[$c];
# 
#       push @data, { symbol => $symbol[$c],
#                     name   => $name[$c],
#                     date   => $date,
#                     close  => $price,
#                   };
#     }
#   }
#   if (DEBUG) {
#     require List::Util;
#     print "min date ",List::Util::minstr(map {$_->{'date'}} @data),"\n";
#     print "max date ",List::Util::maxstr(map {$_->{'date'}} @data),"\n";
#   }
#   return $h;
# }



( run in 0.942 second using v1.01-cache-2.11-cpan-437f7b0c052 )