line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Reserve Bank of Australia setups. |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2016 Kevin Ryde |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# This file is part of Chart. |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# Chart is free software; you can redistribute it and/or modify it under the |
8
|
|
|
|
|
|
|
# terms of the GNU General Public License as published by the Free Software |
9
|
|
|
|
|
|
|
# Foundation; either version 3, or (at your option) any later version. |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# Chart is distributed in the hope that it will be useful, but WITHOUT ANY |
12
|
|
|
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
13
|
|
|
|
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
14
|
|
|
|
|
|
|
# details. |
15
|
|
|
|
|
|
|
# |
16
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License along |
17
|
|
|
|
|
|
|
# with Chart. If not, see <http://www.gnu.org/licenses/>. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
package App::Chart::Suffix::RBA; |
20
|
1
|
|
|
1
|
|
382
|
use 5.010; |
|
1
|
|
|
|
|
3
|
|
21
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
16
|
|
22
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
20
|
|
23
|
1
|
|
|
1
|
|
3
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
44
|
|
24
|
1
|
|
|
1
|
|
215
|
use Date::Calc; |
|
1
|
|
|
|
|
4207
|
|
|
1
|
|
|
|
|
33
|
|
25
|
1
|
|
|
1
|
|
6
|
use List::Util qw(min max); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
99
|
|
26
|
1
|
|
|
1
|
|
259
|
use Set::IntSpan::Fast; |
|
1
|
|
|
|
|
8209
|
|
|
1
|
|
|
|
|
46
|
|
27
|
1
|
|
|
1
|
|
12
|
use Locale::TextDomain ('App-Chart'); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
28
|
|
|
|
|
|
|
|
29
|
1
|
|
|
1
|
|
517
|
use App::Chart; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use App::Chart::Database; |
31
|
|
|
|
|
|
|
use App::Chart::Download; |
32
|
|
|
|
|
|
|
use App::Chart::DownloadHandler; |
33
|
|
|
|
|
|
|
use App::Chart::Sympred; |
34
|
|
|
|
|
|
|
use App::Chart::Latest; |
35
|
|
|
|
|
|
|
use App::Chart::TZ; |
36
|
|
|
|
|
|
|
use App::Chart::Weblink; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# uncomment this to run the ### lines |
39
|
|
|
|
|
|
|
# use Smart::Comments; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Not yet using Finance::Quote::RBA |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my $pred = App::Chart::Sympred::Suffix->new ('.RBA'); |
44
|
|
|
|
|
|
|
App::Chart::TZ->sydney->setup_for_symbol ($pred); |
45
|
|
|
|
|
|
|
App::Chart::setup_source_help |
46
|
|
|
|
|
|
|
($pred, __p('manual-node','Reserve Bank of Australia')); |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
use constant RBA_COPYRIGHT_URL => |
49
|
|
|
|
|
|
|
'http://www.rba.gov.au/copyright/'; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
53
|
|
|
|
|
|
|
# weblink - home page |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
App::Chart::Weblink->new |
56
|
|
|
|
|
|
|
(pred => $pred, |
57
|
|
|
|
|
|
|
name => __('_RBA Home Page'), |
58
|
|
|
|
|
|
|
desc => __('Open web browser at the Reserve Bank of Australia home page'), |
59
|
|
|
|
|
|
|
url => 'http://www.rba.gov.au'); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
63
|
|
|
|
|
|
|
# three day page |
64
|
|
|
|
|
|
|
# |
65
|
|
|
|
|
|
|
# This uses the rates at: |
66
|
|
|
|
|
|
|
# |
67
|
|
|
|
|
|
|
use constant RBA_EXCHANGE_URL => |
68
|
|
|
|
|
|
|
'http://www.rba.gov.au/statistics/frequency/exchange-rates.html'; |
69
|
|
|
|
|
|
|
use constant RBA_EXCHANGE_URL_DAYS => 3; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# would it be a few minutes after 4pm ? |
72
|
|
|
|
|
|
|
sub threeday_available_date_time { |
73
|
|
|
|
|
|
|
return (App::Chart::Download::weekday_date_after_time |
74
|
|
|
|
|
|
|
(16,0, App::Chart::TZ->sydney), |
75
|
|
|
|
|
|
|
'16:01:00'); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub threeday_available_tdate { |
79
|
|
|
|
|
|
|
my ($iso, $time) = threeday_available_date_time(); |
80
|
|
|
|
|
|
|
return App::Chart::Download::iso_to_tdate_floor ($iso); |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub threeday_parse { |
84
|
|
|
|
|
|
|
my ($resp) = @_; |
85
|
|
|
|
|
|
|
my @data = (); |
86
|
|
|
|
|
|
|
my $h = { url => RBA_EXCHANGE_URL, |
87
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
88
|
|
|
|
|
|
|
source => __PACKAGE__, |
89
|
|
|
|
|
|
|
resp => $resp, |
90
|
|
|
|
|
|
|
cover_pred => $pred, |
91
|
|
|
|
|
|
|
data => \@data }; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# mung <tr id="USD"> to add <td>USD</td> so it appears in the TableExtract |
96
|
|
|
|
|
|
|
$content =~ s{<tr>}{<tr><td></td>}ig; |
97
|
|
|
|
|
|
|
$content =~ s{(<tr +id="([^"]*)">)}{$1<td>$2</td>}ig; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
require HTML::TableExtract; |
100
|
|
|
|
|
|
|
my $te = HTML::TableExtract->new |
101
|
|
|
|
|
|
|
(headers => ['Units of foreign currency per'], |
102
|
|
|
|
|
|
|
slice_columns => 0); |
103
|
|
|
|
|
|
|
$te->parse($content); |
104
|
|
|
|
|
|
|
my $ts = $te->first_table_found(); |
105
|
|
|
|
|
|
|
if (! $ts) { die "RBA: html table not found\n"; } |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
my $rows = $ts->rows(); |
108
|
|
|
|
|
|
|
my $lastrow = $#$rows; |
109
|
|
|
|
|
|
|
my $lastcol = $#{$rows->[0]}; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# date like "03 Sep 2007" |
112
|
|
|
|
|
|
|
my @dates; |
113
|
|
|
|
|
|
|
foreach my $c (2 .. $lastcol) { |
114
|
|
|
|
|
|
|
$dates[$c] = App::Chart::Download::Decode_Date_EU_to_iso($rows->[0]->[$c]); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
$h->{'lo_date'} = List::Util::minstr (grep {defined} @dates); |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
foreach my $r (1 .. $lastrow) { |
119
|
|
|
|
|
|
|
my $row = $rows->[$r]; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my $symbol = $row->[0] // next; |
122
|
|
|
|
|
|
|
$symbol =~ s/_.*//; # _4pm on TWI |
123
|
|
|
|
|
|
|
$symbol = "AUD$symbol.RBA"; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my $name = $row->[1]; |
126
|
|
|
|
|
|
|
$name =~ s/ \(4pm\)$//; # 4pm on TWI |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
foreach my $c (2 .. $lastcol) { |
129
|
|
|
|
|
|
|
my $rate = $row->[$c]; |
130
|
|
|
|
|
|
|
# bank holiday columns have "BANK HOLIDAY" with one letter per row or |
131
|
|
|
|
|
|
|
# blank which comes through as undef, skip those |
132
|
|
|
|
|
|
|
next if ! Scalar::Util::looks_like_number($rate); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
push @data, { symbol => $symbol, |
135
|
|
|
|
|
|
|
name => $name, |
136
|
|
|
|
|
|
|
date => $dates[$c], |
137
|
|
|
|
|
|
|
last_time => '16:00:00', |
138
|
|
|
|
|
|
|
close => $rate, |
139
|
|
|
|
|
|
|
currency => substr($symbol,3,3), |
140
|
|
|
|
|
|
|
}; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
return $h; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
148
|
|
|
|
|
|
|
# latest quotes |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
App::Chart::LatestHandler->new |
151
|
|
|
|
|
|
|
(pred => $pred, |
152
|
|
|
|
|
|
|
url_tags_key => 'RBA-latest', |
153
|
|
|
|
|
|
|
proc => \&latest_download, |
154
|
|
|
|
|
|
|
available_date_time => \&threeday_available_date_time); |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub latest_download { |
157
|
|
|
|
|
|
|
my ($symbol_list) = @_; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
App::Chart::Download::status (__('RBA past three days')); |
160
|
|
|
|
|
|
|
my $resp = App::Chart::Download->get (RBA_EXCHANGE_URL, |
161
|
|
|
|
|
|
|
url_tags_key => 'RBA-latest'); |
162
|
|
|
|
|
|
|
App::Chart::Download::write_latest_group (threeday_parse ($resp)); |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
167
|
|
|
|
|
|
|
# historical xls page |
168
|
|
|
|
|
|
|
# |
169
|
|
|
|
|
|
|
# This downloads and parses up the page: |
170
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/historical-data.html |
171
|
|
|
|
|
|
|
# |
172
|
|
|
|
|
|
|
# 2014 to present |
173
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/csv/f11.1-data.csv |
174
|
|
|
|
|
|
|
# 213k or 47k compressed, also byte ranges |
175
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/xls-hist/2014-current.xls |
176
|
|
|
|
|
|
|
# 438k |
177
|
|
|
|
|
|
|
# |
178
|
|
|
|
|
|
|
use constant RBA_HISTORICAL_PAGE_URL => |
179
|
|
|
|
|
|
|
'http://www.rba.gov.au/statistics/hist-exchange-rates/index.html'; |
180
|
|
|
|
|
|
|
# |
181
|
|
|
|
|
|
|
# which offers various xls files for past rates. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub historical_info { |
184
|
|
|
|
|
|
|
require App::Chart::Pagebits; |
185
|
|
|
|
|
|
|
return App::Chart::Pagebits::get |
186
|
|
|
|
|
|
|
(name => __('RBA historical page'), |
187
|
|
|
|
|
|
|
url => RBA_HISTORICAL_PAGE_URL, |
188
|
|
|
|
|
|
|
key => 'rba-historical', |
189
|
|
|
|
|
|
|
freq_days => 1, |
190
|
|
|
|
|
|
|
parse => \&historical_parse); |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub historical_parse { |
194
|
|
|
|
|
|
|
my ($content) = @_; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Eg. |
197
|
|
|
|
|
|
|
# <a href="/statistics/hist-exchange-rates/2007-2009.xls" |
198
|
|
|
|
|
|
|
# target="_blank" title="Link, opening in a new window, to XLS file."> |
199
|
|
|
|
|
|
|
# 2007 to 2009</a> <span class="nonHtml">[XLS 227K]</span> |
200
|
|
|
|
|
|
|
# |
201
|
|
|
|
|
|
|
# because the size is outside the link it doesn't really suit |
202
|
|
|
|
|
|
|
# HTML::LinkExtor / HTML::Parser etc |
203
|
|
|
|
|
|
|
# |
204
|
|
|
|
|
|
|
my @files; |
205
|
|
|
|
|
|
|
while ($content =~ m%href=\"([^\"]*[0-9][0-9][0-9][0-9]\.xls)\" |
206
|
|
|
|
|
|
|
(.*\n){0,3} |
207
|
|
|
|
|
|
|
.*\[XLS\s*([0-9.]+)([MK]) |
208
|
|
|
|
|
|
|
%igmx) { |
209
|
|
|
|
|
|
|
my $link = $1; |
210
|
|
|
|
|
|
|
my $size = $3; |
211
|
|
|
|
|
|
|
my $size_units = uc($4); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
if ($size_units eq 'K') { $size *= 1_000 } |
214
|
|
|
|
|
|
|
if ($size_units eq 'M') { $size *= 1_000_000 } |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my $uri = URI->new_abs($link, RBA_HISTORICAL_PAGE_URL); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# eg per above: 2003-2007.xls |
219
|
|
|
|
|
|
|
# or just: 2007.xls |
220
|
|
|
|
|
|
|
$link =~ /([0-9][0-9][0-9][0-9])((to|-)([0-9][0-9][0-9][0-9]))?\.xls$/ |
221
|
|
|
|
|
|
|
or die "RBA: oops, unrecognised link: $link"; |
222
|
|
|
|
|
|
|
my $lo_year = $1; |
223
|
|
|
|
|
|
|
my $hi_year = $4 || $1; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
push @files, { url => $uri->as_string, |
226
|
|
|
|
|
|
|
cost => $size, |
227
|
|
|
|
|
|
|
lo_year => $lo_year, |
228
|
|
|
|
|
|
|
hi_year => $hi_year, |
229
|
|
|
|
|
|
|
}; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
return { files => \@files }; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
236
|
|
|
|
|
|
|
# download - historical monthly prices |
237
|
|
|
|
|
|
|
# |
238
|
|
|
|
|
|
|
# This parses the monthly rates spreadsheet file from the |
239
|
|
|
|
|
|
|
# RBA_HISTORICAL_PAGE_URL page above, |
240
|
|
|
|
|
|
|
# |
241
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/xls/f11hist-1969-2009.xls |
242
|
|
|
|
|
|
|
# |
243
|
|
|
|
|
|
|
# but only the part from 1983 back is wanted since there's daily data for |
244
|
|
|
|
|
|
|
# 1983 onwards. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
my %monthly_fx_to_currency |
247
|
|
|
|
|
|
|
= ( |
248
|
|
|
|
|
|
|
# 'TWI' # trade weighted index |
249
|
|
|
|
|
|
|
'CR' => 'CNY', # chinese renminbi |
250
|
|
|
|
|
|
|
'JY' => 'JPY', # japanese yen |
251
|
|
|
|
|
|
|
# 'EUR' |
252
|
|
|
|
|
|
|
# 'USD' |
253
|
|
|
|
|
|
|
'SKW' => 'KRW', # South Korean won |
254
|
|
|
|
|
|
|
'UKPS' => 'GBP', # british pound sterling |
255
|
|
|
|
|
|
|
'SD' => 'SGD', # singapore dollar |
256
|
|
|
|
|
|
|
'IRE' => 'INR', # Indian rupee |
257
|
|
|
|
|
|
|
'TB' => 'THB', # Thai baht |
258
|
|
|
|
|
|
|
# 'NZD' |
259
|
|
|
|
|
|
|
'NTD' => 'TWD', # taiwan dollar |
260
|
|
|
|
|
|
|
'MR' => 'MYR', # malaysian ringgit |
261
|
|
|
|
|
|
|
'IR' => 'IDR', # indonesian rupiah |
262
|
|
|
|
|
|
|
'VD' => 'VND', # Vietnamese dong |
263
|
|
|
|
|
|
|
'UAED' => 'AED', # UAE dirham |
264
|
|
|
|
|
|
|
'PNGK' => 'PGK', # PNG kina |
265
|
|
|
|
|
|
|
# 'HKD' # Hong Kong dollar |
266
|
|
|
|
|
|
|
'CD' => 'CAD', # Canadian dollar |
267
|
|
|
|
|
|
|
'SARD' => 'ZAR', # South African rand |
268
|
|
|
|
|
|
|
'SARY' => 'SAR', # Saudi riyal |
269
|
|
|
|
|
|
|
'SF' => 'CHF', # Swiss franc |
270
|
|
|
|
|
|
|
'SK' => 'SEK', # Swedish krona |
271
|
|
|
|
|
|
|
# 'SDR' # special drawing right |
272
|
|
|
|
|
|
|
); |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub monthly_parse { |
275
|
|
|
|
|
|
|
my ($resp, $stop_iso) = @_; |
276
|
|
|
|
|
|
|
### RBA monthly_parse() ... |
277
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
my @data = (); |
280
|
|
|
|
|
|
|
my $h = { source => __PACKAGE__, |
281
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
282
|
|
|
|
|
|
|
data => \@data }; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel; |
285
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel::Utility; |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
my $excel = Spreadsheet::ParseExcel::Workbook->Parse (\$content); |
288
|
|
|
|
|
|
|
my $sheet = $excel->Worksheet (0); |
289
|
|
|
|
|
|
|
### SheetCount: $excel->{'SheetCount'} |
290
|
|
|
|
|
|
|
### Name: $sheet->{'Name'} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
my ($minrow, $maxrow) = $sheet->RowRange; |
293
|
|
|
|
|
|
|
my ($mincol, $maxcol) = $sheet->ColRange; |
294
|
|
|
|
|
|
|
### rows: $minrow, $maxrow |
295
|
|
|
|
|
|
|
### cols: $mincol, $maxcol |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# heading row repeats the filename "F11HIST.XLS" and then the currencies |
298
|
|
|
|
|
|
|
# in columns as say "FXRJY" |
299
|
|
|
|
|
|
|
my $heading_row = List::Util::first { |
300
|
|
|
|
|
|
|
my $cell = $sheet->Cell($_,$mincol); |
301
|
|
|
|
|
|
|
$cell && $cell->Value eq 'F11HIST.XLS' } |
302
|
|
|
|
|
|
|
($minrow .. $maxrow) |
303
|
|
|
|
|
|
|
or die "RBA monthly: headings not found"; |
304
|
|
|
|
|
|
|
### $heading_row |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
my @currencies = map { |
307
|
|
|
|
|
|
|
my $cell = $sheet->Cell($heading_row,$_); |
308
|
|
|
|
|
|
|
my $currency = $cell ? $cell->Value : ''; |
309
|
|
|
|
|
|
|
$currency =~ s/^FXR//; |
310
|
|
|
|
|
|
|
($monthly_fx_to_currency{$currency} || $currency) |
311
|
|
|
|
|
|
|
} ($mincol .. $maxcol); |
312
|
|
|
|
|
|
|
### @currencies |
313
|
|
|
|
|
|
|
### count: scalar(@currencies) |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
my %currency_started; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
ROW: foreach my $row ($heading_row+1 .. $maxrow) { |
318
|
|
|
|
|
|
|
my $datecell = $sheet->Cell($row,0) or next; |
319
|
|
|
|
|
|
|
# seen 'Numeric', but presumably 'Date' is ok |
320
|
|
|
|
|
|
|
if ($datecell->{'Type'} ne 'Numeric' |
321
|
|
|
|
|
|
|
&& $datecell->{'Type'} ne 'Date') { |
322
|
|
|
|
|
|
|
next; # skip blanks at end |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
my $month = Spreadsheet::ParseExcel::Utility::ExcelFmt |
325
|
|
|
|
|
|
|
('yyyy-mm-dd', $datecell->{'Val'}, $excel->{'Flg1904'}); |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
foreach my $col ($mincol+1 .. $maxcol) { |
328
|
|
|
|
|
|
|
my $currency = $currencies[$col-$mincol] or next; |
329
|
|
|
|
|
|
|
my $ratecell = $sheet->Cell($row,$col) or next; |
330
|
|
|
|
|
|
|
my $rate = $ratecell->Value; |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# avoid empty records until the start of data for a given currency is |
333
|
|
|
|
|
|
|
# reached |
334
|
|
|
|
|
|
|
if (! $rate && ! $currency_started{$currency}) { next; } |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
my $symbol = "AUD$currency.RBA"; |
337
|
|
|
|
|
|
|
$currency_started{$currency} = 1; |
338
|
|
|
|
|
|
|
foreach my $date (iso_weekdays_in_month ($month)) { |
339
|
|
|
|
|
|
|
if ($date gt $stop_iso) { last ROW; } |
340
|
|
|
|
|
|
|
push @data, { symbol => $symbol, |
341
|
|
|
|
|
|
|
currency => $currency, |
342
|
|
|
|
|
|
|
date => $date, |
343
|
|
|
|
|
|
|
close => $rate, |
344
|
|
|
|
|
|
|
}; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
return $h; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# return a list of ISO date strings like '2008-09-08' which is all the |
353
|
|
|
|
|
|
|
# weekdays in the month of ISO date $str |
354
|
|
|
|
|
|
|
sub iso_weekdays_in_month { |
355
|
|
|
|
|
|
|
my ($str) = @_; |
356
|
|
|
|
|
|
|
my ($lo_year, $lo_month, undef) = App::Chart::iso_to_ymd ($str); |
357
|
|
|
|
|
|
|
my ($hi_year, $hi_month, undef) = Date::Calc::Add_Delta_YM |
358
|
|
|
|
|
|
|
($lo_year,$lo_month,1, 0,1); |
359
|
|
|
|
|
|
|
my $lo = App::Chart::ymd_to_tdate_ceil ($lo_year, $lo_month, 1); |
360
|
|
|
|
|
|
|
my $hi = App::Chart::ymd_to_tdate_ceil ($hi_year, $hi_month, 1) - 1; |
361
|
|
|
|
|
|
|
return map { App::Chart::tdate_to_iso($_) } ($lo .. $hi); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
366
|
|
|
|
|
|
|
# xls parse |
367
|
|
|
|
|
|
|
# |
368
|
|
|
|
|
|
|
# This parses xls spreadsheet files like |
369
|
|
|
|
|
|
|
# |
370
|
|
|
|
|
|
|
# http://www.rba.gov.au/Statistics/HistoricalExchangeRates/2003to2007.xls |
371
|
|
|
|
|
|
|
# |
372
|
|
|
|
|
|
|
# The files aren't huge (500k upwards) but there's a lot of cells, which |
373
|
|
|
|
|
|
|
# makes Spreadsheet::ParseExcel fairly slow and eat up about 50Mb of core |
374
|
|
|
|
|
|
|
# (as of version 0.32), even before getting to the $h data build. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub xls_parse { |
377
|
|
|
|
|
|
|
my ($resp) = @_; |
378
|
|
|
|
|
|
|
### RBA xls_parse() ... |
379
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
my @data = (); |
382
|
|
|
|
|
|
|
my $h = { source => __PACKAGE__, |
383
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
384
|
|
|
|
|
|
|
data => \@data }; |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel; |
387
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel::Utility; |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
my $excel = Spreadsheet::ParseExcel::Workbook->Parse (\$content); |
390
|
|
|
|
|
|
|
my $sheet = $excel->Worksheet (0); |
391
|
|
|
|
|
|
|
### sheet: $sheet->{'Name'} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my ($minrow, $maxrow) = $sheet->RowRange; |
394
|
|
|
|
|
|
|
my ($mincol, $maxcol) = $sheet->ColRange; |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# heading row "DAILY 4PM", or "Mnemonic" and the currencies in columns |
397
|
|
|
|
|
|
|
my $heading_row = List::Util::first { |
398
|
|
|
|
|
|
|
my $cell = $sheet->Cell($_,$mincol); |
399
|
|
|
|
|
|
|
$cell && ($cell->Value eq 'DAILY 4PM' |
400
|
|
|
|
|
|
|
|| $cell->Value eq 'Mnemonic') } |
401
|
|
|
|
|
|
|
($minrow .. $maxrow) |
402
|
|
|
|
|
|
|
or die "RBA historical: currency code headings row not found"; |
403
|
|
|
|
|
|
|
### heading row: $heading_row |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
foreach my $row ($heading_row+1 .. $maxrow) { |
406
|
|
|
|
|
|
|
my $datecell = $sheet->Cell($row,$mincol) or next; |
407
|
|
|
|
|
|
|
$datecell->{'Type'} eq 'Date' or next; # skip blanks |
408
|
|
|
|
|
|
|
my $date = Spreadsheet::ParseExcel::Utility::ExcelFmt |
409
|
|
|
|
|
|
|
('yyyy-mm-dd', $datecell->{'Val'}, $excel->{'Flg1904'}); |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
foreach my $col ($mincol+1 .. $maxcol) { |
412
|
|
|
|
|
|
|
my $cell = $sheet->Cell($row,$col) |
413
|
|
|
|
|
|
|
or next; # skip lots of blanks |
414
|
|
|
|
|
|
|
my $rate = $cell->Value |
415
|
|
|
|
|
|
|
or next; # skip lots of blanks |
416
|
|
|
|
|
|
|
my $currency = $sheet->Cell($heading_row,$col)->Value; |
417
|
|
|
|
|
|
|
$currency =~ s/^FXR//; # leading "FXR" circa 2012 |
418
|
|
|
|
|
|
|
$currency = ($monthly_fx_to_currency{$currency} || $currency); |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
push @data, { symbol => "AUD$currency.RBA", |
421
|
|
|
|
|
|
|
currency => $currency, |
422
|
|
|
|
|
|
|
date => $date, |
423
|
|
|
|
|
|
|
close => $rate, |
424
|
|
|
|
|
|
|
}; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
return $h; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
434
|
|
|
|
|
|
|
# csv parse |
435
|
|
|
|
|
|
|
# |
436
|
|
|
|
|
|
|
# This parses csv download files like |
437
|
|
|
|
|
|
|
# |
438
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/csv/f11.1-data.csv |
439
|
|
|
|
|
|
|
# |
440
|
|
|
|
|
|
|
# row Title, A$1=USD, Trade-weighted Index May 1970 = 100, A$1=CNY, A$1=JPY, |
441
|
|
|
|
|
|
|
# |
442
|
|
|
|
|
|
|
# row Description,AUD/USD Exchange Rate; see notes for further detail., |
443
|
|
|
|
|
|
|
# Australian Dollar Trade-weighted Index, |
444
|
|
|
|
|
|
|
# AUD/CNY Exchange Rate,AUD/JPY Exchange Rate, |
445
|
|
|
|
|
|
|
# |
446
|
|
|
|
|
|
|
# row Frequency,Daily,Daily,Daily |
447
|
|
|
|
|
|
|
# row Type,Indicative,Indicative, |
448
|
|
|
|
|
|
|
# row Units,USD,Index,CNY,JPY, |
449
|
|
|
|
|
|
|
# row Source,WM/Reuters,RBA,RBA, |
450
|
|
|
|
|
|
|
# row Publication date,17-Feb-2016,17-Feb-2016 |
451
|
|
|
|
|
|
|
# row Series ID,FXRUSD,FXRTWI,FXRCR,FXRJY,FXREUR, |
452
|
|
|
|
|
|
|
# |
453
|
|
|
|
|
|
|
# row 17-Feb-2016,0.7090,60.80,4.6265,80.61, |
454
|
|
|
|
|
|
|
# |
455
|
|
|
|
|
|
|
# csv |
456
|
|
|
|
|
|
|
# AED CAD CHF CNY EUR GBP HKD IDR INR JPY KRW MYR NZD PGK PHP SDR SGD THB TWD TWI USD VND ZAR |
457
|
|
|
|
|
|
|
# |
458
|
|
|
|
|
|
|
# exchange page |
459
|
|
|
|
|
|
|
# AED CAD CHF CNY EUR GBP HKD IDR INR JPY KRW MYR NZD PGK PHP SDR SGD THB TWD TWI USD VND |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
use constant RBA_CURRENT_CSV_URL => |
462
|
|
|
|
|
|
|
'http://www.rba.gov.au/statistics/tables/csv/f11.1-data.csv'; |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub csv_parse { |
465
|
|
|
|
|
|
|
my ($resp) = @_; |
466
|
|
|
|
|
|
|
### RBA csv_parse() ... |
467
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
my @data = (); |
470
|
|
|
|
|
|
|
my $h = { source => __PACKAGE__, |
471
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
472
|
|
|
|
|
|
|
date_format => 'dmy', |
473
|
|
|
|
|
|
|
data => \@data }; |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
my @currencies; |
476
|
|
|
|
|
|
|
foreach my $line (split /\n/, $content) { |
477
|
|
|
|
|
|
|
my @fields = split /,\s*/, $line; |
478
|
|
|
|
|
|
|
my $key = shift @fields // next; |
479
|
|
|
|
|
|
|
### $key |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
if ($key eq 'Title') { |
482
|
|
|
|
|
|
|
@currencies = map { |
483
|
|
|
|
|
|
|
my $field = $_; |
484
|
|
|
|
|
|
|
if ($field eq '') { # empty fields at end of line |
485
|
|
|
|
|
|
|
undef; |
486
|
|
|
|
|
|
|
} elsif ($field =~ /Trade.Weighted.Index/i) { |
487
|
|
|
|
|
|
|
'TWI'; |
488
|
|
|
|
|
|
|
} elsif ($field =~ /A\$1=(.*)/i) { |
489
|
|
|
|
|
|
|
$1; |
490
|
|
|
|
|
|
|
} else { |
491
|
|
|
|
|
|
|
warn "RBA: unrecognised Title field: $field"; |
492
|
|
|
|
|
|
|
undef; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
} @fields; |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
} elsif ($key =~ /\d+-[a-z]+-\d+/i) { |
497
|
|
|
|
|
|
|
# row like 17-Feb-2016,0.7090,60.80,4.6265,80.61, |
498
|
|
|
|
|
|
|
foreach my $i (0 .. $#fields) { |
499
|
|
|
|
|
|
|
my $currency = $currencies[$i] // next; |
500
|
|
|
|
|
|
|
push @data, { symbol => "AUD$currency.RBA", |
501
|
|
|
|
|
|
|
currency => $currency, |
502
|
|
|
|
|
|
|
date => $key, |
503
|
|
|
|
|
|
|
close => $fields[$i], |
504
|
|
|
|
|
|
|
}; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
return $h; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
514
|
|
|
|
|
|
|
# data downloading |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
App::Chart::DownloadHandler->new |
517
|
|
|
|
|
|
|
(name => __('RBA'), |
518
|
|
|
|
|
|
|
pred => $pred, |
519
|
|
|
|
|
|
|
proc => \&download, |
520
|
|
|
|
|
|
|
# backto => \&backto, |
521
|
|
|
|
|
|
|
available_date_time => \&threeday_available_date_time); |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
sub download { |
524
|
|
|
|
|
|
|
my ($symbol_list) = @_; |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
my $lo_tdate = App::Chart::Download::start_tdate_for_update (@$symbol_list); |
527
|
|
|
|
|
|
|
my $hi_tdate = threeday_available_tdate(); |
528
|
|
|
|
|
|
|
### RBA wanting ... |
529
|
|
|
|
|
|
|
### $lo_tdate |
530
|
|
|
|
|
|
|
### $hi_tdate |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
# desired range is <= 3 days, so try the threeday page |
533
|
|
|
|
|
|
|
if ($hi_tdate - $lo_tdate + 1 <= RBA_EXCHANGE_URL_DAYS) { |
534
|
|
|
|
|
|
|
App::Chart::Download::status (__('RBA past three days')); |
535
|
|
|
|
|
|
|
my $resp = App::Chart::Download->get (RBA_EXCHANGE_URL); |
536
|
|
|
|
|
|
|
my $h = threeday_parse ($resp); |
537
|
|
|
|
|
|
|
App::Chart::Download::write_latest_group($h); |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# if $lo_tdate is within the threeday data then write that and done |
540
|
|
|
|
|
|
|
my $threeday_lo_tdate |
541
|
|
|
|
|
|
|
= App::Chart::Download::iso_to_tdate_ceil ($h->{'lo_date'}); |
542
|
|
|
|
|
|
|
if ($threeday_lo_tdate <= $lo_tdate) { |
543
|
|
|
|
|
|
|
App::Chart::Download::write_daily_group ($h); |
544
|
|
|
|
|
|
|
return; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# desired range > 3 days, or the threeday found was in fact not enough, |
549
|
|
|
|
|
|
|
# get the csv |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
my $url = RBA_CURRENT_CSV_URL; |
552
|
|
|
|
|
|
|
require File::Basename; |
553
|
|
|
|
|
|
|
my $filename = File::Basename::basename($url); |
554
|
|
|
|
|
|
|
App::Chart::Download::status (__x('RBA data {filename}', |
555
|
|
|
|
|
|
|
filename => $filename)); |
556
|
|
|
|
|
|
|
my $resp = App::Chart::Download->get($url); |
557
|
|
|
|
|
|
|
my $h = csv_parse($resp); |
558
|
|
|
|
|
|
|
App::Chart::Download::write_daily_group ($h); |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# This code looked at the historical downloads page and chose among the |
561
|
|
|
|
|
|
|
# various XLS files. |
562
|
|
|
|
|
|
|
# |
563
|
|
|
|
|
|
|
# my $info = historical_info(); |
564
|
|
|
|
|
|
|
# my $files = $info->{'files'}; |
565
|
|
|
|
|
|
|
# $files = App::Chart::Download::choose_files ($files, $lo_tdate, $hi_tdate); |
566
|
|
|
|
|
|
|
# $files = [ sort {$a->{'lo_tdate'} <=> $b->{'lo_tdate'}} @$files ]; |
567
|
|
|
|
|
|
|
# foreach my $f (@$files) { |
568
|
|
|
|
|
|
|
# my $url = $f->{'url'}; |
569
|
|
|
|
|
|
|
# require File::Basename; |
570
|
|
|
|
|
|
|
# my $filename = File::Basename::basename($url); |
571
|
|
|
|
|
|
|
# App::Chart::Download::status (__x('RBA data {filename}', |
572
|
|
|
|
|
|
|
# filename => $filename)); |
573
|
|
|
|
|
|
|
# my $resp = App::Chart::Download->get ($url); |
574
|
|
|
|
|
|
|
# my $h = xls_parse ($resp); |
575
|
|
|
|
|
|
|
# App::Chart::Download::write_daily_group ($h); |
576
|
|
|
|
|
|
|
# } |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
sub backto { |
580
|
|
|
|
|
|
|
my ($symbol_list, $backto_tdate) = @_; |
581
|
|
|
|
|
|
|
die "Not implemented"; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
1; |
585
|
|
|
|
|
|
|
__END__ |