Finance::Currency::Convert::Yahoo - convert currencies using Yahoo


Finance-Currency-Convert-Yahoo documentation Contained in the Finance-Currency-Convert-Yahoo distribution.

Index


Code Index:

NAME

Top

Finance::Currency::Convert::Yahoo - convert currencies using Yahoo

SYNOPSIS

Top

	use Finance::Currency::Convert::Yahoo;
	$Finance::Currency::Convert::Yahoo::CHAT = 1;
	$_ = Finance::Currency::Convert::Yahoo::convert(1,'USD','GBP');
	print defined($_)? "Is $_\n" : "Error.";
	exit;

	# See the currencies in a dirty way:
	use Finance::Currency::Convert::Yahoo;
	use Data::Dumper;
	warn %Finance::Currency::Convert::Yahoo::currencies;
	exit;

DESCRIPTION

Top

Using Finance.Yahoo.com, converts a sum between two currencies.

USE

Top

Call the module's &convert routine, supplying three arguments: the amount to convert, and the currencies to convert from and to.

Codes are used to identify currencies: you may view them in the values of the %currencies hash, where keys are descriptions of the currencies.

In the event that attempts to convert fail, you will recieve undef in response, with errors going to STDERR, and notes displayed if the modules global $CHAT is defined.

SUBROUTINE convert

	$value = &convert( $amount_to_convert, $from, $to);

Requires the sum to convert, and two symbols to represent the source and target currencies.

In more detail, access http://finance.yahoo.com/d/quotes.csv?s=GBPEUR=X&f=l1, where the value of s (in the example, GBPUSD) is the value of the source and target currencies, and the rest is stuff I've not looked into....

DEPRECATED SUBROUTINE deprecated_convert

The old convert routine: accesses http://finance.yahoo.com/m5?a=amount&s=start&t=to, where start is the currency being converted, to is the target currency, and amount is the amount being converted. The latter is a number; the former two codes defined in our %currencies hash. (For the date this was last checked, print $DATE).

EXPORTS

Top

None.

REVISIONS

Top

Please see the enclosed file CHANGES.

PROBLEMS?

Top

If this doesn't work, Yahoo have probably changed their URI or HTML format. Let me know and I'll fix the code. Or by all means send a patch. Please don't just post a bad review on CPAN, I don't get CC'd them.

SEE ALSO

Top

LWP::UserAgent: HTTP::Request; http://www.macosxhints.com/article.php?story=20050622055810252; http://www.gummy-stuff.org/Yahoo-data.htm.

AUTHOR

Top

Lee Goddard, lgoddard -at- cpan -dot- org.

COPYRIGHT

Top


Finance-Currency-Convert-Yahoo documentation Contained in the Finance-Currency-Convert-Yahoo distribution.
package Finance::Currency::Convert::Yahoo;

use vars qw/$VERSION $DATE $CHAT %currencies/;

$VERSION = 0.2;
$DATE = "14 December 2005";

use strict;
use Carp;
use warnings;
use LWP::UserAgent;
use HTTP::Request;

#
# Glabal variables
#

$CHAT = 0;		# Set for real-time notes to STDERR

