File Coverage

blib/lib/Finance/Quote/MorningstarUK.pm
Criterion Covered Total %
statement 23 105 21.9
branch 0 34 0.0
condition 0 9 0.0
subroutine 9 10 90.0
pod 0 3 0.0
total 32 161 19.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             # MorningstarUK.pm
4             #
5             # Obtains quotes for UK Unit Trusts from http://morningstar.co.uk/ - please
6             # refer to the end of this file for further information.
7             #
8             # author: Martin Sadler (martinsadler@users.sourceforge.net)
9             #
10             # version: 0.1 Initial version - 01 April 2013
11             #
12             # This program is free software; you can redistribute it and/or modify
13             # it under the terms of the GNU General Public License as published by
14             # the Free Software Foundation; either version 2 of the License, or
15             # (at your option) any later version.
16             #
17             # This program is distributed in the hope that it will be useful,
18             # but WITHOUT ANY WARRANTY; without even the implied warranty of
19             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20             # GNU General Public License for more details.
21             #
22             # You should have received a copy of the GNU General Public License
23             # along with this program; if not, write to the Free Software
24             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25             # 02110-1301, USA
26             #
27              
28              
29             package Finance::Quote::MorningstarUK;
30             require 5.005;
31              
32 5     5   2863 use strict;
  5         17  
  5         157  
33 5     5   25 use warnings;
  5         26  
  5         172  
34              
35             # URLs
36 5     5   27 use vars qw($VERSION $MSTARUK_NEXT_URL $MSTARUK_LOOK_UP $MSTARUK_MAIN_URL);
  5         17  
  5         346  
37              
38 5     5   35 use LWP::Simple;
  5         30  
  5         41  
39 5     5   1660 use LWP::UserAgent;
  5         15  
  5         37  
40 5     5   172 use HTTP::Request::Common;
  5         14  
  5         384  
41 5     5   50 use HTTP::Cookies;
  5         12  
  5         42  
