line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Copyright (C) 1998, Dj Padzensky <djpadz@padz.net> |
4
|
|
|
|
|
|
|
# Copyright (C) 1998, 1999 Linas Vepstas <linas@linas.org> |
5
|
|
|
|
|
|
|
# Copyright (C) 2000, Yannick LE NY <y-le-ny@ifrance.com> |
6
|
|
|
|
|
|
|
# Copyright (C) 2000, Paul Fenwick <pjf@cpan.org> |
7
|
|
|
|
|
|
|
# Copyright (C) 2000, Brent Neal <brentn@users.sourceforge.net> |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify |
10
|
|
|
|
|
|
|
# it under the terms of the GNU General Public License as published by |
11
|
|
|
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or |
12
|
|
|
|
|
|
|
# (at your option) any later version. |
13
|
|
|
|
|
|
|
# |
14
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, |
15
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
16
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17
|
|
|
|
|
|
|
# GNU General Public License for more details. |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License |
20
|
|
|
|
|
|
|
# along with this program; if not, write to the Free Software |
21
|
|
|
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
22
|
|
|
|
|
|
|
# 02110-1301, USA |
23
|
|
|
|
|
|
|
# |
24
|
|
|
|
|
|
|
# |
25
|
|
|
|
|
|
|
# This code derived from Padzensky's work on package Finance::YahooQuote, |
26
|
|
|
|
|
|
|
# but extends its capabilites to encompas a greater number of data sources. |
27
|
|
|
|
|
|
|
# |
28
|
|
|
|
|
|
|
# This code was developed as part of GnuCash <http://www.gnucash.org/> |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
package Finance::Quote; |
31
|
|
|
|
|
|
|
|
32
|
62
|
|
|
62
|
|
6839239
|
use strict; |
|
62
|
|
|
|
|
790
|
|
|
62
|
|
|
|
|
2566
|
|
33
|
|
|
|
|
|
|
|
34
|
62
|
|
|
62
|
|
415
|
use constant DEBUG => $ENV{DEBUG}; |
|
62
|
|
|
|
|
159
|
|
|
62
|
|
|
|
|
6445
|
|
35
|
62
|
|
|
62
|
|
21462
|
use if DEBUG, 'Smart::Comments', '###'; |
|
62
|
|
|
|
|
517
|
|
|
62
|
|
|
|
|
453
|
|
36
|
|
|
|
|
|
|
|
37
|
62
|
|
|
62
|
|
34468
|
use Module::Load; |
|
62
|
|
|
|
|
72158
|
|
|
62
|
|
|
|
|
417
|
|
38
|
62
|
|
|
62
|
|
3129
|
use Exporter (); |
|
62
|
|
|
|
|
151
|
|
|
62
|
|
|
|
|
2994
|
|
39
|
62
|
|
|
62
|
|
340
|
use Carp; |
|
62
|
|
|
|
|
128
|
|
|
62
|
|
|
|
|
3741
|
|
40
|
62
|
|
|
62
|
|
26642
|
use Finance::Quote::UserAgent; |
|
62
|
|
|
|
|
224
|
|
|
62
|
|
|
|
|
2346
|
|
41
|
62
|
|
|
62
|
|
33803
|
use HTTP::Request::Common; |
|
62
|
|
|
|
|
149194
|
|
|
62
|
|
|
|
|
4402
|
|
42
|
62
|
|
|
62
|
|
33655
|
use Encode; |
|
62
|
|
|
|
|
920567
|
|
|
62
|
|
|
|
|
5099
|
|
43
|
62
|
|
|
62
|
|
42472
|
use JSON qw( decode_json ); |
|
62
|
|
|
|
|
649495
|
|
|
62
|
|
|
|
|
417
|
|
44
|
|
|
|
|
|
|
|
45
|
62
|
|
|
|
|
14741
|
use vars qw/@ISA @EXPORT @EXPORT_OK @EXPORT_TAGS |
46
|
|
|
|
|
|
|
$TIMEOUT @MODULES %MODULES %METHODS $AUTOLOAD |
47
|
62
|
|
|
62
|
|
9673
|
@CURRENCY_RATES_MODULES $USE_EXPERIMENTAL_UA/; |
|
62
|
|
|
|
|
169
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
our $VERSION = '1.58_01'; # TRIAL VERSION |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
@CURRENCY_RATES_MODULES = qw/ |
52
|
|
|
|
|
|
|
AlphaVantage |
53
|
|
|
|
|
|
|
ECB |
54
|
|
|
|
|
|
|
Fixer |
55
|
|
|
|
|
|
|
OpenExchange |
56
|
|
|
|
|
|
|
YahooJSON |
57
|
|
|
|
|
|
|
/; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
@MODULES = qw/ |
60
|
|
|
|
|
|
|
AEX |
61
|
|
|
|
|
|
|
ASEGR |
62
|
|
|
|
|
|
|
ASX |
63
|
|
|
|
|
|
|
AlphaVantage |
64
|
|
|
|
|
|
|
BSEIndia |
65
|
|
|
|
|
|
|
Bloomberg |
66
|
|
|
|
|
|
|
Bourso |
67
|
|
|
|
|
|
|
BVB |
68
|
|
|
|
|
|
|
CSE |
69
|
|
|
|
|
|
|
Cdnfundlibrary |
70
|
|
|
|
|
|
|
Comdirect |
71
|
|
|
|
|
|
|
Consorsbank |
72
|
|
|
|
|
|
|
Currencies |
73
|
|
|
|
|
|
|
DWS |
74
|
|
|
|
|
|
|
Deka |
75
|
|
|
|
|
|
|
FTfunds |
76
|
|
|
|
|
|
|
Fidelity |
77
|
|
|
|
|
|
|
Finanzpartner |
78
|
|
|
|
|
|
|
Fondsweb |
79
|
|
|
|
|
|
|
Fool |
80
|
|
|
|
|
|
|
Fundata |
81
|
|
|
|
|
|
|
GoldMoney |
82
|
|
|
|
|
|
|
GoogleWeb |
83
|
|
|
|
|
|
|
HU |
84
|
|
|
|
|
|
|
IEXCloud |
85
|
|
|
|
|
|
|
IndiaMutual |
86
|
|
|
|
|
|
|
MarketWatch |
87
|
|
|
|
|
|
|
MorningstarAU |
88
|
|
|
|
|
|
|
MorningstarCH |
89
|
|
|
|
|
|
|
MorningstarJP |
90
|
|
|
|
|
|
|
MorningstarUK |
91
|
|
|
|
|
|
|
NSEIndia |
92
|
|
|
|
|
|
|
NZX |
93
|
|
|
|
|
|
|
OnVista |
94
|
|
|
|
|
|
|
Oslobors |
95
|
|
|
|
|
|
|
SEB |
96
|
|
|
|
|
|
|
SIX |
97
|
|
|
|
|
|
|
Sinvestor |
98
|
|
|
|
|
|
|
Stooq |
99
|
|
|
|
|
|
|
TesouroDireto |
100
|
|
|
|
|
|
|
Tiaacref |
101
|
|
|
|
|
|
|
TMX |
102
|
|
|
|
|
|
|
Tradegate |
103
|
|
|
|
|
|
|
TreasuryDirect |
104
|
|
|
|
|
|
|
Troweprice |
105
|
|
|
|
|
|
|
TSP |
106
|
|
|
|
|
|
|
TwelveData |
107
|
|
|
|
|
|
|
Union |
108
|
|
|
|
|
|
|
XETRA |
109
|
|
|
|
|
|
|
YahooJSON |
110
|
|
|
|
|
|
|
YahooWeb |
111
|
|
|
|
|
|
|
ZA |
112
|
|
|
|
|
|
|
/; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
@ISA = qw/Exporter/; |
115
|
|
|
|
|
|
|
@EXPORT = (); |
116
|
|
|
|
|
|
|
@EXPORT_OK = qw/fidelity troweprice asx tiaacref |
117
|
|
|
|
|
|
|
currency_lookup/; |
118
|
|
|
|
|
|
|
@EXPORT_TAGS = ( all => [@EXPORT_OK]); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
$USE_EXPERIMENTAL_UA = 0; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
################################################################################ |
123
|
|
|
|
|
|
|
# |
124
|
|
|
|
|
|
|
# Private Class Methods |
125
|
|
|
|
|
|
|
# |
126
|
|
|
|
|
|
|
################################################################################ |
127
|
|
|
|
|
|
|
# Autoload method for obsolete methods. This also allows people to |
128
|
|
|
|
|
|
|
# call methods that objects export without having to go through fetch. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub AUTOLOAD { |
131
|
0
|
|
|
0
|
|
0
|
my $method = $AUTOLOAD; |
132
|
0
|
|
|
|
|
0
|
(my $name = $method) =~ s/.*:://; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Force the dummy object (and hence default methods) to be loaded. |
135
|
0
|
|
|
|
|
0
|
_dummy(); |
136
|
|
|
|
|
|
|
|
137
|
0
|
0
|
|
|
|
0
|
if (exists($METHODS{$name})) { |
138
|
62
|
|
|
62
|
|
482
|
no strict 'refs'; ## no critic |
|
62
|
|
|
|
|
172
|
|
|
62
|
|
|
|
|
272724
|
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
*$method = sub { |
141
|
0
|
0
|
|
0
|
|
0
|
my $this = ref($_[0]) ? shift : _dummy(); |
142
|
0
|
|
|
|
|
0
|
$this->fetch($name, @_); |
143
|
0
|
|
|
|
|
0
|
}; |
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
0
|
return &$method; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
carp "$AUTOLOAD does not refer to a known method."; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Dummy destroy function to avoid AUTOLOAD catching it. |
152
|
0
|
|
|
0
|
|
0
|
sub DESTROY { return; } |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# _convert (private object method) |
155
|
|
|
|
|
|
|
# |
156
|
|
|
|
|
|
|
# This function converts between one currency and another. It expects |
157
|
|
|
|
|
|
|
# to receive a hashref to the information, a reference to a list |
158
|
|
|
|
|
|
|
# of the stocks to be converted, and a reference to a list of fields |
159
|
|
|
|
|
|
|
# that conversion should apply to. |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
{ |
162
|
|
|
|
|
|
|
my %conversion; # Conversion lookup table. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub _convert { |
165
|
0
|
|
|
0
|
|
0
|
my $this = shift; |
166
|
0
|
|
|
|
|
0
|
my $info = shift; |
167
|
0
|
|
|
|
|
0
|
my $stocks = shift; |
168
|
0
|
|
|
|
|
0
|
my $convert_fields = shift; |
169
|
0
|
|
|
|
|
0
|
my $new_currency = $this->{"currency"}; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# Skip all this unless they actually want conversion. |
172
|
0
|
0
|
|
|
|
0
|
return unless $new_currency; |
173
|
|
|
|
|
|
|
|
174
|
0
|
|
|
|
|
0
|
foreach my $stock (@$stocks) { |
175
|
0
|
|
|
|
|
0
|
my $currency; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# Skip stocks that don't have a currency. |
178
|
0
|
0
|
|
|
|
0
|
next unless ($currency = $info->{$stock,"currency"}); |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# Skip if it's already in the same currency. |
181
|
0
|
0
|
|
|
|
0
|
next if ($currency eq $new_currency); |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Lookup the currency conversion if we haven't |
184
|
|
|
|
|
|
|
# already. |
185
|
0
|
0
|
|
|
|
0
|
unless (exists $conversion{$currency,$new_currency}) { |
186
|
0
|
|
|
|
|
0
|
$conversion{$currency,$new_currency} = |
187
|
|
|
|
|
|
|
$this->currency($currency,$new_currency); |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# Make sure we have a reasonable currency conversion. |
191
|
|
|
|
|
|
|
# If we don't, mark the stock as bad. |
192
|
0
|
0
|
|
|
|
0
|
unless ($conversion{$currency,$new_currency}) { |
193
|
0
|
|
|
|
|
0
|
$info->{$stock,"success"} = 0; |
194
|
0
|
|
|
|
|
0
|
$info->{$stock,"errormsg"} = |
195
|
|
|
|
|
|
|
"Currency conversion failed."; |
196
|
0
|
|
|
|
|
0
|
next; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# Okay, we have clean data. Convert it. Ideally |
200
|
|
|
|
|
|
|
# we'd like to just *= entire fields, but |
201
|
|
|
|
|
|
|
# unfortunately some things (like ranges, |
202
|
|
|
|
|
|
|
# capitalisation, etc) don't take well to that. |
203
|
|
|
|
|
|
|
# Hence we pull out any numbers we see, convert |
204
|
|
|
|
|
|
|
# them, and stick them back in. That's pretty |
205
|
|
|
|
|
|
|
# yucky, but it works. |
206
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
0
|
foreach my $field (@$convert_fields) { |
208
|
0
|
0
|
|
|
|
0
|
next unless (defined $info->{$stock,$field}); |
209
|
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
0
|
$info->{$stock,$field} = $this->scale_field($info->{$stock,$field},$conversion{$currency,$new_currency}); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Set the new currency. |
214
|
0
|
|
|
|
|
0
|
$info->{$stock,"currency"} = $new_currency; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# ======================================================================= |
220
|
|
|
|
|
|
|
# _dummy (private function) |
221
|
|
|
|
|
|
|
# |
222
|
|
|
|
|
|
|
# _dummy returns a Finance::Quote object. I'd really rather not have |
223
|
|
|
|
|
|
|
# this, but to maintain backwards compatibility we hold on to it. |
224
|
|
|
|
|
|
|
{ |
225
|
|
|
|
|
|
|
my $dummy_obj; |
226
|
|
|
|
|
|
|
sub _dummy { |
227
|
6
|
|
66
|
6
|
|
26
|
return $dummy_obj ||= Finance::Quote->new; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# _load_module (private class method) |
232
|
|
|
|
|
|
|
# _load_module loads a module(s) and registers its various methods for |
233
|
|
|
|
|
|
|
# use. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub _load_modules { |
236
|
14
|
|
|
14
|
|
27
|
my $class = shift; |
237
|
14
|
|
33
|
|
|
42
|
my $baseclass = ref $class || $class; |
238
|
|
|
|
|
|
|
|
239
|
14
|
|
|
|
|
108
|
my @modules = @_; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Go to each module and use them. Also record what methods |
242
|
|
|
|
|
|
|
# they support and enter them into the %METHODS hash. |
243
|
|
|
|
|
|
|
|
244
|
14
|
|
|
|
|
35
|
foreach my $module (@modules) { |
245
|
728
|
|
|
|
|
1592
|
my $modpath = "${baseclass}::${module}"; |
246
|
728
|
100
|
|
|
|
1727
|
unless (defined($MODULES{$modpath})) { |
247
|
|
|
|
|
|
|
|
248
|
260
|
|
|
|
|
443
|
eval { |
249
|
260
|
|
|
|
|
894
|
load $modpath; |
250
|
260
|
|
|
|
|
3505
|
$MODULES{$modpath} = 1; |
251
|
|
|
|
|
|
|
|
252
|
260
|
|
|
|
|
1227
|
my %methodhash = $modpath->methods; |
253
|
260
|
|
|
|
|
886
|
my %labelhash = $modpath->labels; |
254
|
260
|
|
50
|
|
|
3925
|
my $curr_fields_func = $modpath->can("currency_fields") || \&default_currency_fields; |
255
|
260
|
|
|
|
|
739
|
my @currency_fields = &$curr_fields_func; |
256
|
260
|
|
|
|
|
455
|
my %seen; |
257
|
260
|
|
|
|
|
521
|
@currency_fields = grep {!$seen{$_}++} @currency_fields; |
|
3900
|
|
|
|
|
9242
|
|
258
|
|
|
|
|
|
|
|
259
|
260
|
|
|
|
|
850
|
foreach my $method (keys %methodhash) { |
260
|
505
|
|
|
|
|
3762
|
push (@{$METHODS{$method}}, |
261
|
|
|
|
|
|
|
{ name => $module, |
262
|
|
|
|
|
|
|
modpath => $modpath, |
263
|
|
|
|
|
|
|
function => $methodhash{$method}, |
264
|
505
|
|
|
|
|
749
|
labels => $labelhash{$method}, |
265
|
|
|
|
|
|
|
currency_fields => \@currency_fields}); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
}; |
268
|
260
|
50
|
|
|
|
1081
|
carp $@ if $@; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# _smart_compare (private method function) |
274
|
|
|
|
|
|
|
# |
275
|
|
|
|
|
|
|
# This function compares values where the method depends on the |
276
|
|
|
|
|
|
|
# type of the parameters. |
277
|
|
|
|
|
|
|
# val1, val2 |
278
|
|
|
|
|
|
|
# scalar,scaler - test for substring match |
279
|
|
|
|
|
|
|
# scalar,regex - test val1 against val2 regex |
280
|
|
|
|
|
|
|
# array,scalar - return true if any element of array substring matches scalar |
281
|
|
|
|
|
|
|
# array,regex - return true if any element of array matches regex |
282
|
|
|
|
|
|
|
sub _smart_compare { |
283
|
675
|
|
|
675
|
|
1136
|
my ($val1, $val2) = @_; |
284
|
|
|
|
|
|
|
|
285
|
675
|
50
|
|
|
|
1042
|
if ( ref $val1 eq 'ARRAY' ) { |
286
|
0
|
0
|
|
|
|
0
|
if ( ref $val2 eq 'Regexp' ) { |
287
|
0
|
|
|
|
|
0
|
my @r = grep {$_ =~ $val2} @$val1; |
|
0
|
|
|
|
|
0
|
|
288
|
0
|
|
|
|
|
0
|
return @r > 0; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
else { |
291
|
0
|
|
|
|
|
0
|
my @r = grep {$_ =~ /$val2/} @$val1; |
|
0
|
|
|
|
|
0
|
|
292
|
0
|
|
|
|
|
0
|
return @r > 0; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
else { |
296
|
675
|
100
|
|
|
|
1028
|
if ( ref $val2 eq 'Regexp' ) { |
297
|
504
|
|
|
|
|
1450
|
return $val1 =~ $val2; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
else { |
300
|
171
|
|
|
|
|
358
|
return index($val1, $val2) > -1 |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# This is a list of fields that will be automatically converted during |
306
|
|
|
|
|
|
|
# currency conversion. If a module provides a currency_fields() |
307
|
|
|
|
|
|
|
# function then that list will be used instead. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub get_default_currency_fields { |
310
|
261
|
|
|
261
|
1
|
2691
|
return qw/last high low net bid ask close open day_range year_range |
311
|
|
|
|
|
|
|
eps div cap nav price/; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub get_default_timeout { |
315
|
2
|
|
|
2
|
1
|
295
|
return $TIMEOUT; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# get_methods returns a list of sources which can be passed to fetch to |
319
|
|
|
|
|
|
|
# obtain information. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub get_methods { |
322
|
|
|
|
|
|
|
# Create a dummy object to ensure METHODS is populated |
323
|
1
|
|
|
1
|
1
|
315
|
my $t = Finance::Quote->new(); |
324
|
1
|
50
|
|
|
|
30
|
return(wantarray ? keys %METHODS : [keys %METHODS]); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# return hash: |
328
|
|
|
|
|
|
|
# |
329
|
|
|
|
|
|
|
# quote_methods => hash of |
330
|
|
|
|
|
|
|
# method_name => array of module names |
331
|
|
|
|
|
|
|
# quote_modules => hash of |
332
|
|
|
|
|
|
|
# module_name => array of parameters |
333
|
|
|
|
|
|
|
# currency_modules => hash of |
334
|
|
|
|
|
|
|
# module_name => array of parameters |
335
|
|
|
|
|
|
|
# |
336
|
|
|
|
|
|
|
# { |
337
|
|
|
|
|
|
|
# 'quote_methods' => {'group' => ['module', 'module'], ...}, |
338
|
|
|
|
|
|
|
# 'quote_modules' => {'abc' => ['API_KEY'], ...}, |
339
|
|
|
|
|
|
|
# 'currency_modules' => {'xyz' => [], 'lmn' => ['USER_NAME', 'API_KEY']}, |
340
|
|
|
|
|
|
|
# } |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub get_features { |
343
|
|
|
|
|
|
|
# Create a dummy object to ensure METHODS is populated |
344
|
1
|
|
|
1
|
1
|
10
|
my $t = Finance::Quote->new(currency_rates => {order => \@CURRENCY_RATES_MODULES}); |
345
|
1
|
|
|
|
|
3
|
my $baseclass = ref $t; |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
my %feature = ( |
348
|
80
|
|
|
|
|
100
|
'quote_methods' => {map {$_, [map {$_->{name}} @{$METHODS{$_}}]} keys %METHODS}, |
|
101
|
|
|
|
|
307
|
|
|
80
|
|
|
|
|
158
|
|
349
|
52
|
|
|
|
|
123
|
'quote_modules' => {map {$_, []} @MODULES}, |
350
|
1
|
|
|
|
|
21
|
'currency_modules' => {map {$_, []} @CURRENCY_RATES_MODULES}, |
|
5
|
|
|
|
|
27
|
|
351
|
|
|
|
|
|
|
); |
352
|
|
|
|
|
|
|
|
353
|
1
|
|
|
|
|
18
|
my %mods = ('quote_modules' => $baseclass, |
354
|
|
|
|
|
|
|
'currency_modules' => "${baseclass}::CurrencyRates"); |
355
|
|
|
|
|
|
|
|
356
|
1
|
|
|
|
|
8
|
while (my ($field, $base) = each %mods) { |
357
|
2
|
|
|
|
|
5
|
foreach my $name (keys %{$feature{$field}}) { |
|
2
|
|
|
|
|
13
|
|
358
|
57
|
|
|
|
|
104
|
my $modpath = "${base}::${name}"; |
359
|
|
|
|
|
|
|
|
360
|
57
|
100
|
|
|
|
511
|
if ($modpath->can("parameters")) { |
361
|
9
|
|
|
|
|
14
|
push (@{$feature{$field}->{$name}}, $modpath->parameters()); |
|
9
|
|
|
|
|
38
|
|
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
1
|
|
|
|
|
13
|
return %feature; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# ======================================================================= |
370
|
|
|
|
|
|
|
# new (public class method) |
371
|
|
|
|
|
|
|
# |
372
|
|
|
|
|
|
|
# Returns a new Finance::Quote object. |
373
|
|
|
|
|
|
|
# |
374
|
|
|
|
|
|
|
# Arguments :: |
375
|
|
|
|
|
|
|
# - zero or more module names from the Finance::Quote::get_sources list |
376
|
|
|
|
|
|
|
# - zero or more named parameters, passes as name => value |
377
|
|
|
|
|
|
|
# |
378
|
|
|
|
|
|
|
# Named Parameters :: |
379
|
|
|
|
|
|
|
# - timeout # timeout in seconds for web requests |
380
|
|
|
|
|
|
|
# - failover # boolean value indicating if failover is acceptable |
381
|
|
|
|
|
|
|
# - fetch_currency # currency code for fetch results |
382
|
|
|
|
|
|
|
# - required_labels # array of required labels in fetch results |
383
|
|
|
|
|
|
|
# - <module-name> # hash specific to various Finance::Quote modules |
384
|
|
|
|
|
|
|
# |
385
|
|
|
|
|
|
|
# new() # default constructor |
386
|
|
|
|
|
|
|
# new('a', 'b') # load only modules a and b |
387
|
|
|
|
|
|
|
# new(timeout => 30) # load all default modules, set timeout |
388
|
|
|
|
|
|
|
# new('a', fetch_currency => 'X') # load only module a, use currency X for results |
389
|
|
|
|
|
|
|
# new('z' => {API_KEY => 'K'}) # load all modules, pass hash to module z constructor |
390
|
|
|
|
|
|
|
# new('z', 'z' => {API_KEY => 'K'}) # load only module z and pass hash to its constructor |
391
|
|
|
|
|
|
|
# |
392
|
|
|
|
|
|
|
# Enivornment Variables :: |
393
|
|
|
|
|
|
|
# - FQ_LOAD_QUOTELET # if no modules named in argument list, use ones in this variable |
394
|
|
|
|
|
|
|
# |
395
|
|
|
|
|
|
|
# Return Value :: |
396
|
|
|
|
|
|
|
# - Finanace::Quote object |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub new { |
399
|
|
|
|
|
|
|
# Create and bless object |
400
|
14
|
|
|
14
|
1
|
4593
|
my $self = shift; |
401
|
14
|
|
33
|
|
|
75
|
my $class = ref($self) || $self; |
402
|
|
|
|
|
|
|
|
403
|
14
|
|
|
|
|
34
|
my $this = {}; |
404
|
14
|
|
|
|
|
30
|
bless $this, $class; |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# To add a named parameter: |
407
|
|
|
|
|
|
|
# 0. Document it in the POD for new |
408
|
|
|
|
|
|
|
# 1. Add a default value for $this->{object-name} |
409
|
|
|
|
|
|
|
# 2. Add the 'user-visible-name' => [type, object-name] to %named_parameter |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# Check for FQ_CURRENCY - preferred currency module |
412
|
|
|
|
|
|
|
# Set to AlphaVantage if not set or not in @CURRENCY_RATES_MODULES |
413
|
14
|
|
|
|
|
21
|
my $CURRENCY_MODULE; |
414
|
14
|
50
|
|
|
|
50
|
if (!$ENV{FQ_CURRENCY}) { |
415
|
14
|
|
|
|
|
29
|
$CURRENCY_MODULE='AlphaVantage'; |
416
|
|
|
|
|
|
|
} else { |
417
|
0
|
0
|
|
|
|
0
|
if ( grep( /^$ENV{FQ_CURRENCY}$/, @CURRENCY_RATES_MODULES ) ) { |
418
|
|
|
|
|
|
|
$CURRENCY_MODULE=$ENV{FQ_CURRENCY} |
419
|
0
|
|
|
|
|
0
|
} else { |
420
|
0
|
|
|
|
|
0
|
$CURRENCY_MODULE='AlphaVantage'; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# Default values |
425
|
14
|
|
|
|
|
54
|
$this->{FAILOVER} = 1; |
426
|
14
|
|
|
|
|
35
|
$this->{REQUIRED} = []; |
427
|
14
|
100
|
|
|
|
40
|
$this->{TIMEOUT} = $TIMEOUT if defined($TIMEOUT); |
428
|
14
|
|
|
|
|
46
|
$this->{currency_rates} = {order => [$CURRENCY_MODULE]}; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# Sort out arguments |
431
|
14
|
|
|
|
|
93
|
my %named_parameter = (timeout => ['', 'TIMEOUT'], |
432
|
|
|
|
|
|
|
failover => ['', 'FAILOVER'], |
433
|
|
|
|
|
|
|
fetch_currency => ['', 'currency'], |
434
|
|
|
|
|
|
|
required_labels => ['ARRAY', 'REQUIRED'], |
435
|
|
|
|
|
|
|
currency_rates => ['HASH', 'currency_rates']); |
436
|
|
|
|
|
|
|
|
437
|
14
|
|
|
|
|
30
|
$this->{module_specific_data} = {}; |
438
|
14
|
|
|
|
|
30
|
my @load_modules = (); |
439
|
|
|
|
|
|
|
|
440
|
14
|
|
|
|
|
51
|
for (my $i = 0; $i < @_; $i++) { |
441
|
5
|
50
|
0
|
|
|
18
|
if (exists $named_parameter{$_[$i]}) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
442
|
5
|
50
|
|
|
|
19
|
die "missing value for named parameter $_[$i]" if $i + 1 == @_; |
443
|
5
|
50
|
|
|
|
21
|
die "unexpect type for value of named parameter $_[$i]" if ref $_[$i+1] ne $named_parameter{$_[$i]}[0]; |
444
|
|
|
|
|
|
|
|
445
|
5
|
|
|
|
|
15
|
$this->{$named_parameter{$_[$i]}[1]} = $_[$i+1]; |
446
|
5
|
|
|
|
|
16
|
$i += 1; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
elsif ($i + 1 < @_ and ref $_[$i+1] eq 'HASH') { |
449
|
0
|
|
|
|
|
0
|
$this->{module_specific_data}->{$_[$i]} = $_[$i+1]; |
450
|
0
|
|
|
|
|
0
|
$i += 1; |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
elsif ($_[$i] eq '-defaults') { |
453
|
0
|
|
|
|
|
0
|
push (@load_modules, @MODULES); |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
else { |
456
|
0
|
|
|
|
|
0
|
push (@load_modules, $_[$i]); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
# Honor FQ_LOAD_QUOTELET if @load_modules is empty |
461
|
14
|
50
|
33
|
|
|
67
|
if ($ENV{FQ_LOAD_QUOTELET} and !@load_modules) { |
|
|
50
|
|
|
|
|
|
462
|
0
|
|
|
|
|
0
|
@load_modules = split(' ',$ENV{FQ_LOAD_QUOTELET}); |
463
|
0
|
0
|
|
|
|
0
|
if ($load_modules[0] eq '-defaults') { |
464
|
0
|
|
|
|
|
0
|
shift @load_modules; |
465
|
0
|
|
|
|
|
0
|
push(@load_modules, @MODULES); |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
elsif (@load_modules == 0) { |
469
|
14
|
|
|
|
|
141
|
push(@load_modules, @MODULES); |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
14
|
|
|
|
|
56
|
$this->_load_modules(@load_modules); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Load the currency rate methods |
475
|
14
|
|
|
|
|
38
|
my %currency_check = map { $_ => 1 } @CURRENCY_RATES_MODULES; |
|
70
|
|
|
|
|
179
|
|
476
|
14
|
|
|
|
|
46
|
$this->{currency_rate_method} = []; |
477
|
14
|
|
|
|
|
25
|
foreach my $method (@{$this->{currency_rates}->{order}}) { |
|
14
|
|
|
|
|
50
|
|
478
|
18
|
50
|
|
|
|
89
|
unless (defined($currency_check{$method})) { |
479
|
0
|
|
|
|
|
0
|
carp "Unknown curreny rates method: $method"; |
480
|
0
|
|
|
|
|
0
|
return; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
18
|
|
|
|
|
52
|
my $method_path = "${class}::CurrencyRates::${method}"; |
484
|
18
|
|
|
|
|
35
|
eval { |
485
|
18
|
|
|
|
|
66
|
autoload $method_path; |
486
|
18
|
50
|
|
|
|
1026
|
my $args = exists $this->{currency_rates}->{lc($method)} ? $this->{currency_rates}->{lc($method)} : {}; |
487
|
18
|
|
|
|
|
67
|
my $rate = $method_path->new($args); |
488
|
18
|
100
|
|
|
|
66
|
die unless defined $rate; |
489
|
|
|
|
|
|
|
|
490
|
16
|
|
|
|
|
29
|
push(@{$this->{currency_rate_method}}, $rate); |
|
16
|
|
|
|
|
51
|
|
491
|
|
|
|
|
|
|
}; |
492
|
|
|
|
|
|
|
|
493
|
18
|
100
|
|
|
|
60
|
if ($@) { |
494
|
2
|
|
|
|
|
5
|
next; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
14
|
|
|
|
|
177
|
return $this; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
sub set_default_timeout { |
502
|
1
|
|
|
1
|
1
|
4
|
$TIMEOUT = shift; |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
################################################################################ |
506
|
|
|
|
|
|
|
# |
507
|
|
|
|
|
|
|
# Private Object Methods |
508
|
|
|
|
|
|
|
# |
509
|
|
|
|
|
|
|
################################################################################ |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# _require_test (private object method) |
512
|
|
|
|
|
|
|
# |
513
|
|
|
|
|
|
|
# This function takes an array. It returns true if all required |
514
|
|
|
|
|
|
|
# labels appear in the arrayref. It returns false otherwise. |
515
|
|
|
|
|
|
|
# |
516
|
|
|
|
|
|
|
# This function could probably be made more efficient. |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
sub _require_test { |
519
|
0
|
|
|
0
|
|
0
|
my $this = shift; |
520
|
0
|
|
|
|
|
0
|
my %available; |
521
|
0
|
|
|
|
|
0
|
@available{@_} = (); # Ooooh, hash-slice. :) |
522
|
0
|
|
|
|
|
0
|
my @required = @{$this->{REQUIRED}}; |
|
0
|
|
|
|
|
0
|
|
523
|
0
|
0
|
|
|
|
0
|
return 1 unless @required; |
524
|
0
|
|
|
|
|
0
|
for (my $i = 0; $i < @required; $i++) { |
525
|
0
|
0
|
|
|
|
0
|
return 0 unless exists $available{$required[$i]}; |
526
|
|
|
|
|
|
|
} |
527
|
0
|
|
|
|
|
0
|
return 1; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
################################################################################ |
531
|
|
|
|
|
|
|
# |
532
|
|
|
|
|
|
|
# Public Object Methods |
533
|
|
|
|
|
|
|
# |
534
|
|
|
|
|
|
|
################################################################################ |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# If $str ends with a B like "20B" or "1.6B" then expand it as billions like |
537
|
|
|
|
|
|
|
# "20000000000" or "1600000000". |
538
|
|
|
|
|
|
|
# |
539
|
|
|
|
|
|
|
# This is done with string manipulations so floating-point rounding doesn't |
540
|
|
|
|
|
|
|
# produce spurious digits for values like "1.6" which aren't exactly |
541
|
|
|
|
|
|
|
# representable in binary. |
542
|
|
|
|
|
|
|
# |
543
|
|
|
|
|
|
|
# Is "B" for billions the only abbreviation from Yahoo? |
544
|
|
|
|
|
|
|
# Could extend and rename this if there's also millions or thousands. |
545
|
|
|
|
|
|
|
# |
546
|
|
|
|
|
|
|
# For reference, if the value was just for use within perl then simply |
547
|
|
|
|
|
|
|
# substituting to exponential "1.5e9" might work. But expanding to full |
548
|
|
|
|
|
|
|
# digits seems a better idea as the value is likely to be printed directly |
549
|
|
|
|
|
|
|
# as a string. |
550
|
|
|
|
|
|
|
sub B_to_billions { |
551
|
4
|
|
|
4
|
1
|
18
|
my ($self,$str) = @_; |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# B_to_billions() $str |
554
|
4
|
50
|
|
|
|
29
|
if ($str =~ s/B$//i) { |
555
|
4
|
|
|
|
|
14
|
$str = $self->decimal_shiftup ($str, 9); |
556
|
|
|
|
|
|
|
} |
557
|
4
|
|
|
|
|
37
|
return $str; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# $str is a number like "123" or "123.45" |
561
|
|
|
|
|
|
|
# return it with the decimal point moved $shift places to the right |
562
|
|
|
|
|
|
|
# must have $shift>=1 |
563
|
|
|
|
|
|
|
# eg. decimal_shiftup("123",3) -> "123000" |
564
|
|
|
|
|
|
|
# decimal_shiftup("123.45",1) -> "1234.5" |
565
|
|
|
|
|
|
|
# decimal_shiftup("0.25",1) -> "2.5" |
566
|
|
|
|
|
|
|
# |
567
|
|
|
|
|
|
|
sub decimal_shiftup { |
568
|
19
|
|
|
19
|
1
|
53
|
my ($self, $str, $shift) = @_; |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# delete decimal point and set $after to count of chars after decimal. |
571
|
|
|
|
|
|
|
# Leading "0" as in "0.25" is deleted too giving "25" so as not to end up |
572
|
|
|
|
|
|
|
# with something that might look like leading 0 for octal. |
573
|
19
|
100
|
|
|
|
155
|
my $after = ($str =~ s/(?:^0)?\.(.*)/$1/ ? length($1) : 0); |
574
|
|
|
|
|
|
|
|
575
|
19
|
|
|
|
|
39
|
$shift -= $after; |
576
|
|
|
|
|
|
|
# now $str is an integer and $shift is relative to the end of $str |
577
|
|
|
|
|
|
|
|
578
|
19
|
100
|
|
|
|
48
|
if ($shift >= 0) { |
579
|
|
|
|
|
|
|
# moving right, eg. "1234" becomes "12334000" |
580
|
12
|
|
|
|
|
81
|
return $str . ('0' x $shift); # extra zeros appended |
581
|
|
|
|
|
|
|
} else { |
582
|
|
|
|
|
|
|
# negative means left, eg. "12345" becomes "12.345" |
583
|
|
|
|
|
|
|
# no need to prepend zeros since demanding initial $shift>=1 |
584
|
7
|
|
|
|
|
17
|
substr ($str, $shift,0, '.'); # new '.' at shifted spot from end |
585
|
7
|
|
|
|
|
37
|
return $str; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# ======================================================================= |
590
|
|
|
|
|
|
|
# fetch (public object method) |
591
|
|
|
|
|
|
|
# |
592
|
|
|
|
|
|
|
# Fetch is a wonderful generic fetcher. It takes a method and stuff to |
593
|
|
|
|
|
|
|
# fetch. It's a nicer interface for when you have a list of stocks with |
594
|
|
|
|
|
|
|
# different sources which you wish to deal with. |
595
|
|
|
|
|
|
|
sub fetch { |
596
|
0
|
0
|
|
0
|
1
|
0
|
my $this = ref($_[0]) ? shift : _dummy(); |
597
|
|
|
|
|
|
|
|
598
|
0
|
|
|
|
|
0
|
my $method = lc(shift); |
599
|
0
|
|
|
|
|
0
|
my @stocks = @_; |
600
|
|
|
|
|
|
|
|
601
|
0
|
0
|
|
|
|
0
|
unless (exists $METHODS{$method}) { |
602
|
0
|
|
|
|
|
0
|
carp "Undefined fetch-method $method passed to ". |
603
|
|
|
|
|
|
|
"Finance::Quote::fetch"; |
604
|
0
|
|
|
|
|
0
|
return; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# Temporary Counting - not concerned about return code |
608
|
0
|
|
|
|
|
0
|
my $COUNT_URL = |
609
|
|
|
|
|
|
|
'http://www.panix.com/~hd-fxsts/finance-quote.html?' . $method; |
610
|
0
|
|
|
|
|
0
|
my $count_ua = LWP::UserAgent->new(timeout => 10); |
611
|
0
|
|
|
|
|
0
|
my $count_response = $count_ua->head($COUNT_URL); |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
### COUNT_URL: $COUNT_URL |
614
|
|
|
|
|
|
|
### Code: $count_response->code |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# Failover code. This steps through all available methods while |
617
|
|
|
|
|
|
|
# we still have failed stocks to look-up. This loop only |
618
|
|
|
|
|
|
|
# runs a single time unless FAILOVER is defined. |
619
|
0
|
|
|
|
|
0
|
my %returnhash = (); |
620
|
|
|
|
|
|
|
|
621
|
0
|
|
|
|
|
0
|
foreach my $methodinfo (@{$METHODS{$method}}) { |
|
0
|
|
|
|
|
0
|
|
622
|
0
|
|
|
|
|
0
|
my $funcref = $methodinfo->{"function"}; |
623
|
0
|
0
|
|
|
|
0
|
next unless $this->_require_test(@{$methodinfo->{"labels"}}); |
|
0
|
|
|
|
|
0
|
|
624
|
0
|
|
|
|
|
0
|
my @failed_stocks = (); |
625
|
0
|
|
|
|
|
0
|
%returnhash = (%returnhash,&$funcref($this,@stocks)); |
626
|
|
|
|
|
|
|
|
627
|
0
|
|
|
|
|
0
|
foreach my $stock (@stocks) { |
628
|
|
|
|
|
|
|
push(@failed_stocks,$stock) |
629
|
0
|
0
|
|
|
|
0
|
unless ($returnhash{$stock,"success"}); |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
$this->_convert(\%returnhash,\@stocks, |
633
|
0
|
|
|
|
|
0
|
$methodinfo->{"currency_fields"}); |
634
|
|
|
|
|
|
|
|
635
|
0
|
0
|
|
|
|
0
|
last unless $this->{FAILOVER}; |
636
|
0
|
0
|
|
|
|
0
|
last unless @failed_stocks; |
637
|
0
|
|
|
|
|
0
|
@stocks = @failed_stocks; |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
0
|
0
|
|
|
|
0
|
return wantarray() ? %returnhash : \%returnhash; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
sub get_failover { |
644
|
3
|
|
|
3
|
1
|
10
|
my $self = shift; |
645
|
3
|
|
|
|
|
14
|
return $self->{FAILOVER}; |
646
|
|
|
|
|
|
|
} |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
sub get_fetch_currency { |
649
|
3
|
|
|
3
|
1
|
13
|
my $self = shift; |
650
|
3
|
|
|
|
|
15
|
return $self->{currency}; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub get_required_labels { |
654
|
3
|
|
|
3
|
1
|
15
|
my $self = shift; |
655
|
3
|
|
|
|
|
15
|
return $self->{REQUIRED}; |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
sub get_timeout { |
659
|
4
|
|
|
4
|
1
|
19
|
my $self = shift; |
660
|
4
|
|
|
|
|
17
|
return $self->{TIMEOUT}; |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub get_user_agent { |
664
|
2
|
|
|
2
|
1
|
12
|
my $this = shift; |
665
|
|
|
|
|
|
|
|
666
|
2
|
100
|
|
|
|
12
|
return $this->{UserAgent} if $this->{UserAgent}; |
667
|
|
|
|
|
|
|
|
668
|
1
|
|
|
|
|
2
|
my $ua; |
669
|
|
|
|
|
|
|
|
670
|
1
|
50
|
|
|
|
4
|
if ($USE_EXPERIMENTAL_UA) { |
671
|
0
|
|
|
|
|
0
|
$ua = Finance::Quote::UserAgent->new; |
672
|
|
|
|
|
|
|
} else { |
673
|
1
|
|
|
|
|
7
|
$ua = LWP::UserAgent->new; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
1
|
50
|
|
|
|
280
|
$ua->timeout($this->{TIMEOUT}) if defined($this->{TIMEOUT}); |
677
|
1
|
|
|
|
|
24
|
$ua->env_proxy; |
678
|
|
|
|
|
|
|
|
679
|
1
|
|
|
|
|
352
|
$this->{UserAgent} = $ua; |
680
|
|
|
|
|
|
|
|
681
|
1
|
|
|
|
|
48
|
return $ua; |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sub isoTime { |
685
|
11
|
|
|
11
|
1
|
37
|
my ($self,$timeString) = @_ ; |
686
|
11
|
|
|
|
|
25
|
$timeString =~ tr/ //d ; |
687
|
11
|
|
|
|
|
23
|
$timeString = uc $timeString ; |
688
|
11
|
|
|
|
|
20
|
my $retTime = "00:00"; # return zero time if unparsable input |
689
|
11
|
50
|
|
|
|
79
|
if ($timeString=~m/^(\d+)[\.:UH](\d+) *(AM|am|PM|pm)?/) { |
690
|
11
|
|
|
|
|
64
|
my ($hours,$mins)= ($1-0,$2-0) ; |
691
|
11
|
50
|
66
|
|
|
45
|
$hours-=12 if ($hours==12 && $3 && ($3 =~ /AM/i)); |
|
|
|
33
|
|
|
|
|
692
|
11
|
100
|
100
|
|
|
65
|
$hours+=12 if ($3 && ($3 =~ /PM/i) && ($hours != 12)); |
|
|
|
100
|
|
|
|
|
693
|
11
|
100
|
33
|
|
|
61
|
if ($hours>=0 && $hours<=23 && $mins>=0 && $mins<=59 ) { |
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
694
|
10
|
|
|
|
|
42
|
$retTime = sprintf ("%02d:%02d", $hours, $mins) ; |
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
} |
697
|
11
|
|
|
|
|
108
|
return $retTime; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
sub set_failover { |
701
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
702
|
1
|
|
|
|
|
3
|
$self->{FAILOVER} = shift; |
703
|
|
|
|
|
|
|
} |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub set_fetch_currency { |
706
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
707
|
1
|
|
|
|
|
3
|
$self->{currency} = shift; |
708
|
|
|
|
|
|
|
} |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
sub set_required_labels { |
711
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
712
|
1
|
|
|
|
|
4
|
$self->{REQUIRED} = shift; |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
sub set_timeout { |
716
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
717
|
1
|
|
|
|
|
3
|
$self->{TIMEOUT} = shift; |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
# ======================================================================= |
721
|
|
|
|
|
|
|
# store_date (public object method) |
722
|
|
|
|
|
|
|
# |
723
|
|
|
|
|
|
|
# Given the various pieces of a date, this functions figure out how to |
724
|
|
|
|
|
|
|
# store them in both the pre-existing US date format (mm/dd/yyyy), and |
725
|
|
|
|
|
|
|
# also in the ISO date format (yyyy-mm-dd). This function expects to |
726
|
|
|
|
|
|
|
# be called with the arguments: |
727
|
|
|
|
|
|
|
# |
728
|
|
|
|
|
|
|
# (inforef, symbol_name, data_hash) |
729
|
|
|
|
|
|
|
# |
730
|
|
|
|
|
|
|
# The components of date hash can be any of: |
731
|
|
|
|
|
|
|
# |
732
|
|
|
|
|
|
|
# usdate - A date in mm/dd/yy or mm/dd/yyyy |
733
|
|
|
|
|
|
|
# eurodate - A date in dd/mm/yy or dd/mm/yyyy |
734
|
|
|
|
|
|
|
# isodate - A date in yy-mm-dd or yyyy-mm-dd, yyyy/mm/dd, yyyy.mm.dd, or yyyymmdd |
735
|
|
|
|
|
|
|
# year - The year in yyyy |
736
|
|
|
|
|
|
|
# month - The month in mm or mmm format (i.e. 07 or Jul) |
737
|
|
|
|
|
|
|
# day - The day |
738
|
|
|
|
|
|
|
# today - A flag to indicate todays date should be used. |
739
|
|
|
|
|
|
|
# |
740
|
|
|
|
|
|
|
# The separator for the *date forms is ignored. It can be any |
741
|
|
|
|
|
|
|
# non-alphanumeric character. Any combination of year, month, and day |
742
|
|
|
|
|
|
|
# values can be provided. Missing fields are filled in based upon |
743
|
|
|
|
|
|
|
# today's date. |
744
|
|
|
|
|
|
|
# |
745
|
|
|
|
|
|
|
sub store_date |
746
|
|
|
|
|
|
|
{ |
747
|
13
|
|
|
13
|
1
|
5868
|
my $this = shift; |
748
|
13
|
|
|
|
|
22
|
my $inforef = shift; |
749
|
13
|
|
|
|
|
18
|
my $symbol = shift; |
750
|
13
|
|
|
|
|
22
|
my $piecesref = shift; |
751
|
|
|
|
|
|
|
|
752
|
13
|
|
|
|
|
19
|
my ($year, $month, $day, $this_month, $year_specified); |
753
|
13
|
|
|
|
|
71
|
my %mnames = (jan => 1, feb => 2, mar => 3, apr => 4, may => 5, jun => 6, |
754
|
|
|
|
|
|
|
jul => 7, aug => 8, sep => 9, oct =>10, nov =>11, dec =>12); |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
### store_date symbol: $symbol |
757
|
|
|
|
|
|
|
### store_date pieces: $piecesref |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
# Default to today's date. |
760
|
13
|
|
|
|
|
295
|
($month, $day, $year) = (localtime())[4,3,5]; |
761
|
13
|
|
|
|
|
41
|
$month++; |
762
|
13
|
|
|
|
|
27
|
$year += 1900; |
763
|
13
|
|
|
|
|
21
|
$this_month = $month; |
764
|
13
|
|
|
|
|
19
|
$year_specified = 0; |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
# Process the inputs |
767
|
13
|
50
|
66
|
|
|
52
|
if ((defined $piecesref->{isodate}) && ($piecesref->{isodate})) { |
768
|
3
|
50
|
|
|
|
18
|
if ($piecesref->{isodate} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})$/) { |
769
|
0
|
|
|
|
|
0
|
($year, $month, $day) = ($1, $2, $3); |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
else { |
772
|
3
|
|
|
|
|
19
|
($year, $month, $day) = ($piecesref->{isodate} =~ m|([0-9]{4})\W+(\w+)\W+(\w+)|); |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
3
|
50
|
|
|
|
12
|
$year += 2000 if $year < 100; |
776
|
3
|
|
|
|
|
6
|
$year_specified = 1; |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
### format: printf "isodate %s -> Day %d, Month %s, Year %d\n", $piecesref->{isodate}, $day, $month, $year |
779
|
|
|
|
|
|
|
} |
780
|
|
|
|
|
|
|
|
781
|
13
|
50
|
66
|
|
|
38
|
if ((defined $piecesref->{usdate}) && ($piecesref->{usdate})) { |
782
|
3
|
|
|
|
|
28
|
($month, $day, $year) = ($piecesref->{usdate} =~ /(\w+)\W+(\d+)\W+(\d+)/); |
783
|
3
|
50
|
|
|
|
12
|
$year += 2000 if $year < 100; |
784
|
3
|
|
|
|
|
6
|
$year_specified = 1; |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
### format: printf "usdate %s -> Day %d, Month %s, Year %d\n", $piecesref->{usdate}, $day, $month, $year |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
13
|
50
|
66
|
|
|
36
|
if ((defined $piecesref->{eurodate}) && ($piecesref->{eurodate})) { |
790
|
3
|
|
|
|
|
37
|
($day, $month, $year) = ($piecesref->{eurodate} =~ /(\d+)\W+(\w+)\W+(\d+)/); |
791
|
3
|
50
|
|
|
|
12
|
$year += 2000 if $year < 100; |
792
|
3
|
|
|
|
|
8
|
$year_specified = 1; |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
### format: printf "eurodate %s -> Day %d, Month %s, Year %d\n", $piecesref->{eurodate}, $day, $month, $year |
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
|
797
|
13
|
100
|
|
|
|
26
|
if (defined ($piecesref->{year})) { |
798
|
1
|
|
|
|
|
3
|
$year = $piecesref->{year}; |
799
|
1
|
50
|
|
|
|
5
|
$year += 2000 if $year < 100; |
800
|
1
|
|
|
|
|
2
|
$year_specified = 1; |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
### format: printf "year %s -> Year %d\n", $piecesref->{year}, $year |
803
|
|
|
|
|
|
|
} |
804
|
|
|
|
|
|
|
|
805
|
13
|
100
|
|
|
|
25
|
if (defined ($piecesref->{month})) { |
806
|
3
|
|
|
|
|
9
|
$month = $piecesref->{month}; |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
### format: printf "month %s -> Month %s\n", $piecesref->{month}, $month |
809
|
|
|
|
|
|
|
} |
810
|
|
|
|
|
|
|
|
811
|
13
|
100
|
|
|
|
31
|
if (defined ($piecesref->{day})) { |
812
|
3
|
|
|
|
|
11
|
$day = $piecesref->{day}; |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
### format: printf "day %s -> Day %d\n", $piecesref->{day}, $day |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
|
817
|
13
|
100
|
|
|
|
55
|
$month = $mnames{lc(substr($month,0,3))} if ($month =~ /\D/); |
818
|
13
|
100
|
100
|
|
|
40
|
$year-- if (($year_specified == 0) && ($this_month < $month)); |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
### format: printf "Final Year-Month-Day -> %04d-%02d-%02d\n", $year, $month, $day |
821
|
|
|
|
|
|
|
|
822
|
13
|
|
|
|
|
79
|
$inforef->{$symbol, "date"} = sprintf "%02d/%02d/%04d", $month, $day, $year; |
823
|
13
|
|
|
|
|
99
|
$inforef->{$symbol, "isodate"} = sprintf "%04d-%02d-%02d", $year, $month, $day; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
################################################################################ |
827
|
|
|
|
|
|
|
# |
828
|
|
|
|
|
|
|
# Public Class or Object Methods |
829
|
|
|
|
|
|
|
# |
830
|
|
|
|
|
|
|
################################################################################ |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
# ======================================================================= |
833
|
|
|
|
|
|
|
# Helper function that can scale a field. This is useful because it |
834
|
|
|
|
|
|
|
# handles things like ranges "105.4 - 108.3", and not just straight fields. |
835
|
|
|
|
|
|
|
# |
836
|
|
|
|
|
|
|
# The function takes a string or number to scale, and the factor to scale |
837
|
|
|
|
|
|
|
# it by. For example, scale_field("1023","0.01") would return "10.23". |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
sub scale_field { |
840
|
1
|
50
|
|
1
|
1
|
863
|
shift if ref $_[0]; # Shift off the object, if there is one. |
841
|
|
|
|
|
|
|
|
842
|
1
|
|
|
|
|
3
|
my ($field, $scale) = @_; |
843
|
1
|
|
|
|
|
6
|
my @chunks = split(/([^0-9.])/,$field); |
844
|
|
|
|
|
|
|
|
845
|
1
|
|
|
|
|
5
|
for (my $i=0; $i < @chunks; $i++) { |
846
|
1
|
50
|
|
|
|
6
|
next unless $chunks[$i] =~ /\d/; |
847
|
1
|
|
|
|
|
6
|
$chunks[$i] *= $scale; |
848
|
|
|
|
|
|
|
} |
849
|
1
|
|
|
|
|
15
|
return join("",@chunks); |
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
# ======================================================================= |
853
|
|
|
|
|
|
|
# currency (public object method) |
854
|
|
|
|
|
|
|
# |
855
|
|
|
|
|
|
|
# currency allows the conversion of one currency to another. |
856
|
|
|
|
|
|
|
# |
857
|
|
|
|
|
|
|
# Usage: $quoter->currency("USD","AUD"); |
858
|
|
|
|
|
|
|
# $quoter->currency("15.95 USD","AUD"); |
859
|
|
|
|
|
|
|
# |
860
|
|
|
|
|
|
|
# undef is returned upon error. |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
sub currency { |
863
|
0
|
0
|
|
0
|
1
|
0
|
my $this = ref($_[0]) ? shift : _dummy(); |
864
|
|
|
|
|
|
|
|
865
|
0
|
|
|
|
|
0
|
my ($from_code, $to_code) = @_; |
866
|
0
|
0
|
0
|
|
|
0
|
return unless ($from_code and $to_code); |
867
|
|
|
|
|
|
|
|
868
|
0
|
|
|
|
|
0
|
$from_code =~ s/^\s*(\d*\.?\d*)\s*//; |
869
|
0
|
|
0
|
|
|
0
|
my $amount = $1 || 1; |
870
|
|
|
|
|
|
|
|
871
|
0
|
|
|
|
|
0
|
$to_code = uc($to_code); |
872
|
0
|
|
|
|
|
0
|
$from_code = uc($from_code); |
873
|
|
|
|
|
|
|
|
874
|
0
|
0
|
|
|
|
0
|
return $amount if ($from_code eq $to_code); # Trivial case. |
875
|
|
|
|
|
|
|
|
876
|
0
|
|
|
|
|
0
|
my $ua = $this->get_user_agent; |
877
|
|
|
|
|
|
|
|
878
|
0
|
|
|
|
|
0
|
foreach my $rate (@{$this->{currency_rate_method}}) { |
|
0
|
|
|
|
|
0
|
|
879
|
|
|
|
|
|
|
### rate: ref($rate) |
880
|
0
|
|
|
|
|
0
|
my $final = eval { |
881
|
0
|
|
|
|
|
0
|
my ($from, $to) = $rate->multipliers($ua, $from_code, $to_code); |
882
|
|
|
|
|
|
|
|
883
|
0
|
0
|
0
|
|
|
0
|
die("Failed to find currency rates for $from_code or $to_code") unless defined $from and defined $to; |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
### to weight : $to |
886
|
|
|
|
|
|
|
### from weight: $from |
887
|
|
|
|
|
|
|
### amount : $amount |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
# Is from closest to (amount, to, amount * to)? |
890
|
|
|
|
|
|
|
# (amount * to) / from |
891
|
0
|
|
|
|
|
0
|
my $delta = abs($amount - $from); |
892
|
0
|
|
|
|
|
0
|
my $result = ($amount/$from) * $to; |
893
|
|
|
|
|
|
|
### amount/from -> delta/result : ($delta, $result) |
894
|
0
|
0
|
|
|
|
0
|
if ($delta > abs($to - $from)) { |
895
|
0
|
|
|
|
|
0
|
$delta = abs($to - $from); |
896
|
0
|
|
|
|
|
0
|
$result = ($to/$from) * $amount; |
897
|
|
|
|
|
|
|
### to/from -> delta/result : ($delta, $result) |
898
|
|
|
|
|
|
|
} |
899
|
0
|
0
|
|
|
|
0
|
if ($delta > abs($amount*$to - $from)) { |
900
|
0
|
|
|
|
|
0
|
$delta = abs($amount*$to - $from); |
901
|
0
|
|
|
|
|
0
|
$result = ($amount * $to)/$from; |
902
|
|
|
|
|
|
|
### (amount * to)/from -> delta/result : ($delta, $result) |
903
|
|
|
|
|
|
|
} |
904
|
|
|
|
|
|
|
|
905
|
0
|
|
|
|
|
0
|
return $result; |
906
|
|
|
|
|
|
|
}; |
907
|
|
|
|
|
|
|
|
908
|
0
|
0
|
|
|
|
0
|
if ($@) { |
909
|
|
|
|
|
|
|
### Rate Error: chomp($@), $@ |
910
|
0
|
|
|
|
|
0
|
next; |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
0
|
|
|
|
|
0
|
return $final; |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
|
916
|
0
|
|
|
|
|
0
|
return; |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
# ======================================================================= |
920
|
|
|
|
|
|
|
# currency_lookup (public object method) |
921
|
|
|
|
|
|
|
# |
922
|
|
|
|
|
|
|
# search for available currency codes |
923
|
|
|
|
|
|
|
# |
924
|
|
|
|
|
|
|
# Usage: |
925
|
|
|
|
|
|
|
# $currency = $quoter->currency_lookup(); |
926
|
|
|
|
|
|
|
# $currency = $quoter->currency_lookup( name => "Dollar"); |
927
|
|
|
|
|
|
|
# $currency = $quoter->currency_loopup( country => qw/denmark/i ); |
928
|
|
|
|
|
|
|
# $currency = $q->currency_lookup(country => qr/united states/i, number => 840); |
929
|
|
|
|
|
|
|
# |
930
|
|
|
|
|
|
|
# If more than one lookup parameter is given all must match for |
931
|
|
|
|
|
|
|
# a currency to match. |
932
|
|
|
|
|
|
|
# |
933
|
|
|
|
|
|
|
# undef is returned upon error. |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
sub currency_lookup { |
936
|
6
|
50
|
|
6
|
1
|
6186
|
my $this = ref $_[0] ? shift : _dummy(); |
937
|
|
|
|
|
|
|
|
938
|
6
|
|
|
|
|
22
|
my %params = @_; |
939
|
6
|
|
|
|
|
20
|
my $currencies = Finance::Quote::Currencies::known_currencies(); |
940
|
|
|
|
|
|
|
|
941
|
6
|
|
|
|
|
58
|
my %attributes = map {$_ => 1} map {keys %$_} values %$currencies; |
|
4032
|
|
|
|
|
5568
|
|
|
1008
|
|
|
|
|
1832
|
|
942
|
|
|
|
|
|
|
|
943
|
6
|
|
|
|
|
316
|
for my $key (keys %params ) { |
944
|
7
|
100
|
|
|
|
31
|
if ( ! exists $attributes{$key}) { |
945
|
1
|
|
|
|
|
22
|
warn "Invalid parameter: $key"; |
946
|
1
|
|
|
|
|
9
|
return; |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
|
950
|
5
|
|
|
|
|
34
|
while (my ($tag, $check) = each(%params)) { |
951
|
6
|
|
|
|
|
81
|
$currencies = {map {$_ => $currencies->{$_}} grep {_smart_compare($currencies->{$_}->{$tag}, $check)} keys %$currencies}; |
|
13
|
|
|
|
|
65
|
|
|
675
|
|
|
|
|
8098
|
|
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
|
954
|
5
|
|
|
|
|
27
|
return $currencies; |
955
|
|
|
|
|
|
|
} |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
# ======================================================================= |
958
|
|
|
|
|
|
|
# parse_csv (public object method) |
959
|
|
|
|
|
|
|
# |
960
|
|
|
|
|
|
|
# Grabbed from the Perl Cookbook. Parsing csv isn't as simple as you thought! |
961
|
|
|
|
|
|
|
# |
962
|
|
|
|
|
|
|
sub parse_csv |
963
|
|
|
|
|
|
|
{ |
964
|
0
|
0
|
|
0
|
1
|
0
|
shift if (ref $_[0]); # Shift off the object if we have one. |
965
|
0
|
|
|
|
|
0
|
my $text = shift; # record containing comma-separated values |
966
|
0
|
|
|
|
|
0
|
my @new = (); |
967
|
|
|
|
|
|
|
|
968
|
0
|
|
|
|
|
0
|
push(@new, $+) while $text =~ m{ |
969
|
|
|
|
|
|
|
# the first part groups the phrase inside the quotes. |
970
|
|
|
|
|
|
|
# see explanation of this pattern in MRE |
971
|
|
|
|
|
|
|
"([^\"\\]*(?:\\.[^\"\\]*)*)",? |
972
|
|
|
|
|
|
|
| ([^,]+),? |
973
|
|
|
|
|
|
|
| , |
974
|
|
|
|
|
|
|
}gx; |
975
|
0
|
0
|
|
|
|
0
|
push(@new, undef) if substr($text, -1,1) eq ','; |
976
|
|
|
|
|
|
|
|
977
|
0
|
|
|
|
|
0
|
return @new; # list of values that were comma-separated |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
# ======================================================================= |
981
|
|
|
|
|
|
|
# parse_csv_semicolon (public object method) |
982
|
|
|
|
|
|
|
# |
983
|
|
|
|
|
|
|
# Grabbed from the Perl Cookbook. Parsing csv isn't as simple as you thought! |
984
|
|
|
|
|
|
|
# |
985
|
|
|
|
|
|
|
sub parse_csv_semicolon |
986
|
|
|
|
|
|
|
{ |
987
|
0
|
0
|
|
0
|
1
|
0
|
shift if (ref $_[0]); # Shift off the object if we have one. |
988
|
0
|
|
|
|
|
0
|
my $text = shift; # record containing comma-separated values |
989
|
0
|
|
|
|
|
0
|
my @new = (); |
990
|
|
|
|
|
|
|
|
991
|
0
|
|
|
|
|
0
|
push(@new, $+) while $text =~ m{ |
992
|
|
|
|
|
|
|
# the first part groups the phrase inside the quotes. |
993
|
|
|
|
|
|
|
# see explanation of this pattern in MRE |
994
|
|
|
|
|
|
|
"([^\"\\]*(?:\\.[^\"\\]*)*)";? |
995
|
|
|
|
|
|
|
| ([^;]+);? |
996
|
|
|
|
|
|
|
| ; |
997
|
|
|
|
|
|
|
}gx; |
998
|
0
|
0
|
|
|
|
0
|
push(@new, undef) if substr($text, -1,1) eq ';'; |
999
|
|
|
|
|
|
|
|
1000
|
0
|
|
|
|
|
0
|
return @new; # list of values that were comma-separated |
1001
|
|
|
|
|
|
|
} |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
############################################################################### |
1004
|
|
|
|
|
|
|
# |
1005
|
|
|
|
|
|
|
# Legacy Class Methods |
1006
|
|
|
|
|
|
|
# |
1007
|
|
|
|
|
|
|
############################################################################### |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
sub sources { |
1010
|
0
|
|
|
0
|
1
|
0
|
return get_methods(); |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
sub default_currency_fields { |
1014
|
260
|
|
|
260
|
1
|
634
|
return get_default_currency_fields(); |
1015
|
|
|
|
|
|
|
} |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
############################################################################### |
1018
|
|
|
|
|
|
|
# |
1019
|
|
|
|
|
|
|
# Legacy Class or Object Methods |
1020
|
|
|
|
|
|
|
# |
1021
|
|
|
|
|
|
|
############################################################################### |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
# ======================================================================= |
1024
|
|
|
|
|
|
|
# set_currency (public object method) |
1025
|
|
|
|
|
|
|
# |
1026
|
|
|
|
|
|
|
# set_currency allows information to be requested in the specified |
1027
|
|
|
|
|
|
|
# currency. If called with no arguments then information is returned |
1028
|
|
|
|
|
|
|
# in the default currency. |
1029
|
|
|
|
|
|
|
# |
1030
|
|
|
|
|
|
|
# Requesting stocks in a particular currency increases the time taken, |
1031
|
|
|
|
|
|
|
# and the likelyhood of failure, as additional operations are required |
1032
|
|
|
|
|
|
|
# to fetch the currency conversion information. |
1033
|
|
|
|
|
|
|
# |
1034
|
|
|
|
|
|
|
# This method should only be called from the quote object unless you |
1035
|
|
|
|
|
|
|
# know what you are doing. |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
sub set_currency { |
1038
|
0
|
0
|
0
|
0
|
1
|
|
if (@_ == 1 or !ref($_[0])) { |
1039
|
|
|
|
|
|
|
# Direct or class call - there is no class default currency |
1040
|
0
|
|
|
|
|
|
return; |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
|
1043
|
0
|
|
|
|
|
|
my $this = shift; |
1044
|
0
|
0
|
|
|
|
|
if (defined($_[0])) { |
1045
|
0
|
|
|
|
|
|
$this->set_fetch_currency($_[0]); |
1046
|
|
|
|
|
|
|
} |
1047
|
|
|
|
|
|
|
|
1048
|
0
|
|
|
|
|
|
return $this->get_fetch_currency(); |
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
# ======================================================================= |
1052
|
|
|
|
|
|
|
# Timeout code. If called on a particular object, then it sets |
1053
|
|
|
|
|
|
|
# the timout for that object only. If called as a class method |
1054
|
|
|
|
|
|
|
# (or as Finance::Quote::timeout) then it sets the default timeout |
1055
|
|
|
|
|
|
|
# for all new objects that will be created. |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
sub timeout { |
1058
|
0
|
0
|
0
|
0
|
1
|
|
if (@_ == 1 or !ref($_[0])) { |
1059
|
|
|
|
|
|
|
# Direct or class call |
1060
|
0
|
|
|
|
|
|
Finance::Quote::set_default_timeout(shift); |
1061
|
0
|
|
|
|
|
|
return Finance::Quote::get_default_timeout(); |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
# Otherwise we were called through an object. Yay. |
1065
|
|
|
|
|
|
|
# Set the timeout in this object only. |
1066
|
0
|
|
|
|
|
|
my $this = shift; |
1067
|
0
|
|
|
|
|
|
$this->set_timeout(shift); |
1068
|
0
|
|
|
|
|
|
return $this->get_timeout(); |
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
############################################################################### |
1072
|
|
|
|
|
|
|
# |
1073
|
|
|
|
|
|
|
# Legacy Object Methods |
1074
|
|
|
|
|
|
|
# |
1075
|
|
|
|
|
|
|
############################################################################### |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
# ======================================================================= |
1078
|
|
|
|
|
|
|
# failover (public object method) |
1079
|
|
|
|
|
|
|
# |
1080
|
|
|
|
|
|
|
# This sets/gets whether or not it's acceptable to use failover techniques. |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
sub failover { |
1083
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1084
|
0
|
|
|
|
|
|
my $value = shift; |
1085
|
|
|
|
|
|
|
|
1086
|
0
|
0
|
|
|
|
|
$this->set_failover($value) if defined $value; |
1087
|
0
|
|
|
|
|
|
return $this->get_failover(); |
1088
|
|
|
|
|
|
|
} |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
# ======================================================================= |
1091
|
|
|
|
|
|
|
# require_labels (public object method) |
1092
|
|
|
|
|
|
|
# |
1093
|
|
|
|
|
|
|
# Require_labels indicates which labels are required for lookups. Only methods |
1094
|
|
|
|
|
|
|
# that have registered all the labels specified in the list passed to |
1095
|
|
|
|
|
|
|
# require_labels() will be called. |
1096
|
|
|
|
|
|
|
# |
1097
|
|
|
|
|
|
|
# require_labels takes a list of required labels. When called with no |
1098
|
|
|
|
|
|
|
# arguments, the require list is cleared. |
1099
|
|
|
|
|
|
|
# |
1100
|
|
|
|
|
|
|
# This method always succeeds. |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
sub require_labels { |
1103
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1104
|
0
|
|
|
|
|
|
my @labels = @_; |
1105
|
0
|
|
|
|
|
|
$this->set_required_labels(\@labels); |
1106
|
0
|
|
|
|
|
|
return; |
1107
|
|
|
|
|
|
|
} |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
# ======================================================================= |
1110
|
|
|
|
|
|
|
# user_agent (public object method) |
1111
|
|
|
|
|
|
|
# |
1112
|
|
|
|
|
|
|
# Returns a LWP::UserAgent which conforms to the relevant timeouts, |
1113
|
|
|
|
|
|
|
# proxies, and other settings on the particular Finance::Quote object. |
1114
|
|
|
|
|
|
|
# |
1115
|
|
|
|
|
|
|
# This function is mainly intended to be used by the modules that we load, |
1116
|
|
|
|
|
|
|
# but it can be used by the application to directly play with the |
1117
|
|
|
|
|
|
|
# user-agent settings. |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
sub user_agent { |
1120
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1121
|
0
|
|
|
|
|
|
return $this->get_user_agent(); |
1122
|
|
|
|
|
|
|
} |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
1; |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
__END__ |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
=for comment README.md generated from lib/Finance/Quote.pm |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head1 NAME |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
Finance::Quote - Get stock and mutual fund quotes from various exchanges |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
use Finance::Quote; |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
$q = Finance::Quote->new; |
1139
|
|
|
|
|
|
|
%quotes = $q->fetch("nasdaq", @stocks); |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
This module gets stock quotes from various internet sources all over the world. |
1144
|
|
|
|
|
|
|
Quotes are obtained by constructing a quoter object and using the fetch method |
1145
|
|
|
|
|
|
|
to gather data, which is returned as a two-dimensional hash (or a reference to |
1146
|
|
|
|
|
|
|
such a hash, if called in a scalar context). For example: |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
$q = Finance::Quote->new; |
1149
|
|
|
|
|
|
|
%info = $q->fetch("australia", "CML"); |
1150
|
|
|
|
|
|
|
print "The price of CML is ".$info{"CML", "price"}; |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
The first part of the hash (eg, "CML") is referred to as the stock. |
1153
|
|
|
|
|
|
|
The second part (in this case, "price") is referred to as the label. |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=head2 LABELS |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
When information about a stock is returned, the following standard labels may |
1158
|
|
|
|
|
|
|
be used. Some custom-written modules may use labels not mentioned here. If |
1159
|
|
|
|
|
|
|
you wish to be certain that you obtain a certain set of labels for a given |
1160
|
|
|
|
|
|
|
stock, you can specify that using require_labels(). |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
ask Ask |
1163
|
|
|
|
|
|
|
avg_vol Average Daily Vol |
1164
|
|
|
|
|
|
|
bid Bid |
1165
|
|
|
|
|
|
|
cap Market Capitalization |
1166
|
|
|
|
|
|
|
close Previous Close |
1167
|
|
|
|
|
|
|
currency Currency code for the returned data |
1168
|
|
|
|
|
|
|
date Last Trade Date (MM/DD/YY format) |
1169
|
|
|
|
|
|
|
day_range Day's Range |
1170
|
|
|
|
|
|
|
div Dividend per Share |
1171
|
|
|
|
|
|
|
div_date Dividend Pay Date |
1172
|
|
|
|
|
|
|
div_yield Dividend Yield |
1173
|
|
|
|
|
|
|
eps Earnings per Share |
1174
|
|
|
|
|
|
|
errormsg If success is false, this field may contain the reason why. |
1175
|
|
|
|
|
|
|
ex_div Ex-Dividend Date. |
1176
|
|
|
|
|
|
|
exchange The exchange the information was obtained from. |
1177
|
|
|
|
|
|
|
high Highest trade today |
1178
|
|
|
|
|
|
|
isin International Securities Identification Number |
1179
|
|
|
|
|
|
|
isodate ISO 8601 formatted date |
1180
|
|
|
|
|
|
|
last Last Price |
1181
|
|
|
|
|
|
|
low Lowest trade today |
1182
|
|
|
|
|
|
|
method The module (as could be passed to fetch) which found this information. |
1183
|
|
|
|
|
|
|
name Company or Mutual Fund Name |
1184
|
|
|
|
|
|
|
nav Net Asset Value |
1185
|
|
|
|
|
|
|
net Net Change |
1186
|
|
|
|
|
|
|
open Today's Open |
1187
|
|
|
|
|
|
|
p_change Percent Change from previous day's close |
1188
|
|
|
|
|
|
|
pe P/E Ratio |
1189
|
|
|
|
|
|
|
success Did the stock successfully return information? (true/false) |
1190
|
|
|
|
|
|
|
time Last Trade Time |
1191
|
|
|
|
|
|
|
type The type of equity returned |
1192
|
|
|
|
|
|
|
volume Volume |
1193
|
|
|
|
|
|
|
year_range 52-Week Range |
1194
|
|
|
|
|
|
|
yield Yield (usually 30 day avg) |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
If all stock lookups fail (possibly because of a failed connection) then the |
1197
|
|
|
|
|
|
|
empty list may be returned, or undef in a scalar context. |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
=head1 INSTALLATION |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
Please note that the Github repository is not meant for general users |
1202
|
|
|
|
|
|
|
of Finance::Quote for installation. |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
If you downloaded the Finance-Quote-N.NN.tar.gz tarball from CPAN |
1205
|
|
|
|
|
|
|
(N.NN is the version number, ex: Finance-Quote-1.50.tar.gz), |
1206
|
|
|
|
|
|
|
run the following commands: |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
tar xzf Finance-Quote-1.50.tar.gz |
1209
|
|
|
|
|
|
|
cd Finance-Quote-1.50.tar.gz |
1210
|
|
|
|
|
|
|
perl Makefile.PL |
1211
|
|
|
|
|
|
|
make |
1212
|
|
|
|
|
|
|
make test |
1213
|
|
|
|
|
|
|
make install |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
If you have the CPAN module installed: |
1216
|
|
|
|
|
|
|
Using cpanm (Requires App::cpanminus) |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
cpanm Finance::Quote |
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
or |
1221
|
|
|
|
|
|
|
Using CPAN shell |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
perl -MCPAN -e shell |
1224
|
|
|
|
|
|
|
install Finance::Quote |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
=head1 SUPPORT AND DOCUMENTATION |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
After installing, you can find documentation for this module with the |
1229
|
|
|
|
|
|
|
perldoc command. |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
perldoc Finance::Quote |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
You can also look for information at: |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
=over |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
=item Finance::Quote GitHub project |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
https://github.com/finance-quote/finance-quote |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
=item Search CPAN |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
http://search.cpan.org/dist/Finance-Quote |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
=item The Finance::Quote home page |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
http://finance-quote.sourceforge.net/ |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
=item The Finance::YahooQuote home page |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
http://www.padz.net/~djpadz/YahooQuote/ |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
=item The GnuCash home page |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
http://www.gnucash.org/ |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
=back |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
=head1 PUBLIC CLASS METHODS |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
Finance::Quote implements public class methods for constructing a quoter |
1262
|
|
|
|
|
|
|
object, getting or setting default class values, and for listing available |
1263
|
|
|
|
|
|
|
methods. |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
=head2 new |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
my $q = Finance::Quote->new() |
1268
|
|
|
|
|
|
|
my $q = Finance::Quote->new('-defaults') |
1269
|
|
|
|
|
|
|
my $q = Finance::Quote->new('AEX', 'Fool') |
1270
|
|
|
|
|
|
|
my $q = Finance::Quote->new(timeout => 30) |
1271
|
|
|
|
|
|
|
my $q = Finance::Quote->new('YahooJSON', fetch_currency => 'EUR') |
1272
|
|
|
|
|
|
|
my $q = Finance::Quote->new('alphavantage' => {API_KEY => '...'}) |
1273
|
|
|
|
|
|
|
my $q = Finance::Quote->new('IEXCloud', 'iexcloud' => {API_KEY => '...'}); |
1274
|
|
|
|
|
|
|
my $q = Finance::Quote->new(currency_rates => {order => ['ECB', 'Fixer'], 'fixer' => {API_KEY => '...'}}); |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
Finance::Quote modules access a wide range of sources to provide quotes. A |
1277
|
|
|
|
|
|
|
module provides one or more methods to fetch quotes. One method is usually the |
1278
|
|
|
|
|
|
|
name of the module in lower case. Other methods, if provided, are descriptive |
1279
|
|
|
|
|
|
|
names, such as 'canada', 'nasdaq', or 'nyse'. |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
A Finance::Quote object uses one or more methods to fetch quotes for |
1282
|
|
|
|
|
|
|
securities. |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
C<new> constructs a Finance::Quote object and enables the caller to load only |
1285
|
|
|
|
|
|
|
specific modules, set parameters that control the behavior of the fetch method, |
1286
|
|
|
|
|
|
|
and pass method specific parameters. |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
=over |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=item C<timeout => T> sets the web request timeout to C<T> seconds |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
=item C<failover => B> where C<B> is a boolean value indicating if failover in |
1293
|
|
|
|
|
|
|
fetch is permitted |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
=item C<fetch_currency => C> sets the desired currency code to C<C> for fetch |
1296
|
|
|
|
|
|
|
results |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
=item C<currency_rates => H> configures the order currency rate modules are |
1299
|
|
|
|
|
|
|
consulted for exchange rates and currency rate module options |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
=item C<required_labels => A> sets the required labels for fetch results to |
1302
|
|
|
|
|
|
|
array C<A> |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=item C<<ModuleName>> as a string is the name of a specific |
1305
|
|
|
|
|
|
|
Finance::Quote::Module to load |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
=item C<<methodname> => H> passes hash C<H> to methodname during fetch to |
1308
|
|
|
|
|
|
|
configure the method |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
=back |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
With no arguments, C<new> creates a Finance::Quote object with the default |
1313
|
|
|
|
|
|
|
methods. If the environment variable FQ_LOAD_QUOTELET is set, then the |
1314
|
|
|
|
|
|
|
contents of FQ_LOAD_QUOTELET (split on whitespace) will be used as the argument |
1315
|
|
|
|
|
|
|
list. This allows users to load their own custom modules without having to |
1316
|
|
|
|
|
|
|
change existing code. If any method names are passed to C<new> or the flag |
1317
|
|
|
|
|
|
|
'-defaults' is included in the argument list, then FQ_LOAD_QUOTELET is ignored. |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
When new() is passed one or more class name arguments, an object is created with |
1320
|
|
|
|
|
|
|
only the specified modules loaded. If the first argument is '-defaults', then |
1321
|
|
|
|
|
|
|
the default modules will be loaded first, followed by any other specified |
1322
|
|
|
|
|
|
|
modules. Note that the FQ_LOAD_QUOTELET environment variable must begin with |
1323
|
|
|
|
|
|
|
'-defaults' if you wish the default modules to be loaded. |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
Method names correspond to the Perl module in the Finance::Quote module space. |
1326
|
|
|
|
|
|
|
For example, C<Finance::Quote->new('ASX')> will load the module |
1327
|
|
|
|
|
|
|
Finance::Quote::ASX, which provides the method "asx". |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
Some methods require API keys or have unique options. Passing 'method => HASH' |
1330
|
|
|
|
|
|
|
to new() enables the caller to provide a configuration HASH to the corresponding |
1331
|
|
|
|
|
|
|
method. |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
The key 'currency_rates' configures the Finanace::Quote currency rate |
1334
|
|
|
|
|
|
|
conversion. By default, to maintain backward compatibility, |
1335
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates::AlphaVantage is used for currency conversion. |
1336
|
|
|
|
|
|
|
This end point requires an API key, which can either be set in the environment |
1337
|
|
|
|
|
|
|
or included in the configuration hash. To specify a different primary currency |
1338
|
|
|
|
|
|
|
conversion method or configure fallback methods, include the 'order' key, which |
1339
|
|
|
|
|
|
|
points to an array of Finance::Quote::CurrencyRates module names. |
1340
|
|
|
|
|
|
|
Setting the environment variable FQ_CURRENCY will change the default |
1341
|
|
|
|
|
|
|
endpoint used for currency conversion. |
1342
|
|
|
|
|
|
|
See the documentation for the individual Finance::Quote::CurrencyRates to |
1343
|
|
|
|
|
|
|
learn more. |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
=head2 get_default_currency_fields |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
my @fields = Finance::Quote::get_default_currency_fields(); |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
C<get_default_currency_fields> returns the standard list of fields in a quote |
1350
|
|
|
|
|
|
|
that are automatically converted during currency conversion. Individual modules |
1351
|
|
|
|
|
|
|
may override this list. |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
=head2 get_default_timeout |
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
my $value = Finance::Quote::get_default_timeout(); |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
C<get_default_timeout> returns the current Finance::Quote default timeout in |
1358
|
|
|
|
|
|
|
seconds for web requests. Finance::Quote does not specify a default timeout, |
1359
|
|
|
|
|
|
|
deferring to the underlying user agent for web requests. So this function |
1360
|
|
|
|
|
|
|
will return undef unless C<set_default_timeout> was previously called. |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
=head2 set_default_timeout |
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
Finance::Quote::set_default_timeout(45); |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
C<set_default_timeout> sets the Finance::Quote default timeout to a new value. |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
=head2 get_methods |
1369
|
|
|
|
|
|
|
|
1370
|
|
|
|
|
|
|
my @methods = Finance::Quote::get_methods(); |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
C<get_methods> returns the list of methods that can be passed to C<new> when |
1373
|
|
|
|
|
|
|
creating a quoter object and as the first argument to C<fetch>. |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=head2 get_features |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
my %features = Finance::Quote::get_features(); |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
C<get_features> returns a hash with three keys: quote_methods, quote_modules, and currency_modules. |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
$features{quote_methods} is a hash with key/value pairs of method_name => [array of module names] |
1382
|
|
|
|
|
|
|
$features{quote_modules} is a hash with key/value pairs of module_name => [array of parameter names] |
1383
|
|
|
|
|
|
|
$features{currency_modules} is a hash with key/value pairs of currency_module_name => [array of paramater names] |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
Parameter names are values that the module needs to function, such as API_KEY. Most |
1386
|
|
|
|
|
|
|
modules will have an empty list. Modules with a parameter are configured when creating |
1387
|
|
|
|
|
|
|
the Finance::Quote by passing the argument |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
'module_name_in_lower_case' => {paramter => value} |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
to Finance::Quote->new(). |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
The keys of the $features{currency_modules} hash are the names of currency |
1394
|
|
|
|
|
|
|
modules that can be used for currency conversion and the order in which the |
1395
|
|
|
|
|
|
|
modules are used is controlled by the argument |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
currency_rates => {order => [subset of $features{currency_modules}]} |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
to Finance::Quote->new(). By default, only AlphaVantage in used for |
1400
|
|
|
|
|
|
|
currency conversion, so "order" must be set to use other currency modules. |
1401
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
=head1 PUBLIC OBJECT METHODS |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
=head2 B_to_billions |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
my $value = $q->B_to_billions("20B"); |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
C<B_to_billions> is a utility function that expands a numeric string with a "B" |
1410
|
|
|
|
|
|
|
suffix to the corresponding multiple of 1000000000. |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
=head2 decimal_shiftup |
1413
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
my $value = $q->decimal_shiftup("123.45", 1); # returns 1234.5 |
1415
|
|
|
|
|
|
|
my $value = $q->decimal_shiftup("0.25", 1); # returns 2.5 |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
C<decimal_shiftup> moves a the decimal point in a numeric string the specified |
1418
|
|
|
|
|
|
|
number of places to the right. |
1419
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
=head2 fetch |
1421
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
my %stocks = $q->fetch("alphavantage", "IBM", "MSFT", "LNUX"); |
1423
|
|
|
|
|
|
|
my $hashref = $q->fetch("nasdaq", "IBM", "MSFT", "LNUX"); |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
C<fetch> takes a method as its first argument and the remaining arguments are |
1426
|
|
|
|
|
|
|
treated as securities. If the quoter C<$q> was constructed with a specific |
1427
|
|
|
|
|
|
|
method or methods, then only those methods are available. |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
When called in an array context, a hash is returned. In a scalar context, a |
1430
|
|
|
|
|
|
|
reference to a hash will be returned. The keys for the returned hash are |
1431
|
|
|
|
|
|
|
C<{SECURITY,LABEL}>. For the above example call, C<$stocks{"IBM","high"}> is |
1432
|
|
|
|
|
|
|
the high value for IBM. |
1433
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
$q->get_methods() returns the list of valid methods for quoter object $q. Some |
1435
|
|
|
|
|
|
|
methods specify a specific Finance::Quote module, such as 'alphavantage'. Other |
1436
|
|
|
|
|
|
|
methods are available from multiple Finance::Quote modules, such as 'nasdaq'. |
1437
|
|
|
|
|
|
|
The quoter failover over option determines if multiple modules are consulted |
1438
|
|
|
|
|
|
|
for methods such as 'nasdaq' that more than one implementation. |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
=head2 get_failover |
1441
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
my $failover = $q->get_failover(); |
1443
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
Failover is when the C<fetch> method attempts to retrieve quote information for |
1445
|
|
|
|
|
|
|
a security from alternate sources when the requested method fails. |
1446
|
|
|
|
|
|
|
C<get_failover> returns a boolean value indicating if the quoter object will |
1447
|
|
|
|
|
|
|
use failover or not. |
1448
|
|
|
|
|
|
|
|
1449
|
|
|
|
|
|
|
=head2 set_failover |
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
$q->set_failover(False); |
1452
|
|
|
|
|
|
|
|
1453
|
|
|
|
|
|
|
C<set_failover> sets the failover flag on the quoter object. |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
=head2 get_fetch_currency |
1456
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
my $currency = $q->get_fetch_currency(); |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
C<get_fetch_currency> returns either the desired currency code for the quoter |
1460
|
|
|
|
|
|
|
object or undef if no target currency was set during construction or with the |
1461
|
|
|
|
|
|
|
C<set_fetch_currency> function. |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
=head2 set_fetch_currency |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
$q->set_fetch_currency("FRF"); # Get results in French Francs. |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
C<set_fetch_currency> method is used to request that all information be |
1468
|
|
|
|
|
|
|
returned in the specified currency. Note that this increases the chance |
1469
|
|
|
|
|
|
|
stock-lookup failure, as remote requests must be made to fetch both the stock |
1470
|
|
|
|
|
|
|
information and the currency rates. In order to improve reliability and speed |
1471
|
|
|
|
|
|
|
performance, currency conversion rates are cached and are assumed not to change |
1472
|
|
|
|
|
|
|
for the duration of the Finance::Quote object. |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
See the introduction to this page for information on how to configure the |
1475
|
|
|
|
|
|
|
source of currency conversion rates. |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
=head2 get_required_labels |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
my @labels = $q->get_required_labels(); |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
C<get_required_labels> returns the list of labels that must be populated for a |
1482
|
|
|
|
|
|
|
security quote to be considered valid and returned by C<fetch>. |
1483
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
=head2 set_required_labels |
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
my $labels = ['close', 'isodate', 'last']; |
1487
|
|
|
|
|
|
|
$q->set_required_labels($labels); |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
C<set_required_labels> updates the list of required labels for the quoter object. |
1490
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
=head2 get_timeout |
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
my $timeout = $q->get_timeout(); |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
C<get_timeout> returns the timeout in seconds the quoter object is using for |
1496
|
|
|
|
|
|
|
web requests. |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
=head2 set_timeout |
1499
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
$q->set_timeout(45); |
1501
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
C<set_timeout> updated the timeout in seconds for the quoter object. |
1503
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
=head2 store_date |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
$quoter->store_date(\%info, $stocks, {eurodate => '06/11/2020'}); |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
C<store_date> is used by modules to consistent store date information about |
1509
|
|
|
|
|
|
|
securities. Given the various pieces of a date, this function figures out how to |
1510
|
|
|
|
|
|
|
construct a ISO date (yyyy-mm-dd) and US date (mm/dd/yyyy) and stores those |
1511
|
|
|
|
|
|
|
values in C<%info> for security C<$stock>. |
1512
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
=head2 get_user_agent |
1514
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
my $ua = $q->get_user_agent(); |
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
C<get_user_agent> returns the LWP::UserAgent the quoter object is using for web |
1518
|
|
|
|
|
|
|
requests. |
1519
|
|
|
|
|
|
|
|
1520
|
|
|
|
|
|
|
=head2 isoTime |
1521
|
|
|
|
|
|
|
|
1522
|
|
|
|
|
|
|
$q->isoTime("11:39PM"); # returns "23:39" |
1523
|
|
|
|
|
|
|
$q->isoTime("9:10 AM"); # returns "09:10" |
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
C<isoTime> returns an ISO formatted time. |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
=head1 PUBLIC CLASS OR OBJECT METHODS |
1528
|
|
|
|
|
|
|
|
1529
|
|
|
|
|
|
|
The following methods are available as class methods, but can also be called |
1530
|
|
|
|
|
|
|
from Finance::Quote objects. |
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
=head2 scale_field |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
my $value = Finance::Quote->scale_field('1023', '0.01') |
1535
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
C<scale_field> is a utility function that scales the first argument by the |
1537
|
|
|
|
|
|
|
second argument. In the above example, C<value> is C<'10.23'>. |
1538
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
=head2 currency |
1540
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
my $value = $q->currency('15.95 USD', 'AUD'); |
1542
|
|
|
|
|
|
|
my $value = Finance::Quote->currency('23.45 EUR', 'RUB'); |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
C<currency> converts a value with a currency code suffix to another currency |
1545
|
|
|
|
|
|
|
using the current exchange rate as determined by the |
1546
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates method or methods configured for the quoter $q. |
1547
|
|
|
|
|
|
|
When called as a class method, only Finance::Quote::AlphaVantage is used, which |
1548
|
|
|
|
|
|
|
requires an API key. See the introduction for information on configuring |
1549
|
|
|
|
|
|
|
currency rate conversions and see Finance::Quote::CurrencyRates::AlphaVantage |
1550
|
|
|
|
|
|
|
for information about the API key. |
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
=head2 currency_lookup |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
my $currency = $quoter->currency_lookup(); |
1555
|
|
|
|
|
|
|
my $currency = $quoter->currency_lookup( name => "Caribbean"); |
1556
|
|
|
|
|
|
|
my $currency = $quoter->currency_loopup( country => qw/denmark/i ); |
1557
|
|
|
|
|
|
|
my $currency = $q->currency_lookup(country => qr/united states/i, number => 840); |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
C<currency_lookup> takes zero or more constraints and filters the list of |
1560
|
|
|
|
|
|
|
currencies known to Finance::Quote. It returns a hash reference where the keys |
1561
|
|
|
|
|
|
|
are ISO currency codes and the values are hash references containing metadata |
1562
|
|
|
|
|
|
|
about the currency. |
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
A constraint is a key name and either a scalar or regular expression. A |
1565
|
|
|
|
|
|
|
currency satisfies the constraint if its metadata hash contains the constraint |
1566
|
|
|
|
|
|
|
key and the value of that metadata field matches the regular expression or |
1567
|
|
|
|
|
|
|
contains the constraint value as a substring. If the metadata field is an |
1568
|
|
|
|
|
|
|
array, then it satisfies the constraint if any value in the array satisfies the |
1569
|
|
|
|
|
|
|
constraint. |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
=head2 parse_csv |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
my @list = Finance::Quote::parse_csv($string); |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
C<parse_csv> is a utility function for splitting a comma separated value string |
1576
|
|
|
|
|
|
|
into a list of terms, treating double-quoted strings that contain commas as a |
1577
|
|
|
|
|
|
|
single value. |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
=head2 parse_csv_semicolon |
1580
|
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
my @list = Finance::Quote::parse_csv_semicolon($string); |
1582
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
C<parse_csv> is a utility function for splitting a semicolon separated value string |
1584
|
|
|
|
|
|
|
into a list of terms, treating double-quoted strings that contain semicolons as a |
1585
|
|
|
|
|
|
|
single value. |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
=head1 LEGACY METHODS |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
=head2 default_currency_fields |
1590
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
Replaced with get_default_currency_fields(). |
1592
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
=head2 sources |
1594
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
Replaced with get_methods(). |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
=head2 failover |
1598
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
Replaced with get_failover() and set_failover(). |
1600
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
=head2 require_labels |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
Replaced with get_required_labels() and set_required_labels(). |
1604
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
=head2 user_agent |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
Replaced with get_user_agent(). |
1608
|
|
|
|
|
|
|
|
1609
|
|
|
|
|
|
|
=head2 set_currency |
1610
|
|
|
|
|
|
|
|
1611
|
|
|
|
|
|
|
Replaced with get_fetch_currency() and set_fetch_currency(). |
1612
|
|
|
|
|
|
|
|
1613
|
|
|
|
|
|
|
=head1 ENVIRONMENT |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
Finance::Quote respects all environment that your installed version of |
1616
|
|
|
|
|
|
|
LWP::UserAgent respects. Most importantly, it respects the http_proxy |
1617
|
|
|
|
|
|
|
environment variable. |
1618
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
=head1 BUGS |
1620
|
|
|
|
|
|
|
|
1621
|
|
|
|
|
|
|
The caller cannot control the fetch failover order. |
1622
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
The two-dimensional hash is a somewhat unwieldly method of passing around |
1624
|
|
|
|
|
|
|
information when compared to references |
1625
|
|
|
|
|
|
|
|
1626
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
1627
|
|
|
|
|
|
|
|
1628
|
|
|
|
|
|
|
Copyright 1998, Dj Padzensky |
1629
|
|
|
|
|
|
|
Copyright 1998, 1999 Linas Vepstas |
1630
|
|
|
|
|
|
|
Copyright 2000, Yannick LE NY (update for Yahoo Europe and YahooQuote) |
1631
|
|
|
|
|
|
|
Copyright 2000-2001, Paul Fenwick (updates for ASX, maintenance and release) |
1632
|
|
|
|
|
|
|
Copyright 2000-2001, Brent Neal (update for TIAA-CREF) |
1633
|
|
|
|
|
|
|
Copyright 2000 Volker Stuerzl (DWS) |
1634
|
|
|
|
|
|
|
Copyright 2001 Rob Sessink (AEX support) |
1635
|
|
|
|
|
|
|
Copyright 2001 Leigh Wedding (ASX updates) |
1636
|
|
|
|
|
|
|
Copyright 2001 Tobias Vancura (Fool support) |
1637
|
|
|
|
|
|
|
Copyright 2001 James Treacy (TD Waterhouse support) |
1638
|
|
|
|
|
|
|
Copyright 2008 Erik Colson (isoTime) |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under |
1641
|
|
|
|
|
|
|
the terms of the GNU General Public License as published by the Free Software |
1642
|
|
|
|
|
|
|
Foundation; either version 2 of the License, or (at your option) any later |
1643
|
|
|
|
|
|
|
version. |
1644
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
Currency information fetched through this module is bound by the terms and |
1646
|
|
|
|
|
|
|
conditons of the data source. |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
Other copyrights and conditions may apply to data fetched through this module. |
1649
|
|
|
|
|
|
|
Please refer to the sub-modules for further information. |
1650
|
|
|
|
|
|
|
|
1651
|
|
|
|
|
|
|
=head1 AUTHORS |
1652
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
Dj Padzensky <djpadz@padz.net>, PadzNet, Inc. |
1654
|
|
|
|
|
|
|
Linas Vepstas <linas@linas.org> |
1655
|
|
|
|
|
|
|
Yannick LE NY <y-le-ny@ifrance.com> |
1656
|
|
|
|
|
|
|
Paul Fenwick <pjf@cpan.org> |
1657
|
|
|
|
|
|
|
Brent Neal <brentn@users.sourceforge.net> |
1658
|
|
|
|
|
|
|
Volker Stuerzl <volker.stuerzl@gmx.de> |
1659
|
|
|
|
|
|
|
Keith Refson <Keith.Refson#earth.ox.ac.uk> |
1660
|
|
|
|
|
|
|
Rob Sessink <rob_ses@users.sourceforge.net> |
1661
|
|
|
|
|
|
|
Leigh Wedding <leigh.wedding@telstra.com> |
1662
|
|
|
|
|
|
|
Tobias Vancura <tvancura@altavista.net> |
1663
|
|
|
|
|
|
|
James Treacy <treacy@debian.org> |
1664
|
|
|
|
|
|
|
Bradley Dean <bjdean@bjdean.id.au> |
1665
|
|
|
|
|
|
|
Erik Colson <eco@ecocode.net> |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
The Finance::Quote home page can be found at |
1668
|
|
|
|
|
|
|
http://finance-quote.sourceforge.net/ |
1669
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
The Finance::YahooQuote home page can be found at |
1671
|
|
|
|
|
|
|
http://www.padz.net/~djpadz/YahooQuote/ |
1672
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
The GnuCash home page can be found at |
1674
|
|
|
|
|
|
|
http://www.gnucash.org/ |
1675
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
=head1 SEE ALSO |
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates::AlphaVantage, |
1679
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates::ECB, |
1680
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates::Fixer, |
1681
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates::OpenExchange, |
1682
|
|
|
|
|
|
|
Finance::Quote::CurrencyRates::YahooJSON, |
1683
|
|
|
|
|
|
|
Finance::Quote::AEX, |
1684
|
|
|
|
|
|
|
Finance::Quote::ASEGR, |
1685
|
|
|
|
|
|
|
Finance::Quote::ASX, |
1686
|
|
|
|
|
|
|
Finance::Quote::Bloomberg, |
1687
|
|
|
|
|
|
|
Finance::Quote::BSEIndia, |
1688
|
|
|
|
|
|
|
Finance::Quote::Bourso, |
1689
|
|
|
|
|
|
|
Finance::Quote::BVB, |
1690
|
|
|
|
|
|
|
Finance::Quote::CSE, |
1691
|
|
|
|
|
|
|
Finance::Quote::Cdnfundlibrary, |
1692
|
|
|
|
|
|
|
Finance::Quote::Comdirect, |
1693
|
|
|
|
|
|
|
Finance::Quote::Consorsbank, |
1694
|
|
|
|
|
|
|
Finance::Quote::Currencies, |
1695
|
|
|
|
|
|
|
Finance::Quote::DWS, |
1696
|
|
|
|
|
|
|
Finance::Quote::Deka, |
1697
|
|
|
|
|
|
|
Finance::Quote::FTfunds, |
1698
|
|
|
|
|
|
|
Finance::Quote::Fidelity, |
1699
|
|
|
|
|
|
|
Finance::Quote::Finanzpartner, |
1700
|
|
|
|
|
|
|
Finance::Quote::Fondsweb, |
1701
|
|
|
|
|
|
|
Finance::Quote::Fool, |
1702
|
|
|
|
|
|
|
Finance::Quote::Fundata |
1703
|
|
|
|
|
|
|
Finance::Quote::GoldMoney, |
1704
|
|
|
|
|
|
|
Finance::Quote::GoogleWeb, |
1705
|
|
|
|
|
|
|
Finance::Quote::HU, |
1706
|
|
|
|
|
|
|
Finance::Quote::IEXCloud, |
1707
|
|
|
|
|
|
|
Finance::Quote::IndiaMutual, |
1708
|
|
|
|
|
|
|
Finance::Quote::MorningstarAU, |
1709
|
|
|
|
|
|
|
Finance::Quote::MorningstarCH, |
1710
|
|
|
|
|
|
|
Finance::Quote::MorningstarJP, |
1711
|
|
|
|
|
|
|
Finance::Quote::MorningstarUK, |
1712
|
|
|
|
|
|
|
Finance::Quote::NSEIndia, |
1713
|
|
|
|
|
|
|
Finance::Quote::NZX, |
1714
|
|
|
|
|
|
|
Finance::Quote::OnVista, |
1715
|
|
|
|
|
|
|
Finance::Quote::Oslobors, |
1716
|
|
|
|
|
|
|
Finance::Quote::SEB, |
1717
|
|
|
|
|
|
|
Finance::Quote::SIX, |
1718
|
|
|
|
|
|
|
Finance::Quote::TSP, |
1719
|
|
|
|
|
|
|
Finance::Quote::TMX, |
1720
|
|
|
|
|
|
|
Finance::Quote::Tiaacref, |
1721
|
|
|
|
|
|
|
Finance::Quote::TesouroDireto, |
1722
|
|
|
|
|
|
|
Finance::Quote::TreasuryDirect, |
1723
|
|
|
|
|
|
|
Finance::Quote::Troweprice, |
1724
|
|
|
|
|
|
|
Finance::Quote::TwelveData, |
1725
|
|
|
|
|
|
|
Finance::Quote::Union, |
1726
|
|
|
|
|
|
|
Finance::Quote::YahooJSON, |
1727
|
|
|
|
|
|
|
Finance::Quote::YahooWeb, |
1728
|
|
|
|
|
|
|
Finance::Quote::ZA |
1729
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
You should have received the Finance::Quote hacker's guide with this package. |
1731
|
|
|
|
|
|
|
Please read it if you are interested in adding extra methods to this package. |
1732
|
|
|
|
|
|
|
The latest hacker's guide can also be found on GitHub at |
1733
|
|
|
|
|
|
|
https://github.com/finance-quote/finance-quote/blob/master/Documentation/Hackers-Guide |
1734
|
|
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
=cut |