File Coverage

blib/lib/Finance/YahooProfile.pm
Criterion Covered Total %
statement 12 133 9.0
branch 0 60 0.0
condition 0 15 0.0
subroutine 4 8 50.0
pod 1 2 50.0
total 17 218 7.8


line stmt bran cond sub pod time code
1             package Finance::YahooProfile;
2            
3 1     1   7440 use strict;
  1         5  
  1         54  
4 1     1   6 use vars qw($VERSION);
  1         2  
  1         236  
5            
6             $VERSION = '1.12';
7            
8 1     1   1345 use LWP::UserAgent;
  1         92352  
  1         43  
9 1     1   736390 use HTTP::Request::Common;
  1         3493  
  1         2239  
10            
11             sub new {
12 0     0 1   my $type = shift;
13 0   0       my $class = ref $type || $type;
14 0           my $self = {};
15 0           bless $self, $class;
16 0 0         $self->_init(@_) if @_;
17 0           return $self;
18             }
19            
20             sub _init {
21 0     0     my ($self, %params) = @_;
22 0           for (keys %params) {
23 0           $self->{$_} = $params{$_};
24 0 0         $self->{'dollar_symbol'} = 1 unless defined $params{'dollar_symbol'};
25             }
26             }
27            
28             sub profile {
29 0     0 0   my ($self, %params) = @_;
30            
31 0           my (@res, $ua);
32            
33 0           $ua = LWP::UserAgent->new;
34 0 0         $ua->timeout($self->{'timeout'}) if defined $self->{'timeout'};
35 0           $ua->env_proxy();
36            
37 0 0         if (not ref $params{'s'}) { $params{'s'} = [$params{'s'}]; }
  0            
38            
39 0           my ($s, $firstletter, $url, $content, $res);
40 0           for $s ( @{$params{'s'}} ) {
  0            
41            
42 0           $firstletter = substr($s, 0, 1);
43 0           $url = "http://biz.yahoo.com/p/$firstletter/$s.html";
44 0           $content = $ua->request(GET $url)->content();
45 0           $res = {}; ## to store the results
46 0           $res->{'requested_symbol'} = $s;
47            
48             {
49 0           local $/;
  0            
50 0           $content =~ s/<[^>]*>//gs;
51             }
52            
53             ## Prepare Content for extraction
54 0           $content =~ s/ / /g; ## insert spaces
55 0           $content =~ s/.*(Statistics at a Glance -- )//s; ## remove the beginning
56 0           $content =~ s/(See Profile).*//s; ## remove the end
57            
58             ## Check for non-existent data
59 0 0         if ($content =~ /\nNo Such Data/) {
60 0           $res->{'success'} = 0;
61 0           push (@res, $res);
62 0           next;
63             }
64            
65             ## Market
66 0           $content =~ s/(.+?)://s;
67 0           $res->{'market'} = $1;
68            
69             ## Ticker Symbol
70 0 0         if ($content =~ s/(.+?)As of //s) {
71 0           $res->{'symbol'} = $1;
72             }
73            
74             ## Last Updated
75 0 0         if ($content =~ s/(.+?)Price and Volume//s) {
76 0           $res->{'last_updated'} = $1;
77             }
78            
79             ## 52-Week Prices
80 0 0         if ($content =~ s/52-Week Lowon (.+?)\$(.+?) Recent Price(.+?) 52-Week Highon (.+?)\$(.+?)( )?Beta/Beta/s) {
81 0           $res->{'52_week_low_date'} = $1;
82 0           $res->{'52_week_low'} = $2;
83 0           $res->{'recent_price'} = $3;
84 0           $res->{'52_week_high_date'} = $4;
85 0           $res->{'52_week_high'} = $5;
86             }
87            
88             ## Beta, and Volume Data
89 0 0         if ($content =~ s/Beta(.+?) Daily Volume \(3-month avg\)(.+?)Daily Volume \(10-day avg\)(.+?)Stock/Stock/s) {
90 0           $res->{'beta'} = $1;
91 0           $res->{'daily_volume_3ma'} = $2;
92 0           $res->{'daily_volume_10da'} = $3;
93             }
94            
95             ## 52-Week Price Change
96 0 0         if ($content =~ s/.+?52-Week Change(.+?)52-Week Changerelative to S&P500(.+?)Share//s) {
97 0           $res->{'52_week_change'} = $1;
98 0           $res->{'52_week_change_sp500'} = $2;
99             }
100            
101             ## Share related items
102 0 0         if ($content =~ s/.+?Market Capitalization(.+?)Shares Outstanding(.+?)Float(.+?)Dividends//s) {
103 0           $res->{'market_capitalization'} = $1;
104 0           $res->{'shares_outstanding'} = $2;
105 0           $res->{'shares_float'} = $3;
106             }
107            
108 0 0         if ($content =~ s/.*?Annual Dividend( \(indicated\))?(.+?)( Dividend Yield(.+?))?Last Split(.+?)Per//s) {
109 0           $res->{'dividend'} = $2;
110 0           $res->{'dividend_yield'} = $4;
111            
112 0 0         if ($5 ne 'none') {
113 0 0         if ($5 =~ m/factor (.+?) on (.+)/) {
114 0           $res->{'last_split_factor'} = $1;
115 0           $res->{'last_split_date'} = $2;
116             }
117             }
118             }
119            
120             ## Per-Share Data
121 0 0         if ($content =~ s/.+?Book Value \(mrq\*\)(.+?) Earnings \(ttm\)(.+?) (Earnings \(mrq\)(.+?) )?Sales \(ttm\)(.+?) Cash( \(mrq\*\)?)?(.+?) Valuation//s) {
122 0           $res->{'book_value'} = $1;
123 0           $res->{'earnings_ttm'} = $2;
124 0           $res->{'earnings_mrq'} = $4;
125 0           $res->{'sales_ttm'} = $5;
126 0           $res->{'cash'} = $7;
127             }
128            
129             ## Valuation Ratios
130 0 0         if ($content =~ s!.+?Price/Book \(mrq\*\)(.+?) Price/Earnings( \(ttm\))?(.+?) Price/Sales \(ttm\)(.+?) Income!!s) {
131 0           $res->{'price_book'} = $1;
132 0           $res->{'price_earnings'} = $3;
133 0           $res->{'price_sales'} = $4;
134             }
135            
136             ## Income Statements
137 0 0         if ($content =~ s/.+?Sales \(ttm\)(.+?)EBITDA \(ttm\*\)(.+?)Income available to common \(ttm\)(.+?)Profitability//s) {
138 0           $res->{'sales'} = $1;
139 0           $res->{'ebitda'} = $2;
140 0           $res->{'income'} = $3;
141             }
142            
143             ## Profitability
144 0 0         if ($content =~ s/.*?Profit Margin \(ttm\)(.+?)Operating Margin \(ttm\)(.+?)Fiscal//s) {
145 0           $res->{'profit_margin'} = $1;
146 0           $res->{'operating_margin'} = $2;
147             }
148            
149             ## Fiscal Year
150 0 0         if ($content =~ s/.*?Fiscal Year Ends(.+?)Most recent quarter(\(fully\nupdated\))?(.+?)(Most recent quarter\(flash earnings\)(.+?))?Management//s) {
151 0           $res->{'fiscal_year_ends'} = $1;
152 0           $res->{'most_recent_quarter'} = $3;
153 0           $res->{'most_recent_quarter_fe'} = $5;
154 0           $res->{'fiscal_year_ends'} =~ s/\n|\r/ /s;
155            
156             }
157            
158             ## Management Effectiveness
159 0 0         if ($content =~ s/.*?Return on Assets \(ttm\)(.+?)Return on Equity \(ttm\)(.+?)Financial//s) {
160 0           $res->{'return_on_assets'} = $1;
161 0           $res->{'return_on_equity'} = $2;
162             }
163            
164             ## Financial Strength
165 0 0         if ($content =~ s/.*?Current Ratio( \(mrq\*?\))?(.+?) Debt\/Equity( \(mrq\*?\))?(.+?) Total Cash( \(mrq\*?\))?(.+?)Short//s) {
166 0           $res->{'current_ratio'} = $2;
167 0           $res->{'debt_equity'} = $4;
168 0           $res->{'total_cash'} = $6;
169             }
170            
171             ## Short Interest
172 0 0         if ($content =~ s/.*?InterestAs\nof (.+?)Shares\nShort(.+?)Percent of Float(.+?)Shares Short\(Prior Month\)(.+?)Short Ratio(.+?) Daily Volume([\d\.KMB]+)//s) {
173 0           $res->{'short_interest_date'} = $1;
174 0           $res->{'short_interest'} = $2;
175 0           $res->{'short_percent'} = $3;
176 0           $res->{'short_previous_month'} = $4;
177 0           $res->{'short_ratio'} = $5;
178 0           $res->{'short_daily_volume'} = $6;
179             }
180            
181 0 0 0       $self->_expand($res) if $self->{'expand_numbers'} || $self->{'expand_percent'} || $self->{'expand'};
      0        
182 0           $res->{'success'} = 1;
183            
184 0           push(@res, $res);
185             }
186            
187 0 0         return wantarray ? @res : \@res;
188            
189             }
190            
191             sub _expand {
192 0     0     my ($self, $res) = @_;
193 0           my (%factors, $key, $dollar, $factor, @number_keys, @percent_keys);
194            
195 0           %factors = ( 'K' => 1_000,
196             'M' => 1_000_000,
197             'B' => 1_000_000_000,
198             'T' => 1_000_000_000_000 ## for when we have the first trillion dollar company.
199             );
200            
201 0           @number_keys = qw(
202             daily_volume_10da
203             daily_volume_3ma
204             ebitda
205             income
206             market_capitalization
207             sales
208             shares_float
209             shares_outstanding
210             short_interest
211             short_daily_volume
212             short_previous_month
213             total_cash
214             );
215            
216 0           @percent_keys = qw(
217             52_week_change
218             52_week_change_sp500
219             dividend_yield
220             operating_margin
221             profit_margin
222             return_on_assets
223             return_on_equity
224             short_percent
225             );
226            
227 0 0 0       if ($self->{'expand_numbers'} || $self->{'expand'}) {
228 0           for $key (@number_keys) {
229 0 0         $dollar = (($res->{$key} =~ s/^\$//) ? '$' : ''); ## save dollar sign'
230            
231 0 0         if ($res->{$key} =~ s/(K|M|B|T)$//) {
232 0           $res->{$key} *= $factors{$1};
233             }
234            
235 0 0         if ($self->{'dollar_symbol'}) {
236 0           $res->{$key} = $dollar . $res->{$key};
237             }
238             }
239             }
240            
241 0 0 0       if ($self->{'expand_percent'} || $self->{'expand'}) {
242 0           for $key (@percent_keys) {
243 0 0         if ($res->{$key} =~ s/^([\+\-\d\.]+)%$/$1/) {
244 0           $res->{$key} /= 100;
245             }
246             }
247             }
248             }
249            
250            
251             1;
252            
253             __END__