# Should have been %CURRENCIES but too late now.
our %CURRENCIES = %currencies = (
	'AFA'=>'Afghanistan Afghani', 	'ALL'=>'Albanian Lek', 	'DZD'=>'Algerian Dinar',
	'ADF'=>'Andorran Franc', 	'ADP'=>'Andorran Peseta', 	'ARS'=>'Argentine Peso',
	'AWG'=>'Aruba Florin', 	'AUD'=>'Australian Dollar', 	'ATS'=>'Austrian Schilling',
	'BSD'=>'Bahamian Dollar', 	'BHD'=>'Bahraini Dinar', 	'BDT'=>'Bangladesh Taka',
	'BBD'=>'Barbados Dollar', 	'BEF'=>'Belgian Franc', 	'BZD'=>'Belize Dollar',
	'BMD'=>'Bermuda Dollar', 	'BTN'=>'Bhutan Ngultrum', 	'BOB'=>'Bolivian Boliviano',
	'BWP'=>'Botswana Pula', 	'BRL'=>'Brazilian Real', 	'GBP'=>'British Pound',
	'BND'=>'Brunei Dollar', 	'BIF'=>'Burundi Franc', 	'XOF'=>'CFA Franc (BCEAO)',
	'XAF'=>'CFA Franc (BEAC)', 	'KHR'=>'Cambodia Riel', 	'CAD'=>'Canadian Dollar',
	'CVE'=>'Cape Verde Escudo', 	'KYD'=>'Cayman Islands Dollar', 	'CLP'=>'Chilean Peso',
	'CNY'=>'Chinese Yuan', 	'COP'=>'Colombian Peso', 	'KMF'=>'Comoros Franc',
	'CRC'=>'Costa Rica Colon', 	'HRK'=>'Croatian Kuna', 	'CUP'=>'Cuban Peso',
	'CYP'=>'Cyprus Pound', 	'CZK'=>'Czech Koruna', 	'DKK'=>'Danish Krone',
	'DJF'=>'Dijibouti Franc', 	'DOP'=>'Dominican Peso', 	'NLG'=>'Dutch Guilder',
	'XCD'=>'East Caribbean Dollar', 	'ECS'=>'Ecuadorian Sucre', 	'EGP'=>'Egyptian Pound',
	'SVC'=>'El Salvador Colon', 	'EEK'=>'Estonian Kroon', 	'ETB'=>'Ethiopian Birr',
	'EUR'=>'Euro', 	'FKP'=>'Falkland Islands Pound', 	'FJD'=>'Fiji Dollar',
	'FIM'=>'Finnish Mark', 	'FRF'=>'French Franc', 	'GMD'=>'Gambian Dalasi',
	'DEM'=>'German Mark', 	'GHC'=>'Ghanian Cedi', 	'GIP'=>'Gibraltar Pound',
	'XAU'=>'Gold Ounces', 	'GRD'=>'Greek Drachma', 	'GTQ'=>'Guatemala Quetzal',
	'GNF'=>'Guinea Franc', 	'GYD'=>'Guyana Dollar', 	'HTG'=>'Haiti Gourde',
	'HNL'=>'Honduras Lempira', 	'HKD'=>'Hong Kong Dollar', 	'HUF'=>'Hungarian Forint',
	'ISK'=>'Iceland Krona', 	'INR'=>'Indian Rupee', 	'IDR'=>'Indonesian Rupiah',
	'IQD'=>'Iraqi Dinar', 	'IEP'=>'Irish Punt', 	'ILS'=>'Israeli Shekel',
	'ITL'=>'Italian Lira', 	'JMD'=>'Jamaican Dollar', 	'JPY'=>'Japanese Yen',
	'JOD'=>'Jordanian Dinar', 	'KZT'=>'Kazakhstan Tenge', 	'KES'=>'Kenyan Shilling',
	'KRW'=>'Korean Won', 	'KWD'=>'Kuwaiti Dinar', 	'LAK'=>'Lao Kip', 	'LVL'=>'Latvian Lat',
	'LBP'=>'Lebanese Pound', 	'LSL'=>'Lesotho Loti', 	'LRD'=>'Liberian Dollar',
	'LYD'=>'Libyan Dinar', 	'LTL'=>'Lithuanian Lita', 	'LUF'=>'Luxembourg Franc',
	'MOP'=>'Macau Pataca', 	'MKD'=>'Macedonian Denar', 	'MGF'=>'Malagasy Franc',
	'MWK'=>'Malawi Kwacha', 	'MYR'=>'Malaysian Ringgit', 	'MVR'=>'Maldives Rufiyaa',
	'MTL'=>'Maltese Lira', 	'MRO'=>'Mauritania Ougulya', 	'MUR'=>'Mauritius Rupee',
	'MXN'=>'Mexican Peso', 	'MDL'=>'Moldovan Leu', 	'MNT'=>'Mongolian Tugrik',
	'MAD'=>'Moroccan Dirham', 	'MZM'=>'Mozambique Metical', 	'MMK'=>'Myanmar Kyat',
	'NAD'=>'Namibian Dollar', 	'NPR'=>'Nepalese Rupee', 	'ANG'=>'Neth Antilles Guilder',
	'NZD'=>'New Zealand Dollar', 	'NIO'=>'Nicaragua Cordoba', 	'NGN'=>'Nigerian Naira',
	'KPW'=>'North Korean Won', 	'NOK'=>'Norwegian Krone', 	'OMR'=>'Omani Rial',
	'XPF'=>'Pacific Franc', 	'PKR'=>'Pakistani Rupee', 	'XPD'=>'Palladium Ounces',
	'PAB'=>'Panama Balboa', 	'PGK'=>'Papua New Guinea Kina', 	'PYG'=>'Paraguayan Guarani',
	'PEN'=>'Peruvian Nuevo Sol', 	'PHP'=>'Philippine Peso', 	'XPT'=>'Platinum Ounces',
	'PLN'=>'Polish Zloty', 	'PTE'=>'Portuguese Escudo', 	'QAR'=>'Qatar Rial',
	'ROL'=>'Romanian Leu', 	'RUB'=>'Russian Rouble', 	'WST'=>'Samoa Tala',
	'STD'=>'Sao Tome Dobra', 	'SAR'=>'Saudi Arabian Riyal', 	'SCR'=>'Seychelles Rupee',
	'SLL'=>'Sierra Leone Leone', 	'XAG'=>'Silver Ounces', 	'SGD'=>'Singapore Dollar',
	'SKK'=>'Slovak Koruna', 	'SIT'=>'Slovenian Tolar', 	'SBD'=>'Solomon Islands Dollar',
	'SOS'=>'Somali Shilling', 	'ZAR'=>'South African Rand', 	'ESP'=>'Spanish Peseta',
	'LKR'=>'Sri Lanka Rupee', 	'SHP'=>'St Helena Pound', 	'SDD'=>'Sudanese Dinar',
	'SRG'=>'Surinam Guilder', 	'SZL'=>'Swaziland Lilageni', 	'SEK'=>'Swedish Krona',
	'CHF'=>'Swiss Franc', 	'SYP'=>'Syrian Pound', 	'TWD'=>'Taiwan Dollar',
	'TZS'=>'Tanzanian Shilling', 	'THB'=>'Thai Baht', 	'TOP'=>"Tonga Pa'anga",
	'TTD'=>'Trinida and Tobago Dollar', 	'TND'=>'Tunisian Dinar', 	'TRL'=>'Turkish Lira',
	'USD'=>'US Dollar', 	'AED'=>'UAE Dirham', 	'UGX'=>'Ugandan Shilling',
	'UAH'=>'Ukraine Hryvnia', 	'UYU'=>'Uruguayan New Peso', 	'VUV'=>'Vanuatu Vatu',
	'VEB'=>'Venezuelan Bolivar', 	'VND'=>'Vietnam Dong', 	'YER'=>'Yemen Riyal',
	'YUM'=>'Yugoslav Dinar', 	'ZMK'=>'Zambian Kwacha', 	'ZWD'=>'Zimbabwe Dollar'
);


