The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright 2014, 2015 Kevin Ryde

# Finance-Quote-Grab 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 3, or (at your option) any
# later version.
#
# Finance-Quote-Grab 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, see <http://www.gnu.org/licenses/>.

package Finance::Quote::Ghana;
use 5.004;
use strict;
use Carp;

# uncomment this to run the ### lines
# use Smart::Comments;

use vars '$VERSION';
$VERSION = 14;


use constant GHANA_MARKET_URL =>
  'http://www.gse.com.gh/index1.php?linkid=5&sublinkid=12';
   
sub methods {
  return (ghana => \&ghana_quotes);
}
sub labels {
  return (ghana => [ qw(date isodate
                        isin year_range
                        open net bid ask volume last
                        currency
                        method source success errormsg
                      ) ]);
}

sub ghana_quotes {
  my ($fq, @symbol_list) = @_;
  if (! @symbol_list) { return; }

  # The gse server (an Apache) will serve gzipped, so accept_decodable()
  # here saves some data transmission.
  #
  # user_agent() is set only to the package.  Would normally include the
  # libwww-perl identifier too, but some bad setup on the gse server gives
  # "406 Not acceptable" if more than one identifier.
  #
  require HTTP::Request;
  my $req = HTTP::Request->new ('GET', GHANA_MARKET_URL);
  $req->accept_decodable; # using decoded_content() below
  $req->user_agent (__PACKAGE__."/".$VERSION);
  ### req: $req->as_string

  my $ua = $fq->user_agent;
  my $resp = $ua->request ($req);
  ### resp headers: $resp->headers->as_string

  my %quotes;
  _parse ($fq, $resp, \%quotes, \@symbol_list);
  return wantarray() ? %quotes : \%quotes;
}

sub _parse {
  my ($fq, $resp, $quotes, $symbol_list) = @_;

  foreach my $symbol (@$symbol_list) {
    $quotes->{$symbol,'method'}   = 'ghana';
    $quotes->{$symbol,'source'}   = __PACKAGE__;
    $quotes->{$symbol,'success'}  = 0;
  }

  if (! $resp->is_success) {
    _errormsg ($quotes, $symbol_list, $resp->status_line);
    return;
  }
  my $content = $resp->decoded_content (raise_error => 1, charset => 'none');

  # Eg. <span class="datetradeddate">Thursday, February 05th, 2015</span>
  $content =~ m{<span class="datetradeddate">[a-z]+, ([a-z]+) (\d)+[a-z]*, (\d\d\d\d)</span>}i
    or die "GSE: daily page cannot find trade date\n";
  my $date = "$1/$2/$3";  # "October/03/2014"

  require HTML::TableExtract;
  my $te = HTML::TableExtract->new
    (headers => [ # qr/Session/i,
                 qr/ISIN/i,
                 qr/Share Code/i,
                 qr/Year High/i,
                 qr/Year Low/,
                 qr/Previous Closing Price VWAP/i,
                 qr/Opening/i,
                 qr/Closing Price VWAP/,
                 qr/Price Change/i,
                 qr/Closing Bid Price/i,
                 qr/Closing Offer Price/i,
                 qr/Total Shares Traded/i,
                 qr/Last Transaction Price/i,
                ]);
  $te->parse($content);
  my $ts = $te->first_table_found;
  if (! $ts) {
    _errormsg ($quotes, $symbol_list, 'rates table not found in HTML');
    return;
  }

  my %want_symbol;
  @want_symbol{@$symbol_list} = (); # hash slice
  my %seen_symbol;

  foreach my $row (@{$ts->rows()}) {
    ### $row

    my ($isin, $symbol, $year_high, $year_low,
        $previous_vwap, $open, $close_vwap, $change,
        $close_bid, $close_offer, $volume, $last)
      = @$row;
    if (! exists $want_symbol{$symbol}) { next; } # unwanted row

    # volume is for example "58,600"
    # strip the commas
    $volume =~ tr/,//d;

    # volume has been seen as "0.00", eg. from GLD
    # prefer to return it as an integer
    $volume =~ s/\.0*$//;

    $fq->store_date($quotes, $symbol, {usdate => $date});
    $quotes->{$symbol,'isin'}       = $isin;
    $quotes->{$symbol,'year_range'} = "$year_high-$year_low";
    $quotes->{$symbol,'open'}       = $open;
    $quotes->{$symbol,'net'}        = $change;
    $quotes->{$symbol,'bid'}        = $close_bid    if defined $close_bid;
    $quotes->{$symbol,'ask'}        = $close_offer  if defined $close_offer;
    $quotes->{$symbol,'volume'}     = $volume;
    $quotes->{$symbol,'last'}       = $last;
    $quotes->{$symbol,'currency'}   = 'GHS';

    # $quotes->{$symbol,'previous_vwap'} = $previous_vwap;
    # $quotes->{$symbol,'vwap'}          = $close_vwap;
    # $quotes->{$symbol,'copyright_url'} = COPYRIGHT_URL;
    $quotes->{$symbol,'success'}  = 1;
    $seen_symbol{$symbol} = 1;
  }

  # any not seen
  delete @want_symbol{keys %seen_symbol}; # hash slice
  foreach my $symbol (keys %want_symbol) {
    $quotes->{$symbol,'errormsg'} = 'No such symbol';
  }
}

sub _errormsg {
  my ($quotes, $symbol_list, $errormsg) = @_;
  foreach my $symbol (@$symbol_list) {
    $quotes->{$symbol,'errormsg'} = $errormsg;
  }
}

1;
__END__

=head1 NAME

Finance::Quote::Ghana - download quotes from Ghana Stock Exchange

=for Finance_Quote_Grab symbols CAL

=head1 SYNOPSIS

 use Finance::Quote;
 my $fq = Finance::Quote->new ('Ghana');
 my %quotes = $fq->fetch('ghana', 'CAL');

=head1 DESCRIPTION

This module downloads share prices from the Ghana Stock Exchange,

=over 4

L<http://www.gse.com.gh>

=back

Using the market trading results page

=over 4

L<http://www.gse.com.gh/index1.php?linkid=5&sublinkid=12>

=back

=head2 Fields

The following standard C<Finance::Quote> fields are available

=for Finance_Quote_Grab fields flowed standard

    date isodate currency
    open last net volume
    bid ask
    year_range
    method source success errormsg

Plus the following extra

=for Finance_Quote_Grab fields table extra

    isin              ISIN share code

=head1 SEE ALSO

L<Finance::Quote>, L<LWP>

GSE web site L<http://www.gse.com.gh>

=head1 HOME PAGE

L<http://user42.tuxfamily.org/finance-quote-grab/index.html>

=head1 LICENCE

Copyright 2014, 2015 Kevin Ryde

Finance-Quote-Grab 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 3, or (at your option) any later
version.

Finance-Quote-Grab 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
Finance-Quote-Grab; see the file F<COPYING>.  If not, see
L<http://www.gnu.org/licenses/>.

=cut