File Coverage

blib/lib/Finance/QuoteHist/Yahoo.pm
Criterion Covered Total %
statement 15 91 16.4
branch 0 32 0.0
condition 0 43 0.0
subroutine 5 10 50.0
pod 3 4 75.0
total 23 180 12.7


line stmt bran cond sub pod time code
1             package Finance::QuoteHist::Yahoo;
2              
3 1     1   8016 use strict;
  1         1  
  1         28  
4 1     1   5 use vars qw(@ISA $VERSION);
  1         1  
  1         40  
5 1     1   5 use Carp;
  1         2  
  1         49  
6              
7             $VERSION = "1.26";
8              
9 1     1   4 use Finance::QuoteHist::Generic;
  1         2  
  1         41  
10             @ISA = qw(Finance::QuoteHist::Generic);
11              
12 1     1   5 use Date::Manip;
  1         2  
  1         937  
13              
14             # https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=1495391410&period2=1498069810&interval=1d&events=history&crumb=bB6k340lPXt
15             # https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=993096000&period2=1498017600&interval=1wk&events=history&crumb=bB6k340lPXt
16             # https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=993096000&period2=1498017600&interval=1mo&events=history&crumb=bB6k340lPXt
17             #
18             # Dividends:
19             # https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=993096000&period2=1498017600&interval=1d&events=div&crumb=bB6k340lPXt
20             #
21             # Splits:
22             # https://query1.finance.yahoo.com/v7/finance/download/NKE?period1=993096000&period2=1498017600&interval=1d&events=split&crumb=bB6k340lPXt
23              
24             sub new {
25 0     0 1   my $that = shift;
26 0   0       my $class = ref($that) || $that;
27 0           my %parms = @_;
28              
29 0           $parms{parse_mode} = 'csv';
30 0   0       $parms{ua_params} ||= {};
31 0   0       $parms{ua_params}{cookie_jar} ||= {};
32              
33 0           my $self = __PACKAGE__->SUPER::new(%parms);
34 0           bless $self, $class;
35              
36             # set initial cookie (the cookie crumbs are hashed out of this)
37             # https://finance.yahoo.com/quote/IBM/history
38 0           my $ticker = $parms{symbols};
39 0 0         $ticker = $ticker->[0] if ref $ticker eq 'ARRAY';
40 0           my $html = $self->fetch("https://finance.yahoo.com/quote/$ticker/history");
41              
42 0           open(F, ">/tmp/hmm.html");
43 0           print F $html;
44 0           close(F);
45              
46             # extract the cookie crumb
47 0           my %crumbs;
48 0           for my $c ($html =~ /"crumb"\s*:\s*"([^"]+)"/g) {
49 0 0         next if $c =~ /[{}]/;
50 0           $c =~ s/\\u002F/\//;
51 0           ++$crumbs{$c};
52             }
53 0           my $crumb = '';
54 0           my $max = 0;
55 0           for my $c (keys %crumbs) {
56 0 0         if ($crumbs{$c} >= $max) {
57 0           $crumb = $c;
58 0           $max = $crumbs{$c};
59             }
60             }
61              
62 0           $self->{crumb} = $crumb;
63              
64 0           $self;
65             }
66              
67 0     0 0   sub granularities { qw( daily weekly monthly ) }
68              
69             sub labels {
70 0     0 1   my $self = shift;
71 0           my %parms = @_;
72 0   0       my $target_mode = $parms{target_mode} || $self->target_mode;
73 0           my @labels;
74 0 0         if ($target_mode eq 'split') {
75 0           @labels = qw( date stock );
76             }
77             else {
78 0           @labels = $self->SUPER::labels(%parms);
79 0 0         push(@labels, 'adj') if $target_mode eq 'quote';
80             }
81 0           @labels;
82             }
83              
84             sub url_maker {
85 0     0 1   my($self, %parms) = @_;
86 0   0       my $target_mode = $parms{target_mode} || $self->target_mode;
87 0   0       my $parse_mode = $parms{parse_mode} || $self->parse_mode;
88              
89             # *always* block unknown target mode and parse mode combinations for
90             # cascade to work properly!
91 0 0 0       return undef unless $target_mode eq 'quote' ||
      0        
92             $target_mode eq 'split' ||
93             $target_mode eq 'dividend';
94 0 0 0       return undef unless $parse_mode eq 'html' || $parse_mode eq 'csv';
95              
96 0   0       my $granularity = lc($parms{granularity} || $self->granularity);
97 0           my $grain = 'd';
98 0           $granularity =~ /^\s*(\w)/;
99 0 0 0       $grain = $1 if $1 eq 'w' || $1 eq 'm';
100             my($ticker, $start_date, $end_date) =
101 0           @parms{qw(symbol start_date end_date)};
102 0   0       $start_date ||= $self->start_date;
103 0   0       $end_date ||= $self->end_date;
104 0 0 0       if ($start_date && $end_date && $start_date gt $end_date) {
      0        
105 0           ($start_date, $end_date) = ($end_date, $start_date);
106             }
107              
108 0           my $host = "query1.finance.yahoo.com";
109 0           my $base_url = "https://$host/v7/finance/download/$ticker?";
110 0           my @base_parms;
111 0 0         if ($start_date) {
112 0           my($y, $m, $d) = $self->ymd($start_date);
113 0           my $ts = Date_SecsSince1970($m, $d, $y, 0, 0, 0);
114 0           push(@base_parms, "period1=$ts");
115             }
116 0 0         if ($end_date) {
117 0           my($y, $m, $d) = $self->ymd($end_date);
118 0           my $ts = Date_SecsSince1970($m, $d, $y, 0, 0, 0);
119 0           $ts += 24*60*60;
120 0           push(@base_parms, "period2=$ts");
121             }
122              
123 0           my $interval = "1d";
124 0 0         if ($grain eq 'w') {
    0          
125 0           $interval = "1wk";
126             }
127             elsif ($grain eq 'm') {
128 0           $interval = "1mo";
129             }
130 0           push(@base_parms, "interval=$interval");
131              
132 0 0         if ($target_mode eq "quote") {
    0          
    0          
133 0           push(@base_parms, "events=history");
134             }
135             elsif ($target_mode eq "dividend") {
136 0           push(@base_parms, "events=div");
137             }
138             elsif ($target_mode eq "split") {
139 0           push(@base_parms, "events=split");
140             }
141              
142 0           push(@base_parms, "crumb=" . $self->{crumb});
143              
144 0           my @urls = $base_url . join('&', @base_parms);
145 0     0     return sub { pop @urls };
  0            
146             }
147              
148             1;
149              
150             __END__