sub convert { my ($amount, $from, $to) = (shift,shift,shift);
	die "Please call as ...::convert(\$amount,\$from,\$to) " unless (defined $amount and defined $from and defined $to);
	carp "No such currency code as <$from>." and return undef if not exists $currencies{$from};
	carp "No such currency code as <$to>." and return undef if not exists $currencies{$to};
	carp "Please supply a positive sum to convert <received $amount>." and return undef if $amount<0;
	warn "Converting <$amount> from <$from> to <$to> " if $CHAT;
	my ($value);
	for my $attempt (0..3){
		warn "Attempt $attempt ...\n" if $CHAT;
		$value = _get_document_csv($amount,$from,$to);
		# Can't really say "last if defined $doc" as $doc may be a Yahoo 404-like error?
		last if defined $value;
	}
	return $value;
}

sub deprecated_convert { my ($amount, $from, $to) = (shift,shift,shift);
	require HTML::TokeParser;
	import HTML::TokeParser;
	die 'You not have HTML::TokeParser...?' unless HTML::TokeParser->VERSION;
	die "Please call as ...::convert(\$amount,\$from,\$to) " unless (defined $amount and defined $from and defined $to);
	carp "No such currency code as <$from>." and return undef if not exists $currencies{$from};
	carp "No such currency code as <$to>." and return undef if not exists $currencies{$to};
	carp "Please supply a positive sum to convert <received $amount>." and return undef if $amount<0;
	warn "Converting <$amount> from <$from> to <$to> " if $CHAT;
	my ($doc,$result);
	for my $attempt (0..3){
		warn "Attempt $attempt ...\n" if $CHAT;
		$doc = _get_document_html($amount,$from,$to);
		# Can't really say "last if defined $doc"
		# as $doc may be a Yahoo 404-like error?
		last if defined $doc;
	}
	if (defined $doc){
		if ($result = _extract_data($doc)){
			warn "Got doc, result is $result" if defined $CHAT;
		}
	}
	if (defined $doc and defined $result){
		warn "Result:$result\n" if defined $result and defined $CHAT;
		return $result;
	} elsif (defined $doc and not defined $result){
		carp "Connected to Yahoo but could not read the page: sorry" if defined $CHAT;
		return undef;
	} else {
		carp "Could not connect to Yahoo" if defined $CHAT;
		return undef;
	}
}


