File Coverage

blib/lib/Finance/Quote/Fundata.pm
Criterion Covered Total %
statement 18 86 20.9
branch 0 38 0.0
condition 0 6 0.0
subroutine 7 9 77.7
pod 0 4 0.0
total 25 143 17.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             # The code has been modified by AbstractMethod to
3             # retrieve stock information from Fundata
4             #
5             # This program is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by
7             # the Free Software Foundation; either version 2 of the License, or
8             # (at your option) any later version.
9             #
10             # This program is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13             # GNU General Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License
16             # along with this program; if not, write to the Free Software
17             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18             # 02110-1301, USA
19              
20              
21             package Finance::Quote::Fundata;
22             require 5.005;
23              
24 5     5   2875 use strict;
  5         10  
  5         160  
25              
26 5     5   29 use LWP::UserAgent;
  5         13  
  5         49  
27 5     5   112 use HTTP::Request::Common;
  5         10  
  5         288  
28 5     5   45 use HTTP::Request::Common;
  5         10  
  5         258  
29 5     5   3386 use HTML::TokeParser::Simple;
  5         46801  
  5         61  
30              
31             our $VERSION = '1.58'; # VERSION
32              
33             my $DEBUG = 0;
34              
35             my $FUNDATA_MAINURL = "http://www.fundata.com";
36             my $FUNDATA_URL = "http://idata.fundata.com/MutualFunds/FundSnapshot.aspx?IID=";
37              
38             our @totalqueries=();
39             my $maxQueries = { quantity => 3, seconds => 10}; # allow 'quantity' calls in 'seconds', then sleep
40              
41              
42             sub methods {
43 5     5 0 28 return (canadamutual => \&fundata,
44             fundata => \&fundata);
45             }
46              
47             sub labels {
48 5     5 0 19 my @labels = qw/method source name symbol currency date isodate nav/;
49 5         17 return (canadamutual => \@labels,
50             fundata => \@labels);
51             }
52              
53             sub sleep_before_query {
54             # wait till we can query again
55 0     0 0   my $q = $maxQueries->{quantity}-1;
56 0 0         if ( $#totalqueries >= $q ) {
57 0           my $time_since_x_queries = time()-$totalqueries[$q];
58 0 0         print STDERR "LAST QUERY $time_since_x_queries\n" if $DEBUG;
59 0 0         if ($time_since_x_queries < $maxQueries->{seconds}) {
60 0           my $sleeptime = ($maxQueries->{seconds} - $time_since_x_queries) ;
61 0 0         print STDERR "SLEEP $sleeptime\n" if $DEBUG;
62 0           sleep( $sleeptime );
63 0 0         print STDERR "CONTINUE\n" if $DEBUG;
64             }
65             }
66 0           unshift @totalqueries, time();
67 0           pop @totalqueries while $#totalqueries>$q; # remove unnecessary data
68             # print STDERR join(",",@totalqueries)."\n";
69             }
70              
71              
72             sub fundata {
73 0     0 0   my $quoter = shift;
74 0           my @symbols = @_;
75 0           my %info;
76              
77 0 0         return unless @symbols;
78              
79 0           my $ua = $quoter->user_agent;
80              
81 0           foreach my $symbol (@symbols) {
82 0           my ($day_high, $day_low, $year_high, $year_low);
83              
84 0           $info{$symbol, "success"} = 0;
85 0           $info{$symbol, "symbol"} = $symbol;
86 0           $info{$symbol, "method"} = "fundata";
87 0           $info{$symbol, "source"} = $FUNDATA_MAINURL;
88 0           $info{$symbol, "timezone"} = "EST";
89              
90             # Pull the data from the web site
91 0           my $url = $FUNDATA_URL.$symbol;
92 0 0         print $url."\n" if ($DEBUG);
93              
94 0           my $reply = $ua->request(GET $url);
95 0           my $code = $reply->code;
96 0           my $desc = HTTP::Status::status_message($code);
97 0           my $body = $reply->content;
98              
99 0 0         if (!$reply->is_success) {
100 0           $info{$symbol, "errormsg"} = "Error contacting URL";
101 0           next;
102             }
103              
104 0           my $parser = HTML::TokeParser::Simple->new(string => $reply->content);
105              
106 0           my $nav = 0;
107              
108 0           while (my $h1 = $parser->get_tag('h1')) {
109 0           my $class = $h1->get_attr('class');
110             #print $class if $DEBUG;
111              
112 0 0         if ($class eq "SnapshotHeader") {
113 0           my $name = $parser->get_trimmed_text('/h1');
114 0 0         print $name if $DEBUG;
115 0           $info{$symbol, "name"} = $name;
116             }
117             }
118              
119 0           $parser = HTML::TokeParser::Simple->new(string => $reply->content);
120              
121 0           while (my $span = $parser->get_tag('span')) {
122 0           my $class = $span->get_attr('class');
123 0           my $id = $span->get_attr('id');
124              
125             #print $span if ($DEBUG);
126             #print $class if ($DEBUG);
127             #print $id if ($DEBUG);
128            
129 0 0 0       if (defined $id and $id eq "ctl00_MainContent_lblNavpsDate") {
130 0           my $rawline = $parser->get_trimmed_text('/span');
131 0 0         print $rawline."\n" if ($DEBUG);
132             # (9/3/2020)
133 0 0         if ($rawline =~ m/(\d+)\/(\d+)\/(\d\d\d\d)/) {
134 0           my $month = $1;
135 0           my $day = $2;
136 0           my $year = $3;
137 0 0         print $month." ".$day." ".$year if ($DEBUG);
138 0           $quoter->store_date(\%info, $symbol, {month=>$month, day=>$day, year=>$year});
139             }
140             }
141              
142 0 0 0       if (defined $id and $id eq "ctl00_MainContent_txtNavps") {
143 0           $nav = $parser->get_trimmed_text('/span');
144 0           $nav =~ s/\$//g;
145 0 0         print $nav if ($DEBUG);
146 0           $info{$symbol, "nav"} = $nav;
147 0           $info{$symbol, "success"} = 1;
148             }
149              
150 0 0         print "\n" if ($DEBUG);
151             }
152              
153 0 0         if ($nav == 0) {
154 0           $info{$symbol, "success"} = 0;
155 0           $info{$symbol, "errormsg"} = "Cannot parse quote data";
156 0           next;
157             }
158              
159 0           $info{$symbol, "success"} = 1;
160 0           $info{$symbol, "currency"} = "CAD";
161              
162 0           sleep_before_query();
163             }
164              
165 0 0         return wantarray() ? %info : \%info;
166             }
167              
168             1;
169              
170              
171             __END__
172              
173             =head1 NAME
174              
175             Finance::Quote::Fundata - Obtain Canadian mutual fund quotes from Fundata
176              
177             =head1 SYNOPSIS
178              
179             use Finance::Quote;
180              
181             $q = Finance::Quote->new;
182              
183             %info = Finance::Quote->fetch("fundata","234263");
184              
185             =head1 DESCRIPTION
186              
187             This module fetches mutual fund information from Fundata.
188              
189             Mutual fund symbols on the site are specified numerically. The best
190             way to determine the correct symbol is to navigate to the site,
191             search for the relevant mutual fund, and note the "IID=#####"
192             which appears in the URL.
193              
194             In order to not tax the provider with too many requests, by
195             default the module limits requests to 3 every 10 seconds.
196              
197             =head1 LABELS RETURNED
198              
199             The following labels may be returned by Finance::Quote::Fundata :
200             symbol, name, method, source, timezone, isodate, nav, currency
201              
202             =head1 SEE ALSO
203              
204             =cut
205