42              
43             our $VERSION = '1.57_03'; # TRIAL VERSION
44              
45             $MSTARUK_MAIN_URL = "http://www.morningstar.co.uk";
46             $MSTARUK_LOOK_UP = "http://www.morningstar.co.uk/uk/funds/SecuritySearchResults.aspx?search=";
47             $MSTARUK_NEXT_URL = "http://www.morningstar.co.uk/uk/funds/snapshot/snapshot.aspx?id=";
48              
49             # FIXME -
50              
51 5     5 0 62 sub methods { return (morningstaruk => \&mstaruk_fund,
52             mstaruk => \&mstaruk_fund,
53             ukfunds => \&mstaruk_fund); }
54              
55             {
56             my @labels = qw/name currency last date time price nav source iso_date method net p_change success errormsg/;
57              
58 5     5 0 21 sub labels { return (morningstaruk => \@labels,
59             mstaruk => \@labels,
60             ukfunds => \@labels); }
61             }
62              
63             #
64             # =======================================================================
65              
66             sub mstaruk_fund {
67 0     0 0   my $quoter = shift;
68 0           my @symbols = @_;
69              
70 0 0         return unless @symbols;
71              
72 0           my %fundquote;
73              
74 0           my $ua = $quoter->user_agent;
75 0           my $cj = HTTP::Cookies->new();
76 0           $ua->cookie_jar( $cj );
77              
78 0           foreach (@symbols)
79             {
80 0           my $code = $_;
81              
82 0           my $code_type = "** Invalid **";
83 0 0 0       if ($code =~ m/^[a-zA-Z]{2}[a-zA-Z0-9]{9}\d(.*)/ && !$1) { $code_type = "ISIN"; }
  0 0 0        
    0 0        
84 0           elsif ($code =~ m/^[a-zA-Z0-9]{6}\d(.*)/ && !$1 ) { $code_type = "SEDOL"; }
85 0           elsif ($code =~ m/^[a-zA-Z]{4,6}(.*)/ && !$1) { $code_type = "MEXID"; }
86              
87             # current version can only use ISIN - report an error and exit if any other type
88              
89 0 0         if ($code_type ne "ISIN")
90             {
91 0           $fundquote {$code,"success"} = 0;
92 0           $fundquote {$code,"errormsg"} = "Error - invalid symbol";
93 0           next;
94             }
95              
96 0           $fundquote {$code,"success"} = 1; # ever the optimist....
97 0           $fundquote {$code,"errormsg"} = "Success";
98              
99             # perform the look-up - if not found, return with error
100              
101 0           my $webdoc = get($MSTARUK_LOOK_UP.$code);
102 0 0         if (!$webdoc)
103             {
104             # serious error, report it and give up
105 0           $fundquote {$code,"success"} = 0;
106 0           $fundquote {$code,"errormsg"} =
107             "Error - failed to retrieve fund data";
108 0           next;
109             }
110 0           $fundquote {$code, "symbol"} = $code;
111 0           $fundquote {$code, "source"} = $MSTARUK_MAIN_URL;
112              
113             # Find name by regexp
114              
115 0           my ($name, $nexturl, $isin);
116 0 0         if ($webdoc =~
117             m[<td class="msDataText searchLink"><a href="(.*?)">(.*?)</a></td><td class="msDataText searchIsin"><span>[a-zA-Z]{2}[a-zA-Z0-9]{9}\d(.*)</span></td>] )
118             {
119 0           $nexturl = $1;
120 0           $name = $2;
121 0           $isin = $3;
122             }
123              
124 0 0         if (!defined($name)) {
125             # not a serious error - don't report it ....
126             # $fundquote {$code,"success"} = 0;
127             # ... but set a useful message ....
128 0           $fundquote {$code,"errormsg"} = "Warning - failed to find fund name";
129 0           $name = "*** UNKNOWN ***";
130             # ... and continue
131             }
132 0           $fundquote {$code, "name"} = $name; # set name
133              
134 0 0         if (!defined($nexturl)) {
135             # serious error, report it and give up
136 0           $fundquote {$code,"success"} = 0;
137 0           $fundquote {$code,"errormsg"} =
138             "Error - failed to retrieve fund data";
139 0           next;
140             }
141              
142             # modify $nexturl to remove html escape encoding for the Ampersand (&) character
143              
144 0           $nexturl =~ s/&amp;/&/;
145              
146             # Now need to look-up next page using $next_url
147              
148 0           $webdoc = get($MSTARUK_MAIN_URL.$nexturl);
149 0 0         if (!$webdoc)
150             {
151             # serious error, report it and give up
152 0           $fundquote {$code,"success"} = 0;
153 0           $fundquote {$code,"errormsg"} =
154             "Error - failed to retrieve fund data";
155 0           next;
156             }
157              
158             # Find date, currency and price all in one table row
159              
160 0           my ($currency, $date, $price, $pchange);
161 0 0         if ($webdoc =~
162             m[<td class="line heading">NAV<span class="heading"><br />([0-9]{2}/[0-9]{2}/[0-9]{4})</span>.*([A-Z]{3}).([0-9\.]+).*Day Change[^%]*>([0-9\.\-]+)] )
163             {
164              
165 0           $date = $1;
166 0           $currency = $2;
167 0           $price = $3;
168 0           $pchange = $4;
169             }
170              
171 0 0         if (!defined($pchange)) {
172             # not a serious error - don't report it ....
173             # $fundquote {$code,"success"} = 0;
174             # ... but set a useful message ....
175 0           $fundquote {$code,"errormsg"} = "Warning - failed to find net or %-age change";
176             # set to (minus)zero
177 0           $pchange = -0.00;
178             # ... and continue
179             }
180 0           $fundquote {$code, "p_change"} = $pchange; # set %-change
181              
182 0 0         if (!defined($date)) {
183             # not a serious error - don't report it ....
184             # $fundquote {$code,"success"} = 0;
185             # ... but set a useful message ....
186 0           $fundquote {$code,"errormsg"} = "Warning - failed to find a date";
187             # use today's date
188 0           $quoter->store_date(\%fundquote, $code, {today => 1});
189             # ... and continue
190             }
191             else
192             {
193 0           $quoter->store_date(\%fundquote, $code, {eurodate => $date});
194             }
195              
196 0 0         if (!defined($price)) {
197             # serious error, report it and give up
198 0           $fundquote {$code,"success"} = 0;
199 0           $fundquote {$code,"errormsg"} = "Error - failed to find a price";
200 0           next;
201             }
202              
203 0 0         if (!defined($currency)) {
204             # serious error, report it and give up
205 0           $fundquote {$code,"success"} = 0;
206 0           $fundquote {$code,"errormsg"} = "Error - failed to find a currency";
207 0           next;
208             }
209              
210             # defer setting currency and price until we've dealt with possible GBX currency...
211              
212             # Calculate net change - it's not included in the morningstar factsheets
213              
214 0           my $net = ($price * $pchange) / 100 ;
215              
216             # deal with GBX pricing of UK unit trusts
217              
218 0 0         if ($currency eq "GBX")
219             {
220 0           $currency = "GBP" ;
221 0           $price = $price / 100 ;
222 0           $net = $net / 100 ;
223             }
224              
225             # now set prices and currency
226              
227 0           $fundquote {$code, "price"} = $price;
228 0           $fundquote {$code, "last"} = $price;
229 0           $fundquote {$code, "nav"} = $price;
230 0           $fundquote {$code, "net"} = $net;
231 0           $fundquote {$code, "currency"} = $currency;
232              
233             # Set a dummy time as gnucash insists on having a valid format
234              
235 0           my $time = "12:00"; # set to Midday if no time supplied ???
236             # gnucash insists on having a valid-format
237              
238 0           $fundquote {$code, "time"} = $time; # set time
239              
240 0           $fundquote {$code, "method"} = "mstaruk"; # set method
241              
242             }
243              
244 0 0         return wantarray ? %fundquote : \%fundquote;
245             }
246              
247             1;
248              
249             =head1 NAME
250              
251             Finance::Quote::MorningstarUK - Obtain UK Unit Trust quotes from morningstar.co.uk.
252              
253             =head1 SYNOPSIS
254              
255             $q = Finance::Quote->new;
256              
257             %info = $q->fetch("mstaruk","<isin> ..."); # Only query morningstar.co.uk using ISINs
258             %info = $q->fetch("ukfunds","<isin> ..."); # Failover to other sources
259              
260             =head1 DESCRIPTION
261              
262             This module fetches information from the MorningStar Funds service,
263             http://morningstar.com/uk/. There are many UK Unit Trusts and OEICs quoted,
264             as well as many Offshore Funds and Exhange Traded Funds (ETFs). It converts
265             any funds quoted in GBX (pence) to GBP, dividing the price by 100 in the
266             process.
267              
268             Funds are identified by their ISIN code.
269              
270             This module is loaded by default on a Finance::Quote object. It's
271             also possible to load it explicitly by placing "mstaruk" in the argument
272             list to Finance::Quote->new().
273              
274             Information obtained by this module may be covered by funds.ft.com
275             terms and conditions See http://morningstar.co.ukfor details.
276              
277             =head2 Stocks And Indices
278              
279             This module provides both the "mstaruk" and "ukfunds" fetch methods for
280             fetching UK and Offshore Unit Trusts and OEICs prices and other information
281             from funds.ft.com. Please use the "ukfunds" fetch method if you wish to have
282             failover with future sources for UK and Offshore Unit Trusts and OEICs - the
283             author has plans to develop Finance::Quote modules for other source providing
284             unit trust prices. Using the "mstaruk" method will guarantee
285             that your information only comes from the morningstar.co.uk website.
286              
287             =head1 LABELS RETURNED
288              
289             The following labels may be returned by Finance::Quote::MorningstarUK :
290              
291             name, currency, last, date, time, price, nav, source, method,
292             iso_date, net, p_change, success, errormsg.
293              
294              
295             =head1 SEE ALSO
296              
297             Morning Star websites, http://morningstar.co.uk
298              
299              
300             =head1 AUTHOR
301              
302             Martin Sadler, E<lt>martinsadler@users.sourceforge.netE<gt>
303              
304             =head1 COPYRIGHT AND LICENSE
305              
306             Copyright (C) 2010 by Martin Sadler
307              
308             This library is free software; you can redistribute it and/or modify
309             it under the same terms as Perl itself, either Perl version 5.10.1 or,
310             at your option, any later version of Perl 5 you may have available.
311              
312              
313             =cut
314              
315             __END__