#
# PRIVATE SUB get_document_csv
# Accepts: amount, starting currency, target currency
# Returns: HTML content
# URI: http://finance.yahoo.com/d/quotes.csv?s=GBPEUR=X&f=l1
#
sub _get_document_csv { my ($amount,$from,$to) = (shift,shift,shift);
	die "get_document requires a \$amount,\$from_currency,\$target_currency arrity" unless (defined $amount and defined $to and defined $from);

	my $ua = LWP::UserAgent->new;												# Create a new UserAgent
	$ua->agent('Mozilla/25.'.(localtime)." (PERL ".__PACKAGE__." $VERSION");	# Give it a type name

	my $url =
		'http://finance.yahoo.com/d/quotes.csv?'
		. 's='.$from.$to
		. '=X&f=l1'
	;
	warn "Attempting to access <$url> ...\n" if $CHAT;

	# Format URL request
	my $req = new HTTP::Request ('GET',$url) or die "...could not GET.\n" and return undef;
	my $res = $ua->request($req);						# $res is the object UA returned
	if (not $res->is_success()) {						# If successful
		warn"...failed to retrieve currency document from Yahoo...\nTried: $url\n";
		return undef;
	}
	warn "...ok.\n" if $CHAT;

	my $r = $res->content;
	$r =~ s/^\s*([\d.]+)\s*$/$1/sg;
	if ($r eq ''){
		warn "...document contained no data/unexpected data\n";
		return undef;
	}

	return $amount * $r;
}



#
# PRIVATE SUB get_document_html
# Accepts: amount, starting currency, target currency
# Returns: HTML content
# URI: http://finance.yahoo.com/currency/convert?amt=1&from=GBP&to=HUF&submit=Convert
#
sub _get_document_html { my ($amount,$from,$to) = (shift,shift,shift);
	die "get_document requires a \$amount,\$from_currency,\$target_currency arrity" unless (defined $amount and defined $to and defined $from);

	my $ua = LWP::UserAgent->new;												# Create a new UserAgent
	$ua->agent('Mozilla/25.'.(localtime)." (PERL ".__PACKAGE__." $VERSION");	# Give it a type name

	my $url =
		'http://finance.yahoo.com/currency/convert?'
		. 'amt='.$amount
		. '&from='.$from
		. '&to='.$to
		. '&submit=Convert'
	;
	warn "Attempting to access <$url> ...\n" if $CHAT;

	# Format URL request
	my $req = new HTTP::Request ('GET',$url) or die "...could not GET.\n" and return undef;
	my $res = $ua->request($req);						# $res is the object UA returned
	if (not $res->is_success()) {						# If successful
		warn"...failed to retrieve currency document.\n" if $CHAT;
		return undef
	}
	warn "...ok.\n" if $CHAT;

	return $res->content;
}


#
# PRIVATE SUB _extract_data
# Accept: HTML doc as arg
# Return amount on success, undef on failure
# NOV  2004: Fifth yfnc_tabledata1 class TD, and bold
# MAY  2003: Sloopy errors fixed. Sorry.
# APR  2003: Data is now in SIXTH table, second row, second (non-header) cell, in bold
# JAN  2003: Data is now in SEVENTH table, second row, second (non-header) cell, in bold
# JULY 2001: Data is in fourth table's fourth TD
# DEC  2001: Data is in FIFTH table
#
sub _extract_data { my $doc = shift;
	my $token;
	my $p = HTML::TokeParser->new(\$doc) or die "Couldn't create TokePraser: $!";
	# Fifth TD and class is 'yfnc_tabledata1'
	for (1..5){
		while ($token = $p->get_token and not (
				@$token[0] eq 'S' and @$token[1] eq 'td'
				and @$token[2]->{class}
				and @$token[2]->{class} eq 'yfnc_tabledata1'
		) ){}
	}
	$token = $p->get_token or return undef;
	return undef if @$token[0] ne 'S' and @$token[1] ne 'b';

	$token = $p->get_token or return undef;
	return undef if @$token[0] ne 'T';

	return @$token[1] =~ /^[\d.,]+$/ ? @$token[1] : undef;
}





# $Finance::Currency::Convert::Yahoo::CHAT=1;
# print Finance::Currency::Convert::Yahoo::convert(1,'EUR','GBP');


1;

__END__