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