Skip Menu |

This queue is for tickets about the Finance-Quote CPAN distribution.

Report information
The Basics
Id: 52098
Status: open
Priority: 0/
Queue: Finance-Quote

People
Owner: Nobody in particular
Requestors: philip.john.haynes [...] googlemail.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 1.17
Fixed in: 1.17



Subject: New MLC fund module
Hi, I have implemented a module called MLC.pm (and one called APIR.pm which it uses, although APIR could just be pasted into MLC if it was not useful anywhere else) for the purpose of downloading prices from the MLC fund manager in Australia. I thought they might be of value to someone else and so I have uploaded them here. Feel free to add them to the Finance::Quote package or not. I currently use them from within gnucash to retrieve prices for half a dozen MLC funds on a regular basis using the APIR code as the commodity value. I hope that it should work just as well with ISIN as the key but it is not tested. I am not in a position to commit to maintain them going forward although I will probably have to keep them working for my own purposes if the website changes and would push those changes back in here if desired. Regards... Environment: Finance::Quote->VERSION = 1.17 Linux odin 2.6.28-16-generic #55-Ubuntu SMP Tue Oct 20 19:48:32 UTC 2009 x86_64 GNU/Linux This is perl, v5.10.0 built for x86_64-linux-gnu-thread-multi
Subject: APIR.pm
#!/usr/bin/perl -w # # Copyright (C) 1998, Dj Padzensky <djpadz@padz.net> # Copyright (C) 1998, 1999 Linas Vepstas <linas@linas.org> # Copyright (C) 2000, Yannick LE NY <y-le-ny@ifrance.com> # Copyright (C) 2000, Paul Fenwick <pjf@cpan.org> # Copyright (C) 2000, Brent Neal <brentn@users.sourceforge.net> # Copyright (C) 2001, Leigh Wedding <leigh.wedding@telstra.com> # Copyright (C) 2003, Ian Dall <ian@beware.dropbear.id.au> # Copyright (C) 2009, Philip Haynes <philip@london.mine.nu> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA # # # This code derived from Padzensky's work on package Finance::YahooQuote, # but extends its capabilites to additional data sources. # # This code was developed as part of GnuCash <http://www.gnucash.org/> require 5.005; use strict; package Finance::Quote::APIR; use HTTP::Request::Common; use LWP::UserAgent; use HTML::TableExtract; use WWW::Mechanize; use vars qw/$APIR_SEARCH_URL $APIR_RESULTS_URL $VERSION/; $VERSION = '0.1'; $APIR_SEARCH_URL = 'http://www.apir.com.au/public/investmentFundSearch.jsp'; $APIR_RESULTS_URL = 'http://www.apir.com.au/public/investmentFundSearchResults.jsp'; # APIR provides a site that maps the names of Funds into their own # specific APIR code and the ISIN of the fund through their website. sub apir_map { my $searchString = shift; my $name_munge = shift; my $mech = WWW::Mechanize->new(); $mech->get($APIR_SEARCH_URL); $mech->submit_form( with_fields => { name => $searchString, } ); my $rowCount = 0; my $lastRowCount = -1; my %map = {}; while ($rowCount != $lastRowCount) { $mech->get($APIR_RESULTS_URL . "?placeHolder=1&name=" . $searchString . "&start=" . $rowCount); my $map_response = $mech->response(); my $te = HTML::TableExtract->new( headers => ["APIR. Code", "ISIN", "Product name"] ); $te->parse($map_response->content); $lastRowCount = $rowCount; foreach my $row ($te->rows) { my ($APIR, $ISIN, $apir_name) = @$row; my $mlc_name = &$name_munge($apir_name); %map->{$mlc_name, "name"} = $mlc_name; %map->{$mlc_name, "APIR"} = $APIR; %map->{$mlc_name, "ISIN"} = $ISIN; %map->{$ISIN, "name"} = $mlc_name; %map->{$ISIN, "APIR"} = $APIR; %map->{$ISIN, "ISIN"} = $ISIN; %map->{$APIR, "name"} = $mlc_name; %map->{$APIR, "APIR"} = $APIR; %map->{$APIR, "ISIN"} = $ISIN; #print "DBG: in apir_map:" . $map{$APIR, "ISIN"} . ":\n"; $rowCount++; } } return %map if wantarray; return \%map; } sub find_fund { my ($searchString, $fundKey) = @_; my %map = apir_map($searchString); return ($map{$fundKey, "name"}, $map{$fundKey, "APIR"}, $map{$fundKey, "ISIN"}); } 1; =head1 NAME Finance::Quote::APIR - Obtain fund name mapping from the APIR =head1 SYNOPSIS use Finance::Quote; use Finance::Quote::APIR; %m = Finance::Quote::APIR::apir_map(<search string>); where <search string> is a string by which to constrain the funds to map. This will ensure that you get the right ISIN and APIR when there are the same fund names for different MLC products that can have different prices. $stock_code = $m{"My Fund Name", "APIR"}; $stock_isin = $m{"My Fund Name", "ISIN"}; $stock_name = $m{"ISINCODE001", "name"}; $q = Finance::Quote->new; %stockinfo = $q->fetch("MLC", $stock_code); # Only query MLC =head1 DESCRIPTION MLC is an Australian fund manager that provides end of day prices for their funds on their website. However, they only provide the prices indexed by fund name and the same product, say "Balanced Portfolio Fund" is offered in many different packages under different ISIN and as a result with potentially different prices. In order to use these funds in Finance::Quote it is useful if not necessary to map between the name of the fund and the actual ISIN or APIR code that should be used to identify the specific instance of the product in which to denominate a holding in the fund, such as "commodity" in GNUCash. To that end this module, provides the ability to connect to a company called APIR and download a subset of funds and from that subset produce a mapping of Name, APIR Code and ISIN to solve this problem. =head1 SEE ALSO APIR, http://www.apir.com.au =cut
Subject: MLC.pm
#!/usr/bin/perl -w # # Copyright (C) 1998, Dj Padzensky <djpadz@padz.net> # Copyright (C) 1998, 1999 Linas Vepstas <linas@linas.org> # Copyright (C) 2000, Yannick LE NY <y-le-ny@ifrance.com> # Copyright (C) 2000, Paul Fenwick <pjf@cpan.org> # Copyright (C) 2000, Brent Neal <brentn@users.sourceforge.net> # Copyright (C) 2001, Leigh Wedding <leigh.wedding@telstra.com> # Copyright (C) 2003, Ian Dall <ian@beware.dropbear.id.au> # Copyright (C) 2009, Philip Haynes <philip@london.mine.nu> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA # # # This code derived from Padzensky's work on package Finance::YahooQuote, # but extends its capabilites to additional data sources. # # This code was developed as part of GnuCash <http://www.gnucash.org/> require 5.005; use strict; package Finance::Quote::MLC; use HTTP::Request::Common; use LWP::UserAgent; use HTML::TableExtract; use WWW::Mechanize; use Finance::Quote::APIR; use vars qw/$MLC_URL $MLC_EXCHANGE $VERSION/; $VERSION = '0.1'; $MLC_URL = 'https://www.mlc.com.au/masterkeyWeb/execute/UnitPrices'; $MLC_EXCHANGE = "MLC Asset Management"; sub methods { return ( mlc => \&mlc_masterkey_investment_service, mlc_masterkey_unit_trust => \&mlc_masterkey_unit_trust, mlc_masterkey_allocated_pension => \&mlc_masterkey_allocated_pension, mlc_masterkey_investment_service => \&mlc_masterkey_investment_service, mlc_masterkey_investment_service_fundamentals => \&mlc_masterkey_investment_service_fundamentals ) } { my @labels = qw/name last bid ask date isodate currency/; sub labels { return ( mlc => \@labels, mlc_masterkey_unit_trust => \@labels, mlc_masterkey_investment_service => \@labels, mlc_masterkey_investment_service_fundamentals => \@labels ); } } # MLC Asset Management (MLC) # MLC provides free end of day quotes through their webpage. # sub mlc_generic { my $productMLC = shift; my $apirSearch = shift; my $name_munge = shift; my $quoter = shift; my @stocks = @_; return unless @stocks; my %info; my $ua = $quoter->user_agent; my $response = $ua->request(GET $MLC_URL); unless ($response->is_success) { foreach my $stock (@stocks) { $info{$stock,"success"} = 0; $info{$stock,"errormsg"} = "HTTP session failed"; } return wantarray() ? %info : \%info; } my @rows; foreach (split "\\n", $response->content) { chomp; chop; if ($_ =~ /product[0-9]*funds\[[0-9]*\]=.*/) { s/"//g; my ($arrayString, @info) = split "[=,]", $_; push @rows, [@info]; } } unless (@rows) { foreach my $stock (@stocks) { $info{$stock,"success"} = 0; $info{$stock,"errormsg"} = "Failed to parse HTML table."; } return wantarray() ? %info : \%info; } my %map = Finance::Quote::APIR::apir_map($apirSearch, $name_munge); # Pack the resulting data into our structure. foreach my $row (@rows) { my $name = shift(@$row); next if !defined($name); my $product = shift(@$row); next if !($product eq $productMLC); # Delete spaces and '*' which sometimes appears after the code. # Also delete high bit characters. $name =~ tr/ \000-\037\200-\377/ /s; $name =~ s/^ *//; $name =~ s/ *$//; my $stock = $map{$name, "APIR"}; if (! $stock) { next }; $info{$stock,'symbol'} = $stock; $info{$stock,'name'} = $name; #ignore records with any interest rate denominated values, indicated by a #"%" symbol at the end of the price next if join(',', @$row) =~ /,[0-9.]+\%/; foreach my $label (qw/date bid ask/) { $info{$stock,$label} = shift(@$row); # Again, get rid of nasty high-bit characters. $info{$stock,$label} =~ tr/\200-\377//d unless ($label eq "name"); } $info{$stock,'last'} = $info{$stock,'bid'}; $quoter->store_date(\%info, $stock, {eurodate => $info{$stock,'date'}}); $info{$stock, "currency"} = "AUD"; $info{$stock, "method"} = (reverse(split(/:+/, (caller(1))[3])))[0] ; $info{$stock, "exchange"} = $MLC_EXCHANGE; $info{$stock, "product"} = $product; $info{$stock, "price"} = $info{$stock,"last"}; $info{$stock, "success"} = 1; } return %info if wantarray; return \%info; } sub unit_trust_name_munge { my $name = shift; $name =~ s/MasterKey Unit Trust - //; $name =~ s/MLC MasterKey Unit Trust Horizon/MLC MasterKey Horizon/; return $name; }; sub mlc_masterkey_investment_service { my $quoter = shift; my @stocks = @_; return unless @stocks; return mlc_generic("MasterKey Investment Service", "MLC%20MasterKey%20Unit%20Trust", \&unit_trust_name_munge, $quoter, @stocks) } # This one is broken, FIX the function to do the fund name munging sub mlc_masterkey_investment_service_fundamentals { my $quoter = shift; my @stocks = @_; return unless @stocks; return mlc_generic("MasterKey Investment Service Fundamentals", "MLC%20MasterKey%20Unit%20Trust", \&unit_trust_name_munge, $quoter, @stocks) } sub mlc_masterkey_unit_trust { my $quoter = shift; my @stocks = @_; return unless @stocks; return mlc_generic("MasterKey Unit Trust", "MLC%20MasterKey%20Unit%20Trust", \&unit_trust_name_munge, $quoter, @stocks) } sub mlc_masterkey_allocated_pension { my $quoter = shift; my @stocks = @_; return unless @stocks; return mlc_generic("MasterKey Allocated Pension", "MLC%20MasterKey%20Allocated", \&unit_trust_name_munge, $quoter, @stocks) } 1; =head1 NAME Finance::Quote::MLC - Obtain quotes from the MLC Asset Management. =head1 SYNOPSIS use Finance::Quote; $q = Finance::Quote->new; %stockinfo = $q->fetch("MLC","MLC0011AU"); # Only query MLC =head1 DESCRIPTION This module obtains information from the MLC Financial Planning organisation, the wealth management division of the National Australia Bank http://www.mlc.com.au/mlc This module is loaded by default on a Finance::Quote object. It's also possible to load it explicity by placing "MLC" in the argument list to Finance::Quote->new(). Unfortunately MLC has a mechanism by which the same fund names are offered under several "products" and these funds have different prices depending on the product under which they are offered. =head2 MLC GENERIC LOOKUP FUNCTION This function B<mlc_generic()> implements a generic lookup on the MLC website to retrieve some prices of MLC funds. It takes 5 parameters; =over =item MLC Product The text string that identifies the MLC product under which the funds are offered. Whilst technically this is just a string that is matched against the name of the funds published by MLC, it probably makes sense to constrain it to the known values of the different products in their offering. This includes; MasterKey Investment Service =item APIR Search String The text string that represents the equivalent section of the APIR product database to return the correct prices for the given MLC product. The specific mappings must be known by the programmers adding instances of specific MLC lookup functions. =item Name Munging Function A reference to the function that will munge the APIR name into the MLC name to allow straightforward matching of funds in the two systems. These functions should receive a single paramter, a text string, that is the name of the fund and return a single text string that is the new name as thhe MLC website publishes it. =item Quoter A reference to the Quoter object from the L<Finance::Quote|Finance::Quote> package. =item Stocks The list of stock codes for which prices should be found. =back =head1 LABELS RETURNED The following labels may be returned by Finance::Quote::MLC: name, date, bid, ask, last, currency and price. =head1 SEE ALSO MLC Asset Management, http://www.mlc.com.au/mlc =cut
I made a Finance::Quote::MLC module too, available on cpan, which may be of some value. I combined the fund and product name parts in the "symbol". It comes out very long, but I couldn't spot any shorter symbols etc in the web site at the time. The names have seemed fairly stable over the years though.